Instantly share code, notes, and snippets.
Last active
November 3, 2022 07:07
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save splex7/bfe86b3b034c1e8f3e4bc92e9b270532 to your computer and use it in GitHub Desktop.
This file contains 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
import 'package:flutter/material.dart'; | |
import 'package:go_router/go_router.dart'; | |
void main() { | |
GoRouter.setUrlPathStrategy(UrlPathStrategy.path); | |
runApp(MyApp()); | |
} | |
class MyApp extends StatelessWidget { | |
MyApp({super.key}); | |
// private navigators | |
final _rootNavigatorKey = GlobalKey<NavigatorState>(); | |
final _shellNavigatorKey = GlobalKey<NavigatorState>(); | |
@override | |
Widget build(BuildContext context) { | |
final tabs = [ | |
const ScaffoldWithNavBarTabItem( | |
initialLocation: '/a', | |
icon: Icon(Icons.home), | |
label: 'Section A', | |
), | |
const ScaffoldWithNavBarTabItem( | |
initialLocation: '/b', | |
icon: Icon(Icons.settings), | |
label: 'Section B', | |
), | |
]; | |
final goRouter = GoRouter( | |
initialLocation: '/a', | |
navigatorKey: _rootNavigatorKey, | |
debugLogDiagnostics: true, | |
routes: [ | |
ShellRoute( | |
navigatorKey: _shellNavigatorKey, | |
builder: (context, state, child) { | |
return ScaffoldWithBottomNavBar(tabs: tabs, child: child); | |
}, | |
routes: [ | |
// Products | |
GoRoute( | |
path: '/a', | |
pageBuilder: (context, state) => NoTransitionPage( | |
key: state.pageKey, | |
child: const RootScreen(label: 'A', detailsPath: '/a/details'), | |
), | |
routes: [ | |
GoRoute( | |
path: 'details', | |
builder: (context, state) => const DetailsScreen(label: 'A'), | |
), | |
], | |
), | |
// Shopping Cart | |
GoRoute( | |
path: '/b', | |
pageBuilder: (context, state) => NoTransitionPage( | |
key: state.pageKey, | |
child: const RootScreen(label: 'B', detailsPath: '/b/details'), | |
), | |
routes: [ | |
GoRoute( | |
path: 'details', | |
builder: (context, state) => const DetailsScreen(label: 'B'), | |
), | |
], | |
), | |
], | |
), | |
], | |
); | |
return MaterialApp.router( | |
routerConfig: goRouter, | |
debugShowCheckedModeBanner: false, | |
theme: ThemeData(primarySwatch: Colors.indigo), | |
); | |
} | |
} | |
/// Representation of a tab item in a [ScaffoldWithNavBar] | |
class ScaffoldWithNavBarTabItem extends BottomNavigationBarItem { | |
/// Constructs an [ScaffoldWithNavBarTabItem]. | |
const ScaffoldWithNavBarTabItem( | |
{required this.initialLocation, required Widget icon, String? label}) | |
: super(icon: icon, label: label); | |
/// The initial location/path | |
final String initialLocation; | |
} | |
class ScaffoldWithBottomNavBar extends StatefulWidget { | |
const ScaffoldWithBottomNavBar( | |
{Key? key, required this.child, required this.tabs}) | |
: super(key: key); | |
final Widget child; | |
final List<ScaffoldWithNavBarTabItem> tabs; | |
@override | |
State<ScaffoldWithBottomNavBar> createState() => | |
_ScaffoldWithBottomNavBarState(); | |
} | |
class _ScaffoldWithBottomNavBarState extends State<ScaffoldWithBottomNavBar> { | |
int _locationToTabIndex(String location) { | |
final index = | |
widget.tabs.indexWhere((t) => location.startsWith(t.initialLocation)); | |
// if index not found (-1), return 0 | |
return index < 0 ? 0 : index; | |
} | |
int get _currentIndex => _locationToTabIndex(GoRouter.of(context).location); | |
void _onItemTapped(BuildContext context, int tabIndex) { | |
// Only navigate if the tab index has changed | |
if (tabIndex != _currentIndex) { | |
context.go(widget.tabs[tabIndex].initialLocation); | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: widget.child, | |
bottomNavigationBar: BottomNavigationBar( | |
currentIndex: _currentIndex, | |
items: widget.tabs, | |
onTap: (index) => _onItemTapped(context, index), | |
), | |
); | |
} | |
} | |
/// 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, Key? key}) | |
: super(key: key); | |
/// The label | |
final String label; | |
/// The path to the detail page | |
final String detailsPath; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('Tab root - $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: () => context.go(detailsPath), | |
child: const Text('View details'), | |
), | |
], | |
), | |
), | |
); | |
} | |
} | |
/// The details screen for either the A or B screen. | |
class DetailsScreen extends StatefulWidget { | |
/// Constructs a [DetailsScreen]. | |
const DetailsScreen({ | |
required this.label, | |
Key? key, | |
}) : super(key: key); | |
/// The label to display in the center of the screen. | |
final String label; | |
@override | |
State<StatefulWidget> createState() => DetailsScreenState(); | |
} | |
/// The state for DetailsScreen | |
class DetailsScreenState extends State<DetailsScreen> { | |
int _counter = 0; | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('Details Screen - ${widget.label}'), | |
), | |
body: 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'), | |
), | |
], | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment