Skip to content

Commit

Permalink
[rfw] Add support for widget builders (#5907)
Browse files Browse the repository at this point in the history
This PR adds support for widget builders.

flutter/flutter#141658
  • Loading branch information
tugorez authored Mar 6, 2024
1 parent ceb3dfd commit 6701c9e
Show file tree
Hide file tree
Showing 9 changed files with 1,285 additions and 87 deletions.
3 changes: 3 additions & 0 deletions packages/rfw/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 1.0.25
* Adds support for wildget builders.

## 1.0.24

* Adds `InkResponse` material widget.
Expand Down
25 changes: 25 additions & 0 deletions packages/rfw/lib/src/dart/binary.dart
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,8 @@ const int _msEvent = 0x0E;
const int _msSwitch = 0x0F;
const int _msDefault = 0x10;
const int _msSetState = 0x11;
const int _msWidgetBuilder = 0x12;
const int _msWidgetBuilderArgReference = 0x13;

/// API for decoding Remote Flutter Widgets binary blobs.
///
Expand Down Expand Up @@ -453,6 +455,10 @@ class _BlobDecoder {
return _readSwitch();
case _msSetState:
return SetStateHandler(StateReference(_readPartList()), _readArgument());
case _msWidgetBuilder:
return _readWidgetBuilder();
case _msWidgetBuilderArgReference:
return WidgetBuilderArgReference(_readString(), _readPartList());
default:
return _parseValue(type, _readArgument);
}
Expand All @@ -468,6 +474,16 @@ class _BlobDecoder {
return ConstructorCall(name, _readMap(_readArgument)!);
}

WidgetBuilderDeclaration _readWidgetBuilder() {
final String argumentName = _readString();
final int type = _readByte();
if (type != _msWidget && type != _msSwitch) {
throw FormatException('Unrecognized data type 0x${type.toRadixString(16).toUpperCase().padLeft(2, "0")} while decoding widget builder blob.');
}
final BlobNode widget = type == _msWidget ? _readWidget() : _readSwitch();
return WidgetBuilderDeclaration(argumentName, widget);
}

WidgetDeclaration _readDeclaration() {
final String name = _readString();
final DynamicMap? initialState = _readMap(readValue, nullIfEmpty: true);
Expand Down Expand Up @@ -613,6 +629,10 @@ class _BlobEncoder {
bytes.addByte(_msWidget);
_writeString(value.name);
_writeMap(value.arguments, _writeArgument);
} else if (value is WidgetBuilderDeclaration) {
bytes.addByte(_msWidgetBuilder);
_writeString(value.argumentName);
_writeArgument(value.widget);
} else if (value is ArgsReference) {
bytes.addByte(_msArgsReference);
_writeInt64(value.parts.length);
Expand All @@ -621,6 +641,11 @@ class _BlobEncoder {
bytes.addByte(_msDataReference);
_writeInt64(value.parts.length);
value.parts.forEach(_writePart);
} else if (value is WidgetBuilderArgReference) {
bytes.addByte(_msWidgetBuilderArgReference);
_writeString(value.argumentName);
_writeInt64(value.parts.length);
value.parts.forEach(_writePart);
} else if (value is LoopReference) {
bytes.addByte(_msLoopReference);
_writeInt64(value.loop);
Expand Down
47 changes: 47 additions & 0 deletions packages/rfw/lib/src/dart/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,28 @@ class ConstructorCall extends BlobNode {
String toString() => '$name($arguments)';
}

/// Representation of functions that return widgets in Remote Flutter Widgets library blobs.
class WidgetBuilderDeclaration extends BlobNode {
/// Represents a callback that takes a single argument [argumentName] and returns the [widget].
const WidgetBuilderDeclaration(this.argumentName, this.widget);

/// The callback single argument name.
///
/// In `Builder(builder: (scope) => Container());`, [argumentName] is "scope".
final String argumentName;

/// The widget that will be returned when the builder callback is called.
///
/// This is usually a [ConstructorCall], but may be a [Switch] (so long as
/// that [Switch] resolves to a [ConstructorCall]. Other values (or a [Switch]
/// that does not resolve to a constructor call) will result in an
/// [ErrorWidget] being used.
final BlobNode widget;

@override
String toString() => '($argumentName) => $widget';
}

/// Base class for various kinds of references in the RFW data structures.
abstract class Reference extends BlobNode {
/// Abstract const constructor. This constructor enables subclasses to provide
Expand Down Expand Up @@ -534,6 +556,31 @@ class DataReference extends Reference {
String toString() => 'data.${parts.join(".")}';
}

/// Reference to the single argument of type [DynamicMap] passed into the widget builder.
///
/// This class is used to represent references to a function argument.
/// In `(scope) => Container(width: scope.width)`, this represents "scope.width".
///
/// See also:
///
/// * [WidgetBuilderDeclaration], which represents a widget builder definition.
class WidgetBuilderArgReference extends Reference {
/// Wraps the given [argumentName] and [parts] as a [WidgetBuilderArgReference].
///
/// The parts must not be mutated after the object is created.
const WidgetBuilderArgReference(this.argumentName, super.parts);

/// A reference to a [WidgetBuilderDeclaration.argumentName].
///
/// In `Builder(builder: (scope) => Text(text: scope.result.text));`,
/// "scope.result.text" is the [WidgetBuilderArgReference].
/// The [argumentName] is "scope" and its [parts] are `["result", "text"]`.
final String argumentName;

@override
String toString() => '$argumentName.${parts.join('.')}';
}

/// Unbound reference to a [Loop].
class LoopReference extends Reference {
/// Wraps the given [loop] and [parts] as a [LoopReference].
Expand Down
Loading

0 comments on commit 6701c9e

Please sign in to comment.