Skip to content
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

[rfw] Material slider widget #6610

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
3 changes: 3 additions & 0 deletions packages/rfw/CHANGELOG.md
@@ -1,3 +1,6 @@
## 1.0.27
* Adds `Slider` material widget

## 1.0.26
* Supports overriding the error widget builder.

Expand Down
34 changes: 34 additions & 0 deletions packages/rfw/lib/src/flutter/material_widgets.dart
Expand Up @@ -37,6 +37,7 @@ import 'runtime.dart';
/// * [Material]
/// * [OutlinedButton]
/// * [Scaffold]
/// * [Slider]
/// * [TextButton]
/// * [VerticalDivider]
/// * [OverflowBar]
Expand Down Expand Up @@ -499,6 +500,39 @@ Map<String, LocalWidgetBuilder> get _materialWidgetsDefinitions => <String, Loca
);
},

'Slider': (BuildContext context, DataSource source) {
// not implemented: overlayColor, mouseCursor, semanticFormatterCallback, focusNode, autofocus
final min = source.v<double>(['min']) ?? 0.0;
final value = source.v<double>(['value']) ?? min;
final labelText = source.v<String>(['label']);
final label = labelText != null ? '$labelText:${value.toStringAsFixed(2)}' : value.toStringAsFixed(2);
Copy link
Contributor

Choose a reason for hiding this comment

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

should there be a space after the :?

return Slider(
value: value,
secondaryTrackValue: source.v<double>(['secondaryTrackValue']),
onChanged: source.handler(['onChanged'],
(HandlerTrigger trigger) => (double value) {
trigger({'value': value});
}),
Copy link
Contributor

Choose a reason for hiding this comment

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

Per the style guide, indentation should never have a line that is less indented than a line that is of higher lexical scope (well the style guide doesn't say it quite that way but that's the intent).

Suggested change
onChanged: source.handler(['onChanged'],
(HandlerTrigger trigger) => (double value) {
trigger({'value': value});
}),
onChanged: source.handler(['onChanged'],
(HandlerTrigger trigger) => (double value) {
trigger({'value': value});
},
),

Copy link
Contributor

Choose a reason for hiding this comment

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

(same below)

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<double>(['max']) ?? 1.0,
divisions: source.v<int>(['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>(SliderInteraction.values, source, ['allowedInteraction']),
);
},

'TextButton': (BuildContext context, DataSource source) {
// not implemented: buttonStyle, focusNode
return TextButton(
Expand Down
2 changes: 1 addition & 1 deletion packages/rfw/pubspec.yaml
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.26
version: 1.0.27

environment:
sdk: ^3.2.0
Expand Down
85 changes: 85 additions & 0 deletions packages/rfw/test/material_widgets_test.dart
Expand Up @@ -586,4 +586,89 @@ void main() {
expect(tester.widget<Material>(find.byType(Material)).clipBehavior,
Clip.antiAlias);
});

testWidgets('Slider 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'),
);

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<Slider>(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 tester.slideToValue(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}'));
});
}
13 changes: 13 additions & 0 deletions packages/rfw/test/utils.dart
Expand Up @@ -5,6 +5,7 @@
import 'dart:io' show Platform;

import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';

// Detects if we're running the tests on the main channel.
//
Expand All @@ -21,3 +22,15 @@ bool get isMainChannel {

// See Contributing section of README.md file.
final bool runGoldens = !kIsWeb && Platform.isLinux && isMainChannel;

// slide to value for material slider in tests
extension SlideTo on WidgetTester {
Copy link
Contributor

Choose a reason for hiding this comment

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

can you make this a function instead of an extension? that would make it easier to find when reading the code (otherwise it looks like you have to look on WidgetTester).

See also https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#avoid-using-extension

Future<void> slideToValue(Finder slider, double value,
{double paddingOffset = 24.0}) async {
final Offset zeroPoint =
getTopLeft(slider) + Offset(paddingOffset, getSize(slider).height / 2);
final double totalWidth = getSize(slider).width - (2 * paddingOffset);
final double calculateOffset = value * (totalWidth / 100);
await dragFrom(zeroPoint, Offset(calculateOffset, 0));
}
}