diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart index a29c87609d..989cccc358 100644 --- a/packages/flutter/lib/src/material/scaffold.dart +++ b/packages/flutter/lib/src/material/scaffold.dart @@ -278,7 +278,7 @@ class ScaffoldMessengerState extends State with TickerProvide void _register(ScaffoldState scaffold) { _scaffolds.add(scaffold); - if (_snackBars.isNotEmpty) { + if (_snackBars.isNotEmpty && _isRoot(scaffold)) { scaffold._updateSnackBar(); } } diff --git a/packages/flutter/test/material/scaffold_test.dart b/packages/flutter/test/material/scaffold_test.dart index 5ead37e3df..d2bd09baa9 100644 --- a/packages/flutter/test/material/scaffold_test.dart +++ b/packages/flutter/test/material/scaffold_test.dart @@ -2199,6 +2199,77 @@ void main() { ' MaterialApp at the top of your application widget tree.\n' )); }); + + testWidgets('ScaffoldMessenger checks for nesting when a new Scaffold is registered', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/77251 + const String snackBarContent = 'SnackBar Content'; + await tester.pumpWidget(MaterialApp( + home: Builder( + builder: (BuildContext context) => Scaffold( + body: Scaffold( + body: TextButton( + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (BuildContext context) { + return Scaffold( + body: Column( + children: [ + TextButton( + onPressed: () { + const SnackBar snackBar = SnackBar( + content: Text(snackBarContent), + behavior: SnackBarBehavior.floating, + ); + ScaffoldMessenger.of(context).showSnackBar(snackBar); + }, + child: const Text('Show SnackBar'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, null); + }, + child: const Text('Pop route'), + ) + ], + ), + ); + }, + ), + ); + }, + child: const Text('Push route'), + ), + ), + ), + ) + )); + + expect(find.text(snackBarContent), findsNothing); + await tester.tap(find.text('Push route')); + await tester.pumpAndSettle(); + expect(find.text(snackBarContent), findsNothing); + expect(find.text('Pop route'), findsOneWidget); + + // Show SnackBar on second page + await tester.tap(find.text('Show SnackBar')); + await tester.pump(); + expect(find.text(snackBarContent), findsOneWidget); + // Pop the second page, the SnackBar completes a hero animation to the next route. + // If we have not handled the nested Scaffolds properly, this will throw an + // exception as duplicate SnackBars on the first route would have a common hero tag. + await tester.tap(find.text('Pop route')); + await tester.pump(); + // There are SnackBars two during the execution of the hero animation. + expect(find.text(snackBarContent), findsNWidgets(2)); + await tester.pumpAndSettle(); + expect(find.text(snackBarContent), findsOneWidget); + // Allow the SnackBar to animate out + await tester.pump(const Duration(seconds: 4)); + await tester.pumpAndSettle(); + expect(find.text(snackBarContent), findsNothing); + }); } class _GeometryListener extends StatefulWidget {