Skip to content

Commit 6de502e

Browse files
committed
Add styling for toasts, confirm dialogs and dropdown menus
- ShowToastAction: actionColor, showClose and closeColor, cornerRadius, border, shadows, padding, maxWidth, messageStyle, actionStyle, iconSize and per-screen-size bottomOffsets (bottomOffsetFor). - ConfirmAction: border, shadows, title/message/button text styles, buttonRadius, buttonBorder, buttonShadows, confirmTextColor, cancelColor and cancelTextColor. - DropdownProperties.menuStyle (DropdownMenuStyleModel) for a menu card anchored under the field, and itemImage/itemImageSize/itemImageRadius for a per-item image template such as ${item.image}. - TextStyleModel, a partial text style (family, weight, italic, size, line height, letter spacing, color). All new fields are optional and left out of the JSON when unset, so saved designs keep their look. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F3B1XCZZ7QGvZLUGgSG5H4
1 parent 019dd35 commit 6de502e

15 files changed

Lines changed: 959 additions & 3 deletions

‎CHANGELOG.md‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
## Unreleased
22

3+
- Styling for the runtime's own UI, all optional and left out of the JSON
4+
when unset, so saved designs keep their look:
5+
- `ShowToastAction`: `actionColor`, `showClose`, `closeColor`,
6+
`cornerRadius`, `border` (`BorderSideModel`), `shadows` (drop shadow
7+
`Effect`s), `padding`, `maxWidth`, `messageStyle`, `actionStyle`,
8+
`iconSize`, and `bottomOffsets` (per `ScreenBreakpoint`, e.g.
9+
`{"mobile": 84}`, see `bottomOffsetFor`).
10+
- `ConfirmAction`: `border`, `shadows`, `titleStyle`, `messageStyle`,
11+
`buttonStyle`, `buttonRadius`, `buttonBorder`, `buttonShadows`,
12+
`confirmTextColor`, `cancelColor`, `cancelTextColor`.
13+
- `DropdownProperties`: `menuStyle` (`DropdownMenuStyleModel`: an anchored
14+
menu card's color, border, radius, shadows, offset, sizes, paddings,
15+
selected and hover row colors, selected text style and check mark) and
16+
`itemImage` (a URL or item template such as `${item.image}`),
17+
`itemImageSize`, `itemImageRadius`.
18+
- `TextStyleModel`: a partial text style (font family, weight, italic,
19+
size, line height, letter spacing, color).
320
- `LineChartNode` (`lineChart`) and `LineChartProperties`: a data-driven
421
line chart. The series, domain, bands, grid lines, area bands, reference
522
lines, x ticks, markers and vertical lines are bound through `variables`

‎lib/src/api/models/action/confirm_action.dart‎

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ part 'confirm_action.g.dart';
1111
///
1212
/// By default the dialog is built by the runtime and takes the app's look:
1313
/// its font, primary color and surface come from the layout that shows it.
14-
/// The optional color and radius fields override that look.
14+
/// The optional color, radius, border, shadow and text style fields
15+
/// override that look.
1516
///
1617
/// With a [dialogId] the dialog is that canvas instead, like
1718
/// [ShowDialogAction]. A button in it answers the dialog with a "Navigate
@@ -71,6 +72,44 @@ class ConfirmAction extends ActionModel with Equatable, SerializableMixin {
7172
/// Corner radius of the built-in dialog. Null follows the app's buttons.
7273
final double? cornerRadius;
7374

75+
/// Outline of the built-in dialog. Null draws none.
76+
final BorderSideModel? border;
77+
78+
/// Drop shadows of the built-in dialog, e.g. a hard offset shadow (radius
79+
/// 0). Null keeps the default elevation shadow; an empty list draws none.
80+
final List<Effect>? shadows;
81+
82+
/// Font, size and color of the title. Unset values follow the app.
83+
final TextStyleModel? titleStyle;
84+
85+
/// Font, size and color of the message. Unset values follow the app.
86+
final TextStyleModel? messageStyle;
87+
88+
/// Font, size and color of both buttons' labels. Unset values follow the
89+
/// app; the label colors come from [confirmTextColor] and
90+
/// [cancelTextColor] when this sets none.
91+
final TextStyleModel? buttonStyle;
92+
93+
/// Corner radius of the buttons. Null follows the app's buttons.
94+
final double? buttonRadius;
95+
96+
/// Outline of both buttons. Null draws none.
97+
final BorderSideModel? buttonBorder;
98+
99+
/// Drop shadows of the confirm button, and of the cancel button when it
100+
/// has a [cancelColor]. Null draws none.
101+
final List<Effect>? buttonShadows;
102+
103+
/// Label color of the confirm button. Null picks one that reads on its
104+
/// fill.
105+
final ColorRGBA? confirmTextColor;
106+
107+
/// Fill of the cancel button. Null leaves it a text button.
108+
final ColorRGBA? cancelColor;
109+
110+
/// Label color of the cancel button. Null uses the dialog's text color.
111+
final ColorRGBA? cancelTextColor;
112+
74113
/// The key of the "Navigate back" params that answers a [dialogId] dialog.
75114
static const String resultKey = 'confirmed';
76115

@@ -107,6 +146,17 @@ class ConfirmAction extends ActionModel with Equatable, SerializableMixin {
107146
this.textColor,
108147
this.confirmColor,
109148
this.cornerRadius,
149+
this.border,
150+
this.shadows,
151+
this.titleStyle,
152+
this.messageStyle,
153+
this.buttonStyle,
154+
this.buttonRadius,
155+
this.buttonBorder,
156+
this.buttonShadows,
157+
this.confirmTextColor,
158+
this.cancelColor,
159+
this.cancelTextColor,
110160
super.nonBlocking,
111161
super.enabled,
112162
}) : actions = actions ?? [],
@@ -137,6 +187,28 @@ class ConfirmAction extends ActionModel with Equatable, SerializableMixin {
137187
bool clearConfirmColor = false,
138188
double? cornerRadius,
139189
bool clearCornerRadius = false,
190+
BorderSideModel? border,
191+
bool clearBorder = false,
192+
List<Effect>? shadows,
193+
bool clearShadows = false,
194+
TextStyleModel? titleStyle,
195+
bool clearTitleStyle = false,
196+
TextStyleModel? messageStyle,
197+
bool clearMessageStyle = false,
198+
TextStyleModel? buttonStyle,
199+
bool clearButtonStyle = false,
200+
double? buttonRadius,
201+
bool clearButtonRadius = false,
202+
BorderSideModel? buttonBorder,
203+
bool clearButtonBorder = false,
204+
List<Effect>? buttonShadows,
205+
bool clearButtonShadows = false,
206+
ColorRGBA? confirmTextColor,
207+
bool clearConfirmTextColor = false,
208+
ColorRGBA? cancelColor,
209+
bool clearCancelColor = false,
210+
ColorRGBA? cancelTextColor,
211+
bool clearCancelTextColor = false,
140212
}) => ConfirmAction(
141213
title: title ?? this.title,
142214
message: message ?? this.message,
@@ -155,6 +227,23 @@ class ConfirmAction extends ActionModel with Equatable, SerializableMixin {
155227
textColor: clearTextColor ? null : textColor ?? this.textColor,
156228
confirmColor: clearConfirmColor ? null : confirmColor ?? this.confirmColor,
157229
cornerRadius: clearCornerRadius ? null : cornerRadius ?? this.cornerRadius,
230+
border: clearBorder ? null : border ?? this.border,
231+
shadows: clearShadows ? null : shadows ?? this.shadows,
232+
titleStyle: clearTitleStyle ? null : titleStyle ?? this.titleStyle,
233+
messageStyle: clearMessageStyle ? null : messageStyle ?? this.messageStyle,
234+
buttonStyle: clearButtonStyle ? null : buttonStyle ?? this.buttonStyle,
235+
buttonRadius: clearButtonRadius ? null : buttonRadius ?? this.buttonRadius,
236+
buttonBorder: clearButtonBorder ? null : buttonBorder ?? this.buttonBorder,
237+
buttonShadows: clearButtonShadows
238+
? null
239+
: buttonShadows ?? this.buttonShadows,
240+
confirmTextColor: clearConfirmTextColor
241+
? null
242+
: confirmTextColor ?? this.confirmTextColor,
243+
cancelColor: clearCancelColor ? null : cancelColor ?? this.cancelColor,
244+
cancelTextColor: clearCancelTextColor
245+
? null
246+
: cancelTextColor ?? this.cancelTextColor,
158247
nonBlocking: nonBlocking,
159248
enabled: enabled,
160249
);
@@ -176,6 +265,17 @@ class ConfirmAction extends ActionModel with Equatable, SerializableMixin {
176265
textColor,
177266
confirmColor,
178267
cornerRadius,
268+
border,
269+
shadows,
270+
titleStyle,
271+
messageStyle,
272+
buttonStyle,
273+
buttonRadius,
274+
buttonBorder,
275+
buttonShadows,
276+
confirmTextColor,
277+
cancelColor,
278+
cancelTextColor,
179279
];
180280

181281
/// Creates an instance from a JSON object.

‎lib/src/api/models/action/confirm_action.g.dart‎

Lines changed: 42 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎lib/src/api/models/action/show_toast_action.dart‎

Lines changed: 106 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,9 @@ enum ToastPosition {
5353
/// [actions].
5454
///
5555
/// Its colors come from [style] and the app's theme unless [backgroundColor]
56-
/// or [textColor] are set. Its font is the app's.
56+
/// or [textColor] are set. Its font is the app's unless [messageStyle] or
57+
/// [actionStyle] set one. [cornerRadius], [border], [shadows], [padding] and
58+
/// [maxWidth] shape the toast; [showClose] adds a dismiss (×) button.
5759
@JsonSerializable()
5860
class ShowToastAction extends ActionModel with Equatable, SerializableMixin {
5961
/// The message. Supports variables and formulas, e.g.
@@ -84,13 +86,54 @@ class ShowToastAction extends ActionModel with Equatable, SerializableMixin {
8486
/// Actions to run, in order, when the toast's button is tapped.
8587
final List<ActionModel> actions;
8688

87-
/// Optional icon before the message.
89+
/// Optional icon before the message. Its own color wins over the
90+
/// message's; an image icon shows as is, e.g. a stamp.
8891
final MultiSourceIconModel? icon;
8992

93+
/// Width and height of [icon].
94+
final double iconSize;
95+
9096
/// Distance from the bottom of the screen when [position] is
9197
/// [ToastPosition.bottom].
9298
final double bottomOffset;
9399

100+
/// [bottomOffset] per screen size, e.g. `{"mobile": 84}` to sit above a
101+
/// mobile tab bar. A screen size without an entry uses [bottomOffset].
102+
final Map<ScreenBreakpoint, double> bottomOffsets;
103+
104+
/// Color of the button's label. Null uses the message's color.
105+
final ColorRGBA? actionColor;
106+
107+
/// Whether the toast shows a close (×) button that dismisses it.
108+
final bool showClose;
109+
110+
/// Color of the close button. Null uses the message's color.
111+
final ColorRGBA? closeColor;
112+
113+
/// Corner radius. Null keeps the default: a pill without a button, and the
114+
/// app's button radius (at least 8) with one.
115+
final double? cornerRadius;
116+
117+
/// Outline. Null draws none.
118+
final BorderSideModel? border;
119+
120+
/// Drop shadows, e.g. a hard offset shadow (radius 0). Null keeps the
121+
/// default soft shadow; an empty list draws none.
122+
final List<Effect>? shadows;
123+
124+
/// Space around the content. Null keeps the default.
125+
final EdgeInsetsModel? padding;
126+
127+
/// The widest the toast grows.
128+
final double maxWidth;
129+
130+
/// Font, size and color of the message. Unset values follow the app.
131+
final TextStyleModel? messageStyle;
132+
133+
/// Font, size and color of the button's label. Unset values follow the
134+
/// app.
135+
final TextStyleModel? actionStyle;
136+
94137
/// Creates a new [ShowToastAction].
95138
ShowToastAction({
96139
required this.message,
@@ -102,15 +145,32 @@ class ShowToastAction extends ActionModel with Equatable, SerializableMixin {
102145
this.actionLabel = '',
103146
List<ActionModel>? actions,
104147
this.icon,
148+
this.iconSize = 18,
105149
this.bottomOffset = 104,
150+
Map<ScreenBreakpoint, double>? bottomOffsets,
151+
this.actionColor,
152+
this.showClose = false,
153+
this.closeColor,
154+
this.cornerRadius,
155+
this.border,
156+
this.shadows,
157+
this.padding,
158+
this.maxWidth = 560,
159+
this.messageStyle,
160+
this.actionStyle,
106161
super.nonBlocking,
107162
super.enabled,
108163
}) : actions = actions ?? [],
164+
bottomOffsets = bottomOffsets ?? {},
109165
super(type: ActionType.showToast);
110166

111167
/// Whether the toast shows a button.
112168
bool get hasAction => actionLabel.trim().isNotEmpty;
113169

170+
/// The distance from the bottom of a screen [width] wide.
171+
double bottomOffsetFor(double width) =>
172+
bottomOffsets[ScreenBreakpoint.forWidth(width)] ?? bottomOffset;
173+
114174
@override
115175
List<Object?> get props => [
116176
message,
@@ -122,7 +182,19 @@ class ShowToastAction extends ActionModel with Equatable, SerializableMixin {
122182
actionLabel,
123183
actions,
124184
icon,
185+
iconSize,
125186
bottomOffset,
187+
bottomOffsets,
188+
actionColor,
189+
showClose,
190+
closeColor,
191+
cornerRadius,
192+
border,
193+
shadows,
194+
padding,
195+
maxWidth,
196+
messageStyle,
197+
actionStyle,
126198
];
127199

128200
/// Duplicates this [ShowToastAction] with given data overrides.
@@ -139,7 +211,27 @@ class ShowToastAction extends ActionModel with Equatable, SerializableMixin {
139211
List<ActionModel>? actions,
140212
MultiSourceIconModel? icon,
141213
bool clearIcon = false,
214+
double? iconSize,
142215
double? bottomOffset,
216+
Map<ScreenBreakpoint, double>? bottomOffsets,
217+
ColorRGBA? actionColor,
218+
bool clearActionColor = false,
219+
bool? showClose,
220+
ColorRGBA? closeColor,
221+
bool clearCloseColor = false,
222+
double? cornerRadius,
223+
bool clearCornerRadius = false,
224+
BorderSideModel? border,
225+
bool clearBorder = false,
226+
List<Effect>? shadows,
227+
bool clearShadows = false,
228+
EdgeInsetsModel? padding,
229+
bool clearPadding = false,
230+
double? maxWidth,
231+
TextStyleModel? messageStyle,
232+
bool clearMessageStyle = false,
233+
TextStyleModel? actionStyle,
234+
bool clearActionStyle = false,
143235
}) => ShowToastAction(
144236
message: message ?? this.message,
145237
duration: duration ?? this.duration,
@@ -152,7 +244,19 @@ class ShowToastAction extends ActionModel with Equatable, SerializableMixin {
152244
actionLabel: actionLabel ?? this.actionLabel,
153245
actions: actions ?? this.actions,
154246
icon: clearIcon ? null : icon ?? this.icon,
247+
iconSize: iconSize ?? this.iconSize,
155248
bottomOffset: bottomOffset ?? this.bottomOffset,
249+
bottomOffsets: bottomOffsets ?? this.bottomOffsets,
250+
actionColor: clearActionColor ? null : actionColor ?? this.actionColor,
251+
showClose: showClose ?? this.showClose,
252+
closeColor: clearCloseColor ? null : closeColor ?? this.closeColor,
253+
cornerRadius: clearCornerRadius ? null : cornerRadius ?? this.cornerRadius,
254+
border: clearBorder ? null : border ?? this.border,
255+
shadows: clearShadows ? null : shadows ?? this.shadows,
256+
padding: clearPadding ? null : padding ?? this.padding,
257+
maxWidth: maxWidth ?? this.maxWidth,
258+
messageStyle: clearMessageStyle ? null : messageStyle ?? this.messageStyle,
259+
actionStyle: clearActionStyle ? null : actionStyle ?? this.actionStyle,
156260
nonBlocking: nonBlocking,
157261
enabled: enabled,
158262
);

0 commit comments

Comments
 (0)