diff --git a/packages/rfw/CHANGELOG.md b/packages/rfw/CHANGELOG.md index 002457d65311..532a7277ebf0 100644 --- a/packages/rfw/CHANGELOG.md +++ b/packages/rfw/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.29 + +* Adds support for the `Slider` Material widget. + ## 1.0.28 * Updates documentation to WidgetStateProperty and ButtonBar. diff --git a/packages/rfw/lib/src/flutter/material_widgets.dart b/packages/rfw/lib/src/flutter/material_widgets.dart index f80bcca2cd56..16132b1ad3cd 100644 --- a/packages/rfw/lib/src/flutter/material_widgets.dart +++ b/packages/rfw/lib/src/flutter/material_widgets.dart @@ -37,6 +37,7 @@ import 'runtime.dart'; /// * [Material] /// * [OutlinedButton] /// * [Scaffold] +/// * [Slider] /// * [TextButton] /// * [VerticalDivider] /// * [OverflowBar] @@ -499,6 +500,39 @@ Map get _materialWidgetsDefinitions => (['min']) ?? 0.0; + final value = source.v(['value']) ?? min; + final labelText = source.v(['label']); + final label = labelText != null ? '$labelText: ${value.toStringAsFixed(2)}' : value.toStringAsFixed(2); + return Slider( + value: value, + secondaryTrackValue: source.v(['secondaryTrackValue']), + onChanged: source.handler(['onChanged'], + (HandlerTrigger trigger) => (double value) { + trigger({'value': value}); + }), + onChangeStart: source.handler(['onChangeStart'], + (HandlerTrigger trigger) => (double value) { + trigger({'value': value}); + }), + onChangeEnd: source.handler(['onChangeEnd'], + (HandlerTrigger trigger) => (double value) { + trigger({'value': value}); + }), + min: min, + max: source.v(['max']) ?? 1.0, + divisions: source.v(['divisions']), + label: label, + activeColor: ArgumentDecoders.color(source, ['activeColor']), + inactiveColor: ArgumentDecoders.color(source, ['inactiveColor']), + secondaryActiveColor: ArgumentDecoders.color(source, ['secondaryActiveColor']), + thumbColor: ArgumentDecoders.color(source, ['thumbColor']), + allowedInteraction: ArgumentDecoders.enumValue(SliderInteraction.values, source, ['allowedInteraction']), + ); + }, + 'TextButton': (BuildContext context, DataSource source) { // not implemented: buttonStyle, focusNode return TextButton( diff --git a/packages/rfw/pubspec.yaml b/packages/rfw/pubspec.yaml index 8d6496971356..5f217590123e 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.28 +version: 1.0.29 environment: sdk: ^3.2.0 diff --git a/packages/rfw/test/material_widgets_test.dart b/packages/rfw/test/material_widgets_test.dart index 56da9abfd92b..08ac4351ebf5 100644 --- a/packages/rfw/test/material_widgets_test.dart +++ b/packages/rfw/test/material_widgets_test.dart @@ -586,4 +586,101 @@ void main() { expect(tester.widget(find.byType(Material)).clipBehavior, Clip.antiAlias); }); + + testWidgets('Slider properties', (WidgetTester tester) async { + final Runtime runtime = setupRuntime(); + final DynamicContent data = DynamicContent(); + final List eventLog = []; + 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: Center( + child: Slider( + onChanged: event 'slider' { }, + min: 10.0, + max: 100.0, + divisions: 100, + value: 20.0, + activeColor: 0xFF0000FF, + inactiveColor: 0xFF00FF00, + secondaryActiveColor: 0xFFFF0000, + thumbColor: 0xFF000000, + ))); + ''')); + await tester.pump(); + + final Finder sliderFinder = find.byType(Slider); + final Slider slider = tester.widget(sliderFinder); + expect(slider.value, 20.0); + expect(slider.min, 10.0); + expect(slider.max, 100.0); + expect(slider.divisions, 100); + expect(slider.activeColor, const Color(0xFF0000FF)); + expect(slider.inactiveColor, const Color(0xFF00FF00)); + expect(slider.secondaryActiveColor, const Color(0xFFFF0000)); + expect(slider.thumbColor, const Color(0xFF000000)); + + runtime.update(testName, parseLibraryFile(''' + import core; + import material; + + widget root = Scaffold( + body: Container( + child: Slider( + onChanged: event 'slider' { }, + onChangeStart: event 'slider.start' { }, + onChangeEnd: event 'slider.end' { }, + min: 0.0, + max: 100.0, + divisions: 100, + value: 0.0, + ))); + ''')); + await tester.pump(); + + //drag slider + await _slideToValue(tester, sliderFinder, 20.0); + await tester.pumpAndSettle(); + expect(eventLog, + contains(kIsWeb ? 'slider {value: 20}' : 'slider {value: 20.0}')); + expect( + eventLog, + contains( + kIsWeb ? 'slider.start {value: 0}' : 'slider.start {value: 0.0}')); + expect( + eventLog, + contains( + kIsWeb ? 'slider.end {value: 20}' : 'slider.end {value: 20.0}')); + }); +} + +// slide to value for material slider in tests +Future _slideToValue( + WidgetTester widgetTester, Finder slider, double value, + {double paddingOffset = 24.0}) async { + final Offset zeroPoint = widgetTester.getTopLeft(slider) + + Offset(paddingOffset, widgetTester.getSize(slider).height / 2); + final double totalWidth = + widgetTester.getSize(slider).width - (2 * paddingOffset); + final double calculateOffset = value * (totalWidth / 100); + await widgetTester.dragFrom(zeroPoint, Offset(calculateOffset, 0)); }