diff --git a/packages/rfw/CHANGELOG.md b/packages/rfw/CHANGELOG.md index 99ff72ef28d..1998c2c9a73 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 04f3acab01a..559d908b6ac 100644 --- a/packages/rfw/lib/src/flutter/core_widgets.dart +++ b/packages/rfw/lib/src/flutter/core_widgets.dart @@ -27,6 +27,7 @@ import 'runtime.dart'; /// * [Align] /// * [AspectRatio] /// * [Center] +/// * [ClipRRect] /// * [ColoredBox] /// * [Column] /// * [Container] (actually uses [AnimatedContainer]) @@ -269,6 +270,15 @@ Map get _coreWidgetsDefinitions => clipper, + clipBehavior: ArgumentDecoders.enumValue(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 00a721b2b7c..59fb2903464 100644 --- a/packages/rfw/lib/src/flutter/material_widgets.dart +++ b/packages/rfw/lib/src/flutter/material_widgets.dart @@ -27,6 +27,7 @@ import 'runtime.dart'; /// * [CircularProgressIndicator] /// * [Divider] /// * [DrawerHeader] +/// * [DropdownButton] /// * [ElevatedButton] /// * [FloatingActionButton] /// * [InkWell] @@ -67,6 +68,11 @@ import 'runtime.dart'; /// * 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 @@ Map get _materialWidgetsDefinitions => > dropdownMenuItems = List>.generate( + length, + (int index) => DropdownMenuItem( + onTap: source.voidHandler(['items', index, 'onTap']), + value: source.v(['items', index, 'value']) ?? source.v(['items', index, 'value']) ?? source.v(['items', index, 'value']) ?? source.v(['items', index, 'value']), + enabled: source.v(['items', index, 'enabled']) ?? true, + alignment: ArgumentDecoders.alignment(source, ['items', index, 'alignment']) ?? AlignmentDirectional.centerStart, + child: source.child(['items', index, 'child']), + ), + ); + + return DropdownButton( + items: dropdownMenuItems, + value: source.v(['value']) ?? source.v(['value']) ?? source.v(['value']) ?? source.v(['value']), + disabledHint: source.optionalChild(['disabledHint']), + onChanged: source.handler(['onChanged'], (HandlerTrigger trigger) => (Object? value) => trigger({'value': value})), + onTap: source.voidHandler(['onTap']), + elevation: source.v(['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(['iconSize']) ?? 24.0, + isDense: source.v(['isDense']) ?? false, + isExpanded: source.v(['isExpanded']) ?? false, + itemHeight: source.v(['itemHeight']) ?? kMinInteractiveDimension, + focusColor: ArgumentDecoders.color(source, ['focusColor']), + autofocus: source.v(['autofocus']) ?? false, + dropdownColor: ArgumentDecoders.color(source, ['dropdownColor']), + menuMaxHeight: source.v(['menuMaxHeight']), + enableFeedback: source.v(['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 0d22097d9ec..b683d0dc5a6 100644 --- a/packages/rfw/pubspec.yaml +++ b/packages/rfw/pubspec.yaml @@ -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.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 8765e1315c5..40521a66745 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 @@ void main() { ''')); await tester.pump(); expect(find.byType(Wrap), findsOneWidget); + + runtime.update(const LibraryName(['test']), parseLibraryFile(''' + import core; + widget root = ClipRRect(); + ''')); + await tester.pump(); + expect(find.byType(ClipRRect), findsOneWidget); + final RenderClipRRect renderClip = tester.allRenderObjects.whereType().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 4c240b5b2be..38444dad6a1 100644 Binary files a/packages/rfw/test/goldens/material_test.drawer.png and b/packages/rfw/test/goldens/material_test.drawer.png 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 00000000000..80011c9f676 Binary files /dev/null and b/packages/rfw/test/goldens/material_test.dropdown.png differ diff --git a/packages/rfw/test/goldens/material_test.scaffold.png b/packages/rfw/test/goldens/material_test.scaffold.png index b77bf766543..c0e1c74778c 100644 Binary files a/packages/rfw/test/goldens/material_test.scaffold.png and b/packages/rfw/test/goldens/material_test.scaffold.png differ diff --git a/packages/rfw/test/material_widgets_test.dart b/packages/rfw/test/material_widgets_test.dart index 01aefa9ede0..b3f659bcd1f 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 @@ void main() { ), ), ), + 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 @@ void main() { matchesGoldenFile('goldens/material_test.scaffold.png'), skip: !runGoldens, ); + + await tester.tap(find.byType(DropdownButton).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).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));