Add rfw widgets (#5661)
Add two rfw widgets(ClipRRect and DropdownButton) and `flutter format`
the widget files.
## Pre-launch Checklist
- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [relevant style guides] and ran the
auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages
repo does use `dart format`.)
- [x] I signed the [CLA].
- [x] The title of the PR starts with the name of the package surrounded
by square brackets, e.g. `[shared_preferences]`
- [ ] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated `pubspec.yaml` with an appropriate new version according
to the [pub versioning philosophy], or this PR is [exempt from version
changes].
- [x] I updated `CHANGELOG.md` to add a description of the change,
[following repository CHANGELOG style].
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] All existing and new tests are passing.
If you need help, consider asking for advice on the #hackers-new channel
on [Discord].
<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/packages/blob/main/CONTRIBUTING.md
[Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene
[relevant style guides]:
https://github.com/flutter/packages/blob/main/CONTRIBUTING.md#style
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes
[Discord]: https://github.com/flutter/flutter/wiki/Chat
[pub versioning philosophy]: https://dart.dev/tools/pub/versioning
[exempt from version changes]:
https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#version-and-changelog-updates
[following repository CHANGELOG style]:
https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changelog-style
[test-exempt]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#tests
diff --git a/packages/rfw/CHANGELOG.md b/packages/rfw/CHANGELOG.md
index 99ff72e..1998c2c 100644
--- a/packages/rfw/CHANGELOG.md
+++ b/packages/rfw/CHANGELOG.md
@@ -1,5 +1,7 @@
-## 1.0.18
+## 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`.
diff --git a/packages/rfw/lib/src/flutter/core_widgets.dart b/packages/rfw/lib/src/flutter/core_widgets.dart
index 04f3aca..559d908 100644
--- a/packages/rfw/lib/src/flutter/core_widgets.dart
+++ b/packages/rfw/lib/src/flutter/core_widgets.dart
@@ -27,6 +27,7 @@
/// * [Align]
/// * [AspectRatio]
/// * [Center]
+/// * [ClipRRect]
/// * [ColoredBox]
/// * [Column]
/// * [Container] (actually uses [AnimatedContainer])
@@ -269,6 +270,15 @@
);
},
+ 'ClipRRect': (BuildContext context, DataSource source) {
+ return ClipRRect(
+ borderRadius: ArgumentDecoders.borderRadius(source, ['borderRadius']) ?? BorderRadius.zero,
+ // CustomClipper<RRect> clipper,
+ clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.antiAlias,
+ child: source.optionalChild(['child']),
+ );
+ },
+
'ColoredBox': (BuildContext context, DataSource source) {
return ColoredBox(
color: ArgumentDecoders.color(source, ['color']) ?? const Color(0xFF000000),
diff --git a/packages/rfw/lib/src/flutter/material_widgets.dart b/packages/rfw/lib/src/flutter/material_widgets.dart
index 00a721b..59fb290 100644
--- a/packages/rfw/lib/src/flutter/material_widgets.dart
+++ b/packages/rfw/lib/src/flutter/material_widgets.dart
@@ -27,6 +27,7 @@
/// * [CircularProgressIndicator]
/// * [Divider]
/// * [DrawerHeader]
+/// * [DropdownButton]
/// * [ElevatedButton]
/// * [FloatingActionButton]
/// * [InkWell]
@@ -67,6 +68,11 @@
/// * The [Scaffold]'s floating action button position and animation features
/// are not supported.
///
+/// * [DropdownButton] takes a `items` object which contains a list of
+/// [DropdownMenuItem] configuration objects. Each object may contain
+/// `onTap`, `value`, `enabled` and `child`. The `child` parameter is
+/// required.
+///
/// In general, the trend will all of these unsupported features is that this
/// library doesn't support features that can't be trivially expressed using the
/// JSON-like structures of RFW. For example, [MaterialStateProperty] is
@@ -188,6 +194,46 @@
);
},
+ 'DropdownButton': (BuildContext context, DataSource source) {
+ final int length = source.length(['items']);
+ final List<DropdownMenuItem<Object>> dropdownMenuItems = List<DropdownMenuItem<Object>>.generate(
+ length,
+ (int index) => DropdownMenuItem<Object>(
+ onTap: source.voidHandler(['items', index, 'onTap']),
+ value: source.v<String>(['items', index, 'value']) ?? source.v<int>(['items', index, 'value']) ?? source.v<double>(['items', index, 'value']) ?? source.v<bool>(['items', index, 'value']),
+ enabled: source.v<bool>(['items', index, 'enabled']) ?? true,
+ alignment: ArgumentDecoders.alignment(source, ['items', index, 'alignment']) ?? AlignmentDirectional.centerStart,
+ child: source.child(['items', index, 'child']),
+ ),
+ );
+
+ return DropdownButton<Object>(
+ items: dropdownMenuItems,
+ value: source.v<String>(['value']) ?? source.v<int>(['value']) ?? source.v<double>(['value']) ?? source.v<bool>(['value']),
+ disabledHint: source.optionalChild(['disabledHint']),
+ onChanged: source.handler(<Object>['onChanged'], (HandlerTrigger trigger) => (Object? value) => trigger(<String, Object?>{'value': value})),
+ onTap: source.voidHandler(['onTap']),
+ elevation: source.v<int>(['elevation']) ?? 8,
+ style: ArgumentDecoders.textStyle(source, ['style']),
+ underline: source.optionalChild(['underline']),
+ icon: source.optionalChild(['icon']),
+ iconDisabledColor: ArgumentDecoders.color(source, ['iconDisabledColor']),
+ iconEnabledColor: ArgumentDecoders.color(source, ['iconEnabledColor']),
+ iconSize: source.v<double>(['iconSize']) ?? 24.0,
+ isDense: source.v<bool>(['isDense']) ?? false,
+ isExpanded: source.v<bool>(['isExpanded']) ?? false,
+ itemHeight: source.v<double>(['itemHeight']) ?? kMinInteractiveDimension,
+ focusColor: ArgumentDecoders.color(source, ['focusColor']),
+ autofocus: source.v<bool>(['autofocus']) ?? false,
+ dropdownColor: ArgumentDecoders.color(source, ['dropdownColor']),
+ menuMaxHeight: source.v<double>(['menuMaxHeight']),
+ enableFeedback: source.v<bool>(['enableFeedback']),
+ alignment: ArgumentDecoders.alignment(source, ['alignment']) ?? AlignmentDirectional.centerStart,
+ borderRadius: ArgumentDecoders.borderRadius(source, ['borderRadius'])?.resolve(Directionality.of(context)),
+ padding: ArgumentDecoders.edgeInsets(source, ['padding']),
+ );
+ },
+
'ElevatedButton': (BuildContext context, DataSource source) {
// not implemented: buttonStyle, focusNode
return ElevatedButton(
diff --git a/packages/rfw/pubspec.yaml b/packages/rfw/pubspec.yaml
index 0d22097..b683d0d 100644
--- a/packages/rfw/pubspec.yaml
+++ b/packages/rfw/pubspec.yaml
@@ -2,7 +2,7 @@
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.18
+version: 1.0.19
environment:
sdk: ">=3.0.0 <4.0.0"
diff --git a/packages/rfw/test/core_widgets_test.dart b/packages/rfw/test/core_widgets_test.dart
index 8765e13..40521a6 100644
--- a/packages/rfw/test/core_widgets_test.dart
+++ b/packages/rfw/test/core_widgets_test.dart
@@ -7,6 +7,7 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:rfw/formats.dart' show parseLibraryFile;
import 'package:rfw/rfw.dart';
@@ -278,5 +279,15 @@
'''));
await tester.pump();
expect(find.byType(Wrap), findsOneWidget);
+
+ runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
+ import core;
+ widget root = ClipRRect();
+ '''));
+ await tester.pump();
+ expect(find.byType(ClipRRect), findsOneWidget);
+ final RenderClipRRect renderClip = tester.allRenderObjects.whereType<RenderClipRRect>().first;
+ expect(renderClip.clipBehavior, equals(Clip.antiAlias));
+ expect(renderClip.borderRadius, equals(BorderRadius.zero));
});
}
diff --git a/packages/rfw/test/goldens/material_test.drawer.png b/packages/rfw/test/goldens/material_test.drawer.png
index 4c240b5..38444da 100644
--- a/packages/rfw/test/goldens/material_test.drawer.png
+++ b/packages/rfw/test/goldens/material_test.drawer.png
Binary files differ
diff --git a/packages/rfw/test/goldens/material_test.dropdown.png b/packages/rfw/test/goldens/material_test.dropdown.png
new file mode 100644
index 0000000..80011c9
--- /dev/null
+++ b/packages/rfw/test/goldens/material_test.dropdown.png
Binary files differ
diff --git a/packages/rfw/test/goldens/material_test.scaffold.png b/packages/rfw/test/goldens/material_test.scaffold.png
index b77bf76..c0e1c74 100644
--- a/packages/rfw/test/goldens/material_test.scaffold.png
+++ b/packages/rfw/test/goldens/material_test.scaffold.png
Binary files differ
diff --git a/packages/rfw/test/material_widgets_test.dart b/packages/rfw/test/material_widgets_test.dart
index 01aefa9..b3f659b 100644
--- a/packages/rfw/test/material_widgets_test.dart
+++ b/packages/rfw/test/material_widgets_test.dart
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:rfw/formats.dart' show parseLibraryFile;
@@ -123,6 +124,55 @@
),
),
),
+ Divider(),
+ Padding(
+ padding: [20.0],
+ child: Row(
+ mainAxisAlignment: 'spaceEvenly',
+ children: [
+ DropdownButton(
+ value: 'foo',
+ elevation: 14,
+ dropdownColor: 0xFF9E9E9E,
+ underline: Container(
+ height: 2,
+ color: 0xFF7C4DFF,
+ ),
+ style: {
+ color:0xFF7C4DFF,
+ },
+ items: [
+ {
+ value: 'foo',
+ child: Text(text: 'foo'),
+ },
+ {
+ value: 'bar',
+ child: Text(text: 'bar'),
+ onTap: event 'menu_item' { args: 'bar' },
+ },
+ ],
+ borderRadius:[{x: 8.0, y: 8.0}, {x: 8.0, y: 8.0}, {x: 8.0, y: 8.0}, {x: 8.0, y: 8.0}],
+ onChanged: event 'dropdown' {},
+ ),
+ DropdownButton(
+ value: 1.0,
+ items: [
+ {
+ value: 1.0,
+ child: Text(text: 'first'),
+ },
+ {
+ value: 2.0,
+ child: Text(text: 'second'),
+ onTap: event 'menu_item' { args: 'second' },
+ },
+ ],
+ onChanged: event 'dropdown' {},
+ ),
+ ],
+ ),
+ ),
],
),
floatingActionButton: FloatingActionButton(
@@ -137,6 +187,28 @@
matchesGoldenFile('goldens/material_test.scaffold.png'),
skip: !runGoldens,
);
+
+ await tester.tap(find.byType(DropdownButton<Object>).first);
+ await tester.pumpAndSettle();
+ await expectLater(
+ find.byType(MaterialApp),
+ matchesGoldenFile('goldens/material_test.dropdown.png'),
+ skip: !runGoldens,
+ );
+ // Tap on the second item.
+ await tester.tap(find.text('bar'));
+ await tester.pumpAndSettle();
+ expect(eventLog, contains('menu_item {args: bar}'));
+ expect(eventLog, contains('dropdown {value: bar}'));
+
+ await tester.tap(find.byType(DropdownButton<Object>).last);
+ await tester.pumpAndSettle();
+ await tester.tap(find.text('second'));
+ await tester.pumpAndSettle();
+ expect(eventLog, contains('menu_item {args: second}'));
+ expect(eventLog,
+ contains(kIsWeb ? 'dropdown {value: 2}' : 'dropdown {value: 2.0}'));
+
await tester.tapAt(const Offset(20.0, 20.0));
await tester.pump();
await tester.pump(const Duration(seconds: 1));