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

Add InkResponse, Material and fix Opacity #6199

Merged
merged 12 commits into from
Feb 26, 2024
5 changes: 5 additions & 0 deletions packages/rfw/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.0.24
* Adds `InkResponse` material widget.
* Adds `Material` material widget.
* Fixes the `Opacity` core widget.
Copy link
Contributor

Choose a reason for hiding this comment

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

we should document what was fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated.


## 1.0.23

* Replaces usage of deprecated Flutter APIs.
Expand Down
1 change: 1 addition & 0 deletions packages/rfw/lib/src/flutter/core_widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ Map<String, LocalWidgetBuilder> get _coreWidgetsDefinitions => <String, LocalWid
opacity: source.v<double>(['opacity']) ?? 0.0,
onEnd: source.voidHandler(['onEnd']),
alwaysIncludeSemantics: source.v<bool>(['alwaysIncludeSemantics']) ?? true,
child: source.optionalChild(['child']),
);
},

Expand Down
56 changes: 56 additions & 0 deletions packages/rfw/lib/src/flutter/material_widgets.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';

import 'argument_decoders.dart';
import 'runtime.dart';
Expand All @@ -30,9 +31,11 @@ import 'runtime.dart';
/// * [DropdownButton]
/// * [ElevatedButton]
/// * [FloatingActionButton]
/// * [InkResponse]
/// * [InkWell]
/// * [LinearProgressIndicator]
/// * [ListTile]
/// * [Material]
/// * [OutlinedButton]
/// * [Scaffold]
/// * [TextButton]
Expand Down Expand Up @@ -337,6 +340,42 @@ Map<String, LocalWidgetBuilder> get _materialWidgetsDefinitions => <String, Loca
);
},

'InkResponse': (BuildContext context, DataSource source) {
// not implemented: mouseCursor, overlayColor, splashFactory, focusNode.
return InkResponse(
onTap: source.voidHandler(['onTap']),
onTapDown: source.handler(['onTapDown'], (VoidCallback trigger) => (TapDownDetails details) => trigger()),
Copy link
Contributor

Choose a reason for hiding this comment

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

do we want to expose the details in the event?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Filed the issue #144175. We should expose the details for all the tap events.

onTapUp: source.handler(['onTapUp'], (VoidCallback trigger) => (TapUpDetails details) => trigger()),
onTapCancel: source.voidHandler(['onTapCancel']),
onDoubleTap: source.voidHandler(['onDoubleTap']),
onLongPress: source.voidHandler(['onLongPress']),
onSecondaryTap: source.voidHandler(['onSecondaryTap']),
onSecondaryTapUp: source.handler(['onSecondaryTapUp'], (VoidCallback trigger) => (TapUpDetails details) => trigger()),
onSecondaryTapDown: source.handler(['onSecondaryTapDown'], (VoidCallback trigger) => (TapDownDetails details) => trigger()),
onSecondaryTapCancel: source.voidHandler(['onSecondaryTapCancel']),
onHighlightChanged: source.handler(['onHighlightChanged'], (VoidCallback trigger) => (bool highlighted) => trigger()),
onHover: source.handler(['onHover'], (VoidCallback trigger) => (bool hovered) => trigger()),
containedInkWell: source.v<bool>(['containedInkWell']) ?? false,
highlightShape: ArgumentDecoders.enumValue<BoxShape>(BoxShape.values, source, ['highlightShape']) ?? BoxShape.circle,
radius: source.v<double>(['radius']),
borderRadius: ArgumentDecoders.borderRadius(source, ['borderRadius'])
?.resolve(Directionality.of(context)),
Copy link
Contributor

Choose a reason for hiding this comment

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

weird wrapping

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

customBorder: ArgumentDecoders.shapeBorder(source, ['customBorder']),
focusColor: ArgumentDecoders.color(source, ['focusColor']),
hoverColor: ArgumentDecoders.color(source, ['hoverColor']),
highlightColor: ArgumentDecoders.color(source, ['highlightColor']),
splashColor: ArgumentDecoders.color(source, ['splashColor']),
enableFeedback: source.v<bool>(['enableFeedback']) ?? true,
excludeFromSemantics: source.v<bool>(['excludeFromSemantics']) ?? false,
canRequestFocus: source.v<bool>(['canRequestFocus']) ?? true,
onFocusChange: source.handler(['onFocusChange'], (VoidCallback trigger) => (bool focus) => trigger()),
autofocus: source.v<bool>(['autofocus']) ?? false,
hoverDuration:
ArgumentDecoders.duration(source, ['hoverDuration'], context),
Copy link
Contributor

Choose a reason for hiding this comment

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

weird wrapping

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

child: source.child(['child']),
Copy link
Contributor

Choose a reason for hiding this comment

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

pretty sure this one can be optional

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

);
},

'InkWell': (BuildContext context, DataSource source) {
// not implemented: onHighlightChanged, onHover; mouseCursor; focusColor, hoverColor, highlightColor, overlayColor, splashColor; splashFactory; focusNode, onFocusChange
Copy link
Contributor

Choose a reason for hiding this comment

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

since you've implemented some of these for InkResponse, we might as well add them here too for consistency

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done!

return InkWell(
Expand Down Expand Up @@ -395,6 +434,23 @@ Map<String, LocalWidgetBuilder> get _materialWidgetsDefinitions => <String, Loca
);
},

'Material': (BuildContext context, DataSource source) {
return Material(
type: ArgumentDecoders.enumValue<MaterialType>(MaterialType.values,source, ['type']) ?? MaterialType.canvas,
elevation: source.v<double>(['elevation']) ?? 0.0,
color: ArgumentDecoders.color(source, ['color']),
shadowColor: ArgumentDecoders.color(source, ['shadowColor']),
surfaceTintColor: ArgumentDecoders.color(source, ['surfaceTintColor']),
textStyle: ArgumentDecoders.textStyle(source, ['textStyle']),
borderRadius: ArgumentDecoders.borderRadius(source, ['borderRadius']),
shape: ArgumentDecoders.shapeBorder(source, ['shape']),
borderOnForeground: source.v<bool>(['borderOnForeground']) ?? true,
clipBehavior: ArgumentDecoders.enumValue<Clip>(Clip.values, source, ['clipBehavior']) ?? Clip.none,
animationDuration: ArgumentDecoders.duration(source, ['animationDuration'], context),
child: source.child(['child']),
);
},

'OutlinedButton': (BuildContext context, DataSource source) {
// not implemented: buttonStyle, focusNode
return OutlinedButton(
Expand Down
11 changes: 9 additions & 2 deletions packages/rfw/test/core_widgets_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@ void main() {

runtime.update(const LibraryName(<String>['test']), parseLibraryFile('''
import core;
widget root = Opacity(onEnd: event 'end' {});
widget root = Opacity(
onEnd: event 'end' {},
child: Placeholder(),
);
'''));
await tester.pump();
expect(tester.widget<AnimatedOpacity>(find.byType(AnimatedOpacity)).onEnd, isNot(isNull));
Expand Down Expand Up @@ -226,7 +229,10 @@ void main() {
child: FractionallySizedBox(
widthFactor: 0.5,
heightFactor: 0.8,
child: Text(text: "test"),
child: Text(
text: "test",
textScaleFactor: 3.0,
),
),
);
'''));
Expand All @@ -235,6 +241,7 @@ void main() {
final Size childSize = tester.getSize(find.text('test'));
expect(childSize.width, fractionallySizedBoxSize.width * 0.5);
expect(childSize.height, fractionallySizedBoxSize.height * 0.8);
expect(tester.widget<Text>(find.text('test')).textScaler, const TextScaler.linear(3));
expect(tester.widget<FractionallySizedBox>(find.byType(FractionallySizedBox)).alignment, Alignment.center);
});

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
143 changes: 143 additions & 0 deletions packages/rfw/test/material_widgets_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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/gestures.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
Expand Down Expand Up @@ -438,4 +439,146 @@ void main() {
skip: !runGoldens,
);
});

testWidgets('Implement InkResponse 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: InkResponse(
onTap: event 'onTap' {},
onHover: event 'onHover' {},
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}],
hoverColor: 0xFF00FF00,
splashColor: 0xAA0000FF,
highlightColor: 0xAAFF0000,
containedInkWell: true,
highlightShape: 'circle',
child: Text(text: 'InkResponse'),
),
),
);
'''));
await tester.pump();

expect(find.byType(InkResponse), findsOneWidget);

// Hover
final Offset center = tester.getCenter(find.byType(InkResponse));
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer();
addTearDown(gesture.removePointer);
await gesture.moveTo(center);
await tester.pumpAndSettle();

await expectLater(
find.byType(RemoteWidget),
matchesGoldenFile('goldens/material_test.ink_response_hover.png'),
skip: !runGoldens,
);
expect(eventLog, contains('onHover {}'));

// Tap
await gesture.down(center);
await tester.pump(); // start gesture
await tester.pump(const Duration(milliseconds: 200)); // wait for splash to be well under way

await expectLater(
find.byType(RemoteWidget),
matchesGoldenFile('goldens/material_test.ink_response_tap.png'),
skip: !runGoldens,
);
await gesture.up();
await tester.pump();

expect(eventLog, contains('onTap {}'));
});

testWidgets('Implement Material 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 = Material(
type: 'circle',
elevation: 6.0,
color: 0xFF0000FF,
shadowColor: 0xFF00FF00,
surfaceTintColor: 0xff0000ff,
animationDuration: 300,
borderOnForeground: false,
child: SizedBox(
width: 20.0,
height: 20.0,
),
);
'''));
await tester.pump();

expect(tester.widget<Material>(find.byType(Material)).animationDuration, const Duration(milliseconds: 300));
expect(tester.widget<Material>(find.byType(Material)).borderOnForeground, false);
await expectLater(
find.byType(RemoteWidget),
matchesGoldenFile('goldens/material_test.material_properties.png'),
skip: !runGoldens,
);

runtime.update(testName, parseLibraryFile('''
import core;
import material;
widget root = Material(
clipBehavior: 'antiAlias',
shape: { type: 'circle', side: { width: 10.0, color: 0xFF0066FF } },
child: SizedBox(
width: 20.0,
height: 20.0,
),
);
'''));
await tester.pump();

expect(tester.widget<Material>(find.byType(Material)).clipBehavior, Clip.antiAlias);
});
}
4 changes: 2 additions & 2 deletions packages/rfw/test_coverage/bin/test_coverage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ import 'package:meta/meta.dart';
// Please update these targets when you update this package.
// Please ensure that test coverage continues to be 100%.
// Don't forget to update the lastUpdate date too!
const int targetLines = 3273;
const int targetLines = 3324;
const String targetPercent = '100';
const String lastUpdate = '2024-01-30';
const String lastUpdate = '2024-02-23';

@immutable
/* final */ class LcovLine {
Expand Down