Created
March 15, 2024 08:50
-
-
Save osmanyz/8717b7d113333659dd8ecb5ccaa75975 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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> _sectionANavigatorKey = | |
| GlobalKey<NavigatorState>(debugLabel: 'sectionANav'); | |
| // This example demonstrates how to setup nested navigation using a | |
| // BottomNavigationBar, where each bar item uses its own persistent navigator, | |
| // i.e. navigation state is maintained separately for each item. This setup also | |
| // enables deep linking into nested pages. | |
| void main() { | |
| runApp(NestedTabNavigationExampleApp()); | |
| } | |
| /// An example demonstrating how to use nested navigators | |
| class NestedTabNavigationExampleApp extends StatelessWidget { | |
| /// Creates a NestedTabNavigationExampleApp | |
| NestedTabNavigationExampleApp({super.key}); | |
| final GoRouter _router = GoRouter( | |
| navigatorKey: _rootNavigatorKey, | |
| initialLocation: '/a', | |
| routes: <RouteBase>[ | |
| StatefulShellRoute.indexedStack( | |
| builder: (BuildContext context, GoRouterState state, | |
| StatefulNavigationShell navigationShell) { | |
| // Return the widget that implements the custom shell (in this case | |
| // using a BottomNavigationBar). The StatefulNavigationShell is passed | |
| // to be able access the state of the shell and to navigate to other | |
| // branches in a stateful way. | |
| return ScaffoldWithNavBar(navigationShell: navigationShell); | |
| }, | |
| branches: <StatefulShellBranch>[ | |
| // The route branch for the first tab of the bottom navigation bar. | |
| StatefulShellBranch( | |
| navigatorKey: _sectionANavigatorKey, | |
| routes: <RouteBase>[ | |
| GoRoute( | |
| // The screen to display as the root in the first tab of the | |
| // bottom navigation bar. | |
| path: '/a', | |
| builder: (BuildContext context, GoRouterState state) => | |
| const RootScreen(label: 'A', detailsPath: '/a/details'), | |
| routes: <RouteBase>[ | |
| // The details screen to display stacked on navigator of the | |
| // first tab. This will cover screen A but not the application | |
| // shell (bottom navigation bar). | |
| GoRoute( | |
| path: 'details', | |
| builder: (BuildContext context, GoRouterState state) => | |
| const DetailsScreen(label: 'A'), | |
| ), | |
| ], | |
| ), | |
| ], | |
| ), | |
| // The route branch for the second tab of the bottom navigation bar. | |
| StatefulShellBranch( | |
| // It's not necessary to provide a navigatorKey if it isn't also | |
| // needed elsewhere. If not provided, a default key will be used. | |
| routes: <RouteBase>[ | |
| GoRoute( | |
| // The screen to display as the root in the second tab of the | |
| // bottom navigation bar. | |
| path: '/b', | |
| builder: (BuildContext context, GoRouterState state) => | |
| const RootScreen( | |
| label: 'B', | |
| detailsPath: '/b/details/1', | |
| secondDetailsPath: '/b/details/2', | |
| ), | |
| routes: <RouteBase>[ | |
| GoRoute( | |
| path: 'details/:param', | |
| builder: (BuildContext context, GoRouterState state) => | |
| DetailsScreen( | |
| label: 'B', | |
| param: state.pathParameters['param'], | |
| ), | |
| ), | |
| GoRoute( | |
| path: 'test/:param', | |
| builder: (BuildContext context, GoRouterState state) => | |
| TestScreen( | |
| param: state.pathParameters['param'], | |
| ), | |
| ), | |
| ], | |
| ), | |
| ], | |
| ), | |
| // The route branch for the third tab of the bottom navigation bar. | |
| StatefulShellBranch( | |
| routes: <RouteBase>[ | |
| GoRoute( | |
| // The screen to display as the root in the third tab of the | |
| // bottom navigation bar. | |
| path: '/c', | |
| builder: (BuildContext context, GoRouterState state) => | |
| const RootScreen( | |
| label: 'C', | |
| detailsPath: '/c/details', | |
| ), | |
| routes: <RouteBase>[ | |
| GoRoute( | |
| path: 'details', | |
| builder: (BuildContext context, GoRouterState state) => | |
| DetailsScreen( | |
| label: 'C', | |
| extra: state.extra, | |
| ), | |
| ), | |
| ], | |
| ), | |
| ], | |
| ), | |
| ], | |
| ), | |
| ], | |
| ); | |
| @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.navigationShell, | |
| Key? key, | |
| }) : super(key: key ?? const ValueKey<String>('ScaffoldWithNavBar')); | |
| /// The navigation shell and container for the branch Navigators. | |
| final StatefulNavigationShell navigationShell; | |
| @override | |
| Widget build(BuildContext context) { | |
| return Scaffold( | |
| body: navigationShell, | |
| bottomNavigationBar: BottomNavigationBar( | |
| // Here, the items of BottomNavigationBar are hard coded. In a real | |
| // world scenario, the items would most likely be generated from the | |
| // branches of the shell route, which can be fetched using | |
| // `navigationShell.route.branches`. | |
| items: const <BottomNavigationBarItem>[ | |
| BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Section A'), | |
| BottomNavigationBarItem(icon: Icon(Icons.work), label: 'Section B'), | |
| BottomNavigationBarItem(icon: Icon(Icons.tab), label: 'Section C'), | |
| ], | |
| currentIndex: navigationShell.currentIndex, | |
| onTap: (int index) => _onTap(context, index), | |
| ), | |
| ); | |
| } | |
| /// Navigate to the current location of the branch at the provided index when | |
| /// tapping an item in the BottomNavigationBar. | |
| void _onTap(BuildContext context, int index) { | |
| // When navigating to a new branch, it's recommended to use the goBranch | |
| // method, as doing so makes sure the last navigation state of the | |
| // Navigator for the branch is restored. | |
| navigationShell.goBranch( | |
| index, | |
| // A common pattern when using bottom navigation bars is to support | |
| // navigating to the initial location when tapping the item that is | |
| // already active. This example demonstrates how to support this behavior, | |
| // using the initialLocation parameter of goBranch. | |
| initialLocation: index == navigationShell.currentIndex, | |
| ); | |
| } | |
| } | |
| /// Widget for the root/initial pages in the bottom navigation bar. | |
| class RootScreen extends StatelessWidget { | |
| /// Creates a RootScreen | |
| const RootScreen({ | |
| required this.label, | |
| required this.detailsPath, | |
| this.secondDetailsPath, | |
| super.key, | |
| }); | |
| /// The label | |
| final String label; | |
| /// The path to the detail page | |
| final String detailsPath; | |
| /// The path to another detail page | |
| final String? secondDetailsPath; | |
| @override | |
| Widget build(BuildContext context) { | |
| return Scaffold( | |
| appBar: AppBar( | |
| title: Text('Root of section $label'), | |
| ), | |
| body: Center( | |
| child: Column( | |
| mainAxisSize: MainAxisSize.min, | |
| children: <Widget>[ | |
| Text('Screen $label', | |
| style: Theme.of(context).textTheme.titleLarge), | |
| const Padding(padding: EdgeInsets.all(4)), | |
| TextButton( | |
| onPressed: () { | |
| GoRouter.of(context).go(detailsPath, extra: '$label-XYZ'); | |
| }, | |
| child: const Text('View details'), | |
| ), | |
| const Padding(padding: EdgeInsets.all(4)), | |
| if (secondDetailsPath != null) | |
| TextButton( | |
| onPressed: () { | |
| GoRouter.of(context).go(secondDetailsPath!); | |
| }, | |
| child: const Text('View more details'), | |
| ), | |
| TextButton( | |
| onPressed: () { | |
| GoRouter.of(context).go('/b/test/123'); | |
| }, | |
| child: const Text('test ekranina git'), | |
| ), | |
| ], | |
| ), | |
| ), | |
| ); | |
| } | |
| } | |
| /// The details screen for either the A or B screen. | |
| class DetailsScreen extends StatefulWidget { | |
| /// Constructs a [DetailsScreen]. | |
| const DetailsScreen({ | |
| required this.label, | |
| this.param, | |
| this.extra, | |
| this.withScaffold = true, | |
| super.key, | |
| }); | |
| /// The label to display in the center of the screen. | |
| final String label; | |
| /// Optional param | |
| final String? param; | |
| /// Optional extra object | |
| final Object? extra; | |
| /// Wrap in scaffold | |
| final bool withScaffold; | |
| @override | |
| State<StatefulWidget> createState() => DetailsScreenState(); | |
| } | |
| /// The state for DetailsScreen | |
| class DetailsScreenState extends State<DetailsScreen> { | |
| int _counter = 0; | |
| @override | |
| Widget build(BuildContext context) { | |
| if (widget.withScaffold) { | |
| return Scaffold( | |
| appBar: AppBar( | |
| title: Text('Details Screen - ${widget.label}'), | |
| ), | |
| body: _build(context), | |
| ); | |
| } else { | |
| return ColoredBox( | |
| color: Theme.of(context).scaffoldBackgroundColor, | |
| child: _build(context), | |
| ); | |
| } | |
| } | |
| Widget _build(BuildContext context) { | |
| return Center( | |
| child: Column( | |
| mainAxisSize: MainAxisSize.min, | |
| children: <Widget>[ | |
| Text('Details for ${widget.label} - Counter: $_counter', | |
| style: Theme.of(context).textTheme.titleLarge), | |
| const Padding(padding: EdgeInsets.all(4)), | |
| TextButton( | |
| onPressed: () { | |
| setState(() { | |
| _counter++; | |
| }); | |
| }, | |
| child: const Text('Increment counter'), | |
| ), | |
| const Padding(padding: EdgeInsets.all(8)), | |
| if (widget.param != null) | |
| Text('Parameter: ${widget.param!}', | |
| style: Theme.of(context).textTheme.titleMedium), | |
| const Padding(padding: EdgeInsets.all(8)), | |
| if (widget.extra != null) | |
| Text('Extra: ${widget.extra!}', | |
| style: Theme.of(context).textTheme.titleMedium), | |
| if (!widget.withScaffold) ...<Widget>[ | |
| const Padding(padding: EdgeInsets.all(16)), | |
| TextButton( | |
| onPressed: () { | |
| GoRouter.of(context).pop(); | |
| }, | |
| child: const Text('< Back', | |
| style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)), | |
| ), | |
| ] | |
| ], | |
| ), | |
| ); | |
| } | |
| } | |
| class TestScreen extends StatelessWidget { | |
| final String? param; | |
| const TestScreen({super.key, required this.param}); | |
| @override | |
| Widget build(BuildContext context) { | |
| return Scaffold( | |
| appBar: AppBar( | |
| title: const Text('test ekrani'), | |
| ), | |
| body: const Center( | |
| child: Text('merhaba'), | |
| ), | |
| ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment