| // 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:adaptive_navigation/adaptive_navigation.dart'; |
| import 'package:flutter/material.dart'; |
| import 'package:go_router/go_router.dart'; |
| import 'package:package_info_plus/package_info_plus.dart'; |
| |
| void main() => runApp(App()); |
| |
| /// The main app. |
| class App extends StatelessWidget { |
| /// Creates an [App]. |
| App({Key? key}) : super(key: key); |
| |
| /// The title of the app. |
| static const String title = 'GoRouter Example: Shared Scaffold'; |
| |
| @override |
| Widget build(BuildContext context) => MaterialApp.router( |
| routeInformationProvider: _router.routeInformationProvider, |
| routeInformationParser: _router.routeInformationParser, |
| routerDelegate: _router.routerDelegate, |
| title: title, |
| ); |
| |
| late final GoRouter _router = GoRouter( |
| debugLogDiagnostics: true, |
| routes: <GoRoute>[ |
| GoRoute( |
| path: '/', |
| builder: (BuildContext context, GoRouterState state) => |
| _build(const Page1View()), |
| ), |
| GoRoute( |
| path: '/page2', |
| builder: (BuildContext context, GoRouterState state) => |
| _build(const Page2View()), |
| ), |
| ], |
| errorBuilder: (BuildContext context, GoRouterState state) => |
| _build(ErrorView(state.error!)), |
| |
| // use the navigatorBuilder to keep the SharedScaffold from being animated |
| // as new pages as shown; wrappiong that in single-page Navigator at the |
| // root provides an Overlay needed for the adaptive navigation scaffold and |
| // a root Navigator to show the About box |
| navigatorBuilder: |
| (BuildContext context, GoRouterState state, Widget child) => Navigator( |
| onPopPage: (Route<dynamic> route, dynamic result) { |
| route.didPop(result); |
| return false; // don't pop the single page on the root navigator |
| }, |
| pages: <Page<dynamic>>[ |
| MaterialPage<void>( |
| child: state.error != null |
| ? ErrorScaffold(body: child) |
| : SharedScaffold( |
| selectedIndex: state.subloc == '/' ? 0 : 1, |
| body: child, |
| ), |
| ), |
| ], |
| ), |
| ); |
| |
| // wrap the view widgets in a Scaffold to get the exit animation just right on |
| // the page being replaced |
| Widget _build(Widget child) => Scaffold(body: child); |
| } |
| |
| /// A scaffold with multiple pages. |
| class SharedScaffold extends StatefulWidget { |
| /// Creates a shared scaffold. |
| const SharedScaffold({ |
| required this.selectedIndex, |
| required this.body, |
| Key? key, |
| }) : super(key: key); |
| |
| /// The selected index |
| final int selectedIndex; |
| |
| /// The body of the page. |
| final Widget body; |
| |
| @override |
| State<SharedScaffold> createState() => _SharedScaffoldState(); |
| } |
| |
| class _SharedScaffoldState extends State<SharedScaffold> { |
| @override |
| Widget build(BuildContext context) => AdaptiveNavigationScaffold( |
| selectedIndex: widget.selectedIndex, |
| destinations: const <AdaptiveScaffoldDestination>[ |
| AdaptiveScaffoldDestination(title: 'Page 1', icon: Icons.first_page), |
| AdaptiveScaffoldDestination(title: 'Page 2', icon: Icons.last_page), |
| AdaptiveScaffoldDestination(title: 'About', icon: Icons.info), |
| ], |
| appBar: AdaptiveAppBar(title: const Text(App.title)), |
| navigationTypeResolver: (BuildContext context) => |
| _drawerSize ? NavigationType.drawer : NavigationType.bottom, |
| onDestinationSelected: (int index) async { |
| // if there's a drawer, close it |
| if (_drawerSize) { |
| Navigator.pop(context); |
| } |
| |
| switch (index) { |
| case 0: |
| context.go('/'); |
| break; |
| case 1: |
| context.go('/page2'); |
| break; |
| case 2: |
| final PackageInfo packageInfo = await PackageInfo.fromPlatform(); |
| showAboutDialog( |
| context: context, |
| applicationName: packageInfo.appName, |
| applicationVersion: 'v${packageInfo.version}', |
| applicationLegalese: 'Copyright © 2022, Acme, Corp.', |
| ); |
| break; |
| default: |
| throw Exception('Invalid index'); |
| } |
| }, |
| body: widget.body, |
| ); |
| |
| bool get _drawerSize => MediaQuery.of(context).size.width >= 600; |
| } |
| |
| /// The content of the first page. |
| class Page1View extends StatelessWidget { |
| /// Creates a [Page1View]. |
| const Page1View({Key? key}) : super(key: key); |
| |
| @override |
| Widget build(BuildContext context) => Center( |
| child: Column( |
| mainAxisAlignment: MainAxisAlignment.center, |
| children: <Widget>[ |
| ElevatedButton( |
| onPressed: () => context.go('/page2'), |
| child: const Text('Go to page 2'), |
| ), |
| ], |
| ), |
| ); |
| } |
| |
| /// The content of the second page. |
| class Page2View extends StatelessWidget { |
| /// Creates a [Page2View]. |
| const Page2View({Key? key}) : super(key: key); |
| |
| @override |
| Widget build(BuildContext context) => Center( |
| child: Column( |
| mainAxisAlignment: MainAxisAlignment.center, |
| children: <Widget>[ |
| ElevatedButton( |
| onPressed: () => context.go('/'), |
| child: const Text('Go to home page'), |
| ), |
| ], |
| ), |
| ); |
| } |
| |
| /// The error scaffold. |
| class ErrorScaffold extends StatelessWidget { |
| /// Creates an [ErrorScaffold] |
| const ErrorScaffold({ |
| required this.body, |
| Key? key, |
| }) : super(key: key); |
| |
| /// The body of this scaffold. |
| final Widget body; |
| |
| @override |
| Widget build(BuildContext context) => Scaffold( |
| appBar: AdaptiveAppBar(title: const Text('Page Not Found')), |
| body: body, |
| ); |
| } |
| |
| /// A view to display error message. |
| class ErrorView extends StatelessWidget { |
| /// Creates an [ErrorView]. |
| const ErrorView(this.error, {Key? key}) : super(key: key); |
| |
| /// The error to display. |
| final Exception error; |
| |
| @override |
| Widget build(BuildContext context) => Center( |
| child: Column( |
| mainAxisAlignment: MainAxisAlignment.center, |
| children: <Widget>[ |
| SelectableText(error.toString()), |
| TextButton( |
| onPressed: () => context.go('/'), |
| child: const Text('Home'), |
| ), |
| ], |
| ), |
| ); |
| } |