Skip to content

Commit 91d11d6

Browse files
authored
Add InkResponse, Material and fix Opacity (flutter#6199)
Related issue: #141668
1 parent 5e03e0f commit 91d11d6

10 files changed

+231
-6
lines changed

packages/rfw/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## 1.0.24
2+
* Adds `InkResponse` material widget.
3+
* Adds `Material` material widget.
4+
* Adds the `child` to `Opacity` core widget.
5+
* Implements more `InkWell` parameters.
6+
17
## 1.0.23
28

39
* Replaces usage of deprecated Flutter APIs.

packages/rfw/lib/src/flutter/core_widgets.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,7 @@ Map<String, LocalWidgetBuilder> get _coreWidgetsDefinitions => <String, LocalWid
499499
opacity: source.v<double>(['opacity']) ?? 0.0,
500500
onEnd: source.voidHandler(['onEnd']),
501501
alwaysIncludeSemantics: source.v<bool>(['alwaysIncludeSemantics']) ?? true,
502+
child: source.optionalChild(['child']),
502503
);
503504
},
504505

packages/rfw/lib/src/flutter/material_widgets.dart

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@ import 'runtime.dart';
3030
/// * [DropdownButton]
3131
/// * [ElevatedButton]
3232
/// * [FloatingActionButton]
33+
/// * [InkResponse]
3334
/// * [InkWell]
3435
/// * [LinearProgressIndicator]
3536
/// * [ListTile]
37+
/// * [Material]
3638
/// * [OutlinedButton]
3739
/// * [Scaffold]
3840
/// * [TextButton]
@@ -337,14 +339,58 @@ Map<String, LocalWidgetBuilder> get _materialWidgetsDefinitions => <String, Loca
337339
);
338340
},
339341

342+
'InkResponse': (BuildContext context, DataSource source) {
343+
// not implemented: mouseCursor, overlayColor, splashFactory, focusNode.
344+
return InkResponse(
345+
onTap: source.voidHandler(['onTap']),
346+
onTapDown: source.handler(['onTapDown'], (VoidCallback trigger) => (TapDownDetails details) => trigger()),
347+
onTapUp: source.handler(['onTapUp'], (VoidCallback trigger) => (TapUpDetails details) => trigger()),
348+
onTapCancel: source.voidHandler(['onTapCancel']),
349+
onDoubleTap: source.voidHandler(['onDoubleTap']),
350+
onLongPress: source.voidHandler(['onLongPress']),
351+
onSecondaryTap: source.voidHandler(['onSecondaryTap']),
352+
onSecondaryTapUp: source.handler(['onSecondaryTapUp'], (VoidCallback trigger) => (TapUpDetails details) => trigger()),
353+
onSecondaryTapDown: source.handler(['onSecondaryTapDown'], (VoidCallback trigger) => (TapDownDetails details) => trigger()),
354+
onSecondaryTapCancel: source.voidHandler(['onSecondaryTapCancel']),
355+
onHighlightChanged: source.handler(['onHighlightChanged'], (VoidCallback trigger) => (bool highlighted) => trigger()),
356+
onHover: source.handler(['onHover'], (VoidCallback trigger) => (bool hovered) => trigger()),
357+
containedInkWell: source.v<bool>(['containedInkWell']) ?? false,
358+
highlightShape: ArgumentDecoders.enumValue<BoxShape>(BoxShape.values, source, ['highlightShape']) ?? BoxShape.circle,
359+
radius: source.v<double>(['radius']),
360+
borderRadius: ArgumentDecoders.borderRadius(source, ['borderRadius'])?.resolve(Directionality.of(context)),
361+
customBorder: ArgumentDecoders.shapeBorder(source, ['customBorder']),
362+
focusColor: ArgumentDecoders.color(source, ['focusColor']),
363+
hoverColor: ArgumentDecoders.color(source, ['hoverColor']),
364+
highlightColor: ArgumentDecoders.color(source, ['highlightColor']),
365+
splashColor: ArgumentDecoders.color(source, ['splashColor']),
366+
enableFeedback: source.v<bool>(['enableFeedback']) ?? true,
367+
excludeFromSemantics: source.v<bool>(['excludeFromSemantics']) ?? false,
368+
canRequestFocus: source.v<bool>(['canRequestFocus']) ?? true,
369+
onFocusChange: source.handler(['onFocusChange'], (VoidCallback trigger) => (bool focus) => trigger()),
370+
autofocus: source.v<bool>(['autofocus']) ?? false,
371+
hoverDuration: ArgumentDecoders.duration(source, ['hoverDuration'], context),
372+
child: source.optionalChild(['child']),
373+
);
374+
},
375+
340376
'InkWell': (BuildContext context, DataSource source) {
341-
// not implemented: onHighlightChanged, onHover; mouseCursor; focusColor, hoverColor, highlightColor, overlayColor, splashColor; splashFactory; focusNode, onFocusChange
377+
// not implemented: mouseCursor; overlayColor, splashFactory; focusNode, onFocusChange
342378
return InkWell(
343379
onTap: source.voidHandler(['onTap']),
344380
onDoubleTap: source.voidHandler(['onDoubleTap']),
345381
onLongPress: source.voidHandler(['onLongPress']),
346382
onTapDown: source.handler(['onTapDown'], (VoidCallback trigger) => (TapDownDetails details) => trigger()),
347383
onTapCancel: source.voidHandler(['onTapCancel']),
384+
onSecondaryTap: source.voidHandler(['onSecondaryTap']),
385+
onSecondaryTapUp: source.handler(['onSecondaryTapUp'], (VoidCallback trigger) => (TapUpDetails details) => trigger()),
386+
onSecondaryTapDown: source.handler(['onSecondaryTapDown'], (VoidCallback trigger) => (TapDownDetails details) => trigger()),
387+
onSecondaryTapCancel: source.voidHandler(['onSecondaryTapCancel']),
388+
onHighlightChanged: source.handler(['onHighlightChanged'], (VoidCallback trigger) => (bool highlighted) => trigger()),
389+
onHover: source.handler(['onHover'], (VoidCallback trigger) => (bool hovered) => trigger()),
390+
focusColor: ArgumentDecoders.color(source, ['focusColor']),
391+
hoverColor: ArgumentDecoders.color(source, ['hoverColor']),
392+
highlightColor: ArgumentDecoders.color(source, ['highlightColor']),
393+
splashColor: ArgumentDecoders.color(source, ['splashColor']),
348394
radius: source.v<double>(['radius']),
349395
borderRadius: ArgumentDecoders.borderRadius(source, ['borderRadius'])?.resolve(Directionality.of(context)),
350396
customBorder: ArgumentDecoders.shapeBorder(source, ['customBorder']),
@@ -395,6 +441,23 @@ Map<String, LocalWidgetBuilder> get _materialWidgetsDefinitions => <String, Loca
395441
);
396442
},
397443

444+
'Material': (BuildContext context, DataSource source) {
445+
return Material(
446+
type: ArgumentDecoders.enumValue<MaterialType>(MaterialType.values,source, ['type']) ?? MaterialType.canvas,
447+
elevation: source.v<double>(['elevation']) ?? 0.0,
448+
color: ArgumentDecoders.color(source, ['color']),
449+
shadowColor: ArgumentDecoders.color(source, ['shadowColor']),
450+
surfaceTintColor: ArgumentDecoders.color(source, ['surfaceTintColor']),
451+
textStyle: ArgumentDecoders.textStyle(source, ['textStyle']),
452+
borderRadius: ArgumentDecoders.borderRadius(source, ['borderRadius']),
453+
shape: ArgumentDecoders.shapeBorder(source, ['shape']),
454+
borderOnForeground: source.v<bool>(['borderOnForeground']) ?? true,
455+
clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.none,
456+
animationDuration: ArgumentDecoders.duration(source, ['animationDuration'], context),
457+
child: source.child(['child']),
458+
);
459+
},
460+
398461
'OutlinedButton': (BuildContext context, DataSource source) {
399462
// not implemented: buttonStyle, focusNode
400463
return OutlinedButton(

packages/rfw/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: rfw
22
description: "Remote Flutter widgets: a library for rendering declarative widget description files at runtime."
33
repository: https://github.com/flutter/packages/tree/main/packages/rfw
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+rfw%22
5-
version: 1.0.23
5+
version: 1.0.24
66

77
environment:
88
sdk: ^3.2.0

packages/rfw/test/core_widgets_test.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,10 @@ void main() {
106106

107107
runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
108108
import core;
109-
widget root = Opacity(onEnd: event 'end' {});
109+
widget root = Opacity(
110+
onEnd: event 'end' {},
111+
child: Placeholder(),
112+
);
110113
'''));
111114
await tester.pump();
112115
expect(tester.widget<AnimatedOpacity>(find.byType(AnimatedOpacity)).onEnd, isNot(isNull));
@@ -226,7 +229,10 @@ void main() {
226229
child: FractionallySizedBox(
227230
widthFactor: 0.5,
228231
heightFactor: 0.8,
229-
child: Text(text: "test"),
232+
child: Text(
233+
text: "test",
234+
textScaleFactor: 3.0,
235+
),
230236
),
231237
);
232238
'''));
@@ -235,6 +241,7 @@ void main() {
235241
final Size childSize = tester.getSize(find.text('test'));
236242
expect(childSize.width, fractionallySizedBoxSize.width * 0.5);
237243
expect(childSize.height, fractionallySizedBoxSize.height * 0.8);
244+
expect(tester.widget<Text>(find.text('test')).textScaler, const TextScaler.linear(3));
238245
expect(tester.widget<FractionallySizedBox>(find.byType(FractionallySizedBox)).alignment, Alignment.center);
239246
});
240247

Loading
Loading
Loading

packages/rfw/test/material_widgets_test.dart

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// found in the LICENSE file.
44

55
import 'package:flutter/foundation.dart';
6+
import 'package:flutter/gestures.dart';
67
import 'package:flutter/material.dart';
78
import 'package:flutter_test/flutter_test.dart';
89
import 'package:rfw/formats.dart' show parseLibraryFile;
@@ -438,4 +439,151 @@ void main() {
438439
skip: !runGoldens,
439440
);
440441
});
442+
443+
testWidgets('Implement InkResponse properties', (WidgetTester tester) async {
444+
final Runtime runtime = setupRuntime();
445+
final DynamicContent data = DynamicContent();
446+
final List<String> eventLog = <String>[];
447+
await tester.pumpWidget(
448+
MaterialApp(
449+
theme: ThemeData(useMaterial3: false),
450+
home: RemoteWidget(
451+
runtime: runtime,
452+
data: data,
453+
widget: const FullyQualifiedWidgetName(testName, 'root'),
454+
onEvent: (String eventName, DynamicMap eventArguments) {
455+
eventLog.add('$eventName $eventArguments');
456+
},
457+
),
458+
),
459+
);
460+
expect(
461+
tester.takeException().toString(),
462+
contains('Could not find remote widget named'),
463+
);
464+
465+
runtime.update(testName, parseLibraryFile('''
466+
import core;
467+
import material;
468+
widget root = Scaffold(
469+
body: Center(
470+
child: InkResponse(
471+
onTap: event 'onTap' {},
472+
onHover: event 'onHover' {},
473+
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}],
474+
hoverColor: 0xFF00FF00,
475+
splashColor: 0xAA0000FF,
476+
highlightColor: 0xAAFF0000,
477+
containedInkWell: true,
478+
highlightShape: 'circle',
479+
child: Text(text: 'InkResponse'),
480+
),
481+
),
482+
);
483+
'''));
484+
await tester.pump();
485+
486+
expect(find.byType(InkResponse), findsOneWidget);
487+
488+
// Hover
489+
final Offset center = tester.getCenter(find.byType(InkResponse));
490+
final TestGesture gesture =
491+
await tester.createGesture(kind: PointerDeviceKind.mouse);
492+
await gesture.addPointer();
493+
addTearDown(gesture.removePointer);
494+
await gesture.moveTo(center);
495+
await tester.pumpAndSettle();
496+
497+
await expectLater(
498+
find.byType(RemoteWidget),
499+
matchesGoldenFile('goldens/material_test.ink_response_hover.png'),
500+
skip: !runGoldens,
501+
);
502+
expect(eventLog, contains('onHover {}'));
503+
504+
// Tap
505+
await gesture.down(center);
506+
await tester.pump(); // start gesture
507+
await tester.pump(const Duration(
508+
milliseconds: 200)); // wait for splash to be well under way
509+
510+
await expectLater(
511+
find.byType(RemoteWidget),
512+
matchesGoldenFile('goldens/material_test.ink_response_tap.png'),
513+
skip: !runGoldens,
514+
);
515+
await gesture.up();
516+
await tester.pump();
517+
518+
expect(eventLog, contains('onTap {}'));
519+
});
520+
521+
testWidgets('Implement Material properties', (WidgetTester tester) async {
522+
final Runtime runtime = setupRuntime();
523+
final DynamicContent data = DynamicContent();
524+
final List<String> eventLog = <String>[];
525+
await tester.pumpWidget(
526+
MaterialApp(
527+
theme: ThemeData(useMaterial3: false),
528+
home: RemoteWidget(
529+
runtime: runtime,
530+
data: data,
531+
widget: const FullyQualifiedWidgetName(testName, 'root'),
532+
onEvent: (String eventName, DynamicMap eventArguments) {
533+
eventLog.add('$eventName $eventArguments');
534+
},
535+
),
536+
),
537+
);
538+
expect(
539+
tester.takeException().toString(),
540+
contains('Could not find remote widget named'),
541+
);
542+
543+
runtime.update(testName, parseLibraryFile('''
544+
import core;
545+
import material;
546+
widget root = Material(
547+
type: 'circle',
548+
elevation: 6.0,
549+
color: 0xFF0000FF,
550+
shadowColor: 0xFF00FF00,
551+
surfaceTintColor: 0xff0000ff,
552+
animationDuration: 300,
553+
borderOnForeground: false,
554+
child: SizedBox(
555+
width: 20.0,
556+
height: 20.0,
557+
),
558+
);
559+
'''));
560+
await tester.pump();
561+
562+
expect(tester.widget<Material>(find.byType(Material)).animationDuration,
563+
const Duration(milliseconds: 300));
564+
expect(tester.widget<Material>(find.byType(Material)).borderOnForeground,
565+
false);
566+
await expectLater(
567+
find.byType(RemoteWidget),
568+
matchesGoldenFile('goldens/material_test.material_properties.png'),
569+
skip: !runGoldens,
570+
);
571+
572+
runtime.update(testName, parseLibraryFile('''
573+
import core;
574+
import material;
575+
widget root = Material(
576+
clipBehavior: 'antiAlias',
577+
shape: { type: 'circle', side: { width: 10.0, color: 0xFF0066FF } },
578+
child: SizedBox(
579+
width: 20.0,
580+
height: 20.0,
581+
),
582+
);
583+
'''));
584+
await tester.pump();
585+
586+
expect(tester.widget<Material>(find.byType(Material)).clipBehavior,
587+
Clip.antiAlias);
588+
});
441589
}

packages/rfw/test_coverage/bin/test_coverage.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@ import 'package:meta/meta.dart';
2121
// Please update these targets when you update this package.
2222
// Please ensure that test coverage continues to be 100%.
2323
// Don't forget to update the lastUpdate date too!
24-
const int targetLines = 3273;
24+
const int targetLines = 3333;
2525
const String targetPercent = '100';
26-
const String lastUpdate = '2024-01-30';
26+
const String lastUpdate = '2024-02-26';
2727

2828
@immutable
2929
/* final */ class LcovLine {

0 commit comments

Comments
 (0)