Skip to content

GoRouter ShellRoute wrong official example and transition bug #122972

Open

Description

@sovetski

Hi,

There is an en error on this official documentation (or maybe I am wrong): https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/shell_route.dart

As you can see on the code, each Screen widget, starts with a "Scaffold". But they parent with BottomNavigation have also a Scaffold which duplicates the usage of Scaffold and in the widget tree we have 2 Scaffold.

On the flutter documentation we have this : https://api.flutter.dev/flutter/material/Scaffold-class.html#:~:text=It%20is%20typically,updates%20the%20AppBar

It is typically not necessary to nest Scaffolds.

On my app, I wanted to use only 1 shared Scaffold, but it cause a bug during transition. To replicate it, you can use this code: https://github.com/flutter/packages/blob/main/packages/go_router/example/lib/shell_route.dart

And just change the ScreenB widget content to this (just removing the Scaffold parent):

/// The second screen in the bottom navigation bar.
class ScreenB extends StatelessWidget {
  /// Constructs a [ScreenB] widget.
  const ScreenB({super.key});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: <Widget>[
          const Text('Screen B'),
          TextButton(
            onPressed: () {
              GoRouter.of(context).go('/b/details');
            },
            child: const Text('View B details'),
          ),
        ],
      ),
    );
  }
}

Or see the real preview here: https://flutlab.io/editor/46151d54-95e1-49cf-9fe0-ad0afc469c76

Then, if you try to go from A Screen, to B Screen, you will the that there is a transition bug from the view A to B. Like this: (https://user-images.githubusercontent.com/13520683/226175787-7ccc910f-11b5-446c-a106-0f0ba1d79c22.webm)

untitled.webm

As you can see, there is no problem to go From A to C, because the A and C have their own Scaffold so duplicated Scaffolds with BottomNavigationBar. But as I removed the Scaffold from the Screen B to keep only 1 scaffold, there is a transition bug from A to B, C to B etc. But no problem from B to A, or C.

Activity

  1. added
    in triagePresently being triaged by the triage team
    on Mar 20, 2023
  2. exaby73 commented on Mar 20, 2023

    @exaby73
    Member

    Hello @sovetski. This is expected since the Scaffold adds a background to the screen by default of white. Without the Scaffold, the background is transparent and therefore you see this effect. This is not a bug and is expected. You can extract your shared Scaffold into a reusable widget and use it on all screens. If you need more help with your code, I suggest finding help on various communities found on https://flutter.dev/community. Since this is not a bug, I am closing this issue. Thank you

  3. added
    r: invalidIssue is closed as not valid
    and removed
    in triagePresently being triaged by the triage team
    on Mar 20, 2023
  4. sovetski commented on Mar 20, 2023

    @sovetski
    Author

    Hi @exaby73,

    Without the Scaffold, the background is transparent and therefore you see this effect.

    As I mentionned, there is a Scaffold, that is why I don't understand why there is a problem. Did you checked this code? https://flutlab.io/editor/46151d54-95e1-49cf-9fe0-ad0afc469c76 You will see that there is a Scaffold, but instead of 2 Scaffold, on the Screen B, there is only 1 Scaffold so it is very strange behaviour.

    I also tried to use a shared Scaffold but the problem is same. You can try it on your side and you will see that it is not working.

  5. exaby73 commented on Mar 20, 2023

    @exaby73
    Member

    The live preview does not load for me. Please try to include the code in code blocks in the comment itself, or use https://dartpad.dev to provide a code sample. But again, ultimately ScreenB will be shown, and since ScreenB does not have a Scaffold, it will be transparent. Also, lots of widgets rely on a Scaffold like ScaffoldMessager to show SnackBars, so you will get problems later

  6. sovetski commented on Mar 20, 2023

    @sovetski
    Author

    @exaby73 sorry, maybe my explanation is wrong, I will try to explain again. ScreenB itself does not contain a Scaffold. But its parent widget ScaffoldWithNavBar does have a Scaffold. And since ScreenB is a child element of ScaffoldWithNavBar it does have a Scaffold. The reason why I remove the Scaffold from ScreenB is to avoid getting 2 Scaffold on my Widget tree as I mentioned in the issue description, the Flutter documentation says

    It is typically not necessary to nest Scaffolds.

    There is the final code that you can try and see the problem.

    See the code
    // Copyright 2013 The Flutter Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    
    import 'package:flutter/material.dart';
    import 'package:go_router/go_router.dart';
    
    final GlobalKey<NavigatorState> _rootNavigatorKey =
        GlobalKey<NavigatorState>(debugLabel: 'root');
    final GlobalKey<NavigatorState> _shellNavigatorKey =
        GlobalKey<NavigatorState>(debugLabel: 'shell');
    
    // This scenario demonstrates how to set up nested navigation using ShellRoute,
    // which is a pattern where an additional Navigator is placed in the widget tree
    // to be used instead of the root navigator. This allows deep-links to display
    // pages along with other UI components such as a BottomNavigationBar.
    //
    // This example demonstrates how to display a route within a ShellRoute and also
    // push a screen using a different navigator (such as the root Navigator) by
    // providing a `parentNavigatorKey`.
    
    void main() {
      runApp(ShellRouteExampleApp());
    }
    
    /// An example demonstrating how to use [ShellRoute]
    class ShellRouteExampleApp extends StatelessWidget {
      /// Creates a [ShellRouteExampleApp]
      ShellRouteExampleApp({super.key});
    
      final GoRouter _router = GoRouter(
        navigatorKey: _rootNavigatorKey,
        initialLocation: '/a',
        routes: <RouteBase>[
          /// Application shell
          ShellRoute(
            navigatorKey: _shellNavigatorKey,
            builder: (BuildContext context, GoRouterState state, Widget child) {
              return ScaffoldWithNavBar(child: child);
            },
            routes: <RouteBase>[
              /// The first screen to display in the bottom navigation bar.
              GoRoute(
                path: '/a',
                builder: (BuildContext context, GoRouterState state) {
                  return const ScreenA();
                },
                routes: <RouteBase>[
                  // The details screen to display stacked on the inner Navigator.
                  // This will cover screen A but not the application shell.
                  GoRoute(
                    path: 'details',
                    builder: (BuildContext context, GoRouterState state) {
                      return const DetailsScreen(label: 'A');
                    },
                  ),
                ],
              ),
    
              /// Displayed when the second item in the the bottom navigation bar is
              /// selected.
              GoRoute(
                path: '/b',
                builder: (BuildContext context, GoRouterState state) {
                  return const ScreenB();
                },
                routes: <RouteBase>[
                  /// Same as "/a/details", but displayed on the root Navigator by
                  /// specifying [parentNavigatorKey]. This will cover both screen B
                  /// and the application shell.
                  GoRoute(
                    path: 'details',
                    parentNavigatorKey: _rootNavigatorKey,
                    builder: (BuildContext context, GoRouterState state) {
                      return const DetailsScreen(label: 'B');
                    },
                  ),
                ],
              ),
    
              /// The third screen to display in the bottom navigation bar.
              GoRoute(
                path: '/c',
                builder: (BuildContext context, GoRouterState state) {
                  return const ScreenC();
                },
                routes: <RouteBase>[
                  // The details screen to display stacked on the inner Navigator.
                  // This will cover screen A but not the application shell.
                  GoRoute(
                    path: 'details',
                    builder: (BuildContext context, GoRouterState state) {
                      return const DetailsScreen(label: 'C');
                    },
                  ),
                ],
              ),
            ],
          ),
        ],
      );
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp.router(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          routerConfig: _router,
        );
      }
    }
    
    /// Builds the "shell" for the app by building a Scaffold with a
    /// BottomNavigationBar, where [child] is placed in the body of the Scaffold.
    class ScaffoldWithNavBar extends StatelessWidget {
      /// Constructs an [ScaffoldWithNavBar].
      const ScaffoldWithNavBar({
        required this.child,
        super.key,
      });
    
      /// The widget to display in the body of the Scaffold.
      /// In this sample, it is a Navigator.
      final Widget child;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: child,
          bottomNavigationBar: BottomNavigationBar(
            items: const <BottomNavigationBarItem>[
              BottomNavigationBarItem(
                icon: Icon(Icons.home),
                label: 'A Screen',
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.business),
                label: 'B Screen',
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.notification_important_rounded),
                label: 'C Screen',
              ),
            ],
            currentIndex: _calculateSelectedIndex(context),
            onTap: (int idx) => _onItemTapped(idx, context),
          ),
        );
      }
    
      static int _calculateSelectedIndex(BuildContext context) {
        final String location = GoRouterState.of(context).location;
        if (location.startsWith('/a')) {
          return 0;
        }
        if (location.startsWith('/b')) {
          return 1;
        }
        if (location.startsWith('/c')) {
          return 2;
        }
        return 0;
      }
    
      void _onItemTapped(int index, BuildContext context) {
        switch (index) {
          case 0:
            GoRouter.of(context).go('/a');
            break;
          case 1:
            GoRouter.of(context).go('/b');
            break;
          case 2:
            GoRouter.of(context).go('/c');
            break;
        }
      }
    }
    
    /// The first screen in the bottom navigation bar.
    class ScreenA extends StatelessWidget {
      /// Constructs a [ScreenA] widget.
      const ScreenA({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                const Text('Screen A'),
                TextButton(
                  onPressed: () {
                    GoRouter.of(context).go('/a/details');
                  },
                  child: const Text('View A details'),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    /// The second screen in the bottom navigation bar.
    class ScreenB extends StatelessWidget {
      /// Constructs a [ScreenB] widget.
      const ScreenB({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              const Text('Screen B'),
              TextButton(
                onPressed: () {
                  GoRouter.of(context).go('/b/details');
                },
                child: const Text('View B details'),
              ),
            ],
          ),
        );
      }
    }
    
    /// The third screen in the bottom navigation bar.
    class ScreenC extends StatelessWidget {
      /// Constructs a [ScreenC] widget.
      const ScreenC({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                const Text('Screen C'),
                TextButton(
                  onPressed: () {
                    GoRouter.of(context).go('/c/details');
                  },
                  child: const Text('View C details'),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    /// The details screen for either the A, B or C screen.
    class DetailsScreen extends StatelessWidget {
      /// Constructs a [DetailsScreen].
      const DetailsScreen({
        required this.label,
        super.key,
      });
    
      /// The label to display in the center of the screen.
      final String label;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Details Screen'),
          ),
          body: Center(
            child: Text(
              'Details for $label',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ),
        );
      }
    }
  7. exaby73 commented on Mar 20, 2023

    @exaby73
    Member

    Okay I get it. From what I can see, the builder is not being called during the transition, but rather only after the transition is over. I changed the background color of ScaffoldWithNav to red, and you can see in the video below that the red background only appears after the animation. I am not sure if this is intended behavior, therefore I'll label the issue for further insights from the team

    Code Sample
    // Copyright 2013 The Flutter Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    
    import 'package:flutter/material.dart';
    import 'package:go_router/go_router.dart';
    
    final GlobalKey<NavigatorState> _rootNavigatorKey =
        GlobalKey<NavigatorState>(debugLabel: 'root');
    final GlobalKey<NavigatorState> _shellNavigatorKey =
        GlobalKey<NavigatorState>(debugLabel: 'shell');
    
    // This scenario demonstrates how to set up nested navigation using ShellRoute,
    // which is a pattern where an additional Navigator is placed in the widget tree
    // to be used instead of the root navigator. This allows deep-links to display
    // pages along with other UI components such as a BottomNavigationBar.
    //
    // This example demonstrates how to display a route within a ShellRoute and also
    // push a screen using a different navigator (such as the root Navigator) by
    // providing a `parentNavigatorKey`.
    
    void main() {
      runApp(ShellRouteExampleApp());
    }
    
    /// An example demonstrating how to use [ShellRoute]
    class ShellRouteExampleApp extends StatelessWidget {
      /// Creates a [ShellRouteExampleApp]
      ShellRouteExampleApp({super.key});
    
      final GoRouter _router = GoRouter(
        navigatorKey: _rootNavigatorKey,
        initialLocation: '/a',
        routes: <RouteBase>[
          /// Application shell
          ShellRoute(
            navigatorKey: _shellNavigatorKey,
            builder: (BuildContext context, GoRouterState state, Widget child) {
              return ScaffoldWithNavBar(child: child);
            },
            routes: <RouteBase>[
              /// The first screen to display in the bottom navigation bar.
              GoRoute(
                path: '/a',
                builder: (BuildContext context, GoRouterState state) {
                  return const ScreenA();
                },
                routes: <RouteBase>[
                  // The details screen to display stacked on the inner Navigator.
                  // This will cover screen A but not the application shell.
                  GoRoute(
                    path: 'details',
                    builder: (BuildContext context, GoRouterState state) {
                      return const DetailsScreen(label: 'A');
                    },
                  ),
                ],
              ),
    
              /// Displayed when the second item in the the bottom navigation bar is
              /// selected.
              GoRoute(
                path: '/b',
                builder: (BuildContext context, GoRouterState state) {
                  return const ScreenB();
                },
                routes: <RouteBase>[
                  /// Same as "/a/details", but displayed on the root Navigator by
                  /// specifying [parentNavigatorKey]. This will cover both screen B
                  /// and the application shell.
                  GoRoute(
                    path: 'details',
                    parentNavigatorKey: _rootNavigatorKey,
                    builder: (BuildContext context, GoRouterState state) {
                      return const DetailsScreen(label: 'B');
                    },
                  ),
                ],
              ),
    
              /// The third screen to display in the bottom navigation bar.
              GoRoute(
                path: '/c',
                builder: (BuildContext context, GoRouterState state) {
                  return const ScreenC();
                },
                routes: <RouteBase>[
                  // The details screen to display stacked on the inner Navigator.
                  // This will cover screen A but not the application shell.
                  GoRoute(
                    path: 'details',
                    builder: (BuildContext context, GoRouterState state) {
                      return const DetailsScreen(label: 'C');
                    },
                  ),
                ],
              ),
            ],
          ),
        ],
      );
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp.router(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          routerConfig: _router,
        );
      }
    }
    
    /// Builds the "shell" for the app by building a Scaffold with a
    /// BottomNavigationBar, where [child] is placed in the body of the Scaffold.
    class ScaffoldWithNavBar extends StatelessWidget {
      /// Constructs an [ScaffoldWithNavBar].
      const ScaffoldWithNavBar({
        required this.child,
        super.key,
      });
    
      /// The widget to display in the body of the Scaffold.
      /// In this sample, it is a Navigator.
      final Widget child;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: Colors.red,
          body: child,
          bottomNavigationBar: BottomNavigationBar(
            items: const <BottomNavigationBarItem>[
              BottomNavigationBarItem(
                icon: Icon(Icons.home),
                label: 'A Screen',
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.business),
                label: 'B Screen',
              ),
              BottomNavigationBarItem(
                icon: Icon(Icons.notification_important_rounded),
                label: 'C Screen',
              ),
            ],
            currentIndex: _calculateSelectedIndex(context),
            onTap: (int idx) => _onItemTapped(idx, context),
          ),
        );
      }
    
      static int _calculateSelectedIndex(BuildContext context) {
        final String location = GoRouterState.of(context).location;
        if (location.startsWith('/a')) {
          return 0;
        }
        if (location.startsWith('/b')) {
          return 1;
        }
        if (location.startsWith('/c')) {
          return 2;
        }
        return 0;
      }
    
      void _onItemTapped(int index, BuildContext context) {
        switch (index) {
          case 0:
            GoRouter.of(context).go('/a');
            break;
          case 1:
            GoRouter.of(context).go('/b');
            break;
          case 2:
            GoRouter.of(context).go('/c');
            break;
        }
      }
    }
    
    /// The first screen in the bottom navigation bar.
    class ScreenA extends StatelessWidget {
      /// Constructs a [ScreenA] widget.
      const ScreenA({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                const Text('Screen A'),
                TextButton(
                  onPressed: () {
                    GoRouter.of(context).go('/a/details');
                  },
                  child: const Text('View A details'),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    /// The second screen in the bottom navigation bar.
    class ScreenB extends StatelessWidget {
      /// Constructs a [ScreenB] widget.
      const ScreenB({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: <Widget>[
              const Text('Screen B'),
              TextButton(
                onPressed: () {
                  GoRouter.of(context).go('/b/details');
                },
                child: const Text('View B details'),
              ),
            ],
          ),
        );
      }
    }
    
    /// The third screen in the bottom navigation bar.
    class ScreenC extends StatelessWidget {
      /// Constructs a [ScreenC] widget.
      const ScreenC({super.key});
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(),
          body: Center(
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: <Widget>[
                const Text('Screen C'),
                TextButton(
                  onPressed: () {
                    GoRouter.of(context).go('/c/details');
                  },
                  child: const Text('View C details'),
                ),
              ],
            ),
          ),
        );
      }
    }
    
    /// The details screen for either the A, B or C screen.
    class DetailsScreen extends StatelessWidget {
      /// Constructs a [DetailsScreen].
      const DetailsScreen({
        required this.label,
        super.key,
      });
    
      /// The label to display in the center of the screen.
      final String label;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: const Text('Details Screen'),
          ),
          body: Center(
            child: Text(
              'Details for $label',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ),
        );
      }
    }
    Video demonstration
    issue_122972_nZ9y9d9qOb.mp4
    flutter doctor -v (Master)
    [!] Flutter (Channel master, 3.9.0-11.0.pre.7, on Microsoft Windows [Version 10.0.22621.1413], locale en-US)
        • Flutter version 3.9.0-11.0.pre.7 on channel master at C:\Users\Nabeel\fvm\versions\master
        ! The flutter binary is not on your path. Consider adding C:\Users\Nabeel\fvm\versions\master\bin to your path.
        ! Warning: `dart` on your path resolves to C:\tools\dart-sdk\bin\dart.exe, which is not inside your current Flutter SDK checkout at C:\Users\Nabeel\fvm\versions\master. Consider adding C:\Users\Nabeel\fvm\versions\master\bin to the front of your path.
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision 1cd55934f9 (41 minutes ago), 2023-03-20 02:26:27 -0400
        • Engine revision 19a5f61ff7
        • Dart version 3.0.0 (build 3.0.0-339.0.dev)
        • DevTools version 2.22.2
        • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades.
    
    [✓] Windows Version (Installed version of Windows is version 10 or higher)
    
    [✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
        • Android SDK at C:\android-sdk
        • Platform android-33, build-tools 33.0.0
        • Java binary at: C:\Users\Nabeel\AppData\Local\JetBrains\Toolbox\apps\AndroidStudio\ch-0\221.6008.13.2211.9514443\jbr\bin\java
        • Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-9505619)
        • All Android licenses accepted.
    
    [✓] Chrome - develop for the web
        • CHROME_EXECUTABLE = C:\Program Files\BraveSoftware\Brave-Browser\Application\brave.exe
    
    [✓] Visual Studio - develop for Windows (Visual Studio Community 2022 17.5.0)
        • Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Community
        • Visual Studio Community 2022 version 17.5.33414.496
        • Windows 10 SDK version 10.0.22621.0
    
    [✓] Android Studio (version 2021.3)
        • Android Studio at C:\Users\Nabeel\AppData\Local\JetBrains\Toolbox\apps\AndroidStudio\ch-0\213.7172.25.2113.9014738
        • Flutter plugin version 71.2.3
        • Dart plugin version 213.7433
        • Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
    
    [✓] Android Studio (version 2022.1)
        • Android Studio at C:\Users\Nabeel\AppData\Local\JetBrains\Toolbox\apps\AndroidStudio\ch-0\221.6008.13.2211.9514443
        • Flutter plugin version 72.0.2
        • Dart plugin version 221.6096
        • Java version OpenJDK Runtime Environment (build 11.0.15+0-b2043.56-9505619)
    
    [✓] VS Code (version 1.76.2)
        • VS Code at C:\Users\Nabeel\AppData\Local\Programs\Microsoft VS Code
        • Flutter extension version 3.46.0
    
    [✓] Connected device (3 available)
        • Windows (desktop) • windows • windows-x64    • Microsoft Windows [Version 10.0.22621.1413]
        • Chrome (web)      • chrome  • web-javascript • unknown
        • Edge (web)        • edge    • web-javascript • Microsoft Edge 109.0.1518.78
    
    [✓] Network resources
        • All expected network resources are available.
    
    ! Doctor found issues in 1 category.
    
  8. added
    in triagePresently being triaged by the triage team
    packageflutter/packages repository. See also p: labels.
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    and removed
    r: invalidIssue is closed as not valid
    on Mar 20, 2023
  9. 9 remaining items

  10. remeoj commented on Jul 8, 2023

    @remeoj

    I'm getting this issue as well! I don't feel like this should be WAI, I don't think we should have to nest Scaffolds. But going to wrap my widgets in a scaffold for now. I'm also finding this issue, particularly using GoRouter!

  11. sovetski commented on Sep 2, 2023

    @sovetski
    Author

    Any update on this?

    I know the team has a lot of work to do already, but this problem has exists for a while. I would have liked to participate and make a pull request, but unfortunately I don't master flutter at this point

  12. chikko80 commented on Oct 18, 2023

    @chikko80

    Facing the exact same issue using Gorouter ShellRotue

  13. yohanangulo commented on Jan 27, 2024

    @yohanangulo

    yep, same here.

    I had to do this because I don't want to use scaffolds in all my screens

    GoRoute( path: Routes.collectionsScreen, pageBuilder: (context, state) { return const NoTransitionPage(child: CollectionsScreen()); }, ),

  14. SakshamKarnawat commented on Aug 14, 2024

    @SakshamKarnawat

    Facing the exact same issue. Using nested scaffolds for now..

  15. songhee24 commented on Nov 11, 2024

    @songhee24

    Hello everyone, I found this solution
    I don't know if this solution is correct, but maybe it will help someone

    class BottomDestination {
      final String label;
      final IconData icon;
    
      const BottomDestination({required this.label, required this.icon});
    }
    
    const destinations = [
      BottomDestination(label: 'Поставки', icon: Icons.widgets),
      BottomDestination(label: 'Обновить Карточки', icon: Icons.storage),
    ];
    
    final _rootNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'root');
    final router = GoRouter(
      initialLocation: PathKeys.supplieses(),
      navigatorKey: _rootNavigatorKey,
      routes: [
        StatefulShellRoute.indexedStack(
            builder: (context, state, navigationShell) =>
                LayoutScaffold(navigationShell: navigationShell),
            branches: [
              StatefulShellBranch(routes: [
                GoRoute(
                    path: PathKeys.supplieses(),
                    builder: (context, state) => const SuppliesesPage())
              ]),
              StatefulShellBranch(routes: [
                GoRoute(
                    path: PathKeys.uploadExelFile(),
                    builder: (context, state) => const DatabasePage())
              ]),
            ])
    ],
    );
    
    class LayoutScaffold extends StatelessWidget {
      const LayoutScaffold({required this.navigationShell, super.key});
    
      final StatefulNavigationShell navigationShell;
    
      @override
      Widget build(BuildContext context) {
        LinearGradient gradient = const LinearGradient(
            colors: [Color(0xff33265a), Color(0xff1c1925), Color(0xff382375)],
            begin: Alignment.topRight,
            end: Alignment.bottomLeft,
            tileMode: TileMode.clamp);
    
        return Scaffold(
          extendBody: true,
          resizeToAvoidBottomInset: false,
          backgroundColor: Colors.transparent,
          body: Container(
              decoration: BoxDecoration(gradient: gradient),
              child: navigationShell),
          bottomNavigationBar: ClipRRect(
            borderRadius: const BorderRadius.all(Radius.circular(25)),
            child: BackdropFilter(
              filter: ImageFilter.blur(sigmaX: 5, sigmaY: 15),
              child: NavigationBar(
                  surfaceTintColor: Colors.amber,
                  elevation: 0,
                  selectedIndex: navigationShell.currentIndex,
                  onDestinationSelected: navigationShell.goBranch,
                  backgroundColor: Colors.grey.withOpacity(0.11),
                  indicatorColor: Colors.transparent,
                  labelBehavior:
                      NavigationDestinationLabelBehavior.onlyShowSelected,
                  destinations: destinations
                      .map(
                        (destination) => NavigationDestination(
                          icon: Icon(destination.icon),
                          label: destination.label,
                          selectedIcon: Icon(destination.icon, color: Colors.white),
                        ),
                      )
                      .toList()),
            ),
          ),
        );
      }
    }
    

    finall result:
    Screen Recording 2024-11-12 at 03 02 25

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listfound in release: 3.7Found to occur in 3.7found in release: 3.9Found to occur in 3.9has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: go_routerThe go_router packagepackageflutter/packages repository. See also p: labels.team-ecosystemOwned by Ecosystem teamtriaged-ecosystemTriaged by Ecosystem team

    Type

    No type

    Projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions