Skip to content

Commit be0c370

Browse files
authored
Add transitionOnUserGestures to true on SnackBar for back swipe (#106638)
1 parent dbe38ee commit be0c370

File tree

2 files changed

+84
-0
lines changed

2 files changed

+84
-0
lines changed

packages/flutter/lib/src/material/snack_bar.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,13 @@ class _SnackBarActionState extends State<SnackBarAction> {
158158
/// A SnackBar with an action will not time out when TalkBack or VoiceOver are
159159
/// enabled. This is controlled by [AccessibilityFeatures.accessibleNavigation].
160160
///
161+
/// During page transitions, the [SnackBar] will smoothly animate to its
162+
/// location on the other page. For example if the [SnackBar.behavior] is set to
163+
/// [SnackBarBehavior.floating] and the next page has a floating action button,
164+
/// while the current one does not, the [SnackBar] will smoothly animate above
165+
/// the floating action button. It also works in the case of a back gesture
166+
/// transition.
167+
///
161168
/// {@tool dartpad}
162169
/// Here is an example of a [SnackBar] with an [action] button implemented using
163170
/// [SnackBarAction].
@@ -620,6 +627,7 @@ class _SnackBarState extends State<SnackBar> {
620627

621628
return Hero(
622629
tag: '<SnackBar Hero tag - ${widget.content}>',
630+
transitionOnUserGestures: true,
623631
child: ClipRect(
624632
clipBehavior: widget.clipBehavior,
625633
child: snackBarTransition,

packages/flutter/test/material/snack_bar_test.dart

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,6 +1795,82 @@ void main() {
17951795
expect(find.text(secondHeader), findsOneWidget);
17961796
});
17971797

1798+
testWidgets('Should have only one SnackBar during back swipe navigation', (WidgetTester tester) async {
1799+
const String snackBarText = 'hello snackbar';
1800+
const Key snackTarget = Key('snack-target');
1801+
const Key transitionTarget = Key('transition-target');
1802+
1803+
Widget buildApp() {
1804+
final PageTransitionsTheme pageTransitionTheme = PageTransitionsTheme(
1805+
builders: <TargetPlatform, PageTransitionsBuilder>{
1806+
for(final TargetPlatform platform in TargetPlatform.values)
1807+
platform: const CupertinoPageTransitionsBuilder(),
1808+
},
1809+
);
1810+
return MaterialApp(
1811+
theme: ThemeData(pageTransitionsTheme: pageTransitionTheme),
1812+
initialRoute: '/',
1813+
routes: <String, WidgetBuilder> {
1814+
'/': (BuildContext context) {
1815+
return Scaffold(
1816+
body: Center(
1817+
child: ElevatedButton(
1818+
key: transitionTarget,
1819+
child: const Text('PUSH'),
1820+
onPressed: () {
1821+
Navigator.of(context).pushNamed('/second');
1822+
},
1823+
),
1824+
),
1825+
1826+
);
1827+
},
1828+
'/second': (BuildContext context) {
1829+
return Scaffold(
1830+
floatingActionButton: FloatingActionButton(
1831+
key: snackTarget,
1832+
onPressed: () async {
1833+
ScaffoldMessenger.of(context).showSnackBar(
1834+
const SnackBar(
1835+
content: Text(snackBarText),
1836+
),
1837+
);
1838+
},
1839+
child: const Text('X'),
1840+
),
1841+
);
1842+
},
1843+
},
1844+
);
1845+
}
1846+
await tester.pumpWidget(buildApp());
1847+
1848+
// Transition to second page.
1849+
await tester.tap(find.byKey(transitionTarget));
1850+
await tester.pumpAndSettle();
1851+
1852+
// Present SnackBar
1853+
await tester.tap(find.byKey(snackTarget));
1854+
await tester.pump(); // schedule animation
1855+
expect(find.text(snackBarText), findsOneWidget);
1856+
await tester.pump(); // begin animation
1857+
expect(find.text(snackBarText), findsOneWidget);
1858+
await tester.pump(const Duration(milliseconds: 750));
1859+
expect(find.text(snackBarText), findsOneWidget);
1860+
1861+
// Start the gesture at the edge of the screen.
1862+
final TestGesture gesture = await tester.startGesture(const Offset(5.0, 200.0));
1863+
// Trigger the swipe.
1864+
await gesture.moveBy(const Offset(100.0, 0.0));
1865+
1866+
// Back gestures should trigger and draw the hero transition in the very same
1867+
// frame (since the "from" route has already moved to reveal the "to" route).
1868+
await tester.pump();
1869+
1870+
// We should have only one SnackBar displayed on the screen.
1871+
expect(find.text(snackBarText), findsOneWidget);
1872+
});
1873+
17981874
testWidgets('SnackBars should be shown above the bottomSheet', (WidgetTester tester) async {
17991875
await tester.pumpWidget(const MaterialApp(
18001876
home: Scaffold(

0 commit comments

Comments
 (0)