Skip to content

Commit 25abb5d

Browse files
authored
[rfw] Add OverflowBar widget and Update ButtonBar widget implementation (#5807)
fixes [[`RFW`] Replace `ButtonBar` with a backwards compatible implementation using `OverflowBar`](flutter/flutter#140381) --- ### Description This adds `OverflowBar` widget to material widgets and updates `ButtonBar` (to be deprecated widget) to use `OverflowBar` underneath for backwards compatibility.
1 parent 724f52d commit 25abb5d

File tree

7 files changed

+227
-17
lines changed

7 files changed

+227
-17
lines changed

packages/rfw/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
## 1.0.20
2+
3+
* Adds OverflowBox material widget.
4+
* Updates ButtonBar material widget implementation.
5+
16
## 1.0.19
7+
28
* Add `DropdownButton` and `ClipRRect` widgets to rfw widget library.
39

410
## 1.0.18
11+
512
* Exposes `WidgetLibrary`s registered in `Runtime`.
613
* Exposes widgets map in `LocalWidgetLibrary`.
714

packages/rfw/lib/src/flutter/material_widgets.dart

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import 'runtime.dart';
3737
/// * [Scaffold]
3838
/// * [TextButton]
3939
/// * [VerticalDivider]
40+
/// * [OverflowBar]
4041
///
4142
/// For each, every parameter is implemented using the same name. Parameters
4243
/// that take structured types are represented using maps, with each named
@@ -50,6 +51,22 @@ import 'runtime.dart';
5051
/// * [VisualDensity] is represented in the manner described in the documentation
5152
/// of the [ArgumentDecoders.visualDensity] method.
5253
///
54+
/// Some features have changed in the underlying Flutter's material library and are
55+
/// therefore no longer supported, including:
56+
///
57+
/// * The [ButtonBar] widget in the Flutter's material library is planned to be
58+
/// deprecated in favor of the [OverflowBar] widget. The [ButtonBar] widget in
59+
/// `rfw` package uses the [OverflowBar] widget internally for backward compatibility.
60+
/// The [ButtonBar] widget in `rfw` package is not deprecated and will continue to
61+
/// be supported. As a result, the following [ButtonBar] parameters are no longer
62+
/// supported:
63+
///
64+
/// * `buttonMinWidth`
65+
/// * `buttonHeight`
66+
/// * `buttonAlignedDropdown`
67+
///
68+
/// It is recommended to use the [OverflowBar] widget.
69+
///
5370
/// Some features are not supported:
5471
///
5572
/// * [AppBar]s do not support [AppBar.bottom], [AppBar.flexibleSpace], and
@@ -123,18 +140,66 @@ Map<String, LocalWidgetBuilder> get _materialWidgetsDefinitions => <String, Loca
123140
);
124141
},
125142

143+
// The ButtonBar widget in package:flutter/material.dart is planned to be deprecated
144+
// in favor of the OverflowBar widget. This ButtonBar implementation uses the
145+
// OverflowBar widget internally for backward compatibility. The ButtonBar
146+
// widget in rfw package is not deprecated and will continue to be supported.
147+
//
148+
// The ButtonBar widget in package:flutter/material.dart has changed over time.
149+
// The following parameters are no longer supported:
150+
// - buttonMinWidth
151+
// - buttonHeight
152+
// - buttonAlignedDropdown
153+
//
154+
// It is recommended to use the OverflowBar widget.
126155
'ButtonBar': (BuildContext context, DataSource source) {
127-
// not implemented: buttonTextTheme
128-
return ButtonBar(
156+
final EdgeInsetsGeometry buttonPadding = ArgumentDecoders.edgeInsets(source, ['buttonPadding']) ?? const EdgeInsets.all(8.0);
157+
final ButtonBarLayoutBehavior layoutBehavior = ArgumentDecoders.enumValue<ButtonBarLayoutBehavior>(ButtonBarLayoutBehavior.values, source, ['layoutBehavior'])
158+
?? ButtonBarLayoutBehavior.padded;
159+
160+
Widget overflowBar = OverflowBar(
129161
alignment: ArgumentDecoders.enumValue<MainAxisAlignment>(MainAxisAlignment.values, source, ['alignment']) ?? MainAxisAlignment.start,
130-
mainAxisSize: ArgumentDecoders.enumValue<MainAxisSize>(MainAxisSize.values, source, ['mainAxisSize']) ?? MainAxisSize.max,
131-
buttonMinWidth: source.v<double>(['buttonMinWidth']),
132-
buttonHeight: source.v<double>(['buttonHeight']),
133-
buttonPadding: ArgumentDecoders.edgeInsets(source, ['buttonPadding']),
134-
buttonAlignedDropdown: source.v<bool>(['buttonAlignedDropdown']) ?? false,
135-
layoutBehavior: ArgumentDecoders.enumValue<ButtonBarLayoutBehavior>(ButtonBarLayoutBehavior.values, source, ['layoutBehavior']),
136-
overflowDirection: ArgumentDecoders.enumValue<VerticalDirection>(VerticalDirection.values, source, ['overflowDirection']),
137-
overflowButtonSpacing: source.v<double>(['overflowButtonSpacing']),
162+
spacing: buttonPadding.horizontal / 2,
163+
overflowDirection: ArgumentDecoders.enumValue<VerticalDirection>(VerticalDirection.values, source, ['overflowDirection']) ?? VerticalDirection.down,
164+
overflowSpacing: source.v<double>(['overflowButtonSpacing']) ?? 0.0,
165+
children: source.childList(['children']),
166+
);
167+
168+
switch (layoutBehavior) {
169+
case ButtonBarLayoutBehavior.padded:
170+
overflowBar = Padding(
171+
padding: EdgeInsets.symmetric(
172+
vertical: 2.0 * (buttonPadding.horizontal / 4.0),
173+
horizontal: buttonPadding.horizontal / 2.0,
174+
),
175+
child: overflowBar,
176+
);
177+
case ButtonBarLayoutBehavior.constrained:
178+
overflowBar = Container(
179+
padding: EdgeInsets.symmetric(horizontal: buttonPadding.horizontal / 2.0),
180+
constraints: const BoxConstraints(minHeight: 52.0),
181+
alignment: Alignment.center,
182+
child: overflowBar,
183+
);
184+
}
185+
186+
if (ArgumentDecoders.enumValue<MainAxisSize>(MainAxisSize.values, source, ['mainAxisSize']) == MainAxisSize.min) {
187+
return IntrinsicWidth(child: overflowBar);
188+
}
189+
190+
return overflowBar;
191+
},
192+
193+
'OverflowBar': (BuildContext context, DataSource source) {
194+
return OverflowBar(
195+
spacing: source.v<double>(['spacing']) ?? 0.0,
196+
alignment: ArgumentDecoders.enumValue<MainAxisAlignment>(MainAxisAlignment.values, source, ['alignment']),
197+
overflowSpacing: source.v<double>(['overflowSpacing']) ?? 0.0,
198+
overflowAlignment: ArgumentDecoders.enumValue<OverflowBarAlignment>(OverflowBarAlignment.values, source, ['overflowAlignment'])
199+
?? OverflowBarAlignment.start,
200+
overflowDirection: ArgumentDecoders.enumValue<VerticalDirection>(VerticalDirection.values, source, ['overflowDirection'])
201+
?? VerticalDirection.down,
202+
textDirection: ArgumentDecoders.enumValue<TextDirection>(TextDirection.values, source, ['textDirection']),
138203
children: source.childList(['children']),
139204
);
140205
},

packages/rfw/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: rfw
22
description: "Remote Flutter widgets: a library for rendering declarative widget description files at runtime."
33
repository: https://github.com/flutter/packages/tree/main/packages/rfw
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+rfw%22
5-
version: 1.0.19
5+
version: 1.0.20
66

77
environment:
88
sdk: ">=3.0.0 <4.0.0"
2.36 KB
Loading
4.01 KB
Loading
4.42 KB
Loading

packages/rfw/test/material_widgets_test.dart

Lines changed: 144 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,18 @@ import 'package:rfw/rfw.dart';
1111
import 'utils.dart';
1212

1313
void main() {
14+
const LibraryName coreName = LibraryName(<String>['core']);
15+
const LibraryName materialName = LibraryName(<String>['material']);
16+
const LibraryName testName = LibraryName(<String>['test']);
17+
18+
Runtime setupRuntime() {
19+
return Runtime()
20+
..update(coreName, createCoreWidgets())
21+
..update(materialName, createMaterialWidgets());
22+
}
23+
1424
testWidgets('Material widgets', (WidgetTester tester) async {
15-
final Runtime runtime = Runtime()
16-
..update(const LibraryName(<String>['core']), createCoreWidgets())
17-
..update(
18-
const LibraryName(<String>['material']), createMaterialWidgets());
25+
final Runtime runtime = setupRuntime();
1926
final DynamicContent data = DynamicContent();
2027
final List<String> eventLog = <String>[];
2128
await tester.pumpWidget(
@@ -24,8 +31,7 @@ void main() {
2431
home: RemoteWidget(
2532
runtime: runtime,
2633
data: data,
27-
widget: const FullyQualifiedWidgetName(
28-
LibraryName(<String>['test']), 'root'),
34+
widget: const FullyQualifiedWidgetName(testName, 'root'),
2935
onEvent: (String eventName, DynamicMap eventArguments) {
3036
eventLog.add('$eventName $eventArguments');
3137
},
@@ -218,4 +224,136 @@ void main() {
218224
skip: !runGoldens,
219225
);
220226
});
227+
228+
testWidgets('OverflowBar configured to resemble ButtonBar',
229+
(WidgetTester tester) async {
230+
final Runtime runtime = setupRuntime();
231+
final DynamicContent data = DynamicContent();
232+
final List<String> eventLog = <String>[];
233+
await tester.pumpWidget(
234+
MaterialApp(
235+
theme: ThemeData(useMaterial3: false),
236+
home: RemoteWidget(
237+
runtime: runtime,
238+
data: data,
239+
widget: const FullyQualifiedWidgetName(testName, 'root'),
240+
onEvent: (String eventName, DynamicMap eventArguments) {
241+
eventLog.add('$eventName $eventArguments');
242+
},
243+
),
244+
),
245+
);
246+
expect(tester.takeException().toString(),
247+
contains('Could not find remote widget named'));
248+
249+
runtime.update(testName, parseLibraryFile('''
250+
import core;
251+
import material;
252+
widget root = Scaffold(
253+
body: Card(
254+
margin: [20.0],
255+
child: Padding(
256+
padding: [8.0],
257+
child: OverflowBar(
258+
spacing: 8.0,
259+
children: [
260+
ElevatedButton(
261+
onPressed: event 'button' { },
262+
child: Text(text: 'Elevated'),
263+
),
264+
OutlinedButton(
265+
onPressed: event 'button' { },
266+
child: Text(text: 'Outlined'),
267+
),
268+
TextButton(
269+
onPressed: event 'button' { },
270+
child: Text(text: 'Text'),
271+
),
272+
],
273+
),
274+
),
275+
),
276+
);
277+
'''));
278+
await tester.pump();
279+
await expectLater(
280+
find.byType(RemoteWidget),
281+
matchesGoldenFile(
282+
'goldens/material_test.overflow_bar_resembles_button_bar.png'),
283+
skip: !runGoldens,
284+
);
285+
});
286+
287+
testWidgets('Implement OverflowBar properties', (WidgetTester tester) async {
288+
final Runtime runtime = setupRuntime();
289+
final DynamicContent data = DynamicContent();
290+
final List<String> eventLog = <String>[];
291+
await tester.pumpWidget(
292+
MaterialApp(
293+
theme: ThemeData(useMaterial3: false),
294+
home: RemoteWidget(
295+
runtime: runtime,
296+
data: data,
297+
widget: const FullyQualifiedWidgetName(testName, 'root'),
298+
onEvent: (String eventName, DynamicMap eventArguments) {
299+
eventLog.add('$eventName $eventArguments');
300+
},
301+
),
302+
),
303+
);
304+
expect(tester.takeException().toString(),
305+
contains('Could not find remote widget named'));
306+
307+
addTearDown(() async {
308+
await tester.binding.setSurfaceSize(null);
309+
});
310+
311+
runtime.update(testName, parseLibraryFile('''
312+
import core;
313+
import material;
314+
widget root = Scaffold(
315+
body: Center(
316+
child: OverflowBar(
317+
spacing: 16.0,
318+
alignment: 'end',
319+
overflowSpacing: 4.0,
320+
overflowAlignment: 'center',
321+
overflowDirection: 'up',
322+
children: [
323+
ElevatedButton(
324+
onPressed: event 'button' { },
325+
child: Text(text: 'Elevated'),
326+
),
327+
OutlinedButton(
328+
onPressed: event 'button' { },
329+
child: Text(text: 'Outlined'),
330+
),
331+
TextButton(
332+
onPressed: event 'button' { },
333+
child: Text(text: 'Text'),
334+
),
335+
],
336+
),
337+
),
338+
);
339+
'''));
340+
await tester.pump();
341+
342+
await expectLater(
343+
find.byType(RemoteWidget),
344+
matchesGoldenFile('goldens/material_test.overflow_bar_properties.png'),
345+
skip: !runGoldens,
346+
);
347+
348+
// Update the surface size for OverflowBar to overflow.
349+
await tester.binding.setSurfaceSize(const Size(200.0, 600.0));
350+
await tester.pump();
351+
352+
await expectLater(
353+
find.byType(RemoteWidget),
354+
matchesGoldenFile(
355+
'goldens/material_test.overflow_bar_properties.overflow.png'),
356+
skip: !runGoldens,
357+
);
358+
});
221359
}

0 commit comments

Comments
 (0)