Unverified Commit db518732 authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Add onDismiss to AnimatedModalBarrier, update tests (#100162)

I noticed that AnimatedModalBarrier didn't have the onDismiss callback that ModalBarrier does, and so I added that, and while I was at it, I updated the unit tests to perform all of the tests that are done on ModalBarrier also on AnimatedModalBarrier. The tests are unchanged, other than using AnimatedModalBarrier instead.
parent 548e8853
......@@ -60,6 +60,7 @@ class ModalBarrier extends StatelessWidget {
/// [ModalBarrier] built by [ModalRoute] pages.
final bool dismissible;
/// {@template flutter.widgets.ModalBarrier.onDismiss}
/// Called when the barrier is being dismissed.
///
/// If non-null [onDismiss] will be called in place of popping the current
......@@ -68,6 +69,7 @@ class ModalBarrier extends StatelessWidget {
/// If null, the ambient [Navigator]'s current route will be popped.
///
/// This field is ignored if [dismissible] is false.
/// {@endtemplate}
final VoidCallback? onDismiss;
/// Whether the modal barrier semantics are included in the semantics tree.
......@@ -172,6 +174,7 @@ class AnimatedModalBarrier extends AnimatedWidget {
this.dismissible = true,
this.semanticsLabel,
this.barrierSemanticsDismissible,
this.onDismiss,
}) : super(key: key, listenable: color);
/// If non-null, fill the barrier with this color.
......@@ -208,6 +211,9 @@ class AnimatedModalBarrier extends AnimatedWidget {
/// the [ModalBarrier] built by [ModalRoute] pages.
final bool? barrierSemanticsDismissible;
/// {@macro flutter.widgets.ModalBarrier.onDismiss}
final VoidCallback? onDismiss;
@override
Widget build(BuildContext context) {
return ModalBarrier(
......@@ -215,6 +221,7 @@ class AnimatedModalBarrier extends AnimatedWidget {
dismissible: dismissible,
semanticsLabel: semanticsLabel,
barrierSemanticsDismissible: barrierSemanticsDismissible,
onDismiss: onDismiss,
);
}
}
......
......@@ -15,9 +15,11 @@ void main() {
late bool hovered;
late Widget tapTarget;
late Widget hoverTarget;
late Animation<Color?> colorAnimation;
setUp(() {
tapped = false;
colorAnimation = const AlwaysStoppedAnimation<Color?>(Colors.red);
tapTarget = GestureDetector(
onTap: () {
tapped = true;
......@@ -42,7 +44,8 @@ void main() {
);
});
testWidgets('ModalBarrier prevents interactions with widgets behind it', (WidgetTester tester) async {
group('ModalBarrier', () {
testWidgets('prevents interactions with widgets behind it', (WidgetTester tester) async {
final Widget subject = Stack(
textDirection: TextDirection.ltr,
children: <Widget>[
......@@ -57,7 +60,7 @@ void main() {
expect(tapped, isFalse, reason: 'because the tap is not prevented by ModalBarrier');
});
testWidgets('ModalBarrier prevents hover interactions with widgets behind it', (WidgetTester tester) async {
testWidgets('prevents hover interactions with widgets behind it', (WidgetTester tester) async {
final Widget subject = Stack(
textDirection: TextDirection.ltr,
children: <Widget>[
......@@ -85,7 +88,7 @@ void main() {
expect(hovered, isFalse, reason: 'because the hover is not prevented by ModalBarrier');
});
testWidgets('ModalBarrier does not prevent interactions with widgets in front of it', (WidgetTester tester) async {
testWidgets('does not prevent interactions with widgets in front of it', (WidgetTester tester) async {
final Widget subject = Stack(
textDirection: TextDirection.ltr,
children: <Widget>[
......@@ -100,7 +103,7 @@ void main() {
expect(tapped, isTrue, reason: 'because the tap is prevented by ModalBarrier');
});
testWidgets('ModalBarrier does not prevent interactions with translucent widgets in front of it', (WidgetTester tester) async {
testWidgets('does not prevent interactions with translucent widgets in front of it', (WidgetTester tester) async {
bool dragged = false;
final Widget subject = Stack(
textDirection: TextDirection.ltr,
......@@ -127,7 +130,7 @@ void main() {
expect(dragged, isTrue, reason: 'because the drag is prevented by ModalBarrier');
});
testWidgets('ModalBarrier does not prevent hover interactions with widgets in front of it', (WidgetTester tester) async {
testWidgets('does not prevent hover interactions with widgets in front of it', (WidgetTester tester) async {
final Widget subject = Stack(
textDirection: TextDirection.ltr,
children: <Widget>[
......@@ -156,10 +159,11 @@ void main() {
hovered = false;
});
testWidgets('ModalBarrier plays system alert sound when user tries to dismiss it', (WidgetTester tester) async {
testWidgets('plays system alert sound when user tries to dismiss it', (WidgetTester tester) async {
final List<String> playedSystemSounds = <String>[];
try {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.platform, (MethodCall methodCall) async {
if (methodCall.method == 'SystemSound.play') {
playedSystemSounds.add(methodCall.arguments as String);
}
......@@ -184,7 +188,7 @@ void main() {
expect(playedSystemSounds[0], SystemSoundType.alert.toString());
});
testWidgets('ModalBarrier pops the Navigator when dismissed by primary tap', (WidgetTester tester) async {
testWidgets('pops the Navigator when dismissed by primary tap', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) => const SecondWidget(),
......@@ -217,7 +221,7 @@ void main() {
);
});
testWidgets('ModalBarrier pops the Navigator when dismissed by non-primary tap', (WidgetTester tester) async {
testWidgets('pops the Navigator when dismissed by non-primary tap', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) => const SecondWidget(),
......@@ -251,7 +255,7 @@ void main() {
);
});
testWidgets('ModalBarrier may pop the Navigator when competing with other gestures', (WidgetTester tester) async {
testWidgets('may pop the Navigator when competing with other gestures', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) => const SecondWidgetWithCompetence(),
......@@ -279,11 +283,12 @@ void main() {
);
});
testWidgets('ModalBarrier does not pop the Navigator with a WillPopScope that returns false', (WidgetTester tester) async {
testWidgets('does not pop the Navigator with a WillPopScope that returns false', (WidgetTester tester) async {
bool willPopCalled = false;
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) => Stack(
'/modal': (BuildContext context) =>
Stack(
children: <Widget>[
const SecondWidget(),
WillPopScope(
......@@ -323,11 +328,12 @@ void main() {
expect(willPopCalled, isTrue);
});
testWidgets('ModalBarrier pops the Navigator with a WillPopScope that returns true', (WidgetTester tester) async {
testWidgets('pops the Navigator with a WillPopScope that returns true', (WidgetTester tester) async {
bool willPopCalled = false;
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) => Stack(
'/modal': (BuildContext context) =>
Stack(
children: <Widget>[
const SecondWidget(),
WillPopScope(
......@@ -367,11 +373,12 @@ void main() {
expect(willPopCalled, isTrue);
});
testWidgets('ModalBarrier will call onDismiss callback', (WidgetTester tester) async {
testWidgets('will call onDismiss callback', (WidgetTester tester) async {
bool dismissCallbackCalled = false;
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) => SecondWidget(onDismiss: () {
'/modal': (BuildContext context) =>
SecondWidget(onDismiss: () {
dismissCallbackCalled = true;
}),
};
......@@ -394,7 +401,7 @@ void main() {
expect(dismissCallbackCalled, true);
});
testWidgets('ModalBarrier will not pop when given an onDismiss callback', (WidgetTester tester) async {
testWidgets('will not pop when given an onDismiss callback', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) => SecondWidget(onDismiss: () {}),
......@@ -453,9 +460,10 @@ void main() {
expect(semantics, hasSemantics(expectedSemantics, ignoreId: true));
semantics.dispose();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS}));
testWidgets('Dismissible ModalBarrier is hidden on Android (back button is used to dismiss)', (WidgetTester tester) async {
testWidgets(
'Dismissible ModalBarrier is hidden on Android (back button is used to dismiss)', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(const ModalBarrier());
......@@ -464,8 +472,439 @@ void main() {
semantics.dispose();
});
});
group('AnimatedModalBarrier', () {
testWidgets('prevents interactions with widgets behind it', (WidgetTester tester) async {
final Widget subject = Stack(
textDirection: TextDirection.ltr,
children: <Widget>[
tapTarget,
AnimatedModalBarrier(dismissible: false, color: colorAnimation),
],
);
await tester.pumpWidget(subject);
await tester.tap(find.text('target'), warnIfMissed: false);
await tester.pumpWidget(subject);
expect(tapped, isFalse, reason: 'because the tap is not prevented by ModalBarrier');
});
testWidgets('prevents hover interactions with widgets behind it', (WidgetTester tester) async {
final Widget subject = Stack(
textDirection: TextDirection.ltr,
children: <Widget>[
hoverTarget,
AnimatedModalBarrier(dismissible: false, color: colorAnimation),
],
);
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
addTearDown(gesture.removePointer);
// Start out of hoverTarget
await gesture.moveTo(const Offset(100, 100));
await tester.pumpWidget(subject);
// Move into hoverTarget and tap
await gesture.down(const Offset(5, 5));
await tester.pumpWidget(subject);
await gesture.up();
await tester.pumpWidget(subject);
// Move out
await gesture.moveTo(const Offset(100, 100));
await tester.pumpWidget(subject);
expect(hovered, isFalse, reason: 'because the hover is not prevented by AnimatedModalBarrier');
});
testWidgets('does not prevent interactions with widgets in front of it', (WidgetTester tester) async {
final Widget subject = Stack(
textDirection: TextDirection.ltr,
children: <Widget>[
AnimatedModalBarrier(dismissible: false, color: colorAnimation),
tapTarget,
],
);
await tester.pumpWidget(subject);
await tester.tap(find.text('target'));
await tester.pumpWidget(subject);
expect(tapped, isTrue, reason: 'because the tap is prevented by AnimatedModalBarrier');
});
testWidgets('does not prevent interactions with translucent widgets in front of it', (WidgetTester tester) async {
bool dragged = false;
final Widget subject = Stack(
textDirection: TextDirection.ltr,
children: <Widget>[
AnimatedModalBarrier(dismissible: false, color: colorAnimation),
GestureDetector(
behavior: HitTestBehavior.translucent,
onHorizontalDragStart: (_) {
dragged = true;
},
child: const Center(
child: Text('target', textDirection: TextDirection.ltr),
),
),
],
);
await tester.pumpWidget(subject);
await tester.dragFrom(
tester.getBottomRight(find.byType(GestureDetector)) - const Offset(10, 10),
const Offset(-20, 0),
);
await tester.pumpWidget(subject);
expect(dragged, isTrue, reason: 'because the drag is prevented by AnimatedModalBarrier');
});
testWidgets('does not prevent hover interactions with widgets in front of it', (WidgetTester tester) async {
final Widget subject = Stack(
textDirection: TextDirection.ltr,
children: <Widget>[
AnimatedModalBarrier(dismissible: false, color: colorAnimation),
hoverTarget,
],
);
final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse);
addTearDown(gesture.removePointer);
// Start out of hoverTarget
await gesture.moveTo(const Offset(100, 100));
await tester.pumpWidget(subject);
expect(hovered, isFalse);
// Move into hoverTarget
await gesture.moveTo(const Offset(5, 5));
await tester.pumpWidget(subject);
expect(hovered, isTrue, reason: 'because the hover is prevented by AnimatedModalBarrier');
hovered = false;
// Move out
await gesture.moveTo(const Offset(100, 100));
await tester.pumpWidget(subject);
expect(hovered, isTrue, reason: 'because the hover is prevented by AnimatedModalBarrier');
hovered = false;
});
testWidgets('plays system alert sound when user tries to dismiss it', (WidgetTester tester) async {
final List<String> playedSystemSounds = <String>[];
try {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.platform, (MethodCall methodCall) async {
if (methodCall.method == 'SystemSound.play') {
playedSystemSounds.add(methodCall.arguments as String);
}
return null;
});
final Widget subject = Stack(
textDirection: TextDirection.ltr,
children: <Widget>[
tapTarget,
AnimatedModalBarrier(dismissible: false, color: colorAnimation),
],
);
await tester.pumpWidget(subject);
await tester.tap(find.text('target'), warnIfMissed: false);
await tester.pumpWidget(subject);
} finally {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, null);
}
expect(playedSystemSounds, hasLength(1));
expect(playedSystemSounds[0], SystemSoundType.alert.toString());
});
testWidgets('pops the Navigator when dismissed by primary tap', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) => const AnimatedSecondWidget(),
};
await tester.pumpWidget(MaterialApp(routes: routes));
// Initially the barrier is not visible
expect(find.byKey(const ValueKey<String>('barrier')), findsNothing);
// Tapping on X routes to the barrier
await tester.tap(find.text('X'));
await tester.pump(); // begin transition
await tester.pump(const Duration(seconds: 1)); // end transition
// Press the barrier; it shouldn't dismiss yet
final TestGesture gesture = await tester.press(
find.byKey(const ValueKey<String>('barrier')),
);
await tester.pumpAndSettle(); // begin transition
expect(find.byKey(const ValueKey<String>('barrier')), findsOneWidget);
// Release the pointer; the barrier should be dismissed
await gesture.up();
await tester.pumpAndSettle(const Duration(seconds: 1)); // end transition
expect(
find.byKey(const ValueKey<String>('barrier')),
findsNothing,
reason: 'The route should have been dismissed by tapping the barrier.',
);
});
testWidgets('pops the Navigator when dismissed by non-primary tap', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) => const AnimatedSecondWidget(),
};
await tester.pumpWidget(MaterialApp(routes: routes));
// Initially the barrier is not visible
expect(find.byKey(const ValueKey<String>('barrier')), findsNothing);
// Tapping on X routes to the barrier
await tester.tap(find.text('X'));
await tester.pump(); // begin transition
await tester.pump(const Duration(seconds: 1)); // end transition
// Press the barrier; it shouldn't dismiss yet
final TestGesture gesture = await tester.press(
find.byKey(const ValueKey<String>('barrier')),
buttons: kSecondaryButton,
);
await tester.pumpAndSettle(); // begin transition
expect(find.byKey(const ValueKey<String>('barrier')), findsOneWidget);
// Release the pointer; the barrier should be dismissed
await gesture.up();
await tester.pumpAndSettle(const Duration(seconds: 1)); // end transition
expect(
find.byKey(const ValueKey<String>('barrier')),
findsNothing,
reason: 'The route should have been dismissed by tapping the barrier.',
);
});
testWidgets('may pop the Navigator when competing with other gestures', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) => const AnimatedSecondWidgetWithCompetence(),
};
await tester.pumpWidget(MaterialApp(routes: routes));
// Initially the barrier is not visible
expect(find.byKey(const ValueKey<String>('barrier')), findsNothing);
// Tapping on X routes to the barrier
await tester.tap(find.text('X'));
await tester.pump(); // begin transition
await tester.pump(const Duration(seconds: 1)); // end transition
// Tap on the barrier to dismiss it
await tester.tap(find.byKey(const ValueKey<String>('barrier')));
await tester.pump(); // begin transition
await tester.pump(const Duration(seconds: 1)); // end transition
expect(
find.byKey(const ValueKey<String>('barrier')),
findsNothing,
reason: 'The route should have been dismissed by tapping the barrier.',
);
});
testWidgets('does not pop the Navigator with a WillPopScope that returns false', (WidgetTester tester) async {
bool willPopCalled = false;
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) =>
Stack(
children: <Widget>[
const AnimatedSecondWidget(),
WillPopScope(
child: const SizedBox(),
onWillPop: () async {
willPopCalled = true;
return false;
},
),
],
),
};
await tester.pumpWidget(MaterialApp(routes: routes));
// Initially the barrier is not visible
expect(find.byKey(const ValueKey<String>('barrier')), findsNothing);
// Tapping on X routes to the barrier
await tester.tap(find.text('X'));
await tester.pump(); // begin transition
await tester.pump(const Duration(seconds: 1)); // end transition
expect(willPopCalled, isFalse);
// Tap on the barrier to attempt to dismiss it
await tester.tap(find.byKey(const ValueKey<String>('barrier')));
await tester.pump(); // begin transition
await tester.pump(const Duration(seconds: 1)); // end transition
expect(
find.byKey(const ValueKey<String>('barrier')),
findsOneWidget,
reason: 'The route should still be present if the pop is vetoed.',
);
expect(willPopCalled, isTrue);
});
testWidgets('pops the Navigator with a WillPopScope that returns true', (WidgetTester tester) async {
bool willPopCalled = false;
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) =>
Stack(
children: <Widget>[
const AnimatedSecondWidget(),
WillPopScope(
child: const SizedBox(),
onWillPop: () async {
willPopCalled = true;
return true;
},
),
],
),
};
await tester.pumpWidget(MaterialApp(routes: routes));
// Initially the barrier is not visible
expect(find.byKey(const ValueKey<String>('barrier')), findsNothing);
// Tapping on X routes to the barrier
await tester.tap(find.text('X'));
await tester.pump(); // begin transition
await tester.pump(const Duration(seconds: 1)); // end transition
expect(willPopCalled, isFalse);
// Tap on the barrier to attempt to dismiss it
await tester.tap(find.byKey(const ValueKey<String>('barrier')));
await tester.pump(); // begin transition
await tester.pump(const Duration(seconds: 1)); // end transition
expect(
find.byKey(const ValueKey<String>('barrier')),
findsNothing,
reason: 'The route should not be present if the pop is permitted.',
);
expect(willPopCalled, isTrue);
});
testWidgets('will call onDismiss callback', (WidgetTester tester) async {
bool dismissCallbackCalled = false;
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) =>
AnimatedSecondWidget(onDismiss: () {
dismissCallbackCalled = true;
}),
};
await tester.pumpWidget(MaterialApp(routes: routes));
// Initially the barrier is not visible
expect(find.byKey(const ValueKey<String>('barrier')), findsNothing);
// Tapping on X routes to the barrier
await tester.tap(find.text('X'));
await tester.pump(); // begin transition
await tester.pump(const Duration(seconds: 1)); // end transition
expect(find.byKey(const ValueKey<String>('barrier')), findsOneWidget);
expect(dismissCallbackCalled, false);
// Tap on the barrier
await tester.tap(find.byKey(const ValueKey<String>('barrier')));
await tester.pumpAndSettle(const Duration(seconds: 1)); // end transition
expect(dismissCallbackCalled, true);
});
testWidgets('will not pop when given an onDismiss callback', (WidgetTester tester) async {
final Map<String, WidgetBuilder> routes = <String, WidgetBuilder>{
'/': (BuildContext context) => const FirstWidget(),
'/modal': (BuildContext context) => AnimatedSecondWidget(onDismiss: () {}),
};
await tester.pumpWidget(MaterialApp(routes: routes));
// Initially the barrier is not visible
expect(find.byKey(const ValueKey<String>('barrier')), findsNothing);
// Tapping on X routes to the barrier
await tester.tap(find.text('X'));
await tester.pump(); // begin transition
await tester.pump(const Duration(seconds: 1)); // end transition
expect(find.byKey(const ValueKey<String>('barrier')), findsOneWidget);
// Tap on the barrier
await tester.tap(find.byKey(const ValueKey<String>('barrier')));
await tester.pumpAndSettle(const Duration(seconds: 1)); // end transition
expect(
find.byKey(const ValueKey<String>('barrier')),
findsOneWidget,
reason: 'The route should not have been dismissed by tapping the barrier, as there was a onDismiss callback given.',
);
});
testWidgets('Undismissible AnimatedModalBarrier hidden in semantic tree', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(AnimatedModalBarrier(dismissible: false, color: colorAnimation));
final TestSemantics expectedSemantics = TestSemantics.root();
expect(semantics, hasSemantics(expectedSemantics));
semantics.dispose();
});
testWidgets('Dismissible AnimatedModalBarrier includes button in semantic tree on iOS', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(Directionality(
textDirection: TextDirection.ltr,
child: AnimatedModalBarrier(
semanticsLabel: 'Dismiss',
color: colorAnimation,
),
));
final TestSemantics expectedSemantics = TestSemantics.root(
children: <TestSemantics>[
TestSemantics.rootChild(
rect: TestSemantics.fullScreen,
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.dismiss],
label: 'Dismiss',
textDirection: TextDirection.ltr,
),
],
);
expect(semantics, hasSemantics(expectedSemantics, ignoreId: true));
semantics.dispose();
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS}));
testWidgets('ModalBarrier uses default mouse cursor', (WidgetTester tester) async {
testWidgets(
'Dismissible AnimatedModalBarrier is hidden on Android (back button is used to dismiss)', (WidgetTester tester) async {
final SemanticsTester semantics = SemanticsTester(tester);
await tester.pumpWidget(AnimatedModalBarrier(color: colorAnimation));
final TestSemantics expectedSemantics = TestSemantics.root();
expect(semantics, hasSemantics(expectedSemantics));
semantics.dispose();
});
});
testWidgets('uses default mouse cursor', (WidgetTester tester) async {
await tester.pumpWidget(Stack(
textDirection: TextDirection.ltr,
children: const <Widget>[
......@@ -485,7 +924,7 @@ void main() {
}
class FirstWidget extends StatelessWidget {
const FirstWidget({ Key? key }) : super(key: key);
const FirstWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
......@@ -498,7 +937,7 @@ class FirstWidget extends StatelessWidget {
}
class SecondWidget extends StatelessWidget {
const SecondWidget({ Key? key, this.onDismiss }) : super(key: key);
const SecondWidget({Key? key, this.onDismiss}) : super(key: key);
final VoidCallback? onDismiss;
......@@ -511,8 +950,23 @@ class SecondWidget extends StatelessWidget {
}
}
class AnimatedSecondWidget extends StatelessWidget {
const AnimatedSecondWidget({Key? key, this.onDismiss}) : super(key: key);
final VoidCallback? onDismiss;
@override
Widget build(BuildContext context) {
return AnimatedModalBarrier(
key: const ValueKey<String>('barrier'),
color: const AlwaysStoppedAnimation<Color?>(Colors.red),
onDismiss: onDismiss,
);
}
}
class SecondWidgetWithCompetence extends StatelessWidget {
const SecondWidgetWithCompetence({ Key? key }) : super(key: key);
const SecondWidgetWithCompetence({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
......@@ -529,3 +983,22 @@ class SecondWidgetWithCompetence extends StatelessWidget {
);
}
}
class AnimatedSecondWidgetWithCompetence extends StatelessWidget {
const AnimatedSecondWidgetWithCompetence({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
const AnimatedModalBarrier(
key: ValueKey<String>('barrier'),
color: AlwaysStoppedAnimation<Color?>(Colors.red),
),
GestureDetector(
onVerticalDragStart: (_) {},
behavior: HitTestBehavior.translucent,
child: Container(),
),
],
);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment