Skip to content

[rfw] Add OverflowBar widget and Update ButtonBar widget implementation #5807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/rfw/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
## 1.0.20

* Adds OverflowBox material widget.
* Updates ButtonBar material widget implementation.

## 1.0.19

* Add `DropdownButton` and `ClipRRect` widgets to rfw widget library.

## 1.0.18

* Exposes `WidgetLibrary`s registered in `Runtime`.
* Exposes widgets map in `LocalWidgetLibrary`.

Expand Down
85 changes: 75 additions & 10 deletions packages/rfw/lib/src/flutter/material_widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import 'runtime.dart';
/// * [Scaffold]
/// * [TextButton]
/// * [VerticalDivider]
/// * [OverflowBar]
///
/// For each, every parameter is implemented using the same name. Parameters
/// that take structured types are represented using maps, with each named
Expand All @@ -50,6 +51,22 @@ import 'runtime.dart';
/// * [VisualDensity] is represented in the manner described in the documentation
/// of the [ArgumentDecoders.visualDensity] method.
///
/// Some features have changed in the underlying Flutter's material library and are
/// therefore no longer supported, including:
///
/// * The [ButtonBar] widget in the Flutter's material library is planned to be
/// deprecated in favor of the [OverflowBar] widget. The [ButtonBar] widget in
/// `rfw` package uses the [OverflowBar] widget internally for backward compatibility.
/// The [ButtonBar] widget in `rfw` package is not deprecated and will continue to
/// be supported. As a result, the following [ButtonBar] parameters are no longer
/// supported:
///
/// * `buttonMinWidth`
/// * `buttonHeight`
/// * `buttonAlignedDropdown`
///
/// It is recommended to use the [OverflowBar] widget.
///
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this all needs to be markdown with hyperlinks and so on (sorry if i wasn't clear in the earlier comment).

What I mean is e.g. ButtonBar needs to be [ButtonBar], package:flutter/material.dart needs to be whatever the magic incantation is to reference the material library (maybe just [material]?), rfw needs to either be in backticks or uppercase, the parameters in the list should be in backticks, etc.

Copy link
Member Author

@TahaTesser TahaTesser Jan 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh my bad. Thanks for the suggestion!

package:flutter/material.dart can be simply referenced as "Flutter's material library". We reference like this in the framework too in various classes

/// Some features are not supported:
///
/// * [AppBar]s do not support [AppBar.bottom], [AppBar.flexibleSpace], and
Expand Down Expand Up @@ -123,18 +140,66 @@ Map<String, LocalWidgetBuilder> get _materialWidgetsDefinitions => <String, Loca
);
},

// The ButtonBar widget in package:flutter/material.dart is planned to be deprecated
// in favor of the OverflowBar widget. This ButtonBar implementation uses the
// OverflowBar widget internally for backward compatibility. The ButtonBar
// widget in rfw package is not deprecated and will continue to be supported.
//
// The ButtonBar widget in package:flutter/material.dart has changed over time.
// The following parameters are no longer supported:
// - buttonMinWidth
// - buttonHeight
// - buttonAlignedDropdown
//
// It is recommended to use the OverflowBar widget.
'ButtonBar': (BuildContext context, DataSource source) {
// not implemented: buttonTextTheme
return ButtonBar(
final EdgeInsetsGeometry buttonPadding = ArgumentDecoders.edgeInsets(source, ['buttonPadding']) ?? const EdgeInsets.all(8.0);
final ButtonBarLayoutBehavior layoutBehavior = ArgumentDecoders.enumValue<ButtonBarLayoutBehavior>(ButtonBarLayoutBehavior.values, source, ['layoutBehavior'])
?? ButtonBarLayoutBehavior.padded;

Widget overflowBar = OverflowBar(
alignment: ArgumentDecoders.enumValue<MainAxisAlignment>(MainAxisAlignment.values, source, ['alignment']) ?? MainAxisAlignment.start,
mainAxisSize: ArgumentDecoders.enumValue<MainAxisSize>(MainAxisSize.values, source, ['mainAxisSize']) ?? MainAxisSize.max,
buttonMinWidth: source.v<double>(['buttonMinWidth']),
buttonHeight: source.v<double>(['buttonHeight']),
buttonPadding: ArgumentDecoders.edgeInsets(source, ['buttonPadding']),
buttonAlignedDropdown: source.v<bool>(['buttonAlignedDropdown']) ?? false,
layoutBehavior: ArgumentDecoders.enumValue<ButtonBarLayoutBehavior>(ButtonBarLayoutBehavior.values, source, ['layoutBehavior']),
overflowDirection: ArgumentDecoders.enumValue<VerticalDirection>(VerticalDirection.values, source, ['overflowDirection']),
overflowButtonSpacing: source.v<double>(['overflowButtonSpacing']),
spacing: buttonPadding.horizontal / 2,
overflowDirection: ArgumentDecoders.enumValue<VerticalDirection>(VerticalDirection.values, source, ['overflowDirection']) ?? VerticalDirection.down,
overflowSpacing: source.v<double>(['overflowButtonSpacing']) ?? 0.0,
children: source.childList(['children']),
);

switch (layoutBehavior) {
case ButtonBarLayoutBehavior.padded:
overflowBar = Padding(
padding: EdgeInsets.symmetric(
vertical: 2.0 * (buttonPadding.horizontal / 4.0),
horizontal: buttonPadding.horizontal / 2.0,
),
child: overflowBar,
);
case ButtonBarLayoutBehavior.constrained:
overflowBar = Container(
padding: EdgeInsets.symmetric(horizontal: buttonPadding.horizontal / 2.0),
constraints: const BoxConstraints(minHeight: 52.0),
alignment: Alignment.center,
child: overflowBar,
);
}

if (ArgumentDecoders.enumValue<MainAxisSize>(MainAxisSize.values, source, ['mainAxisSize']) == MainAxisSize.min) {
return IntrinsicWidth(child: overflowBar);
}

return overflowBar;
},

'OverflowBar': (BuildContext context, DataSource source) {
return OverflowBar(
spacing: source.v<double>(['spacing']) ?? 0.0,
alignment: ArgumentDecoders.enumValue<MainAxisAlignment>(MainAxisAlignment.values, source, ['alignment']),
overflowSpacing: source.v<double>(['overflowSpacing']) ?? 0.0,
overflowAlignment: ArgumentDecoders.enumValue<OverflowBarAlignment>(OverflowBarAlignment.values, source, ['overflowAlignment'])
?? OverflowBarAlignment.start,
overflowDirection: ArgumentDecoders.enumValue<VerticalDirection>(VerticalDirection.values, source, ['overflowDirection'])
?? VerticalDirection.down,
textDirection: ArgumentDecoders.enumValue<TextDirection>(TextDirection.values, source, ['textDirection']),
children: source.childList(['children']),
);
},
Expand Down
2 changes: 1 addition & 1 deletion packages/rfw/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: rfw
description: "Remote Flutter widgets: a library for rendering declarative widget description files at runtime."
repository: https://github.com/flutter/packages/tree/main/packages/rfw
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+rfw%22
version: 1.0.19
version: 1.0.20

environment:
sdk: ">=3.0.0 <4.0.0"
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
150 changes: 144 additions & 6 deletions packages/rfw/test/material_widgets_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,18 @@ import 'package:rfw/rfw.dart';
import 'utils.dart';

void main() {
const LibraryName coreName = LibraryName(<String>['core']);
const LibraryName materialName = LibraryName(<String>['material']);
const LibraryName testName = LibraryName(<String>['test']);

Runtime setupRuntime() {
return Runtime()
..update(coreName, createCoreWidgets())
..update(materialName, createMaterialWidgets());
}

testWidgets('Material widgets', (WidgetTester tester) async {
final Runtime runtime = Runtime()
..update(const LibraryName(<String>['core']), createCoreWidgets())
..update(
const LibraryName(<String>['material']), createMaterialWidgets());
final Runtime runtime = setupRuntime();
final DynamicContent data = DynamicContent();
final List<String> eventLog = <String>[];
await tester.pumpWidget(
Expand All @@ -24,8 +31,7 @@ void main() {
home: RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(
LibraryName(<String>['test']), 'root'),
widget: const FullyQualifiedWidgetName(testName, 'root'),
onEvent: (String eventName, DynamicMap eventArguments) {
eventLog.add('$eventName $eventArguments');
},
Expand Down Expand Up @@ -218,4 +224,136 @@ void main() {
skip: !runGoldens,
);
});

testWidgets('OverflowBar configured to resemble ButtonBar',
(WidgetTester tester) async {
final Runtime runtime = setupRuntime();
final DynamicContent data = DynamicContent();
final List<String> eventLog = <String>[];
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(useMaterial3: false),
home: RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(testName, 'root'),
onEvent: (String eventName, DynamicMap eventArguments) {
eventLog.add('$eventName $eventArguments');
},
),
),
);
expect(tester.takeException().toString(),
contains('Could not find remote widget named'));

runtime.update(testName, parseLibraryFile('''
import core;
import material;
widget root = Scaffold(
body: Card(
margin: [20.0],
child: Padding(
padding: [8.0],
child: OverflowBar(
spacing: 8.0,
children: [
ElevatedButton(
onPressed: event 'button' { },
child: Text(text: 'Elevated'),
),
OutlinedButton(
onPressed: event 'button' { },
child: Text(text: 'Outlined'),
),
TextButton(
onPressed: event 'button' { },
child: Text(text: 'Text'),
),
],
),
),
),
);
'''));
await tester.pump();
await expectLater(
find.byType(RemoteWidget),
matchesGoldenFile(
'goldens/material_test.overflow_bar_resembles_button_bar.png'),
skip: !runGoldens,
);
});

testWidgets('Implement OverflowBar properties', (WidgetTester tester) async {
final Runtime runtime = setupRuntime();
final DynamicContent data = DynamicContent();
final List<String> eventLog = <String>[];
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(useMaterial3: false),
home: RemoteWidget(
runtime: runtime,
data: data,
widget: const FullyQualifiedWidgetName(testName, 'root'),
onEvent: (String eventName, DynamicMap eventArguments) {
eventLog.add('$eventName $eventArguments');
},
),
),
);
expect(tester.takeException().toString(),
contains('Could not find remote widget named'));

addTearDown(() async {
await tester.binding.setSurfaceSize(null);
});

runtime.update(testName, parseLibraryFile('''
import core;
import material;
widget root = Scaffold(
body: Center(
child: OverflowBar(
spacing: 16.0,
alignment: 'end',
overflowSpacing: 4.0,
overflowAlignment: 'center',
overflowDirection: 'up',
children: [
ElevatedButton(
onPressed: event 'button' { },
child: Text(text: 'Elevated'),
),
OutlinedButton(
onPressed: event 'button' { },
child: Text(text: 'Outlined'),
),
TextButton(
onPressed: event 'button' { },
child: Text(text: 'Text'),
),
],
),
),
);
'''));
await tester.pump();

await expectLater(
find.byType(RemoteWidget),
matchesGoldenFile('goldens/material_test.overflow_bar_properties.png'),
skip: !runGoldens,
);

// Update the surface size for OverflowBar to overflow.
await tester.binding.setSurfaceSize(const Size(200.0, 600.0));
await tester.pump();

await expectLater(
find.byType(RemoteWidget),
matchesGoldenFile(
'goldens/material_test.overflow_bar_properties.overflow.png'),
skip: !runGoldens,
);
});
}