Skip to content
Merged
25 changes: 23 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Unreleased
## 6.2.0

### Layout

Expand All @@ -7,11 +7,32 @@
- Row/column `wrap` and `runSpacing`: children wrap onto further runs, like
Flutter's `Wrap`.

### Variables
### Paint and widgets

- `PaintModel.gradientRepeatEvery`: a linear gradient repeats every N px
instead of stretching across the box (hazard stripes, tick patterns).
- Progress bars take `shape: arc`: a gauge with start and sweep angles,
stroke width, tick count and color, a needle and colored zone `bands`
(`ProgressBand`).

### Actions

- `ShowDialogAction.presentation` (dialog, bottom sheet, or adaptive: a
sheet below 600 px) and `alignment` for where a dialog opens.
- `DelayAction.duration`: a variable or formula for the wait, used instead
of `milliseconds` when it gives a number.

### Data

- A variable whose value is a `${= ...}` formula keeps it for every type
(computed variables).

### JSON

- List, grid, page and tab views and spacers parse from minimal node JSON:
fields with constructor defaults (`isScrollable`, `useFlutterListView`,
`allowedTypes`, `deniedTypes`, ...) may be left out.

## 6.1.0

### States and conditions
Expand Down
22 changes: 18 additions & 4 deletions lib/src/api/models/action/delay_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,33 @@ class DelayAction extends ActionModel with Equatable, SerializableMixin {
/// How long to wait, in milliseconds.
final int milliseconds;

/// A variable or formula giving the wait in milliseconds, e.g.
/// `${= networkMode == 'slow' ? 4000 : 900}`. When it gives a number, it is
/// used instead of [milliseconds].
final String? duration;

/// Creates a [DelayAction].
DelayAction({this.milliseconds = 500, super.nonBlocking, super.enabled})
: super(type: ActionType.delay);
DelayAction({
this.milliseconds = 500,
this.duration,
super.nonBlocking,
super.enabled,
}) : super(type: ActionType.delay);

/// Duplicates this action with the given values replaced.
DelayAction copyWith({int? milliseconds}) => DelayAction(
DelayAction copyWith({
int? milliseconds,
String? duration,
bool clearDuration = false,
}) => DelayAction(
milliseconds: milliseconds ?? this.milliseconds,
duration: clearDuration ? null : duration ?? this.duration,
nonBlocking: nonBlocking,
enabled: enabled,
);

@override
List<Object?> get props => [milliseconds];
List<Object?> get props => [milliseconds, duration];

/// Creates an instance from a JSON object.
factory DelayAction.fromJson(Map json) => _$DelayActionFromJson(json);
Expand Down
2 changes: 2 additions & 0 deletions lib/src/api/models/action/delay_action.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions lib/src/api/models/action/show_dialog_action.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,24 @@ class ShowDialogAction extends ActionModel with Equatable, SerializableMixin {
/// Whether to show a close button on the dialog.
final bool showCloseButton;

/// How the canvas opens: a dialog, a bottom sheet, or adaptive (a bottom
/// sheet on phones, below 600 px wide, and a dialog otherwise).
@JsonKey(unknownEnumValue: DialogPresentation.dialog)
final DialogPresentation presentation;

/// Where a dialog sits on the screen, e.g. top right for a menu. Center by
/// default. Bottom sheets always sit at the bottom.
final AlignmentModel alignment;

/// Creates a new [ShowDialogAction].
ShowDialogAction({
required this.destinationId,
Map<String, String>? params,
this.barrierColor = ColorRGBA.black54,
this.barrierDismissible = true,
this.showCloseButton = false,
this.presentation = DialogPresentation.dialog,
this.alignment = AlignmentModel.center,
super.nonBlocking,
super.enabled,
}) : params = {...params ?? {}},
Expand All @@ -43,6 +54,8 @@ class ShowDialogAction extends ActionModel with Equatable, SerializableMixin {
barrierDismissible,
barrierColor,
showCloseButton,
presentation,
alignment,
];

/// Duplicates this [ShowDialogAction] with given data overrides.
Expand All @@ -52,12 +65,16 @@ class ShowDialogAction extends ActionModel with Equatable, SerializableMixin {
bool? barrierDismissible,
ColorRGBA? barrierColor,
bool? showCloseButton,
DialogPresentation? presentation,
AlignmentModel? alignment,
}) => ShowDialogAction(
destinationId: destinationId ?? this.destinationId,
params: {...this.params, ...params ?? {}},
barrierDismissible: barrierDismissible ?? this.barrierDismissible,
barrierColor: barrierColor ?? this.barrierColor,
showCloseButton: showCloseButton ?? this.showCloseButton,
presentation: presentation ?? this.presentation,
alignment: alignment ?? this.alignment,
);

/// Creates a new [ShowDialogAction] instance from a JSON data.
Expand All @@ -71,3 +88,15 @@ class ShowDialogAction extends ActionModel with Equatable, SerializableMixin {
@override
R? accept<R>(ActionVisitor<R> visitor) => visitor.visitShowDialogAction(this);
}

/// How a [ShowDialogAction] presents its canvas.
enum DialogPresentation {
/// A dialog, centered unless the action gives an alignment.
dialog,

/// A modal sheet that slides up from the bottom edge.
bottomSheet,

/// A bottom sheet on phones (narrower than 600 px), a dialog otherwise.
adaptive,
}
22 changes: 22 additions & 0 deletions lib/src/api/models/action/show_dialog_action.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 25 additions & 4 deletions lib/src/api/models/paint.dart
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,13 @@ class PaintModel with Equatable, SerializableMixin, FieldsHolder {
/// neighboring gradient stops.
final List<ColorStop>? gradientStops;

/// When set, a linear gradient repeats every this many logical pixels
/// along its axis instead of stretching across the box, at any box size.
/// Hard stops (two colors at the same position) then make even stripes,
/// e.g. 45° hazard stripes: stops `[a 0, a .5, b .5, b 1]` repeating every
/// 16 px. Null (the default) stretches the gradient across the box.
final double? gradientRepeatEvery;

// For image paint:

/// Affine transform applied to the image.
Expand Down Expand Up @@ -263,7 +270,8 @@ class PaintModel with Equatable, SerializableMixin, FieldsHolder {
imageRepeat = ImageRepeatEnum.noRepeat,
sourceWidth = null,
sourceHeight = null,
assetID = null;
assetID = null,
gradientRepeatEvery = null;

/// Create an Image Paint with only the required properties.
PaintModel.image({
Expand Down Expand Up @@ -293,7 +301,8 @@ class PaintModel with Equatable, SerializableMixin, FieldsHolder {
type = PaintType.image,
gradientTransform = null,
gradientStops = null,
color = null;
color = null,
gradientRepeatEvery = null;

/// Creates [PaintModel] with linear gradient.
PaintModel.linearGradient({
Expand All @@ -302,6 +311,7 @@ class PaintModel with Equatable, SerializableMixin, FieldsHolder {
this.opacity = 1,
this.gradientTransform,
this.gradientStops,
this.gradientRepeatEvery,
this.blendMode = BlendModeC.srcOver,
}) : type = PaintType.gradientLinear,
color = null,
Expand Down Expand Up @@ -345,7 +355,8 @@ class PaintModel with Equatable, SerializableMixin, FieldsHolder {
imageRepeat = ImageRepeatEnum.noRepeat,
sourceWidth = null,
sourceHeight = null,
assetID = null;
assetID = null,
gradientRepeatEvery = null;

/// Creates [PaintModel] with angular gradient.
PaintModel.angularGradient({
Expand All @@ -371,7 +382,8 @@ class PaintModel with Equatable, SerializableMixin, FieldsHolder {
croppedImageURL = null,
sourceWidth = null,
sourceHeight = null,
assetID = null;
assetID = null,
gradientRepeatEvery = null;

/// Creates [PaintModel] with given data.
PaintModel({
Expand All @@ -383,6 +395,7 @@ class PaintModel with Equatable, SerializableMixin, FieldsHolder {
this.blendMode = BlendModeC.srcOver,
this.gradientTransform,
this.gradientStops,
this.gradientRepeatEvery,
this.imageTransform,
this.hasImageBytes = false,
this.downloadUrl,
Expand All @@ -409,6 +422,8 @@ class PaintModel with Equatable, SerializableMixin, FieldsHolder {
BlendModeC? blendMode,
List<num>? gradientTransform,
List<ColorStop>? gradientStops,
double? gradientRepeatEvery,
bool clearGradientRepeat = false,
List<List<num>>? imageTransform,
double? scalingFactor,
String? downloadUrl,
Expand Down Expand Up @@ -437,6 +452,9 @@ class PaintModel with Equatable, SerializableMixin, FieldsHolder {
blendMode: blendMode ?? this.blendMode,
gradientTransform: gradientTransform ?? this.gradientTransform,
gradientStops: gradientStops ?? this.gradientStops,
gradientRepeatEvery: clearGradientRepeat
? null
: gradientRepeatEvery ?? this.gradientRepeatEvery,
imageTransform: imageTransform ?? this.imageTransform,
hasImageBytes: hasImageBytes ?? this.hasImageBytes,
downloadUrl: downloadUrl ?? this.downloadUrl,
Expand Down Expand Up @@ -487,6 +505,7 @@ class PaintModel with Equatable, SerializableMixin, FieldsHolder {
visible: visible,
gradientStops: gradientStops,
gradientTransform: gradientTransform,
gradientRepeatEvery: gradientRepeatEvery,
);
case PaintType.image:
return PaintModel.image(
Expand Down Expand Up @@ -526,6 +545,7 @@ class PaintModel with Equatable, SerializableMixin, FieldsHolder {
if (blendMode != other.blendMode) return false;
if (gradientTransform != other.gradientTransform) return false;
if (gradientStops != other.gradientStops) return false;
if (gradientRepeatEvery != other.gradientRepeatEvery) return false;
if (imageTransform != other.imageTransform) return false;
if (hasImageBytes != other.hasImageBytes) return false;
if (downloadUrl != other.downloadUrl) return false;
Expand Down Expand Up @@ -554,6 +574,7 @@ class PaintModel with Equatable, SerializableMixin, FieldsHolder {
blendMode,
gradientTransform,
gradientStops,
gradientRepeatEvery,
imageTransform,
hasImageBytes,
downloadUrl,
Expand Down
2 changes: 2 additions & 0 deletions lib/src/api/models/paint.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/src/api/nodes/expansion_tile_node.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion lib/src/api/nodes/grid_view_node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,14 @@ class GridViewNode extends SinglePlaceholderNode
};

/// Creates a [GridViewNode] from a JSON object.
factory GridViewNode.fromJson(Map json) => _$GridViewNodeFromJson(json);
factory GridViewNode.fromJson(Map json) => _$GridViewNodeFromJson({
// Node JSON may leave out scrolling fields; read the constructor's.
'isScrollable': true,
'useFlutterListView': false,
'allowedTypes': <String>[],
'deniedTypes': ['GridView'],
...json,
});

@override
Map toJson() => _$GridViewNodeOmitDefaults(this, _$GridViewNodeToJson(this));
Expand Down
9 changes: 8 additions & 1 deletion lib/src/api/nodes/list_view_node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,14 @@ class ListViewNode extends SinglePlaceholderNode
}

/// Creates a [ListViewNode] from a JSON object.
factory ListViewNode.fromJson(Map json) => _$ListViewNodeFromJson(json);
factory ListViewNode.fromJson(Map json) => _$ListViewNodeFromJson({
// Node JSON may leave out scrolling fields; read the constructor's.
'isScrollable': true,
'useFlutterListView': true,
'allowedTypes': <String>[],
'deniedTypes': ['listView'],
...json,
});

@override
Map toJson() => _$ListViewNodeOmitDefaults(this, _$ListViewNodeToJson(this));
Expand Down
9 changes: 8 additions & 1 deletion lib/src/api/nodes/page_view_node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,14 @@ class PageViewNode extends SinglePlaceholderNode
};

/// Creates a [ListViewNode] from a JSON object.
factory PageViewNode.fromJson(Map json) => _$PageViewNodeFromJson(json);
factory PageViewNode.fromJson(Map json) => _$PageViewNodeFromJson({
// Node JSON may leave out scrolling fields; read the constructor's.
'isScrollable': true,
'useFlutterListView': true,
'allowedTypes': <String>[],
'deniedTypes': ['pageView'],
...json,
});

@override
Map toJson() => _$PageViewNodeOmitDefaults(this, _$PageViewNodeToJson(this));
Expand Down
Loading
Loading