├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── scripts │ ├── install-flutter.sh │ ├── install-tools.sh │ └── validate-formatting.sh │ └── validate.yaml ├── .gitignore ├── README.md ├── docs.json ├── docs ├── async-data.mdx ├── changelog.mdx ├── contributors.mdx ├── debugging.mdx ├── declarative-routing.mdx ├── examples.mdx ├── getting-started.mdx ├── images │ ├── async.gif │ ├── flutter-favorite-logo.png │ ├── keeping-state.gif │ ├── nav_builder.gif │ ├── nested-nav.gif │ ├── transitions.gif │ ├── url-strat-hash.png │ ├── url-strat-no-hash.png │ └── willpopscope.png ├── index.mdx ├── issues.mdx ├── ja │ ├── async-data.mdx │ ├── changelog.mdx │ ├── contributors.mdx │ ├── debugging.mdx │ ├── declarative-routing.mdx │ ├── examples.mdx │ ├── getting-started.mdx │ ├── index.mdx │ ├── issues.mdx │ ├── migrating-to-20.mdx │ ├── migrating-to-25.mdx │ ├── migrating-to-30.mdx │ ├── named-routes.mdx │ ├── navigation.mdx │ ├── navigator-builder.mdx │ ├── nested-navigation.mdx │ ├── parameters.mdx │ ├── redirection.mdx │ ├── sub-routes.mdx │ ├── transitions.mdx │ ├── typed-routing.mdx │ ├── url-path-strategy.mdx │ ├── user-input.mdx │ └── web-history.mdx ├── migrating-to-20.mdx ├── migrating-to-25.mdx ├── migrating-to-30.mdx ├── named-routes.mdx ├── navigation.mdx ├── navigator-builder.mdx ├── nested-navigation.mdx ├── parameters.mdx ├── redirection.mdx ├── sub-routes.mdx ├── transitions.mdx ├── typed-routing.mdx ├── url-path-strategy.mdx ├── user-input.mdx └── web-history.mdx └── go_router ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── example ├── .gitignore ├── .metadata ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ └── Icon-App-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── Runner-Bridging-Header.h ├── lib │ ├── async_data.dart │ ├── books │ │ ├── main.dart │ │ └── src │ │ │ ├── auth.dart │ │ │ ├── data.dart │ │ │ ├── data │ │ │ ├── author.dart │ │ │ ├── book.dart │ │ │ └── library.dart │ │ │ ├── screens │ │ │ ├── author_details.dart │ │ │ ├── authors.dart │ │ │ ├── book_details.dart │ │ │ ├── books.dart │ │ │ ├── scaffold.dart │ │ │ ├── settings.dart │ │ │ └── sign_in.dart │ │ │ └── widgets │ │ │ ├── author_list.dart │ │ │ └── book_list.dart │ ├── cupertino.dart │ ├── error_screen.dart │ ├── extra_param.dart │ ├── generated_plugin_registrant.dart │ ├── init_loc.dart │ ├── loading_page.dart │ ├── main.dart │ ├── named_routes.dart │ ├── nav_builder.dart │ ├── nav_observer.dart │ ├── nested_nav.dart │ ├── push.dart │ ├── query_params.dart │ ├── redirection.dart │ ├── router_neglect.dart │ ├── router_stream_refresh.dart │ ├── shared │ │ └── data.dart │ ├── shared_scaffold.dart │ ├── state_restoration.dart │ ├── sub_routes.dart │ ├── transitions.dart │ ├── url_strategy.dart │ ├── user_input.dart │ └── widgets_app.dart ├── macos │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── Runner │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── app_icon_1024.png │ │ │ ├── app_icon_128.png │ │ │ ├── app_icon_16.png │ │ │ ├── app_icon_256.png │ │ │ ├── app_icon_32.png │ │ │ ├── app_icon_512.png │ │ │ └── app_icon_64.png │ │ ├── Base.lproj │ │ └── MainMenu.xib │ │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ │ ├── DebugProfile.entitlements │ │ ├── Info.plist │ │ ├── MainFlutterWindow.swift │ │ └── Release.entitlements ├── pubspec.yaml └── web │ ├── favicon.png │ ├── icons │ ├── Icon-192.png │ └── Icon-512.png │ ├── index.html │ └── manifest.json ├── lib ├── go_router.dart └── src │ ├── custom_transition_page.dart │ ├── go_route.dart │ ├── go_route_information_parser.dart │ ├── go_route_match.dart │ ├── go_router.dart │ ├── go_router_cupertino.dart │ ├── go_router_delegate.dart │ ├── go_router_error_page.dart │ ├── go_router_material.dart │ ├── go_router_refresh_stream.dart │ ├── go_router_state.dart │ ├── inherited_go_router.dart │ ├── logging.dart │ ├── path_strategy_nonweb.dart │ ├── path_strategy_web.dart │ ├── typedefs.dart │ └── url_path_strategy.dart ├── pubspec.yaml └── test └── go_router_test.dart /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### :sparkles: What kind of change does this PR introduce? (Bug fix, feature, docs update...) 2 | 3 | 4 | ### :arrow_heading_down: What is the current behavior? 5 | 6 | 7 | ### :new: What is the new behavior (if this is a feature change)? 8 | 9 | 10 | ### :boom: Does this PR introduce a breaking change? 11 | 12 | 13 | ### :bug: Recommendations for testing 14 | 15 | 16 | ### :memo: Links to relevant issues/docs 17 | 18 | 19 | ### :thinking: Checklist before submitting 20 | 21 | - [ ] I made sure all projects build. 22 | - [ ] I updated CHANGELOG.md to add a description of the change. 23 | - [ ] I updated the relevant documentation. 24 | - [ ] I updated the relevant code example. 25 | - [ ] I rebased onto current `master`. 26 | -------------------------------------------------------------------------------- /.github/workflows/scripts/install-flutter.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BRANCH=$1 4 | 5 | git clone https://github.com/flutter/flutter.git --depth 1 -b $BRANCH $HOME/_flutter 6 | echo "$HOME/_flutter/bin" >> $GITHUB_PATH 7 | -------------------------------------------------------------------------------- /.github/workflows/scripts/install-tools.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | flutter config --no-analytics 4 | echo "$HOME/.pub-cache/bin" >> $GITHUB_PATH 5 | echo "$HOME/_flutter/.pub-cache/bin" >> $GITHUB_PATH 6 | echo "$HOME/_flutter/bin/cache/dart-sdk/bin" >> $GITHUB_PATH 7 | flutter pub get 8 | -------------------------------------------------------------------------------- /.github/workflows/scripts/validate-formatting.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [[ $(git ls-files '*.dart' --modified) ]]; then 3 | echo "" 4 | echo "" 5 | echo "These files are not formatted correctly:" 6 | for f in $(git ls-files '*.dart' --modified); do 7 | echo "" 8 | echo "" 9 | echo "-----------------------------------------------------------------" 10 | echo "$f" 11 | echo "-----------------------------------------------------------------" 12 | echo "" 13 | git --no-pager diff --unified=0 --minimal $f 14 | echo "" 15 | echo "-----------------------------------------------------------------" 16 | echo "" 17 | echo "" 18 | done 19 | if [[ $GITHUB_WORKFLOW ]]; then 20 | git checkout . > /dev/null 2>&1 21 | fi 22 | echo "" 23 | echo "❌ Some files are incorrectly formatted, see above output." 24 | echo "" 25 | echo "To fix these locally, run Dart formatter." 26 | exit 1 27 | else 28 | echo "" 29 | echo "✅ All files are formatted correctly." 30 | fi 31 | -------------------------------------------------------------------------------- /.github/workflows/validate.yaml: -------------------------------------------------------------------------------- 1 | name: validate 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: 7 | - master 8 | paths-ignore: 9 | - "**.md" 10 | 11 | defaults: 12 | run: 13 | working-directory: go_router 14 | 15 | jobs: 16 | style: 17 | timeout-minutes: 15 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v1 21 | with: 22 | fetch-depth: 0 23 | - name: "Install Flutter" 24 | run: ../.github/workflows/scripts/install-flutter.sh stable 25 | - name: "Install Tools" 26 | run: ../.github/workflows/scripts/install-tools.sh 27 | - name: "Dart Analyzer" 28 | uses: invertase/github-action-dart-analyzer@v1 29 | with: 30 | fatal-infos: true 31 | fatal-warnings: true 32 | - name: "Formatting Check" 33 | run: | 34 | flutter format . 35 | ../.github/workflows/scripts/validate-formatting.sh 36 | 37 | test: 38 | runs-on: ubuntu-latest 39 | timeout-minutes: 15 40 | steps: 41 | - uses: actions/checkout@v1 42 | with: 43 | fetch-depth: 0 44 | - name: "Install Flutter" 45 | run: ../.github/workflows/scripts/install-flutter.sh stable 46 | - name: "Install Tools" 47 | run: ../.github/workflows/scripts/install-tools.sh 48 | - name: "Flutter Test" 49 | run: flutter test --reporter expanded --coverage 50 | - name: "Upload test coverage to Codecov" 51 | uses: codecov/codecov-action@v1 52 | with: 53 | token: ${{ secrets.CODECOV_TOKEN }} 54 | file: coverage/lcov.info 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | pubspec.lock 30 | 31 | # Android related 32 | **/android/**/gradle-wrapper.jar 33 | **/android/.gradle 34 | **/android/captures/ 35 | **/android/gradlew 36 | **/android/gradlew.bat 37 | **/android/local.properties 38 | **/android/**/GeneratedPluginRegistrant.java 39 | 40 | # iOS/XCode related 41 | **/ios/**/*.mode1v3 42 | **/ios/**/*.mode2v3 43 | **/ios/**/*.moved-aside 44 | **/ios/**/*.pbxuser 45 | **/ios/**/*.perspectivev3 46 | **/ios/**/*sync/ 47 | **/ios/**/.sconsign.dblite 48 | **/ios/**/.tags* 49 | **/ios/**/.vagrant/ 50 | **/ios/**/DerivedData/ 51 | **/ios/**/Icon? 52 | **/ios/**/Pods/ 53 | **/ios/**/.symlinks/ 54 | **/ios/**/profile 55 | **/ios/**/xcuserdata 56 | **/ios/.generated/ 57 | **/ios/Flutter/App.framework 58 | **/ios/Flutter/Flutter.framework 59 | **/ios/Flutter/Flutter.podspec 60 | **/ios/Flutter/Generated.xcconfig 61 | **/ios/Flutter/ephemeral 62 | **/ios/Flutter/app.flx 63 | **/ios/Flutter/app.zip 64 | **/ios/Flutter/flutter_assets/ 65 | **/ios/Flutter/flutter_export_environment.sh 66 | **/ios/ServiceDefinitions.json 67 | **/ios/Runner/GeneratedPluginRegistrant.* 68 | 69 | # Exceptions to above rules. 70 | !**/ios/**/default.mode1v3 71 | !**/ios/**/default.mode2v3 72 | !**/ios/**/default.pbxuser 73 | !**/ios/**/default.perspectivev3 74 | 75 | .vscode/ 76 | go_router/build/ 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | go_router/README.md -------------------------------------------------------------------------------- /docs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "go_router", 3 | "zoomImages": "false", 4 | "zoomImages-Comment": "turning off zoom site-wide to enable images w/ links", 5 | "docsearch": { 6 | "appId": "VFJ1638LIS", 7 | "apiKey": "2ff50d339da84829e844f7ec73c0708d", 8 | "indexName": "gorouter" 9 | }, 10 | "sidebar": [ 11 | [ 12 | "Welcome", 13 | [ 14 | [ 15 | "Overview", 16 | "/" 17 | ], 18 | [ 19 | "Contributors", 20 | "/contributors" 21 | ], 22 | [ 23 | "Changelog", 24 | "/changelog" 25 | ], 26 | [ 27 | "Migrating to 3.0", 28 | "/migrating-to-30" 29 | ], 30 | [ 31 | "Migrating to 2.5", 32 | "/migrating-to-25" 33 | ], 34 | [ 35 | "Migrating to 2.0", 36 | "/migrating-to-20" 37 | ] 38 | ] 39 | ], 40 | [ 41 | "Getting Started", 42 | "/getting-started" 43 | ], 44 | [ 45 | "Declarative Routing", 46 | "/declarative-routing" 47 | ], 48 | [ 49 | "Navigation", 50 | "/navigation" 51 | ], 52 | [ 53 | "Parameters", 54 | "/parameters" 55 | ], 56 | [ 57 | "Sub-routes", 58 | "/sub-routes" 59 | ], 60 | [ 61 | "Redirection", 62 | "/redirection" 63 | ], 64 | [ 65 | "Named Routes", 66 | "/named-routes" 67 | ], 68 | [ 69 | "User Input", 70 | "/user-input" 71 | ], 72 | [ 73 | "URL Path Strategy", 74 | "/url-path-strategy" 75 | ], 76 | [ 77 | "Debugging Your Routes", 78 | "/debugging" 79 | ], 80 | [ 81 | "Examples", 82 | "/examples" 83 | ], 84 | [ 85 | "Issues", 86 | "/issues" 87 | ], 88 | [ 89 | "Advanced Routing", 90 | [ 91 | [ 92 | "Transitions", 93 | "/transitions" 94 | ], 95 | [ 96 | "Async Data", 97 | "/async-data" 98 | ], 99 | [ 100 | "Nested Navigation", 101 | "/nested-navigation" 102 | ], 103 | [ 104 | "Navigator Builder", 105 | "/navigator-builder" 106 | ], 107 | [ 108 | "Web History", 109 | "/web-history" 110 | ], 111 | [ 112 | "Type Routing Proposal", 113 | "/typed-routing" 114 | ] 115 | ] 116 | ] 117 | ] 118 | } -------------------------------------------------------------------------------- /docs/async-data.mdx: -------------------------------------------------------------------------------- 1 | # Async Data 2 | 3 | Sometimes you'll want to load data asynchronously. In that case, you'll want to 4 | pass the params to the screen that's going to show the data and let it do the 5 | lookup itself: 6 | 7 | ```dart 8 | late final _router = GoRouter( 9 | routes: [ 10 | GoRoute( 11 | path: '/', 12 | builder: (context, state) => const HomeScreenWithAsync(), 13 | routes: [ 14 | GoRoute( 15 | path: 'family/:fid', 16 | builder: (context, state) => FamilyScreenWithAsync( 17 | fid: state.params['fid']!, 18 | ), 19 | routes: [ 20 | GoRoute( 21 | path: 'person/:pid', 22 | builder: (context, state) => PersonScreenWithAsync( 23 | fid: state.params['fid']!, 24 | pid: state.params['pid']!, 25 | ), 26 | ), 27 | ], 28 | ), 29 | ], 30 | ), 31 | ], 32 | ); 33 | ``` 34 | 35 | The screen can use whatever it likes to do the lookup. For example, the 36 | following shows the use of [the Repository 37 | pattern](https://martinfowler.com/eaaCatalog/repository.html) and [the Flutter 38 | `FutureBuilder`](https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html) 39 | to load and show the data: 40 | 41 | ```dart 42 | class FamilyScreen extends StatefulWidget { 43 | const FamilyScreen({required this.fid, Key? key}) : super(key: key); 44 | final String fid; 45 | 46 | @override 47 | State createState() => _FamilyScreenState(); 48 | } 49 | 50 | class _FamilyScreenState extends State { 51 | Future? _future; 52 | 53 | @override 54 | void initState() { 55 | super.initState(); 56 | _fetch(); 57 | } 58 | 59 | @override 60 | void didUpdateWidget(covariant FamilyScreen oldWidget) { 61 | super.didUpdateWidget(oldWidget); 62 | 63 | // refresh cached data 64 | if (oldWidget.fid != widget.fid) _fetch(); 65 | } 66 | 67 | void _fetch() => _future = App.repo.getFamily(widget.fid); 68 | 69 | @override 70 | Widget build(BuildContext context) => FutureBuilder( 71 | future: _future, 72 | builder: (context, snapshot) { 73 | if (snapshot.connectionState == ConnectionState.waiting) { 74 | return Scaffold( 75 | appBar: AppBar(title: const Text('Loading...')), 76 | body: const Center(child: CircularProgressIndicator()), 77 | ); 78 | } 79 | 80 | if (snapshot.hasError) { 81 | return Scaffold( 82 | appBar: AppBar(title: const Text('Error')), 83 | body: SnapshotError(snapshot.error!), 84 | ); 85 | } 86 | 87 | assert(snapshot.hasData); 88 | final family = snapshot.data!; 89 | return Scaffold( 90 | appBar: AppBar(title: Text(family.name)), 91 | body: ListView( 92 | children: [ 93 | for (final p in family.people) 94 | ListTile( 95 | title: Text(p.name), 96 | onTap: () => context.go( 97 | '/family/${family.id}/person/${p.id}', 98 | ), 99 | ), 100 | ], 101 | ), 102 | ); 103 | }, 104 | ); 105 | } 106 | ``` 107 | 108 | This code shows a progress indicator as the data is being fetched and an error 109 | in the case that the fetch fails. 110 | 111 | ![async data example](/images/async.gif) 112 | 113 | Take a look at the [async 114 | example](https://github.com/csells/go_router/blob/main/go_router/example/lib/async_data.dart) 115 | for the full details. 116 | -------------------------------------------------------------------------------- /docs/changelog.mdx: -------------------------------------------------------------------------------- 1 | ../go_router/CHANGELOG.md -------------------------------------------------------------------------------- /docs/contributors.mdx: -------------------------------------------------------------------------------- 1 | # Contributors 2 | 3 | It's amazing to me how many folks have already contributed to this project. Huge 4 | shout out to the go_router contributors! 5 | 6 | - [Salakar](https://github.com/Salakar) for the CI action on 7 | GitHub that is always helping me track down stuff I forget 8 | - [rydmike](https://github.com/rydmike) for a bunch of README and dartdoc fixes 9 | as well as a great example for keeping state during nested navigation 10 | - [Abhishek01039](https://github.com/Abhishek01039) for helping me change a 11 | life-long habit of sorting constructors after fields, which goes against Dart 12 | best practices 13 | - [SunlightBro](https://github.com/SunlightBro) for the Android system Back 14 | button fix 15 | - [craiglabenz](https://github.com/craiglabenz) for a bunch of README fixes; 16 | also, Craig has been talking about adding build_runner support to 17 | produce typesafe go and push code for named routes, so thumbs up on [this 18 | issues](https://github.com/csells/go_router/issues/66) if that's a feature 19 | you'd like to see in go_router 20 | - [kevmoo](https://github.com/kevmoo) for helping me track down spelling issues 21 | in my README and unused imports and refactoring for easier maintenance 22 | - [andyduke](https://github.com/andyduke) for [the most excellent 23 | Navigation Builder feature](/navigator-builder) and updates to [state 24 | restoration](https://github.com/csells/go_router/blob/master/example/lib/state_restoration.dart). 25 | - [lulupointu](https://github.com/lulupointu) for deep link fixes that keep 26 | pages from flashing willy-nilly. 27 | - [jopmiddelkamp](https://github.com/jopmiddelkamp) for the easy mapping from a 28 | `Stream` to a `Listenable` for use with `refreshListenable` as well as a PR 29 | template 30 | - [mehade369](https://github.com/mehade369) for dartdocs fixups 31 | - [ben-milanko](https://github.com/ben-milanko) for example link fixes 32 | - [johnpryan](https://github.com/johnpryan) for updating implementation to use 33 | logging package for debug diagnostics 34 | - [nullrocket](https://github.com/nullrocket) for removing a hack for notifying 35 | the router of a route change that was no longer needed and adding support for 36 | `Router.neglect`. 37 | - [toshi-kuji](https://github.com/toshi-kuji) for the Japanese translation 38 | of the docs and catching many typos in the English docs -------------------------------------------------------------------------------- /docs/debugging.mdx: -------------------------------------------------------------------------------- 1 | # Debugging Your Routes 2 | 3 | Because go_router asks that you provide a set of paths, sometimes as fragments 4 | to match just part of a location, it's hard to know just what routes you have in 5 | your app. In those cases, it's handy to be able to see the full paths of the 6 | routes you've created as a debugging tool, e.g. 7 | 8 | ```text 9 | GoRouter: known full paths for routes: 10 | GoRouter: => / 11 | GoRouter: => /family/:fid 12 | GoRouter: => /family/:fid/person/:pid 13 | GoRouter: known full paths for route names: 14 | GoRouter: home => / 15 | GoRouter: family => /family/:fid 16 | GoRouter: person => /family/:fid/person/:pid 17 | ``` 18 | 19 | Likewise, there are multiple ways to navigate, e.g. `context.go()`, 20 | `context.goNamed()`, `context.push()`, `context.pushNamed()`, the `Link` widget, 21 | etc., as well as redirection, so it's handy to be able to see how that's going 22 | under the covers, e.g. 23 | 24 | ```text 25 | GoRouter: setting initial location / 26 | GoRouter: location changed to / 27 | GoRouter: getting location for name: "person", params: {fid: f2, pid: p1} 28 | GoRouter: going to /family/f2/person/p1 29 | GoRouter: location changed to /family/f2/person/p1 30 | ``` 31 | 32 | Furthermore, if you use the `builder` instead of the `pageBuilder` method to 33 | create a screen in your app, go_router will look for the app type to determine 34 | what [transitions](/transitions) to provide to your pages: 35 | 36 | ```text 37 | GoRouter: MaterialApp found 38 | ``` 39 | 40 | And finally, if there's an exception in your routing, you'll see that in the 41 | debug output, too, along with the call stack, e.g. 42 | 43 | ```text 44 | GoRouter: Exception: no routes for location: /foobarquux 45 | ...call stack elided... 46 | ``` 47 | 48 | To enable this kind of output when your `GoRouter` is first created, you can use 49 | the `debugLogDiagnostics` argument: 50 | 51 | ```dart 52 | final _router = GoRouter( 53 | routes: ..., 54 | 55 | // log diagnostic info for your routes 56 | debugLogDiagnostics: true, 57 | ); 58 | ``` 59 | 60 | This parameter defaults to `false`, which produces no output. 61 | -------------------------------------------------------------------------------- /docs/getting-started.mdx: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | To use go_router package, [follow these 4 | instructions](https://pub.dev/packages/go_router/install). 5 | 6 | If you'd like to see a video overview of go_router package, you can watch the 7 | following: 8 | 9 | 10 | 11 | In this video, I did some pair programming with Majid Hajian to port an existing 12 | Flutter app built using the original Navigation API to use go_router for both 13 | mobile and web. 14 | 15 | Note that this video was recorded before the great simplification of go_router 16 | v2.5, so I recommend reading [the docs for migration to v2.5](/migrating-to-25) 17 | after watching the video. -------------------------------------------------------------------------------- /docs/images/async.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/docs/images/async.gif -------------------------------------------------------------------------------- /docs/images/flutter-favorite-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/docs/images/flutter-favorite-logo.png -------------------------------------------------------------------------------- /docs/images/keeping-state.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/docs/images/keeping-state.gif -------------------------------------------------------------------------------- /docs/images/nav_builder.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/docs/images/nav_builder.gif -------------------------------------------------------------------------------- /docs/images/nested-nav.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/docs/images/nested-nav.gif -------------------------------------------------------------------------------- /docs/images/transitions.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/docs/images/transitions.gif -------------------------------------------------------------------------------- /docs/images/url-strat-hash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/docs/images/url-strat-hash.png -------------------------------------------------------------------------------- /docs/images/url-strat-no-hash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/docs/images/url-strat-no-hash.png -------------------------------------------------------------------------------- /docs/images/willpopscope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/docs/images/willpopscope.png -------------------------------------------------------------------------------- /docs/index.mdx: -------------------------------------------------------------------------------- 1 | # Welcome to go_router! 2 | 3 | # [Docs in Japanese](https://zenn.dev/inari_sushio/scraps/01ef7604a4b934) 4 | 5 | [The Flutter Router 6 | API](https://api.flutter.dev/flutter/widgets/Router-class.html) as specified by 7 | [the `MaterialApp.router` 8 | constructor](https://api.flutter.dev/flutter/material/MaterialApp/MaterialApp.router.html) 9 | requires an implementation of the 10 | [`RouterDelegate`](https://api.flutter.dev/flutter/widgets/RouterDelegate-class.html) 11 | and 12 | [`RouteInformationParser`](https://api.flutter.dev/flutter/widgets/RouteInformationParser-class.html) 13 | classes. These two implementations themselves imply the definition of a third 14 | type to hold the app state that drives the creation of the 15 | [`Navigator`](https://api.flutter.dev/flutter/widgets/Navigator-class.html). You 16 | can read [an excellent blog post on these requirements on 17 | Medium](https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade). 18 | This separation of responsibilities allows the Flutter developer to implement a 19 | number of routing and navigation policies, including deep and dynamic linking, 20 | but at the cost of 21 | [complexity](https://www.reddit.com/r/FlutterDev/comments/koxx4w/why_navigator_20_sucks/). 22 | 23 | The purpose of [the go_router package](https://pub.dev/packages/go_router) is to 24 | use declarative routes to reduce complexity, regardless of the platform you're 25 | targeting (mobile, web, desktop), handle deep and dynamic linking from 26 | Android, iOS and the web, along with a number of other navigation-related 27 | scenarios, while still (hopefully) providing an easy-to-use developer 28 | experience. 29 | 30 | [![Flutter 31 | Favorite](/images/flutter-favorite-logo.png)](https://docs.flutter.dev/development/packages-and-plugins/favorites) 32 | -------------------------------------------------------------------------------- /docs/issues.mdx: -------------------------------------------------------------------------------- 1 | # Issues 2 | 3 | Do you have an issue with or feature request for go_router? Log it on the [issue 4 | tracker](https://github.com/csells/go_router/issues). 5 | -------------------------------------------------------------------------------- /docs/ja/async-data.mdx: -------------------------------------------------------------------------------- 1 | # 非同期データ 2 | 3 | 非同期に取得したデータを元に画面を生成したい場合は、パラメータを画面側に渡してその先でデータの取得を行います。 4 | 5 | ```dart 6 | late final _router = GoRouter( 7 | routes: [ 8 | GoRoute( 9 | path: '/', 10 | builder: (context, state) => const HomeScreenWithAsync(), 11 | routes: [ 12 | GoRoute( 13 | path: 'family/:fid', 14 | builder: (context, state) => FamilyScreenWithAsync( 15 | fid: state.params['fid']!, 16 | ), 17 | routes: [ 18 | GoRoute( 19 | path: 'person/:pid', 20 | builder: (context, state) => PersonScreenWithAsync( 21 | fid: state.params['fid']!, 22 | pid: state.params['pid']!, 23 | ), 24 | ), 25 | ], 26 | ), 27 | ], 28 | ), 29 | ], 30 | ); 31 | ``` 32 | 33 | データの取得はどのような方法でも構いません。 34 | 例えば、以下のサンプルコードは[リポジトリ・パターン](https://martinfowler.com/eaaCatalog/repository.html)と 35 | [Flutter の FutureBuilder](https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html) を使用してデータのロードと表示を行っています。 36 | 37 | ```dart 38 | class FamilyScreen extends StatefulWidget { 39 | const FamilyScreen({required this.fid, Key? key}) : super(key: key); 40 | final String fid; 41 | 42 | @override 43 | State createState() => _FamilyScreenState(); 44 | } 45 | 46 | class _FamilyScreenState extends State { 47 | Future? _future; 48 | 49 | @override 50 | void initState() { 51 | super.initState(); 52 | _fetch(); 53 | } 54 | 55 | @override 56 | void didUpdateWidget(covariant FamilyScreen oldWidget) { 57 | super.didUpdateWidget(oldWidget); 58 | 59 | // キャッシュデータを更新 60 | if (oldWidget.fid != widget.fid) _fetch(); 61 | } 62 | 63 | void _fetch() => _future = App.repo.getFamily(widget.fid); 64 | 65 | @override 66 | Widget build(BuildContext context) => FutureBuilder( 67 | future: _future, 68 | builder: (context, snapshot) { 69 | if (snapshot.connectionState == ConnectionState.waiting) { 70 | return Scaffold( 71 | appBar: AppBar(title: const Text('Loading...')), 72 | body: const Center(child: CircularProgressIndicator()), 73 | ); 74 | } 75 | 76 | if (snapshot.hasError) { 77 | return Scaffold( 78 | appBar: AppBar(title: const Text('Error')), 79 | body: SnapshotError(snapshot.error!), 80 | ); 81 | } 82 | 83 | assert(snapshot.hasData); 84 | final family = snapshot.data!; 85 | return Scaffold( 86 | appBar: AppBar(title: Text(family.name)), 87 | body: ListView( 88 | children: [ 89 | for (final p in family.people) 90 | ListTile( 91 | title: Text(p.name), 92 | onTap: () => context.go( 93 | '/family/${family.id}/person/${p.id}', 94 | ), 95 | ), 96 | ], 97 | ), 98 | ); 99 | }, 100 | ); 101 | } 102 | ``` 103 | 104 | データ取得中はロードインジケーターを表示し、取得が失敗した場合はエラー内容を表示します。 105 | 106 | ![非同期データ取得のサンプル](../images/async.gif) 107 | 108 | このサンプルコードのフルバージョンは[こちら](https://github.com/csells/go_router/blob/master/example/lib/async_data.dart)でご覧いただけます。 109 | -------------------------------------------------------------------------------- /docs/ja/contributors.mdx: -------------------------------------------------------------------------------- 1 | # コントリビューター 2 | 3 | 既に本プロジェクトに貢献いただいた方がこんなに多いとは驚きです。 4 | この場をお借りして go_router のコントリビューターに感謝申し上げます。 5 | 6 | - [Salakar](https://github.com/Salakar) : GitHub の CI アクション設定(私がいつも忘れがちなことをアシストしてくれる)。 7 | - [rydmike](https://github.com/rydmike) : README およびドキュメントの修正、ネスト型ナビゲーションで状態を保つサンプルコードの提供。 8 | - [Abhishek01039](https://github.com/Abhishek01039) フィールドの後にコンストラクタを書く私の習癖(Dart のベストプラクティスに反する)を直すのを手伝ってくれた。 9 | - [SunlightBro](https://github.com/SunlightBro) Android のシステムバックボタンに関する修正。 10 | - [craiglabenz](https://github.com/craiglabenz) README の修正。 11 | また、Craig は名前付きルートにおいて型安全な go と push のコードを build_runner で生成する[提案](https://github.com/csells/go_router/issues/66)をしてくれた。 12 | 今後の go_router でこの機能を利用したい方は いいね! を。 13 | - [kevmoo](https://github.com/kevmoo) README におけるスペルミスや未使用インポートの発見、そしてメンテナンス容易性を高めるリファクタリングを手伝ってくれた。 14 | - [andyduke](https://github.com/andyduke) 素晴らしい [`navigatorBuilder`](/navigator-builder) の追加と、 15 | [状態復元](https://github.com/csells/go_router/blob/master/example/lib/state_restoration.dart)に関するアップデート。 16 | - [lulupointu](https://github.com/lulupointu) ディープリンクに飛ぶと画面が一瞬チラつく問題の修正。 17 | - [jopmiddelkamp](https://github.com/jopmiddelkamp) `refreshListenable` に使用する `GoRouterRefreshStream`(`Stream` を `Listenable` にマッピング)の追加、 18 | そしてプルリクエストのテンプレート作成。 19 | - [mehade369](https://github.com/mehade369) ドキュメントの修正。 20 | - [ben-milanko](https://github.com/ben-milanko) サンプルリンクの修正。 21 | - [johnpryan](https://github.com/johnpryan) デバッグ診断に logging パッケージを使用するアップデート。 22 | - [nullrocket](https://github.com/nullrocket) ルート変更をルータに通知する、不要になっていたハックを取り除き、`Router.neglect` のサポートを追加してくれた。 23 | - [toshi-kuji](https://github.com/toshi-kuji) ドキュメントの日本語訳を追加し、英語ドキュメントのタイプミスをたくさん修正してくれた。 -------------------------------------------------------------------------------- /docs/ja/debugging.mdx: -------------------------------------------------------------------------------- 1 | # ルートのデバッグ 2 | 3 | go_router では `GoRoute` を通じてパスを設定しますが、そのパスはロケーション名の一部でしかない場合もあります。 4 | そのため、アプリ内にどのようなルートが設定されているのか全体像を把握しづらいことがありますが、 5 | デバッグツールを使えばルートの通しパス一覧を簡単に確認することができます。 6 | 7 | ```text 8 | GoRouter: known full paths for routes: 9 | GoRouter: => / 10 | GoRouter: => /family/:fid 11 | GoRouter: => /family/:fid/person/:pid 12 | GoRouter: known full paths for route names: 13 | GoRouter: home => / 14 | GoRouter: family => /family/:fid 15 | GoRouter: person => /family/:fid/person/:pid 16 | ``` 17 | 18 | また、go_router はナビゲートする方法が多様で(`context.go()`、`context.goNamed()`、`context.push()`、`context.pushNamed()`、`Link` ウィジェット、その他) 19 | リダイレクトの方法も複数あるため、これらが実際に裏側で行っている動作を確認したい場合があります。 20 | これについてもデバッグツールで確認することができます。 21 | 22 | ```text 23 | GoRouter: setting initial location / 24 | GoRouter: location changed to / 25 | GoRouter: getting location for name: "person", params: {fid: f2, pid: p1} 26 | GoRouter: going to /family/f2/person/p1 27 | GoRouter: location changed to /family/f2/person/p1 28 | ``` 29 | 30 | さらに、アプリ内画面を生成するビルダーとして `pageBuilder` の代わりに `builder` を使用した場合は、 31 | go_router が認識しているアプリの種類を確認することができます。ページ間の[トランジション効果](/transitions)はこのアプリの種類により異なります。 32 | 33 | ```text 34 | GoRouter: MaterialApp found 35 | ``` 36 | 37 | 最後に、ルーティングに関する例外が発生した場合はコールスタックとともにデバッグ出力で確認することができます。 38 | 39 | ```text 40 | GoRouter: Exception: no routes for location: /foobarquux 41 | ...call stack elided... 42 | ``` 43 | 44 | このような出力を有効にするには `GoRouter` 作成時に `debugLogDiagnostics` を設定します。 45 | 46 | ```dart 47 | final _router = GoRouter( 48 | routes: ..., 49 | 50 | // ルート診断情報の出力を有効にする 51 | debugLogDiagnostics: true, 52 | ); 53 | ``` 54 | 55 | このパラメータは `false`(出力なし)がデフォルトです。 56 | -------------------------------------------------------------------------------- /docs/ja/examples.mdx: -------------------------------------------------------------------------------- 1 | # サンプルコード集 2 | 3 | 以下のサンプルコードから go_router の具体的な使用方法を確認できます。 4 | 5 | - [`main.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/main.dart): 6 | 宣言的な `GoRoute` オブジェクトのセットを使い、基本のルーティングポリシーを定義する 7 | - [`error_screen.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/error_screen.dart): 8 | ルーティングやビルダーに関するエラーが発生した際に表示するカスタムのエラー画面を定義する 9 | - [`init_loc.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/init_loc.dart): 10 | デフォルトのホーム `/` ではなく指定のロケーションでアプリをスタートする 11 | - [`sub_routes.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/sub_routes.dart): 12 | サブルートのセットを元にページのスタックを生成する 13 | - [`push.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/push.dart): 14 | `context.push()` を連続的に呼び出してページのスタックを生成する 15 | - [`redirection.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/redirection.dart): 16 | アプリの状態の変化を元にルートからルートへリダイレクトする 17 | - [`query_params.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/query_params.dart): 18 | パスパラメータやオプションのクエリパラメータをビルダー内で受け取る 19 | - [`router_stream_refresh.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/router_stream_refresh.dart): 20 | `Listenable` の代わりに `Stream` でルートの更新を行う 21 | - [`loading_page.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/loading_page.dart): 22 | ユーザーがログインした直後にリポジトリをロードし、その間ローディング画面を表示する 23 | - [`named_routes.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/named_routes.dart): 24 | ロケーション URI を指定する代わりに名前付きルートで遷移する 25 | - [`transitions.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/transitions.dart): 26 | ルーティング中にカスタム・トランジション効果を適用する 27 | - [`async_data.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/async_data.dart): 28 | 非同期データの参照 29 | - [`nested_nav.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/nested_nav.dart): 30 | ページにサブルートの情報を表示する(ネスト型ナビゲーション) 31 | - [`nav_builder.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/nav_builder.dart): 32 | `Navigator` ウィジェットの上にウィジェットを挿入する 33 | - [`shared_scaffold.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/shared_scaffold.dart): 34 | `navigatorBuilder` を使ってページ間で共通のカスタム scaffold を使用し、go_router によるトランジション効果の適用範囲を制限する 35 | - [`url_strategy.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/url_strategy.dart): 36 | Flutter ウェブで URL から # を除く 37 | - [`user_input.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/user-input.dart): 38 | go_router を使いつつ、戻るボタンによるポップを打ち消して `Navigator` で確認ダイアログを表示する 39 | - [`nav_observer.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/nav_observer.dart): 40 | `MaterialPage` 等に渡すデフォルトの引数を通じて `NavigatorObserver` にルートの追加情報を伝える 41 | - [`state_restoration.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/state_restoration.dart): 42 | go_router で状態復元(State Restoration)が動作するかテストする 43 | - [`cupertino.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/cupertino.dart): 44 | `MaterialApp` と同様に `CupertinoApp` においても go_router が動作するかテストする 45 | - [`widgets_app.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/widgets_app.dart): 46 | `MaterialApp` と同様に `WidgetsApp` においても go_router が動作するかテストする 47 | - [`router_neglect.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/router_neglect.dart): 48 | ページ間を遷移する際にウェブブラウザが履歴を作成しないようにする 49 | - [`books/main.dart`](https://github.com/csells/go_router/blob/main/go_router/example/lib/books/main.dart): 50 | Flutter 公式のサンプル、[ナビゲーションとルーティング](https://github.com/flutter/samples/tree/master/navigation_and_routing)の go_router 版 51 | 52 | これらのサンプルコードは IDE から直接、もしくは `example` フォルダから次のコマンドラインで実行することができます。 53 | 54 | ```sh 55 | $ flutter run lib/main.dart 56 | ``` 57 | -------------------------------------------------------------------------------- /docs/ja/getting-started.mdx: -------------------------------------------------------------------------------- 1 | # はじめに 2 | 3 | go_router パッケージのインストールは[こちらの手順](https://pub.dev/packages/go_router/install)に従ってください。 4 | 5 | また、go_router の概要は次の YouTube ビデオでご覧いただけます。 6 | 7 | 8 | 9 | このビデオでは Majid Hajian 氏とペアプログラミングを行い、Navigator API を利用した既存 Flutter アプリを go_router を利用したものに変え、モバイルとウェブの両方に対応させました。 10 | 11 | 尚、この映像は go_router v2.5 で API が大幅に簡素化される前に録画したものです。 12 | そのため、映像をご覧になった後は [v2.5 への移行に関するドキュメント](/migrating-to-25)を読まれることをおすすめします。 13 | -------------------------------------------------------------------------------- /docs/ja/index.mdx: -------------------------------------------------------------------------------- 1 | # go_router へようこそ! 2 | 3 | MaterialApp.router コンストラクタに代表される [Flutter の Router API](https://api.flutter.dev/flutter/widgets/Router-class.html) では 4 | [`RouterDelegate`](https://api.flutter.dev/flutter/widgets/RouterDelegate-class.html) と 5 | [`RouteInformationParser`](https://api.flutter.dev/flutter/widgets/RouteInformationParser-class.html) の実装を必要とします。 6 | そしてこれら2つの実装はそれ自体が、[`Navigator`](https://api.flutter.dev/flutter/widgets/Navigator-class.html) の生成に関わる「状態」を保持する第3の型の定義を意味します。 7 | (Router API の詳細については [Medium に素晴らしい記事](https://medium.com/flutter/learning-flutters-new-navigation-and-routing-system-7c9068155ade) があります) 8 | この「責任の分離」があるおかげで、Flutter 開発者はディープリンクや動的リンクを含む、ルーティングやナビゲーションのポリシーを独自に実装することが可能になります。 9 | しかし、その仕組みは[複雑](https://www.reddit.com/r/FlutterDev/comments/koxx4w/why_navigator_20_sucks/)です。 10 | 11 | この [go_router パッケージ](https://pub.dev/packages/go_router)の目的は次の通りです。 12 | まず、プラットフォーム(モバイル、ウェブ、デスクトップ)に依ることなく、宣言的なルート生成により Router API の複雑さを軽減すること。 13 | そして、Android / iOS / ウェブ におけるディープリンクと動的リンク、その他多くのナビゲーションの想定シナリオに対応すること。 14 | それでも尚、(願わくば)使いやすいと感じてもらえる開発者体験を提供すること。 15 | 16 | [![Flutter 17 | Favorite](../images/flutter-favorite-logo.png)](https://docs.flutter.dev/development/packages-and-plugins/favorites) 18 | -------------------------------------------------------------------------------- /docs/ja/issues.mdx: -------------------------------------------------------------------------------- 1 | # Issue について 2 | 3 | go_router に関する問題提起や機能リクエストがある場合は [Issue トラッカー](https://github.com/csells/go_router/issues)に記録をお願いします。 4 | -------------------------------------------------------------------------------- /docs/ja/migrating-to-20.mdx: -------------------------------------------------------------------------------- 1 | # 2.0 への移行について 2 | 3 | [需要](https://twitter.com/csells/status/1445520767190388738)により go_router 2.0 リリースから破壊的変更があります。 4 | `GoRouterState` オブジェクトの `params` プロパティが2つのプロパティに分割されました。 5 | 6 | - `params` : パスパラメータ。パスの一部として渡されるパラメータ。`/family/:fid` など。 7 | - `queryParams` : クエリパラメータ。ロケーション名の末尾にオプションとして追加されるパラメータ。`/login?from=/family/f2` など。 8 | 9 | 1.x リリースでは、`params` プロパティはパスパラメータとクエリパラメータの両方を含む単一の Map オブジェクトでした。 10 | 例えば、クエリパラメータを取得するには以下のように `params` プロパティを使用していました。 11 | 12 | ```dart 13 | GoRoute( 14 | path: '/login', 15 | pageBuilder: (context, state) => MaterialPage( 16 | key: state.pageKey, 17 | // 1.x: クエリパラメータを取得 18 | child: LoginScreen(from: state.params['from']), 19 | ), 20 | ), 21 | ``` 22 | 23 | 2.0 以降のリリースでは、クエリパラメータを取得するには次のようにする必要があります。 24 | 25 | ```dart 26 | GoRoute( 27 | path: '/login', 28 | pageBuilder: (context, state) => MaterialPage( 29 | key: state.pageKey, 30 | // 2.0: クエリパラメータを取得 31 | child: LoginScreen(from: state.queryParams['from']), 32 | ), 33 | ), 34 | ``` 35 | 36 | 同様に 1.x リリースでは、名前付きルートを使用する際、パスパラメータとクエリパラメータを以下のように渡していたかと思います。 37 | 38 | ```dart 39 | ListTile( 40 | title: Text(p.name), 41 | // 1.x: パスパラメータとクエリパラメータの両方を渡す 42 | onTap: () => context.goNamed( 43 | 'person', 44 | // パスパラメータに該当しないものは全てクエリパラメータとみなされていた 45 | {'fid': family.id, 'pid': p.id, 'qid': 'quid'}, 46 | ), 47 | ), 48 | ``` 49 | 50 | 2.0 以降のリリースでは、次のようにする必要があります。 51 | 52 | ```dart 53 | ListTile( 54 | title: Text(p.name), 55 | // 2.0: パスパラメータとクエリパラメータの両方を渡す 56 | onTap: () => context.goNamed( 57 | 'person', 58 | params: {'fid': family.id, 'pid': p.id}, 59 | queryParams: {'qid': 'quid'}, 60 | ), 61 | ), 62 | ``` 63 | 64 | 種類が異なる2つのパラメータをそれぞれ単一のスコープとすることで、より分かりやすくなるといいのですが。 65 | -------------------------------------------------------------------------------- /docs/ja/migrating-to-25.mdx: -------------------------------------------------------------------------------- 1 | # 2.5 への移行について 2 | 3 | go_router v2.5 では、ルートに紐づく画面をよりシンプルな方法で設定することが可能になりました。 4 | v2.5 より前のバージョンでは、以下のように `pageBuilder` 関数を実装する他に方法がありませんでした。 5 | 6 | ```dart 7 | GoRoute( 8 | path: '/', 9 | pageBuilder: (context, state) => MaterialPage( // ボイラープレート 10 | key: state.pageKey, // ボイラープレート 11 | restorationId: state.pageKey.value, // ボイラープレート 12 | child: const Page1Page(), // 本質部分 13 | ), // ボイラープレート 14 | ), 15 | ``` 16 | 17 | `MaterialApp`(もしくは `CupertinoApp`)によるデフォルトのトランジション効果を適用する場合は、 18 | このように全てのルートにおいて `MaterialPage`(または `CupertinoPage`)を生成しなければならないため、ボイラープレートコードを何度も書く必要がありました。 19 | また、各ページのキーを go_router に自動で生成させる場合(`state.pageKey`)においても、`MaterialPage`(または `CupertinoPage`)のインスタンスにキーを手動で渡す必要がありました。 20 | これに加えて、`restorationId` を設定する場合はキーと同じことをやる必要がありました。 21 | 22 | ## pageBuilder の代わりに builder を使う 23 | 24 | v2.5 では `pageBuilder` の代わりに `builder` 関数を使用することで、簡単に画面を設定することが可能です。 25 | go_router がアプリの種類(`MaterialApp` / `CupertinoApp` など)に応じた適切なページの型を選び、キーと状態復元 ID を適切に管理してくれます。 26 | 27 | ```dart 28 | GoRoute( 29 | path: '/', 30 | builder: (context, state) => const Page1Page(), // 本質部分のみ 31 | ), 32 | ``` 33 | 34 | このコードによりボイラープレートコードを書く必要がなくなり、`pageBuilder` を利用した場合に実装するであろう標準的なページと同等のページを生成することができます。 35 | 実際、このコードは冒頭に提示したコードと機能面で同等です。 36 | キーを独自に設定したい場合や、カスタムのページ型に独自のパラメータを設定したい場合は、引き続き `pageBuilder` を使っていただいても問題ありません。 37 | 38 | ```dart 39 | GoRoute( 40 | path: '/', 41 | pageBuilder: (context, state) => MaterialPage( 42 | key: _myPage1Key, 43 | child: const Page1Screen(), 44 | ), 45 | ), 46 | ``` 47 | 48 | 同様に、[カスタムのトランジション効果](/transitions)を設定したい場合も `pageBuilder` を活用できます。 49 | 50 | ```dart 51 | GoRoute( 52 | path: '/fade', 53 | pageBuilder: (context, state) => CustomTransitionPage( 54 | key: state.pageKey, 55 | child: const ExampleTransitionsScreen( 56 | kind: 'fade', 57 | color: Colors.red, 58 | ), 59 | transitionsBuilder: (context, animation, secondaryAnimation, child) => 60 | FadeTransition(opacity: animation, child: child), 61 | ), 62 | ), 63 | ``` 64 | 65 | ただ、特段の事情がない限りは `pageBuilder` の代わりに `builder` を使うようにしてください。 66 | 67 | ## オプションのエラービルダー 68 | 69 | go_router v2.5 では `errorPageBuilder` 関数も必須ではなくなりました。 70 | エラー画面を設定する際は代わりに `errorBuilder` 関数を使用することで、 71 | go_router がアプリの種類(`MaterialApp` / `CupertinoApp` など)に応じたページを生成してくれます。 72 | 73 | ```dart 74 | final _router = GoRouter( 75 | routes: ..., 76 | errorBuilder: (context, state) => ErrorScreen(state.error!), 77 | ); 78 | ``` 79 | 80 | `builder` の場合と同様に、`errorBuilder` を使うことで `errorPageBuilder` を利用した場合に実装するであろう標準的なページと同等のページを生成することができます。 81 | もちろん、`errorPageBuilder` も引き続き使用することができます。 82 | 83 | ## トランジション効果を適用しない 84 | 85 | 最後に、特定ルートにおけるトランジション効果を無効にしたい場合は、新しい `NoTransitionPage` 型を使用してください。 86 | これは既存の `CustomTransitionPage` 型から派生したクラスです。 87 | 88 | ```dart 89 | GoRoute( 90 | path: '/none', 91 | pageBuilder: (context, state) => NoTransitionPage( 92 | key: state.pageKey, 93 | child: const ExampleTransitionsScreen( 94 | kind: 'none', 95 | color: Colors.white, 96 | ), 97 | ), 98 | ), 99 | ``` 100 | 101 | この `NoTransitionPage` は、アプリのウィジェットツリーに `MaterialApp` や `CupertinoApp` がない場合にも適用されます(`WidgetsApp` を使用している場合など)。 102 | カスタムのトランジション効果に関する詳細は[こちら](/transitions)をご覧ください。 103 | -------------------------------------------------------------------------------- /docs/ja/migrating-to-30.mdx: -------------------------------------------------------------------------------- 1 | # 3.0 への移行について 2 | 3 | go_router v3.0 には2点の破壊的変更があります。 4 | 5 | 1. `navigatorBuilder` 関数のシグネチャ 6 | 1. `GoRouter.pop` 関数の動作とシグネチャ 7 | 8 | ## `navigatorBuilder` 関数のシグネチャの変更 9 | 10 | 以前のバージョンでは `navigatorBuilder` 関数には `BuildContext` と `Widget?` が渡されていました。 11 | 例えば、次のように。 12 | 13 | ```dart 14 | final _router = GoRouter( 15 | routes: ..., 16 | 17 | // loginInfo をウィジェットツリーに挿入するために Navigator にラッパーを追加する 18 | navigatorBuilder: (context, child) => ChangeNotifierProvider.value( 19 | value: loginInfo, 20 | child: child, 21 | builder: (context, child) => child!, 22 | ), 23 | ); 24 | ``` 25 | 26 | このシグネチャには2つの問題点がありました。 27 | まず、`child` は絶対に `null` になることがないにも関わらず null 許容型でした。 28 | これは Flutter のタイプデフ `TransitionBuilder` を再利用していたことによるものだったのですが、 29 | このため `navigatorBuilder` 関数を実装する際は `null` チェックをする必要がありました(なんて面倒だ!)。 30 | 31 | さらに、`navigatorBuilder` 関数の中でロケーション情報に応じた処理を行いたくても 32 | `GoRouterState` がパラメータにありませんでした(スーパー面倒だ!)。 33 | 34 | v3 では、`navigatorBuilder` のシグネチャを変更することでこの2つの問題を解決しました。 35 | 36 | ```dart 37 | /// navigatorBuilder コールバックのシグネチャ 38 | typedef GoRouterNavigatorBuilder = Widget Function( 39 | BuildContext context, 40 | GoRouterState state, 41 | Widget child, 42 | ); 43 | ``` 44 | 45 | これによりロケーション情報を使用した実装が可能になりました。 46 | 例えば、次のように([ネスト型ナビゲーションのサンプル](https://github.com/csells/go_router/blob/main/go_router/example/lib/nested_nav.dart)から抜粋)。 47 | 48 | ```dart 49 | navigatorBuilder: (context, state, child) => Material( 50 | child: Column( 51 | children: [ 52 | Expanded(child: child), 53 | Padding( 54 | padding: const EdgeInsets.all(8), 55 | child: Text(state.location), // GoRouterState が使用可能 56 | ), 57 | ], 58 | ), 59 | ), 60 | ``` 61 | 62 | この実装では、ユーザーが画面遷移する度に現在のロケーション情報を表示します。 63 | これは以前のバージョンでは実現が難しかったことです。 64 | 65 | ## `GoRouter.pop` 関数の動作とシグネチャの変更 66 | 67 | 以前のバージョンでは `GoRouter.pop` 関数は単純に `Navigator.pop` を呼び出すだけでした。 68 | そのため、他の `GoRouter` メソッドと異なり `BuildContext` が必要でした。 69 | 70 | ```dart 71 | // グローバルの GoRouter 72 | final _router = GoRouter(...); 73 | 74 | ... 75 | 76 | TextButton( 77 | onPressed: () async { 78 | if (await abandonNewPerson(context)) { 79 | _router.pop(context); // context が必要 80 | } 81 | }, 82 | child: const Text('Cancel'), 83 | ), 84 | ``` 85 | 86 | また開発者の中には `GoRouter` を context レスで呼び出すために `GoRouter` インスタンスをグローバルに置く人もいますが、 87 | その場合でも `pop` を呼び出すには context が必要でした。 88 | go_router がページのスタックを管理しているのだから、`pop` だけで済むはずだと思いますよね? 89 | このシグネチャはあまり直感的とは言えないものでした。 90 | 91 | 本バージョンでは内部の動作を変えることで context が不要になりました(引き続き `Naviator.pop(context)` を使うこともできます)。 92 | 93 | ```dart 94 | // グローバルの GoRouter 95 | final _router = GoRouter(...); 96 | 97 | ... 98 | 99 | TextButton( 100 | onPressed: () async { 101 | if (await abandonNewPerson(context)) { 102 | _router.pop(); // context 不要! 103 | } 104 | }, 105 | child: const Text('Cancel'), 106 | ), 107 | ``` 108 | 109 | Dart の拡張メソッドを通じて `pop` を使用している場合は、現状のコードを変更する必要はありません。 110 | 111 | ```dart 112 | TextButton( 113 | onPressed: () async { 114 | if (await abandonNewPerson(context)) { 115 | context.pop(); // この場合は現状のコードで問題なし 116 | } 117 | }, 118 | child: const Text('Cancel'), 119 | ), 120 | ``` -------------------------------------------------------------------------------- /docs/ja/named-routes.mdx: -------------------------------------------------------------------------------- 1 | # 名前付きルート 2 | 3 | ロケーション名を使ってルートに遷移するということは、URI 生成をアプリ内でハードコーディングすることになります。 4 | 例えば、以下のように。 5 | 6 | ```dart 7 | void _tap(BuildContext context, String fid, String pid) => 8 | context.go('/family/$fid/person/$pid'); 9 | ``` 10 | 11 | これはエラーを招きがちですし、そもそも開発途中で URI の書式が変わる可能性もあります。 12 | その場合、リダイレクト機能を使用すれば古い URI でも動作するようにはできますが、 13 | 本当に様々なバージョンのロケーション URI をコードのあちこちに散らばらせたいですか? 14 | 15 | ## 名前付きルートへの遷移 16 | 17 | そこで登場するのが名前付きルートです。名前付きルートは URI を意識することなく、簡単にルートへの遷移を行うためのものです。 18 | `GoRoute.name` パラメータを使用してルートに一意の名前を付けることができます。 19 | 20 | ```dart 21 | final _router = GoRouter( 22 | routes: [ 23 | GoRoute( 24 | name: 'home', 25 | path: '/', 26 | builder: ..., 27 | routes: [ 28 | GoRoute( 29 | name: 'family', 30 | path: 'family/:fid', 31 | builder: ..., 32 | routes: [ 33 | GoRoute( 34 | name: 'person', 35 | path: 'person/:pid', 36 | builder: ..., 37 | ), 38 | ], 39 | ), 40 | ], 41 | ), 42 | GoRoute( 43 | name: 'login', 44 | path: '/login', 45 | builder: ..., 46 | ), 47 | ], 48 | ``` 49 | 50 | ルートに名前を付けることは必須ではありませんが、付けた場合はその名前を利用して、そのルートへの遷移を行うことができます(パラメータを含めることも可能)。 51 | 52 | ```dart 53 | void _tap(BuildContext context, String fid, String pid) => 54 | context.go(context.namedLocation('person', params: {'fid': fid, 'pid': pid})); 55 | ``` 56 | 57 | `namedLocation` メソッドは、指定した名前を持つルートをケース・インセンシティブ方式で検索し、 58 | そのルート情報から、パラメータを含めた形で URI を生成してくれます。 59 | あるはずのパラメータの指定がなかったり、そのパスにはないパラメータを渡すとエラーが発生します。 60 | また、上記のように `context` オブジェクトを2度逆参照するのはいくらか不便なため、go_router では `goNamed` メソッドも用意しています。 61 | これによりロケーション URI の生成からナビゲーションまで、1ステップで行うことが可能です。 62 | 63 | ```dart 64 | void _tap(BuildContext context, String fid, String pid) => 65 | context.goNamed('person', params: {'fid': fid, 'pid': pid}); 66 | ``` 67 | 68 | さらに、`pushNamed` メソッドもあります。これはルートを名前で検索した後、 69 | マッチした通しルートから末端のサブルートのページのみを抜き出し、既存のページスタックに追加(プッシュ)してくれるものです。 70 | 71 | ## 名前付きルートへのリダイレクト 72 | 73 | 名前付きルートは、通常の遷移に加えてリダイレクトも可能です。 74 | ここでも `GoRouter` もしくは `GoRouterState` の `namedLocation` メソッドを使うことができます。 75 | 76 | ```dart 77 | // ユーザーがログインしていない場合はログインページにリダイレクト 78 | redirect: (state) { 79 | // ユーザーがログインしていない場合はログインが必要 80 | final loggedIn = loginInfo.loggedIn; 81 | final loginloc = state.namedLocation('login'); 82 | final loggingIn = state.subloc == loginloc; 83 | 84 | // ユーザーの元々の行き先のロケーション名をクエリパラメータに紐づける 85 | final homeloc = state.namedLocation('home'); 86 | final fromloc = state.subloc == homeloc ? '' : state.subloc; 87 | if (!loggedIn) { 88 | return loggingIn 89 | ? null 90 | : state.namedLocation( 91 | 'login', 92 | queryParams: {if (fromloc.isNotEmpty) 'from': fromloc}, 93 | ); 94 | } 95 | 96 | // ユーザーがログインしている場合はユーザーを元々の行き先に誘導する 97 | // 行き先がない場合はホーム画面へ 98 | if (loggingIn) return state.queryParams['from'] ?? homeloc; 99 | 100 | // 一切リダイレクトが不要な場合 101 | return null; 102 | }, 103 | ``` 104 | 105 | この例では、`namedLocation` を使って、「login」という名前のルートのロケーション名を取得し、 106 | この時点での行き先情報である `subloc` を比較することでユーザがログイン画面に向かうところなのか否かをチェックしています。 107 | さらに、リダイレクト用にパラメータ付きのロケーション名を生成する際にも `namedLocation` を使用しています。 108 | これらはすべて、URI をハードコーディングすることなく行われています。 109 | -------------------------------------------------------------------------------- /docs/ja/navigation.mdx: -------------------------------------------------------------------------------- 1 | # ナビゲーション 2 | 3 | ページ間を遷移するには `GoRouter.go` メソッドを使用します。 4 | 5 | ```dart 6 | // GoRouter を使って遷移 7 | onTap: () => GoRouter.of(context).go('/page2') 8 | ``` 9 | 10 | また go_router は Dart の拡張メソッドを利用して上記メソッドの簡易バージョンも用意しています。 11 | 12 | ```dart 13 | // より簡単に GoRouter を使って遷移 14 | onTap: () => context.go('/page2') 15 | ``` 16 | 17 | 簡易バージョンにはフルバージョンのものがそのまま割り当てられるため、どちらを使用していただいても構いません。 18 | 余談ですが、`context.go(...)` を実行するだけで魔法のようにページ間を遷移できる... というのが go_router の名前の由来です。 19 | 20 | また、url_launcher の [`Link` ウィジェット](https://pub.dev/documentation/url_launcher/latest/link/link-library.html)を使用して遷移することも可能です。 21 | 22 | ```dart 23 | Link( 24 | uri: Uri.parse('/page2'), 25 | builder: (context, followLink) => TextButton( 26 | onPressed: followLink, 27 | child: const Text('Go to page 2'), 28 | ), 29 | ), 30 | ``` 31 | 32 | `Link` ウィジェットがスキーム付きで URL を渡された場合(例: `https://flutter.dev`)、そのリンクはブラウザで開きます。 33 | スキームなしの場合は、アプリのナビゲーションシステムによりアプリ内リンクに遷移します。 34 | 35 | [名前付きルート](/named-routes)に遷移することも可能です。 36 | 37 | ## ページを「プッシュ」する 38 | 39 | go_router には `go` メソッドに加えて、`push` メソッドがあります。 40 | `go` と `push` はいずれもページのスタックを生成してくれる点では同じですが、その方法が異なります。 41 | `go` メソッドはロケーション名からマッチした通しルートの全ての[サブルート](/sub-routes)からページを生成し、それらを順番にページのスタックに追加します。 42 | 43 | 一方の `push` メソッドは既存のページスタックに、1ページのみ追加(プッシュ)します。 44 | このページはロケーション名とマッチした通しルートの、最も末端のサブルートに紐づくページです。 45 | `go` が宣言的にページのスタックを生成するのに対して、 `push` は命令的にスタックを生成するという違いもあります。 46 | 47 | また、「プッシュ」は[名前付きルート](/named-routes)に対して行うことも可能です。 48 | 49 | ## ページを「ポップ」する 50 | 51 | スタックからページを取り出す(ポップ)には、`pop` メソッドを使用してください。 52 | 53 | ## 初期ロケーション名 54 | 55 | ルーティング(Routing)の初期ロケーション名は、`GoRouter` コンストラクタの `initialLocation` で設定可能です。 56 | 57 | ```dart 58 | final _router = GoRouter( 59 | routes: ..., 60 | initialLocation: '/page2', 61 | ); 62 | ``` 63 | 64 | [ディープリンク](/declarative-routing#ディープリンク)を使用してアプリを立ち上げた場合は `initialLocation` の値は無視されます。 65 | 66 | ## 現在のロケーション名 67 | 68 | 現在のロケーション名を取得するには `GoRouter.location` プロパティを使用します。 69 | 70 | 手動のナビゲーションにより、あるいはディープリンクにより、あるいはユーザーが戻るボタンを押してポップしたことにより、現在のロケーション名は変化します。 71 | その変化に応じて何かアクションを起こしたい場合は `GoRouter` にリスナーを追加します。 72 | `GoRouter` は [`ChangeNotifier`](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html) でもあるため、 73 | `addListener` を呼び出してコールバックを登録することができます。 74 | あるいは、その名称が直感的とは言い難いことで有名な [`AnimatedBuilder`](https://stackoverflow.com/a/67016227) に `GoRouter` を設定しましょう。 75 | 76 | ```dart 77 | class RouterLocationView extends StatelessWidget { 78 | const RouterLocationView({Key? key}) : super(key: key); 79 | 80 | @override 81 | Widget build(BuildContext context) { 82 | final router = GoRouter.of(context); 83 | return AnimatedBuilder( 84 | animation: router, 85 | builder: (context, child) => Text(router.location), 86 | ); 87 | } 88 | } 89 | ``` 90 | 91 | もしくは、[Provider パッケージ](https://pub.dev/packages/provider)を使えば、 92 | `ChangeNotifier` からの通知を受け取って `Widget` を再構築する仕組みを利用することができます。 93 | その型の名称は `AnimatedBuilder` より目的がはっきりしているはずです。 94 | -------------------------------------------------------------------------------- /docs/ja/navigator-builder.mdx: -------------------------------------------------------------------------------- 1 | # Navigator ビルダー 2 | 3 | `MaterialApp`/`CupertinoApp` と `Navigator` の間にウィジェットを置きたいと思ったことはないでしょうか。 4 | 例えば、context 経由で現在のロケールを取得する Provider を置いたり、 5 | ナビゲーションの外側にUIを設けたり、あるいは `Navigator` を他の独自のものに置き換えたり(これについてはこのドキュメントの範囲外ですが)。 6 | 7 | これらの目的を達成するには `GoRouter` コンストラクタの `navigatorBuilder` パラメータを使用する必要があります。 8 | これは `MaterialApp` の `builder` パラメータに似ていますが、`MaterialApp` によって提供されるインフラストラクチャにアクセスすることができます。 9 | 10 | 以下は `navigatorBuilder` を使って Provider ウィジェットを置く例です。 11 | 12 | ```dart 13 | final _router = GoRouter( 14 | routes: ..., 15 | 16 | // loginInfo をウィジェットツリーに挿入するために Navigator にラッパーを追加する 17 | navigatorBuilder: (context, state, child) => 18 | ChangeNotifierProvider.value( 19 | value: loginInfo, 20 | builder: (context, _) => child, 21 | ), 22 | ); 23 | ``` 24 | 25 | また、こちらはより興味深い例です。全てのページにオーバーレイのボタンを表示し、簡単にログアウトできるようにしています。 26 | 27 | ```dart 28 | final _router = GoRouter( 29 | routes: ..., 30 | 31 | // 次の2点の目的のために Navigator にラッパーを追加 32 | // - loginInfo をウィジェットツリーに挿入 33 | // - ログアウト用のオーバーレイを追加 34 | navigatorBuilder: (context, state, child) => 35 | ChangeNotifierProvider.value( 36 | value: loginInfo, 37 | builder: (context, _) => 38 | loginInfo.loggedIn ? AuthOverlay(child: child) : child; 39 | }, 40 | ), 41 | ); 42 | ``` 43 | 44 | この例では `navigatorBuilder` の中でログインステータスをチェックしています。 45 | 46 | - ユーザーがログインしていれば、`AuthOverlay` ウィジェットのインスタンスが作成されます。 47 | `navigatorBuilder` に `child` として渡される `Navigator` の上にこれを置き、全ページにログアウトボタンを表示します。 48 | - ユーザーがログインしていない場合は、`child` パラメータからそのまま `Navigator` を返します。 49 | 50 | `AuthOverlay` は `Stack` を使ってログアウトボタンと `Navigator` を表示しています。 51 | 52 | ```dart 53 | class AuthOverlay extends StatelessWidget { 54 | const AuthOverlay({required this.child, Key? key}) : super(key: key); 55 | 56 | final Widget child; 57 | 58 | @override 59 | Widget build(BuildContext context) => Stack( 60 | children: [ 61 | child, 62 | Positioned( 63 | top: 90, 64 | right: 4, 65 | child: ElevatedButton( 66 | onPressed: () { 67 | context.read().logout(); 68 | context.goNamed('home'); // クエリパラメータ `from` をクリア 69 | }, 70 | child: const Icon(Icons.logout), 71 | ), 72 | ), 73 | ], 74 | ); 75 | } 76 | ``` 77 | 78 | 実際の動作はこのようになります。 79 | 80 | ![navigatorBuilder の実際の動作](../images/nav_builder.gif) 81 | -------------------------------------------------------------------------------- /docs/ja/parameters.mdx: -------------------------------------------------------------------------------- 1 | # パラメータ 2 | 3 | ルートのパス(path)は [path_to_regexp パッケージ](https://pub.dev/packages/path_to_regexp)により定義・実装されています。 4 | これにより、`path` にパラメータを含めることができます。 5 | 6 | ```dart 7 | final _router = GoRouter( 8 | routes: [ 9 | GoRoute( 10 | path: '/family/:fid', 11 | builder: (context, state) { 12 | // パスパラメータの値を取得するには state.params を使用 13 | final family = Families.family(state.params['fid']!); 14 | return FamilyScreen(family: family); 15 | }, 16 | ), 17 | ], 18 | ]); 19 | ``` 20 | 21 | `params` プロパティを使うことで `state` オブジェクト内のパスパラメータを取得することができます。 22 | 23 | ## 動的リンク 24 | 25 | 「動的リンク」とは、ユーザーがアプリにオブジェクトを追加する度に、それに対して固有のリンクを発行することです。 26 | これはまさに、パラメータが可能にしてくれることです。 27 | 例えば、ルートのパスに `/family/:fid` と変数を含めることで、family ごとに固有の識別子を持たせることができます。 28 | 29 | ## クエリパラメータ 30 | 31 | パラメータ変数を `path` に含めることは、ページに情報を渡すための一つの方法です。 32 | その場合パスパラメータを渡すのは必須であり、遷移先として指定するロケーション名に含める必要があります。 33 | ロケーション名の指定とともにページに情報を渡す別の方法は、クエリパラメータを使うことです。 34 | これは URI 末尾の `?` より後の情報を「名称 - 値」ペアのセットとし `GoRouterState` に設定する方法で、指定方法は以下の通りです。 35 | 36 | ```dart 37 | void _tap() => context.go('/search?query=kitties'); 38 | ``` 39 | 40 | このクエリパラメータはオプションですが、指定した場合はマッチした通しルートの全てのページに対して、 41 | `state.queryParams` プロパティ経由で情報を渡すことができます。 42 | 43 | ```dart 44 | GoRoute( 45 | path: '/search', 46 | builder: (context, state) { 47 | // 検索クエリのパラメータを取得するには state.queryParams を使用 48 | final query = state.queryParams['query']; // nullの可能性あり 49 | return SearchPage(query: query); 50 | }, 51 | ), 52 | ``` 53 | 54 | クエリパラメータはオプションのため、渡されていない場合の値は `null` となります。 55 | 56 | ## エキストラ(追加)パラメータ 57 | 58 | パスパラメータとクエリパラメータに加えて、エキストラ(追加)パラメータとしてオブジェクトを渡すこともできます。 59 | 60 | ```dart 61 | void _tap() => context.go('/family', extra: _family); 62 | ``` 63 | 64 | このオブジェクトは `state.extra` で取得可能です。 65 | 66 | ```dart 67 | GoRoute( 68 | path: '/family', 69 | builder: (context, state) => FamilyScreen(family: state.extra! as Family), 70 | ), 71 | ``` 72 | 73 | エキストラパラメータによる `extra` オブジェクトは、URI 経由で識別子を渡してオブジェクトをストアから取得する代わりに、 74 | シンプルにオブジェクト自体を `builder` 関数に渡したい場合に便利です。 75 | また `AppBar` の戻るボタンを押してページがポップされた場合も、`extra` オブジェクトは適切に渡されます。 76 | 77 | _ただし_、`extra` オブジェクトから動的リンクやディープリンクを作成することはできません。 78 | さらに、ブラウザの戻るボタンはナビゲーションの目的でディープリンクのように扱われるため、 79 | ユーザーがブラウザ経由でページを戻ると `extra` オブジェクトは失われます。 80 | これらの理由から、__`extra` オブジェクトの使用は、ウェブアプリをターゲットにする場合は非推奨です__。 -------------------------------------------------------------------------------- /docs/ja/sub-routes.mdx: -------------------------------------------------------------------------------- 1 | # サブルート 2 | 3 | トップレベルのルート(`GoRouter.routes` 直下に置かれる `GoRoute` インスタンス)は全て、1ページ分のナビゲーションスタックを作成します。 4 | これを複数ページのスタックにするにはサブルート(`GoRoute.routes`)を使用します。 5 | トップレベルのルートがロケーション名の一部にしかマッチしない場合は、その一部を除いた残りのロケーション名が以降のサブルートと照合されます。 6 | このルールはどのレベルのサブルートにおいても変わりません。いずれのレベルでもマッチするルートは1つで、ロケーション名全体がマッチするまで照合は続きます。 7 | 8 | 例えば、`/family/f1/person/p2` というロケーション名の場合においても、 9 | ページのスタックを生成するために複数のサブルートとの照合が行われます。 10 | 11 | ```text 12 | / => HomeScreen() 13 | family/f1 => FamilyScreen('f1') 14 | person/p2 => PersonScreen('f1', 'p2') ← このページで戻るボタンを押すとポップして ↑ が表示 15 | ``` 16 | 17 | このようなページのセットを作るには、`GoRoute` コンストラクタの `routes` でサブルートを指定します。 18 | 19 | ```dart 20 | final _router = GoRouter( 21 | routes: [ 22 | GoRoute( 23 | path: '/', 24 | builder: (context, state) => HomeScreen(families: Families.data), 25 | routes: [ 26 | GoRoute( 27 | path: 'family/:fid', 28 | builder: (context, state) { 29 | final family = Families.family(state.params['fid']!); 30 | return FamilyScreen(family: family); 31 | }, 32 | routes: [ 33 | GoRoute( 34 | path: 'person/:pid', 35 | builder: (context, state) { 36 | final family = Families.family(state.params['fid']!); 37 | final person = family.person(state.params['pid']!); 38 | 39 | return PersonScreen(family: family, person: person); 40 | }, 41 | ), 42 | ], 43 | ), 44 | ], 45 | ), 46 | ], 47 | ); 48 | ``` 49 | 50 | go_router はルートツリーの上位レベルから下位レベルまで照合を続けることで、ページのスタックを生成していきます。 51 | ロケーション名に対応する通しルートがない場合は、[エラーハンドラ](/declarative-routing#エラーハンドラ)が呼び出されます。 52 | 53 | また、go_router ではパラメータは上位レベルのルートから受け継がれていくため、下位レベルのルートでも同じパラメータを使用することができます。 54 | 例えば、`fid` は `family/:fid` ルートに渡されますが、そのサブルートである `person/:pid` へも受け継がれます。 55 | -------------------------------------------------------------------------------- /docs/ja/url-path-strategy.mdx: -------------------------------------------------------------------------------- 1 | # URL パス・ストラテジー 2 | 3 | Flutter ウェブの URL はデフォルトでハッシュ記号(#)を含みます。 4 | 5 | ![URL Strategy w/ Hash](../images/url-strat-hash.png) 6 | 7 | ## ハッシュ記号を除く 8 | 9 | ハッシュ記号を除く方法は[公式ドキュメント](https://flutter.dev/docs/development/ui/navigation/url-strategies)に記載がありますが、少し手間がかかります。 10 | go_router にはこの URL パス・ストラテジーの設定を行う機能が組み込まれています。 11 | 12 | ```dart 13 | void main() { 14 | // URL に # を含める(デフォルト) 15 | // GoRouter.setUrlPathStrategy(UrlPathStrategy.hash); 16 | 17 | // ウェブの URL から # を除く 18 | GoRouter.setUrlPathStrategy(UrlPathStrategy.path); 19 | 20 | runApp(App()); 21 | } 22 | ``` 23 | 24 | `hash` の代わりに `path` ストラテジーを設定することで URL から # が除かれます。 25 | 26 | ![ハッシュ記号なしの URL ストラテジー](../images/url-strat-no-hash.png) 27 | 28 | `runApp` メソッドに渡すウィジェットによりルータが生成される場合は `GoRouter` コンストラクタの 29 | `urlPathStrategy` パラメータを使用して URL パス・ストラテジーを設定することも可能です。 30 | 31 | ```dart 32 | // この時点で GoRouter.setUrlPathStrategy() を呼ぶ必要はありません 33 | void main() => runApp(App()); 34 | 35 | /// サンプルアプリでの URL パス・ストラテジーの設定 36 | class App extends StatelessWidget { 37 | ... 38 | final _router = GoRouter( 39 | routes: ..., 40 | 41 | // ウェブの URL から # を除く 42 | urlPathStrategy: UrlPathStrategy.path, 43 | ); 44 | } 45 | ``` 46 | 47 | ## ウェブサーバーの設定 48 | 49 | 最後に、Flutter ウェブアプリをウェブサーバーにデプロイするとき、全ての URL が Flutter ウェブアプリの `index.html` に紐づくよう設定が必要です。 50 | 設定しないと Flutter はページのルーティングができません。Firebase ホスティングを利用する場合は、 51 | [rewrites を設定](https://firebase.google.com/docs/hosting/full-config#rewrites)することで URL が `index.html` に書き換わるようにできます。 52 | 53 | 公開前にリリースビルドをローカルでテストする際に `index.html` にリダイレクトする機能を試したい場合は、 54 | 以下のように `flutter run` を実行します。 55 | 56 | ```sh 57 | $ flutter run -d chrome --release lib/url_strategy.dart 58 | ``` 59 | 60 | このコマンドは `flutter run` が `web/index.html` ファイルを探せる場所で実行する必要がある点に注意してください。 61 | 62 | もちろん、全てのトラフィックを `index.html` にリダイレクトするよう設定できるローカルウェブサーバーがあればそれで事足ります。 63 | (例: [live-server](https://www.npmjs.com/package/live-server)) -------------------------------------------------------------------------------- /docs/ja/web-history.mdx: -------------------------------------------------------------------------------- 1 | # ウェブ履歴 2 | 3 | ページ間を遷移する際にウェブブラウザに履歴エントリーを作成させたくない場合は 4 | [Router.neglect](https://api.flutter.dev/flutter/widgets/Router/neglect.html) を使用してください。 5 | 6 | ```dart 7 | ElevatedButton( 8 | // この遷移におけるブラウザによる履歴の追跡をオフにする 9 | onPressed: () => Router.neglect(context, () => context.go('/page2'))}, 10 | ... 11 | ), 12 | ``` 13 | 14 | `Router.neglect` を使用することで Flutter のルータは遷移先のページをブラウザの履歴に追加しません。 15 | アプリ全体で履歴の追跡を停止したい場合は、`GoRouter` コンストラクタの `routerNeglect` パラメータを設定してください。 16 | これにより go_router を介して行われるナビゲーションは全て履歴エントリーが作成されなくなります。 17 | 18 | ```dart 19 | final _router = GoRouter( 20 | // 全てのナビゲーションにおけるブラウザの履歴追跡をオフにする 21 | routerNeglect: true, 22 | ... 23 | ); 24 | ``` 25 | 26 | 尚、これらの設定を行ってもディープリンクと動的リンクには影響せず、ブラウザのアドレスバーも通常通り更新されます。 27 | 影響が出るのはブラウザの戻るボタンだけです。 -------------------------------------------------------------------------------- /docs/migrating-to-20.mdx: -------------------------------------------------------------------------------- 1 | # Migrating to 2.0 2 | 3 | By [popular demand](https://twitter.com/csells/status/1445520767190388738), 4 | there is a breaking change in the go_router 2.0 release: the `params` property 5 | of the `GoRouterState` object has been split into two properties: 6 | 7 | - `params` for parameters that are part of the path and, e.g. `/family/:fid` 8 | - `queryParams` for parameters that added optionally at the end of the location, 9 | e.g. `/login?from=/family/f2` 10 | 11 | In the 1.x releases, the `params` property was a single object that contained 12 | both the path and query parameters in a single map. For example, if you had been 13 | using the `params` property to access query parameters like this in 1.x: 14 | 15 | ```dart 16 | GoRoute( 17 | path: '/login', 18 | pageBuilder: (context, state) => MaterialPage( 19 | key: state.pageKey, 20 | // 1.x: accessing query parameters 21 | child: LoginScreen(from: state.params['from']), 22 | ), 23 | ), 24 | ``` 25 | 26 | in 2.0, you would access the query parameters like this: 27 | 28 | ```dart 29 | GoRoute( 30 | path: '/login', 31 | pageBuilder: (context, state) => MaterialPage( 32 | key: state.pageKey, 33 | // 2.0: accessing query parameters 34 | child: LoginScreen(from: state.queryParams['from']), 35 | ), 36 | ), 37 | ``` 38 | 39 | Likewise, if you were using named routes in 1.x, you may have been passing both 40 | path and query parameters like so: 41 | 42 | ```dart 43 | ListTile( 44 | title: Text(p.name), 45 | // 1.x: passing both path and query parameters 46 | onTap: () => context.goNamed( 47 | 'person', 48 | // anything not a path param was assumed to be a query param 49 | {'fid': family.id, 'pid': p.id, 'qid': 'quid'}, 50 | ), 51 | ), 52 | ``` 53 | 54 | Now you'll need to change your code to do the following in 2.0: 55 | 56 | ```dart 57 | ListTile( 58 | title: Text(p.name), 59 | // 2.0: passing both path and query parameters 60 | onTap: () => context.goNamed( 61 | 'person', 62 | params: {'fid': family.id, 'pid': p.id}, 63 | queryParams: {'qid': 'quid'}, 64 | ), 65 | ), 66 | ``` 67 | 68 | I got a little clever merging the two kinds of parameters into a single scope 69 | and hopefully this change makes things a little more clear. 70 | -------------------------------------------------------------------------------- /docs/navigation.mdx: -------------------------------------------------------------------------------- 1 | # Navigation 2 | 3 | To navigate between pages, use the `GoRouter.go` method: 4 | 5 | ```dart 6 | // navigate using the GoRouter 7 | onTap: () => GoRouter.of(context).go('/page2') 8 | ``` 9 | 10 | go_router also provides a simplified means of navigation using Dart extension 11 | methods: 12 | 13 | ```dart 14 | // navigate using the GoRouter more easily 15 | onTap: () => context.go('/page2') 16 | ``` 17 | 18 | The simplified version maps directly to the more fully-specified version, so you 19 | can use either. If you're curious, the ability to just call `context.go(...)` 20 | and have magic happen is where the name of go_router came from. 21 | 22 | If you'd like to navigate via [the `Link` 23 | widget](https://pub.dev/documentation/url_launcher/latest/link/link-library.html), 24 | that works, too: 25 | 26 | ```dart 27 | Link( 28 | uri: Uri.parse('/page2'), 29 | builder: (context, followLink) => TextButton( 30 | onPressed: followLink, 31 | child: const Text('Go to page 2'), 32 | ), 33 | ), 34 | ``` 35 | 36 | If the `Link` widget is given a URL with a scheme, e.g. `https://flutter.dev`, 37 | then it will launch the link in a browser. Otherwise, it'll navigate to the link 38 | inside the app using the built-in navigation system. 39 | 40 | You can also navigate to a [named route](/named-routes). 41 | 42 | ## Pushing pages 43 | 44 | In addition to the `go` method, go_router also provides a `push` method. Both 45 | `go` and `push` can be used to build up a stack of pages, but in different ways. 46 | The `go` method does this by turning a single location into any number of pages 47 | in a stack using [sub-routes](/sub-routes). 48 | 49 | The `push` method is used to push a single page onto the stack of existing 50 | pages, which means that you can build up the stack programmatically instead of 51 | declaratively. When the `push` method matches an entire stack via sub-routes, it 52 | will take the top-most page from the stack and push that page onto the stack. 53 | 54 | You can also push a [named route](/named-routes). 55 | 56 | ## Popping pages 57 | 58 | If you'd like to pop a page from the go_router stack, you can use the `pop` 59 | method. 60 | 61 | ## Initial Location 62 | 63 | If you'd like to set an initial location for routing, you can set the 64 | `initialLocation` argument of the `GoRouter` constructor: 65 | 66 | ```dart 67 | final _router = GoRouter( 68 | routes: ..., 69 | initialLocation: '/page2', 70 | ); 71 | ``` 72 | 73 | The value you provide to `initialLocation` will be ignored if your app is 74 | started using [deep linking](/declarative-routing#deep-linking). 75 | 76 | ## Current location 77 | 78 | If you want to know the current location, use the `GoRouter.location` property. 79 | 80 | If you'd like to know when the current location changes, either because of 81 | manual navigation or a deep link or a pop due to the user pushing the Back 82 | button, the `GoRouter` is a 83 | [`ChangeNotifier`](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html), 84 | which means that you can call `addListener` to be notified when the location 85 | changes, either manually or via Flutter's builder widget for `ChangeNotifier` 86 | objects, the non-intuitively named 87 | [`AnimatedBuilder`](https://stackoverflow.com/a/67016227): 88 | 89 | ```dart 90 | class RouterLocationView extends StatelessWidget { 91 | const RouterLocationView({Key? key}) : super(key: key); 92 | 93 | @override 94 | Widget build(BuildContext context) { 95 | final router = GoRouter.of(context); 96 | return AnimatedBuilder( 97 | animation: router, 98 | builder: (context, child) => Text(router.location), 99 | ); 100 | } 101 | } 102 | ``` 103 | 104 | Or, if you're using [the provider package](https://pub.dev/packages/provider), 105 | it comes with built-in support for re-building a `Widget` when a 106 | `ChangeNotifier` changes with a type that is much more clearly suited for the 107 | purpose. 108 | -------------------------------------------------------------------------------- /docs/navigator-builder.mdx: -------------------------------------------------------------------------------- 1 | # Navigator Builder 2 | 3 | Sometimes it is necessary to insert a widget above the `Navigator`, but below 4 | `MaterialApp`/`CupertinoApp`, e.g. to insert a provider that needs access to the 5 | app's context to get the current locale and localization, to build a UI outside 6 | of navigation or to completely replace with `Navigator` with something of your 7 | own (which is outside the scope of this document). 8 | 9 | For these purposes, you need to use the `navigatorBuilder` parameter in the 10 | `GoRouter` constructor. This is similar to the `builder` parameter in the 11 | `MaterialApp` constructor, but gives access to infrastructure provided by 12 | `MaterialApp`. 13 | 14 | An example of placing some data provider widget: 15 | 16 | ```dart 17 | final _router = GoRouter( 18 | routes: ..., 19 | 20 | // add a wrapper around the navigator to put loginInfo into the widget tree 21 | navigatorBuilder: (context, state, child) => 22 | ChangeNotifierProvider.value( 23 | value: loginInfo, 24 | builder: (context, _) => child, 25 | ), 26 | ); 27 | ``` 28 | 29 | A more interesting example of using `navigatorBuilder` is the following, which 30 | puts a floating button on every page to allow for easy logout: 31 | 32 | ```dart 33 | final _router = GoRouter( 34 | routes: ..., 35 | 36 | // add a wrapper around the navigator to: 37 | // - put loginInfo into the widget tree, and to 38 | // - add an overlay to show a logout option 39 | navigatorBuilder: (context, state, child) => 40 | ChangeNotifierProvider.value( 41 | value: loginInfo, 42 | builder: (context, _) => 43 | loginInfo.loggedIn ? AuthOverlay(child: child) : child; 44 | }, 45 | ), 46 | ); 47 | ``` 48 | 49 | This example checks the login status in the `navigatorBuilder`: 50 | 51 | - if the user is logged in, an instance of the `AuthOverlay` widget is created, 52 | which wraps the the `Navigator` passed to `navigatorBuilder` via the `child` 53 | parameter and provides a logout button on every page 54 | - if the user is not logged in, return the `Navigator` via the `child` parameter 55 | 56 | The `AuthOverlay` shows the logout button and the `Navigator` in a `Stack`: 57 | 58 | ```dart 59 | class AuthOverlay extends StatelessWidget { 60 | const AuthOverlay({required this.child, Key? key}) : super(key: key); 61 | 62 | final Widget child; 63 | 64 | @override 65 | Widget build(BuildContext context) => Stack( 66 | children: [ 67 | child, 68 | Positioned( 69 | top: 90, 70 | right: 4, 71 | child: ElevatedButton( 72 | onPressed: () { 73 | context.read().logout(); 74 | context.goNamed('home'); // clear out the `from` query param 75 | }, 76 | child: const Icon(Icons.logout), 77 | ), 78 | ), 79 | ], 80 | ); 81 | } 82 | ``` 83 | 84 | Here's what this look like in action: 85 | 86 | ![navigatorBuilder in 87 | action](/images/nav_builder.gif) 88 | -------------------------------------------------------------------------------- /docs/parameters.mdx: -------------------------------------------------------------------------------- 1 | # Parameters 2 | 3 | Route paths are defined and implemented in [the path_to_regexp 4 | package](https://pub.dev/packages/path_to_regexp), which gives you the ability 5 | to include parameters in your route's `path`: 6 | 7 | ```dart 8 | final _router = GoRouter( 9 | routes: [ 10 | GoRoute( 11 | path: '/family/:fid', 12 | builder: (context, state) { 13 | // use state.params to get router parameter values 14 | final family = Families.family(state.params['fid']!); 15 | return FamilyScreen(family: family); 16 | }, 17 | ), 18 | ], 19 | ]); 20 | ``` 21 | 22 | You can access the matched parameters in the `state` object using the `params` 23 | property. 24 | 25 | ## Dynamic Linking 26 | 27 | The idea of "dynamic linking" is that as the user adds objects to your app, each 28 | of them gets a link of their own, e.g. a new family gets a new link. This is 29 | exactly what route parameters enables, e.g. a new family has its own identifier 30 | when can be a variable in your family route, e.g. path: `/family/:fid`. 31 | 32 | ## Query Parameters 33 | 34 | Including parameters in the `path` is one way to pass information to the page. A 35 | "path" parameter is required and inline with the location. The other way to pass 36 | data as part of a location is to use query parameters, which is a set of 37 | name-value pairs passed at the end of a URI after a `?` character, e.g. 38 | 39 | ```dart 40 | void _tap() => context.go('/search?query=kitties'); 41 | ``` 42 | 43 | These parameters are optional and, if passed, will be provided in the 44 | `state.queryParams` property for every page matched in a stack of routes: 45 | 46 | ```dart 47 | GoRoute( 48 | path: '/search', 49 | builder: (context, state) { 50 | // use state.queryParams to get search query from query parameter 51 | final query = state.queryParams['query']; // may be null 52 | return SearchPage(query: query); 53 | }, 54 | ), 55 | ``` 56 | 57 | Since query parameters are optional, they will be `null` if they're not passed. 58 | 59 | ## Extra Parameter 60 | 61 | In addition to passing along path and query parameters, you can also pass along 62 | an extra object as part of your navigation, e.g. 63 | 64 | ```dart 65 | void _tap() => context.go('/family', extra: _family); 66 | ``` 67 | 68 | This object is provided as `state.extra`: 69 | 70 | ```dart 71 | GoRoute( 72 | path: '/family', 73 | builder: (context, state) => FamilyScreen(family: state.extra! as Family), 74 | ), 75 | ``` 76 | 77 | The `extra` object is useful if you'd like to simply pass along a single object 78 | to the `builder` function w/o passing an object identifier via a URI and looking 79 | up the object from a store. Also, if the user presses the Back button on an 80 | `AppBar`, the `extra` object will be passed along properly. 81 | 82 | _However_, the `extra` object cannot be used to create a dynamic link nor can it 83 | be used in deep linking. Furthermore, since a press of the brower's Back button 84 | is treated like a deep link for purposes of navigation, the `extra` object will 85 | be lost when the user navigates back via the browser. For these reasons, __the 86 | use of the `extra` object is not recommended for use in targeting Flutter web 87 | apps__. -------------------------------------------------------------------------------- /docs/sub-routes.mdx: -------------------------------------------------------------------------------- 1 | # Sub-routes 2 | 3 | Every top-level route will create a navigation stack of one page. To produce an 4 | entire stack of pages, you can use sub-routes. In the case that a top-level 5 | route only matches part of the location, the rest of the location can be matched 6 | against sub-routes. The rules are still the same, i.e. that only a single 7 | route at any level will be matched and the entire location much be matched. 8 | 9 | For example, the location `/family/f1/person/p2`, can be made to match multiple 10 | sub-routes to create a stack of pages: 11 | 12 | ```text 13 | / => HomeScreen() 14 | family/f1 => FamilyScreen('f1') 15 | person/p2 => PersonScreen('f1', 'p2') ← showing this page, Back pops the stack ↑ 16 | ``` 17 | To specify a set of pages like this, you can use sub-page routing via the 18 | `routes` parameter to the `GoRoute` constructor: 19 | 20 | ```dart 21 | final _router = GoRouter( 22 | routes: [ 23 | GoRoute( 24 | path: '/', 25 | builder: (context, state) => HomeScreen(families: Families.data), 26 | routes: [ 27 | GoRoute( 28 | path: 'family/:fid', 29 | builder: (context, state) { 30 | final family = Families.family(state.params['fid']!); 31 | return FamilyScreen(family: family); 32 | }, 33 | routes: [ 34 | GoRoute( 35 | path: 'person/:pid', 36 | builder: (context, state) { 37 | final family = Families.family(state.params['fid']!); 38 | final person = family.person(state.params['pid']!); 39 | 40 | return PersonScreen(family: family, person: person); 41 | }, 42 | ), 43 | ], 44 | ), 45 | ], 46 | ), 47 | ], 48 | ); 49 | ``` 50 | 51 | go_router will match the routes all the way down the tree of sub-routes to build 52 | up a stack of pages. If go_router doesn't find a match, then [the error 53 | handler](/declarative-routing#error-handling) will be called. 54 | 55 | Also, go_router will pass parameters from higher level sub-routes so that they 56 | can be used in lower level routes, e.g. `fid` is matched as part of the 57 | `family/:fid` route, but it's passed along to the `person/:pid` route because 58 | it's a sub-route of the `family/:fid` route. -------------------------------------------------------------------------------- /docs/url-path-strategy.mdx: -------------------------------------------------------------------------------- 1 | # URL Path Strategy 2 | 3 | By default, Flutter adds a hash (#) into the URL for web apps: 4 | 5 | ![URL Strategy w/ Hash](/images/url-strat-hash.png) 6 | 7 | ## Turning Off the Hash 8 | 9 | The process for turning off the hash is 10 | [documented](https://flutter.dev/docs/development/ui/navigation/url-strategies) 11 | but fiddly. go_router has built-in support for setting the URL path strategy, 12 | however, so you can simply call `GoRouter.setUrlPathStrategy` before calling 13 | `runApp` and make your choice: 14 | 15 | ```dart 16 | void main() { 17 | // turn on the # in the URLs on the web (default) 18 | // GoRouter.setUrlPathStrategy(UrlPathStrategy.hash); 19 | 20 | // turn off the # in the URLs on the web 21 | GoRouter.setUrlPathStrategy(UrlPathStrategy.path); 22 | 23 | runApp(App()); 24 | } 25 | ``` 26 | 27 | Setting the path instead of the hash strategy turns off the # in the URLs: 28 | 29 | ![URL Strategy w/o Hash](/images/url-strat-no-hash.png) 30 | 31 | If your router is created as part of the construction of the widget passed to 32 | the `runApp` method, you can use a shortcut to set the URL path strategy by 33 | using the `urlPathStrategy` parameter of the `GoRouter` constructor: 34 | 35 | ```dart 36 | // no need to call GoRouter.setUrlPathStrategy() here 37 | void main() => runApp(App()); 38 | 39 | /// sample app using the path URL strategy, i.e. no # in the URL path 40 | class App extends StatelessWidget { 41 | ... 42 | final _router = GoRouter( 43 | routes: ..., 44 | 45 | // turn off the # in the URLs on the web 46 | urlPathStrategy: UrlPathStrategy.path, 47 | ); 48 | } 49 | ``` 50 | 51 | ## Configuring the Web Server 52 | 53 | Finally, when you deploy your Flutter web app to a web server, it needs to be 54 | configured such that every URL ends up at your Flutter web app's `index.html`, 55 | otherwise Flutter won't be able to route to your pages. If you're using Firebase 56 | hosting, you can [configure 57 | rewrites](https://firebase.google.com/docs/hosting/full-config#rewrites) to 58 | cause all URLs to be rewritten to `index.html`. 59 | 60 | If you'd like to test your release build locally before publishing, and get that 61 | cool redirect to `index.html` feature, you can use `flutter run` itself: 62 | 63 | ```sh 64 | $ flutter run -d chrome --release lib/url_strategy.dart 65 | ``` 66 | 67 | Note that you have to run this command from a place where `flutter run` can find 68 | the `web/index.html` file. 69 | 70 | Of course, any local web server that can be configured to redirect all traffic 71 | to `index.html` will do, e.g. 72 | [live-server](https://www.npmjs.com/package/live-server). 73 | -------------------------------------------------------------------------------- /docs/web-history.mdx: -------------------------------------------------------------------------------- 1 | # Web History 2 | 3 | Sometimes you don't want the browser tracking history when you navigate. In that 4 | case, go_router supports 5 | [Router.neglect](https://api.flutter.dev/flutter/widgets/Router/neglect.html). 6 | 7 | ```dart 8 | ElevatedButton( 9 | // turn off history tracking in the browser for this navigation 10 | onPressed: () => Router.neglect(context, () => context.go('/page2'))}, 11 | ... 12 | ), 13 | ``` 14 | 15 | The use of `Router.neglect` will stop the Flutter router from adding this page 16 | to the browser history. If you'd like the browser to stop tracking history 17 | altogether for your app, set the `routerNeglect` parameter of the `GoRouter` 18 | constructor. This will suppress history for all navigation made using go_router. 19 | 20 | ```dart 21 | final _router = GoRouter( 22 | // turn off history tracking in the browser for all navigation 23 | routerNeglect: true, 24 | ... 25 | ); 26 | ``` 27 | 28 | Even when you ask the router to neglect to add navigation to the brower's 29 | history, deep and dynamic linking will continue to work and the brower's address 30 | bar will update as you navigate through your app. It's only the browser's Back 31 | button that will be affected. -------------------------------------------------------------------------------- /go_router/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: b22742018b3edf16c6cadd7b76d9db5e7f9064b5 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /go_router/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Chris Sells. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, 7 | this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | 3. Neither the name of the copyright holder nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /go_router/README.md: -------------------------------------------------------------------------------- 1 | # go_router is now published and maintained by the Flutter team 2 | As of the 3.0.2 release, [the go_router package](https://pub.dev/packages/go_router) is published by the Flutter team and maintained by Flutter engineering in [the flutter/packages repository](https://github.com/flutter/packages/tree/main/packages/go_router). 3 | 4 | Existing go_router issues have been moved to [the flutter issues list](https://github.com/flutter/flutter/issues) and new go_router-related issues should be filed there. 5 | 6 | The docs on [gorouter.dev](https://gorouter.dev) will also be moving to [docs.flutter.dev](https://docs.flutter.dev) over time. 7 | 8 | This repo has been archived and is read-only. 9 | 10 | [![Pub 11 | Version](https://img.shields.io/pub/v/go_router?label=go_router&labelColor=333940&logo=dart)](https://pub.dev/packages/go_router) 12 | ![Test](https://github.com/csells/go_router/workflows/validate/badge.svg) 13 | [![codecov](https://codecov.io/gh/csells/go_router/branch/master/graph/badge.svg?token=4XJU30IGO3)](https://codecov.io/gh/csells/go_router) 14 | [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) 15 | 16 | # Welcome to go_router! 17 | 18 | The purpose of [the go_router package](https://pub.dev/packages/go_router) is to 19 | use declarative routes to reduce complexity, regardless of the platform you're 20 | targeting (mobile, web, desktop), handle deep and dynamic linking from 21 | Android, iOS and the web, along with a number of other navigation-related 22 | scenarios, while still (hopefully) providing an easy-to-use developer 23 | experience. 24 | 25 | You can get started with go_router with code as simple as this: 26 | 27 | ```dart 28 | class App extends StatelessWidget { 29 | App({Key? key}) : super(key: key); 30 | 31 | @override 32 | Widget build(BuildContext context) => MaterialApp.router( 33 | routeInformationParser: _router.routeInformationParser, 34 | routerDelegate: _router.routerDelegate, 35 | title: 'GoRouter Example', 36 | ); 37 | 38 | final _router = GoRouter( 39 | routes: [ 40 | GoRoute( 41 | path: '/', 42 | builder: (context, state) => const Page1Screen(), 43 | ), 44 | GoRoute( 45 | path: '/page2', 46 | builder: (context, state) => const Page2Screen(), 47 | ), 48 | ], 49 | ); 50 | } 51 | 52 | class Page1Screen extends StatelessWidget {...} 53 | 54 | class Page2Screen extends StatelessWidget {...} 55 | ``` 56 | 57 | But go_router can do oh so much more! 58 | 59 | # See [gorouter.dev](https://gorouter.dev) for go_router docs & samples 60 | -------------------------------------------------------------------------------- /go_router/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:all_lint_rules_community/all.yaml 2 | 3 | analyzer: 4 | exclude: 5 | - "**/*.g.dart" 6 | - "**/*.freezed.dart" 7 | - "test/.test_coverage.dart" 8 | - "bin/cache/**" 9 | - "lib/generated_plugin_registrant.dart" 10 | strong-mode: 11 | implicit-casts: false 12 | implicit-dynamic: false 13 | errors: 14 | # without ignore here, we cause import of all_lint_rules to warn, because 15 | # some rules conflict; instead, we're explicitly enabling even conflicting 16 | # rules and are fixing the conflicts in this file 17 | included_file_warning: ignore 18 | missing_required_param: error 19 | missing_return: error 20 | parameter_assignments: error 21 | 22 | linter: 23 | rules: 24 | prefer_double_quotes: false # Dart prefers single quotes (for some reason) 25 | unnecessary_final: false # love final! 26 | always_specify_types: false # no; prefer use of final instead 27 | prefer_final_parameters: false # I like the sentiment, but too much typing! 28 | prefer_asserts_with_message: false # too lazy for this... 29 | require_trailing_commas: false # not good for things all on one line 30 | # public_member_api_docs: false # except for public libs 31 | avoid_classes_with_only_static_members: false # need this; no namespaces 32 | always_put_control_body_on_new_line: false # single line is nice when we can 33 | always_use_package_imports: false # prefer relative imports for local files 34 | avoid_annotating_with_dynamic: false # be explicit about dynamic 35 | avoid_redundant_argument_values: false # sometimes it's nice to be explicit 36 | one_member_abstracts: false # interfaces can have a single method 37 | flutter_style_todos: false # I'm too lazy for this... 38 | # diagnostic_describe_all_properties: false # too annoying for StatefulWidget 39 | # library_private_types_in_public_api: false # too annoying for StatefulWidget 40 | -------------------------------------------------------------------------------- /go_router/example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /go_router/example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 4cc385b4b84ac2f816d939a49ea1f328c4e0b48e 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /go_router/example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:all_lint_rules_community/all.yaml 2 | 3 | analyzer: 4 | exclude: 5 | - "**/*.g.dart" 6 | - "**/*.freezed.dart" 7 | - "test/.test_coverage.dart" 8 | - "bin/cache/**" 9 | - "lib/generated_plugin_registrant.dart" 10 | strong-mode: 11 | implicit-casts: false 12 | implicit-dynamic: false 13 | errors: 14 | # without ignore here, we cause import of all_lint_rules to warn, because 15 | # some rules conflict; instead, we're explicitly enabling even conflicting 16 | # rules and are fixing the conflicts in this file 17 | included_file_warning: ignore 18 | missing_required_param: error 19 | missing_return: error 20 | parameter_assignments: error 21 | 22 | linter: 23 | rules: 24 | prefer_double_quotes: false # Dart prefers single quotes (for some reason) 25 | unnecessary_final: false # love final! 26 | always_specify_types: false # no; prefer use of final instead 27 | prefer_final_parameters: false # I like the sentiment, but too much typing! 28 | prefer_asserts_with_message: false # too lazy for this... 29 | require_trailing_commas: false # not good for things all on one line 30 | public_member_api_docs: false # except for public libs 31 | avoid_classes_with_only_static_members: false # need this; no namespaces 32 | always_put_control_body_on_new_line: false # single line is nice when we can 33 | always_use_package_imports: false # prefer relative imports for local files 34 | avoid_annotating_with_dynamic: false # be explicit about dynamic 35 | avoid_redundant_argument_values: false # sometimes it's nice to be explicit 36 | one_member_abstracts: false # interfaces can have a single method 37 | flutter_style_todos: false # I'm too lazy for this... 38 | diagnostic_describe_all_properties: false # too annoying for StatefulWidget 39 | library_private_types_in_public_api: false # too annoying for StatefulWidget 40 | -------------------------------------------------------------------------------- /go_router/example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /go_router/example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 30 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "com.example.example" 47 | minSdkVersion 16 48 | targetSdkVersion 30 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | } 52 | 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | } 69 | -------------------------------------------------------------------------------- /go_router/example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /go_router/example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /go_router/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /go_router/example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /go_router/example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /go_router/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /go_router/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /go_router/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /go_router/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /go_router/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /go_router/example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /go_router/example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /go_router/example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /go_router/example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /go_router/example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /go_router/example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /go_router/example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /go_router/example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/ephemeral/ 22 | Flutter/app.flx 23 | Flutter/app.zip 24 | Flutter/flutter_assets/ 25 | Flutter/flutter_export_environment.sh 26 | ServiceDefinitions.json 27 | Runner/GeneratedPluginRegistrant.* 28 | 29 | # Exceptions to above rules. 30 | !default.mode1v3 31 | !default.mode2v3 32 | !default.pbxuser 33 | !default.perspectivev3 34 | -------------------------------------------------------------------------------- /go_router/example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /go_router/example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /go_router/example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /go_router/example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /go_router/example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - url_launcher (0.0.1): 4 | - Flutter 5 | 6 | DEPENDENCIES: 7 | - Flutter (from `Flutter`) 8 | - url_launcher (from `.symlinks/plugins/url_launcher/ios`) 9 | 10 | EXTERNAL SOURCES: 11 | Flutter: 12 | :path: Flutter 13 | url_launcher: 14 | :path: ".symlinks/plugins/url_launcher/ios" 15 | 16 | SPEC CHECKSUMS: 17 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 18 | url_launcher: 6fef411d543ceb26efce54b05a0a40bfd74cbbef 19 | 20 | PODFILE CHECKSUM: aafe91acc616949ddb318b77800a7f51bffa2a4c 21 | 22 | COCOAPODS: 1.10.1 23 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /go_router/example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/auth.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:flutter/widgets.dart'; 6 | 7 | /// A mock authentication service 8 | class BookstoreAuth extends ChangeNotifier { 9 | bool _signedIn = false; 10 | 11 | bool get signedIn => _signedIn; 12 | 13 | Future signOut() async { 14 | await Future.delayed(const Duration(milliseconds: 200)); 15 | // Sign out. 16 | _signedIn = false; 17 | notifyListeners(); 18 | } 19 | 20 | Future signIn(String username, String password) async { 21 | await Future.delayed(const Duration(milliseconds: 200)); 22 | 23 | // Sign in. Allow any password. 24 | _signedIn = true; 25 | notifyListeners(); 26 | return _signedIn; 27 | } 28 | } 29 | 30 | class BookstoreAuthScope extends InheritedNotifier { 31 | const BookstoreAuthScope({ 32 | required BookstoreAuth notifier, 33 | required Widget child, 34 | Key? key, 35 | }) : super(key: key, notifier: notifier, child: child); 36 | 37 | static BookstoreAuth of(BuildContext context) => context 38 | .dependOnInheritedWidgetOfExactType()! 39 | .notifier!; 40 | } 41 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/data.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | export 'data/author.dart'; 6 | export 'data/book.dart'; 7 | export 'data/library.dart'; 8 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/data/author.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'book.dart'; 6 | 7 | class Author { 8 | Author({ 9 | required this.id, 10 | required this.name, 11 | }); 12 | 13 | final int id; 14 | final String name; 15 | final List books = []; 16 | } 17 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/data/book.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'author.dart'; 6 | 7 | class Book { 8 | Book({ 9 | required this.id, 10 | required this.title, 11 | required this.isPopular, 12 | required this.isNew, 13 | required this.author, 14 | }); 15 | 16 | final int id; 17 | final String title; 18 | final Author author; 19 | final bool isPopular; 20 | final bool isNew; 21 | } 22 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/data/library.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'author.dart'; 6 | import 'book.dart'; 7 | 8 | final libraryInstance = Library() 9 | ..addBook( 10 | title: 'Left Hand of Darkness', 11 | authorName: 'Ursula K. Le Guin', 12 | isPopular: true, 13 | isNew: true) 14 | ..addBook( 15 | title: 'Too Like the Lightning', 16 | authorName: 'Ada Palmer', 17 | isPopular: false, 18 | isNew: true) 19 | ..addBook( 20 | title: 'Kindred', 21 | authorName: 'Octavia E. Butler', 22 | isPopular: true, 23 | isNew: false) 24 | ..addBook( 25 | title: 'The Lathe of Heaven', 26 | authorName: 'Ursula K. Le Guin', 27 | isPopular: false, 28 | isNew: false); 29 | 30 | class Library { 31 | final List allBooks = []; 32 | final List allAuthors = []; 33 | 34 | void addBook({ 35 | required String title, 36 | required String authorName, 37 | required bool isPopular, 38 | required bool isNew, 39 | }) { 40 | final author = allAuthors.firstWhere( 41 | (author) => author.name == authorName, 42 | orElse: () { 43 | final value = Author(id: allAuthors.length, name: authorName); 44 | allAuthors.add(value); 45 | return value; 46 | }, 47 | ); 48 | 49 | final book = Book( 50 | id: allBooks.length, 51 | title: title, 52 | isPopular: isPopular, 53 | isNew: isNew, 54 | author: author, 55 | ); 56 | 57 | author.books.add(book); 58 | allBooks.add(book); 59 | } 60 | 61 | List get popularBooks => [ 62 | ...allBooks.where((book) => book.isPopular), 63 | ]; 64 | 65 | List get newBooks => [ 66 | ...allBooks.where((book) => book.isNew), 67 | ]; 68 | } 69 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/screens/author_details.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:flutter/material.dart'; 6 | import 'package:go_router/go_router.dart'; 7 | 8 | import '../data.dart'; 9 | import '../widgets/book_list.dart'; 10 | 11 | class AuthorDetailsScreen extends StatelessWidget { 12 | const AuthorDetailsScreen({ 13 | required this.author, 14 | Key? key, 15 | }) : super(key: key); 16 | 17 | final Author? author; 18 | 19 | @override 20 | Widget build(BuildContext context) { 21 | if (author == null) { 22 | return const Scaffold( 23 | body: Center( 24 | child: Text('No author found.'), 25 | ), 26 | ); 27 | } 28 | return Scaffold( 29 | appBar: AppBar( 30 | title: Text(author!.name), 31 | ), 32 | body: Center( 33 | child: Column( 34 | children: [ 35 | Expanded( 36 | child: BookList( 37 | books: author!.books, 38 | onTap: (book) => context.go('/book/${book.id}'), 39 | ), 40 | ), 41 | ], 42 | ), 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/screens/authors.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:flutter/material.dart'; 6 | import 'package:go_router/go_router.dart'; 7 | 8 | import '../data/library.dart'; 9 | import '../widgets/author_list.dart'; 10 | 11 | class AuthorsScreen extends StatelessWidget { 12 | const AuthorsScreen({Key? key}) : super(key: key); 13 | 14 | static const title = 'Authors'; 15 | 16 | @override 17 | Widget build(BuildContext context) => Scaffold( 18 | appBar: AppBar( 19 | title: const Text(title), 20 | ), 21 | body: AuthorList( 22 | authors: libraryInstance.allAuthors, 23 | onTap: (author) { 24 | context.go('/author/${author.id}'); 25 | }, 26 | ), 27 | ); 28 | } 29 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/screens/book_details.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:flutter/material.dart'; 6 | import 'package:go_router/go_router.dart'; 7 | import 'package:url_launcher/link.dart'; 8 | 9 | import '../data.dart'; 10 | import 'author_details.dart'; 11 | 12 | class BookDetailsScreen extends StatelessWidget { 13 | const BookDetailsScreen({ 14 | Key? key, 15 | this.book, 16 | }) : super(key: key); 17 | 18 | final Book? book; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | if (book == null) { 23 | return const Scaffold( 24 | body: Center( 25 | child: Text('No book found.'), 26 | ), 27 | ); 28 | } 29 | return Scaffold( 30 | appBar: AppBar( 31 | title: Text(book!.title), 32 | ), 33 | body: Center( 34 | child: Column( 35 | children: [ 36 | Text( 37 | book!.title, 38 | style: Theme.of(context).textTheme.headline4, 39 | ), 40 | Text( 41 | book!.author.name, 42 | style: Theme.of(context).textTheme.subtitle1, 43 | ), 44 | TextButton( 45 | onPressed: () { 46 | Navigator.of(context).push( 47 | MaterialPageRoute( 48 | builder: (context) => 49 | AuthorDetailsScreen(author: book!.author), 50 | ), 51 | ); 52 | }, 53 | child: const Text('View author (navigator.push)'), 54 | ), 55 | Link( 56 | uri: Uri.parse('/author/${book!.author.id}'), 57 | builder: (context, followLink) => TextButton( 58 | onPressed: followLink, 59 | child: const Text('View author (Link)'), 60 | ), 61 | ), 62 | TextButton( 63 | onPressed: () { 64 | context.push('/author/${book!.author.id}'); 65 | }, 66 | child: const Text('View author (GoRouter.push)'), 67 | ), 68 | ], 69 | ), 70 | ), 71 | ); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/screens/books.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:flutter/material.dart'; 6 | import 'package:go_router/go_router.dart'; 7 | 8 | import '../data.dart'; 9 | import '../widgets/book_list.dart'; 10 | 11 | class BooksScreen extends StatefulWidget { 12 | const BooksScreen(this.kind, {Key? key}) : super(key: key); 13 | 14 | final String kind; 15 | 16 | @override 17 | _BooksScreenState createState() => _BooksScreenState(); 18 | } 19 | 20 | class _BooksScreenState extends State 21 | with SingleTickerProviderStateMixin { 22 | late TabController _tabController; 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | _tabController = TabController(length: 3, vsync: this); 28 | } 29 | 30 | @override 31 | void didUpdateWidget(BooksScreen oldWidget) { 32 | super.didUpdateWidget(oldWidget); 33 | 34 | switch (widget.kind) { 35 | case 'popular': 36 | _tabController.index = 0; 37 | break; 38 | 39 | case 'new': 40 | _tabController.index = 1; 41 | break; 42 | 43 | case 'all': 44 | _tabController.index = 2; 45 | break; 46 | } 47 | } 48 | 49 | @override 50 | void dispose() { 51 | _tabController.dispose(); 52 | super.dispose(); 53 | } 54 | 55 | @override 56 | Widget build(BuildContext context) => Scaffold( 57 | appBar: AppBar( 58 | title: const Text('Books'), 59 | bottom: TabBar( 60 | controller: _tabController, 61 | onTap: _handleTabTapped, 62 | tabs: const [ 63 | Tab( 64 | text: 'Popular', 65 | icon: Icon(Icons.people), 66 | ), 67 | Tab( 68 | text: 'New', 69 | icon: Icon(Icons.new_releases), 70 | ), 71 | Tab( 72 | text: 'All', 73 | icon: Icon(Icons.list), 74 | ), 75 | ], 76 | ), 77 | ), 78 | body: TabBarView( 79 | controller: _tabController, 80 | children: [ 81 | BookList( 82 | books: libraryInstance.popularBooks, 83 | onTap: _handleBookTapped, 84 | ), 85 | BookList( 86 | books: libraryInstance.newBooks, 87 | onTap: _handleBookTapped, 88 | ), 89 | BookList( 90 | books: libraryInstance.allBooks, 91 | onTap: _handleBookTapped, 92 | ), 93 | ], 94 | ), 95 | ); 96 | 97 | void _handleBookTapped(Book book) { 98 | context.go('/book/${book.id}'); 99 | } 100 | 101 | void _handleTabTapped(int index) { 102 | switch (index) { 103 | case 1: 104 | context.go('/books/new'); 105 | break; 106 | case 2: 107 | context.go('/books/all'); 108 | break; 109 | case 0: 110 | default: 111 | context.go('/books/popular'); 112 | break; 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/screens/scaffold.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:adaptive_navigation/adaptive_navigation.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import 'package:go_router/go_router.dart'; 9 | 10 | enum ScaffoldTab { books, authors, settings } 11 | 12 | class BookstoreScaffold extends StatelessWidget { 13 | const BookstoreScaffold({ 14 | required this.selectedTab, 15 | required this.child, 16 | Key? key, 17 | }) : super(key: key); 18 | 19 | final ScaffoldTab selectedTab; 20 | final Widget child; 21 | 22 | @override 23 | Widget build(BuildContext context) => Scaffold( 24 | body: AdaptiveNavigationScaffold( 25 | selectedIndex: selectedTab.index, 26 | body: child, 27 | onDestinationSelected: (idx) { 28 | switch (ScaffoldTab.values[idx]) { 29 | case ScaffoldTab.books: 30 | context.go('/books'); 31 | break; 32 | case ScaffoldTab.authors: 33 | context.go('/authors'); 34 | break; 35 | case ScaffoldTab.settings: 36 | context.go('/settings'); 37 | break; 38 | } 39 | }, 40 | destinations: const [ 41 | AdaptiveScaffoldDestination( 42 | title: 'Books', 43 | icon: Icons.book, 44 | ), 45 | AdaptiveScaffoldDestination( 46 | title: 'Authors', 47 | icon: Icons.person, 48 | ), 49 | AdaptiveScaffoldDestination( 50 | title: 'Settings', 51 | icon: Icons.settings, 52 | ), 53 | ], 54 | ), 55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/screens/settings.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:flutter/material.dart'; 6 | import 'package:go_router/go_router.dart'; 7 | import 'package:url_launcher/link.dart'; 8 | 9 | import '../auth.dart'; 10 | 11 | class SettingsScreen extends StatefulWidget { 12 | const SettingsScreen({Key? key}) : super(key: key); 13 | 14 | @override 15 | _SettingsScreenState createState() => _SettingsScreenState(); 16 | } 17 | 18 | class _SettingsScreenState extends State { 19 | @override 20 | Widget build(BuildContext context) => Scaffold( 21 | body: SafeArea( 22 | child: SingleChildScrollView( 23 | child: Align( 24 | alignment: Alignment.topCenter, 25 | child: ConstrainedBox( 26 | constraints: const BoxConstraints(maxWidth: 400), 27 | child: const Card( 28 | child: Padding( 29 | padding: EdgeInsets.symmetric(vertical: 18, horizontal: 12), 30 | child: SettingsContent(), 31 | ), 32 | ), 33 | ), 34 | ), 35 | ), 36 | ), 37 | ); 38 | } 39 | 40 | class SettingsContent extends StatelessWidget { 41 | const SettingsContent({ 42 | Key? key, 43 | }) : super(key: key); 44 | 45 | @override 46 | Widget build(BuildContext context) => Column( 47 | children: [ 48 | ...[ 49 | Text( 50 | 'Settings', 51 | style: Theme.of(context).textTheme.headline4, 52 | ), 53 | ElevatedButton( 54 | onPressed: () { 55 | BookstoreAuthScope.of(context).signOut(); 56 | }, 57 | child: const Text('Sign out'), 58 | ), 59 | Link( 60 | uri: Uri.parse('/book/0'), 61 | builder: (context, followLink) => TextButton( 62 | onPressed: followLink, 63 | child: const Text('Go directly to /book/0 (Link)'), 64 | ), 65 | ), 66 | TextButton( 67 | onPressed: () { 68 | context.go('/book/0'); 69 | }, 70 | child: const Text('Go directly to /book/0 (GoRouter)'), 71 | ), 72 | ].map((w) => Padding(padding: const EdgeInsets.all(8), child: w)), 73 | TextButton( 74 | onPressed: () => showDialog( 75 | context: context, 76 | builder: (context) => AlertDialog( 77 | title: const Text('Alert!'), 78 | content: const Text('The alert description goes here.'), 79 | actions: [ 80 | TextButton( 81 | onPressed: () => Navigator.pop(context, 'Cancel'), 82 | child: const Text('Cancel'), 83 | ), 84 | TextButton( 85 | onPressed: () => Navigator.pop(context, 'OK'), 86 | child: const Text('OK'), 87 | ), 88 | ], 89 | ), 90 | ), 91 | child: const Text('Show Dialog'), 92 | ) 93 | ], 94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/screens/sign_in.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:flutter/material.dart'; 6 | 7 | class Credentials { 8 | Credentials(this.username, this.password); 9 | 10 | final String username; 11 | final String password; 12 | } 13 | 14 | class SignInScreen extends StatefulWidget { 15 | const SignInScreen({ 16 | required this.onSignIn, 17 | Key? key, 18 | }) : super(key: key); 19 | 20 | final ValueChanged onSignIn; 21 | 22 | @override 23 | _SignInScreenState createState() => _SignInScreenState(); 24 | } 25 | 26 | class _SignInScreenState extends State { 27 | final _usernameController = TextEditingController(); 28 | final _passwordController = TextEditingController(); 29 | 30 | @override 31 | Widget build(BuildContext context) => Scaffold( 32 | body: Center( 33 | child: Card( 34 | child: Container( 35 | constraints: BoxConstraints.loose(const Size(600, 600)), 36 | padding: const EdgeInsets.all(8), 37 | child: Column( 38 | mainAxisAlignment: MainAxisAlignment.center, 39 | mainAxisSize: MainAxisSize.min, 40 | children: [ 41 | Text('Sign in', style: Theme.of(context).textTheme.headline4), 42 | TextField( 43 | decoration: const InputDecoration(labelText: 'Username'), 44 | controller: _usernameController, 45 | ), 46 | TextField( 47 | decoration: const InputDecoration(labelText: 'Password'), 48 | obscureText: true, 49 | controller: _passwordController, 50 | ), 51 | Padding( 52 | padding: const EdgeInsets.all(16), 53 | child: TextButton( 54 | onPressed: () async { 55 | widget.onSignIn(Credentials( 56 | _usernameController.value.text, 57 | _passwordController.value.text)); 58 | }, 59 | child: const Text('Sign in'), 60 | ), 61 | ), 62 | ], 63 | ), 64 | ), 65 | ), 66 | ), 67 | ); 68 | } 69 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/widgets/author_list.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:flutter/material.dart'; 6 | 7 | import '../data.dart'; 8 | 9 | class AuthorList extends StatelessWidget { 10 | const AuthorList({ 11 | required this.authors, 12 | this.onTap, 13 | Key? key, 14 | }) : super(key: key); 15 | 16 | final List authors; 17 | final ValueChanged? onTap; 18 | 19 | @override 20 | Widget build(BuildContext context) => ListView.builder( 21 | itemCount: authors.length, 22 | itemBuilder: (context, index) => ListTile( 23 | title: Text( 24 | authors[index].name, 25 | ), 26 | subtitle: Text( 27 | '${authors[index].books.length} books', 28 | ), 29 | onTap: onTap != null ? () => onTap!(authors[index]) : null, 30 | ), 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /go_router/example/lib/books/src/widgets/book_list.dart: -------------------------------------------------------------------------------- 1 | // Copyright 2021, the Flutter project authors. Please see the AUTHORS file 2 | // for details. All rights reserved. Use of this source code is governed by a 3 | // BSD-style license that can be found in the LICENSE file. 4 | 5 | import 'package:flutter/material.dart'; 6 | 7 | import '../data.dart'; 8 | 9 | class BookList extends StatelessWidget { 10 | const BookList({ 11 | required this.books, 12 | this.onTap, 13 | Key? key, 14 | }) : super(key: key); 15 | 16 | final List books; 17 | final ValueChanged? onTap; 18 | 19 | @override 20 | Widget build(BuildContext context) => ListView.builder( 21 | itemCount: books.length, 22 | itemBuilder: (context, index) => ListTile( 23 | title: Text( 24 | books[index].title, 25 | ), 26 | subtitle: Text( 27 | books[index].author.name, 28 | ), 29 | onTap: onTap != null ? () => onTap!(books[index]) : null, 30 | ), 31 | ); 32 | } 33 | -------------------------------------------------------------------------------- /go_router/example/lib/cupertino.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | 4 | void main() => runApp(App()); 5 | 6 | class App extends StatelessWidget { 7 | App({Key? key}) : super(key: key); 8 | 9 | static const title = 'GoRouter Example: Cupertino App'; 10 | 11 | @override 12 | Widget build(BuildContext context) => CupertinoApp.router( 13 | routeInformationParser: _router.routeInformationParser, 14 | routerDelegate: _router.routerDelegate, 15 | title: title, 16 | ); 17 | 18 | final _router = GoRouter( 19 | routes: [ 20 | GoRoute( 21 | path: '/', 22 | builder: (context, state) => const Page1Screen(), 23 | ), 24 | GoRoute( 25 | path: '/page2', 26 | builder: (context, state) => const Page2Screen(), 27 | ), 28 | ], 29 | ); 30 | } 31 | 32 | class Page1Screen extends StatelessWidget { 33 | const Page1Screen({Key? key}) : super(key: key); 34 | 35 | @override 36 | Widget build(BuildContext context) => CupertinoPageScaffold( 37 | navigationBar: const CupertinoNavigationBar(middle: Text(App.title)), 38 | child: Center( 39 | child: Column( 40 | mainAxisAlignment: MainAxisAlignment.center, 41 | children: [ 42 | CupertinoButton( 43 | onPressed: () => context.go('/page2'), 44 | child: const Text('Go to page 2'), 45 | ), 46 | ], 47 | ), 48 | ), 49 | ); 50 | } 51 | 52 | class Page2Screen extends StatelessWidget { 53 | const Page2Screen({Key? key}) : super(key: key); 54 | 55 | @override 56 | Widget build(BuildContext context) => CupertinoPageScaffold( 57 | navigationBar: const CupertinoNavigationBar(middle: Text(App.title)), 58 | child: Center( 59 | child: Column( 60 | mainAxisAlignment: MainAxisAlignment.center, 61 | children: [ 62 | CupertinoButton( 63 | onPressed: () => context.go('/'), 64 | child: const Text('Go to home page'), 65 | ), 66 | ], 67 | ), 68 | ), 69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /go_router/example/lib/error_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | 4 | void main() => runApp(App()); 5 | 6 | class App extends StatelessWidget { 7 | App({Key? key}) : super(key: key); 8 | 9 | static const title = 'GoRouter Example: Custom Error Screen'; 10 | 11 | @override 12 | Widget build(BuildContext context) => MaterialApp.router( 13 | routeInformationParser: _router.routeInformationParser, 14 | routerDelegate: _router.routerDelegate, 15 | title: title, 16 | ); 17 | 18 | final _router = GoRouter( 19 | routes: [ 20 | GoRoute( 21 | path: '/', 22 | builder: (context, state) => const Page1Screen(), 23 | ), 24 | GoRoute( 25 | path: '/page2', 26 | builder: (context, state) => const Page2Screen(), 27 | ), 28 | ], 29 | errorBuilder: (context, state) => ErrorScreen(state.error!), 30 | ); 31 | } 32 | 33 | class Page1Screen extends StatelessWidget { 34 | const Page1Screen({Key? key}) : super(key: key); 35 | 36 | @override 37 | Widget build(BuildContext context) => Scaffold( 38 | appBar: AppBar(title: const Text(App.title)), 39 | body: Center( 40 | child: Column( 41 | mainAxisAlignment: MainAxisAlignment.center, 42 | children: [ 43 | ElevatedButton( 44 | onPressed: () => context.go('/page2'), 45 | child: const Text('Go to page 2'), 46 | ), 47 | ], 48 | ), 49 | ), 50 | ); 51 | } 52 | 53 | class Page2Screen extends StatelessWidget { 54 | const Page2Screen({Key? key}) : super(key: key); 55 | 56 | @override 57 | Widget build(BuildContext context) => Scaffold( 58 | appBar: AppBar(title: const Text(App.title)), 59 | body: Center( 60 | child: Column( 61 | mainAxisAlignment: MainAxisAlignment.center, 62 | children: [ 63 | ElevatedButton( 64 | onPressed: () => context.go('/'), 65 | child: const Text('Go to home page'), 66 | ), 67 | ], 68 | ), 69 | ), 70 | ); 71 | } 72 | 73 | class ErrorScreen extends StatelessWidget { 74 | const ErrorScreen(this.error, {Key? key}) : super(key: key); 75 | final Exception error; 76 | 77 | @override 78 | Widget build(BuildContext context) => Scaffold( 79 | appBar: AppBar(title: const Text('My "Page Not Found" Screen')), 80 | body: Center( 81 | child: Column( 82 | mainAxisAlignment: MainAxisAlignment.center, 83 | children: [ 84 | SelectableText(error.toString()), 85 | TextButton( 86 | onPressed: () => context.go('/'), 87 | child: const Text('Home'), 88 | ), 89 | ], 90 | ), 91 | ), 92 | ); 93 | } 94 | -------------------------------------------------------------------------------- /go_router/example/lib/generated_plugin_registrant.dart: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // ignore_for_file: directives_ordering 6 | // ignore_for_file: lines_longer_than_80_chars 7 | 8 | import 'package:package_info_plus_web/package_info_plus_web.dart'; 9 | import 'package:shared_preferences_web/shared_preferences_web.dart'; 10 | import 'package:url_launcher_web/url_launcher_web.dart'; 11 | 12 | import 'package:flutter_web_plugins/flutter_web_plugins.dart'; 13 | 14 | // ignore: public_member_api_docs 15 | void registerPlugins(Registrar registrar) { 16 | PackageInfoPlugin.registerWith(registrar); 17 | SharedPreferencesPlugin.registerWith(registrar); 18 | UrlLauncherPlugin.registerWith(registrar); 19 | registrar.registerMessageHandler(); 20 | } 21 | -------------------------------------------------------------------------------- /go_router/example/lib/init_loc.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | 4 | void main() => runApp(App()); 5 | 6 | class App extends StatelessWidget { 7 | App({Key? key}) : super(key: key); 8 | 9 | static const title = 'GoRouter Example: Initial Location'; 10 | 11 | @override 12 | Widget build(BuildContext context) => MaterialApp.router( 13 | routeInformationParser: _router.routeInformationParser, 14 | routerDelegate: _router.routerDelegate, 15 | title: title, 16 | ); 17 | 18 | final _router = GoRouter( 19 | initialLocation: '/page3', 20 | routes: [ 21 | GoRoute( 22 | path: '/', 23 | builder: (context, state) => const Page1Screen(), 24 | ), 25 | GoRoute( 26 | path: '/page2', 27 | builder: (context, state) => const Page2Screen(), 28 | ), 29 | GoRoute( 30 | path: '/page3', 31 | builder: (context, state) => const Page3Screen(), 32 | ), 33 | ], 34 | ); 35 | } 36 | 37 | class Page1Screen extends StatelessWidget { 38 | const Page1Screen({Key? key}) : super(key: key); 39 | 40 | @override 41 | Widget build(BuildContext context) => Scaffold( 42 | appBar: AppBar(title: const Text(App.title)), 43 | body: Center( 44 | child: Column( 45 | mainAxisAlignment: MainAxisAlignment.center, 46 | children: [ 47 | ElevatedButton( 48 | onPressed: () => context.go('/page2'), 49 | child: const Text('Go to page 2'), 50 | ), 51 | ], 52 | ), 53 | ), 54 | ); 55 | } 56 | 57 | class Page2Screen extends StatelessWidget { 58 | const Page2Screen({Key? key}) : super(key: key); 59 | 60 | @override 61 | Widget build(BuildContext context) => Scaffold( 62 | appBar: AppBar(title: const Text(App.title)), 63 | body: Center( 64 | child: Column( 65 | mainAxisAlignment: MainAxisAlignment.center, 66 | children: [ 67 | ElevatedButton( 68 | onPressed: () => context.go('/'), 69 | child: const Text('Go to home page'), 70 | ), 71 | ], 72 | ), 73 | ), 74 | ); 75 | } 76 | 77 | class Page3Screen extends StatelessWidget { 78 | const Page3Screen({Key? key}) : super(key: key); 79 | 80 | @override 81 | Widget build(BuildContext context) => Scaffold( 82 | appBar: AppBar(title: const Text(App.title)), 83 | body: Center( 84 | child: Column( 85 | mainAxisAlignment: MainAxisAlignment.center, 86 | children: [ 87 | ElevatedButton( 88 | onPressed: () => context.go('/page2'), 89 | child: const Text('Go to page 2'), 90 | ), 91 | ], 92 | ), 93 | ), 94 | ); 95 | } 96 | -------------------------------------------------------------------------------- /go_router/example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | 4 | void main() => runApp(App()); 5 | 6 | class App extends StatelessWidget { 7 | App({Key? key}) : super(key: key); 8 | 9 | static const title = 'GoRouter Example: Declarative Routes'; 10 | 11 | @override 12 | Widget build(BuildContext context) => MaterialApp.router( 13 | routeInformationParser: _router.routeInformationParser, 14 | routerDelegate: _router.routerDelegate, 15 | title: title, 16 | ); 17 | 18 | final _router = GoRouter( 19 | routes: [ 20 | GoRoute( 21 | path: '/', 22 | builder: (context, state) => const Page1Screen(), 23 | ), 24 | GoRoute( 25 | path: '/page2', 26 | builder: (context, state) => const Page2Screen(), 27 | ), 28 | ], 29 | ); 30 | } 31 | 32 | class Page1Screen extends StatelessWidget { 33 | const Page1Screen({Key? key}) : super(key: key); 34 | 35 | @override 36 | Widget build(BuildContext context) => Scaffold( 37 | appBar: AppBar(title: const Text(App.title)), 38 | body: Center( 39 | child: Column( 40 | mainAxisAlignment: MainAxisAlignment.center, 41 | children: [ 42 | ElevatedButton( 43 | onPressed: () => context.go('/page2'), 44 | child: const Text('Go to page 2'), 45 | ), 46 | ], 47 | ), 48 | ), 49 | ); 50 | } 51 | 52 | class Page2Screen extends StatelessWidget { 53 | const Page2Screen({Key? key}) : super(key: key); 54 | 55 | @override 56 | Widget build(BuildContext context) => Scaffold( 57 | appBar: AppBar(title: const Text(App.title)), 58 | body: Center( 59 | child: Column( 60 | mainAxisAlignment: MainAxisAlignment.center, 61 | children: [ 62 | ElevatedButton( 63 | onPressed: () => context.go('/'), 64 | child: const Text('Go to home page'), 65 | ), 66 | ], 67 | ), 68 | ), 69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /go_router/example/lib/push.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | 4 | void main() => runApp(App()); 5 | 6 | class App extends StatelessWidget { 7 | App({Key? key}) : super(key: key); 8 | 9 | static const title = 'GoRouter Example: Push'; 10 | 11 | @override 12 | Widget build(BuildContext context) => MaterialApp.router( 13 | routeInformationParser: _router.routeInformationParser, 14 | routerDelegate: _router.routerDelegate, 15 | title: title, 16 | ); 17 | 18 | late final _router = GoRouter( 19 | routes: [ 20 | GoRoute( 21 | path: '/', 22 | builder: (context, state) => const Page1ScreenWithPush(), 23 | ), 24 | GoRoute( 25 | path: '/page2', 26 | builder: (context, state) => Page2ScreenWithPush( 27 | int.parse(state.queryParams['push-count']!), 28 | ), 29 | ), 30 | ], 31 | ); 32 | } 33 | 34 | class Page1ScreenWithPush extends StatelessWidget { 35 | const Page1ScreenWithPush({Key? key}) : super(key: key); 36 | 37 | @override 38 | Widget build(BuildContext context) => Scaffold( 39 | appBar: AppBar(title: const Text('${App.title}: page 1')), 40 | body: Center( 41 | child: Column( 42 | mainAxisAlignment: MainAxisAlignment.center, 43 | children: [ 44 | ElevatedButton( 45 | onPressed: () => context.push('/page2?push-count=1'), 46 | child: const Text('Push page 2'), 47 | ), 48 | ], 49 | ), 50 | ), 51 | ); 52 | } 53 | 54 | class Page2ScreenWithPush extends StatelessWidget { 55 | const Page2ScreenWithPush(this.pushCount, {Key? key}) : super(key: key); 56 | final int pushCount; 57 | 58 | @override 59 | Widget build(BuildContext context) => Scaffold( 60 | appBar: AppBar( 61 | title: Text('${App.title}: page 2 w/ push count $pushCount'), 62 | ), 63 | body: Center( 64 | child: Column( 65 | mainAxisAlignment: MainAxisAlignment.center, 66 | children: [ 67 | Padding( 68 | padding: const EdgeInsets.all(8), 69 | child: ElevatedButton( 70 | onPressed: () => context.go('/'), 71 | child: const Text('Go to home page'), 72 | ), 73 | ), 74 | Padding( 75 | padding: const EdgeInsets.all(8), 76 | child: ElevatedButton( 77 | onPressed: () => context.push( 78 | '/page2?push-count=${pushCount + 1}', 79 | ), 80 | child: const Text('Push page 2 (again)'), 81 | ), 82 | ), 83 | ], 84 | ), 85 | ), 86 | ); 87 | } 88 | -------------------------------------------------------------------------------- /go_router/example/lib/router_neglect.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | 4 | void main() => runApp(App()); 5 | 6 | class App extends StatelessWidget { 7 | App({Key? key}) : super(key: key); 8 | 9 | static const title = 'GoRouter Example: Router neglect'; 10 | 11 | @override 12 | Widget build(BuildContext context) => MaterialApp.router( 13 | routeInformationParser: _router.routeInformationParser, 14 | routerDelegate: _router.routerDelegate, 15 | title: title, 16 | ); 17 | 18 | final _router = GoRouter( 19 | // turn off history tracking in the browser for this navigation 20 | routerNeglect: true, 21 | routes: [ 22 | GoRoute( 23 | path: '/', 24 | builder: (context, state) => const Page1Screen(), 25 | ), 26 | GoRoute( 27 | path: '/page2', 28 | builder: (context, state) => const Page2Screen(), 29 | ), 30 | ], 31 | ); 32 | } 33 | 34 | class Page1Screen extends StatelessWidget { 35 | const Page1Screen({Key? key}) : super(key: key); 36 | 37 | @override 38 | Widget build(BuildContext context) => Scaffold( 39 | appBar: AppBar(title: const Text(App.title)), 40 | body: Center( 41 | child: Column( 42 | mainAxisAlignment: MainAxisAlignment.center, 43 | children: [ 44 | ElevatedButton( 45 | onPressed: () => context.go('/page2'), 46 | child: const Text('Go to page 2'), 47 | ), 48 | const SizedBox(height: 8), 49 | ElevatedButton( 50 | // turn off history tracking in the browser for this navigation; 51 | // note that this isn't necessary when you've set routerNeglect 52 | // but it does illustrate the technique 53 | onPressed: () => Router.neglect( 54 | context, 55 | () => context.push('/page2'), 56 | ), 57 | child: const Text('Push page 2'), 58 | ), 59 | ], 60 | ), 61 | ), 62 | ); 63 | } 64 | 65 | class Page2Screen extends StatelessWidget { 66 | const Page2Screen({Key? key}) : super(key: key); 67 | 68 | @override 69 | Widget build(BuildContext context) => Scaffold( 70 | appBar: AppBar(title: const Text(App.title)), 71 | body: Center( 72 | child: Column( 73 | mainAxisAlignment: MainAxisAlignment.center, 74 | children: [ 75 | ElevatedButton( 76 | onPressed: () => context.go('/'), 77 | child: const Text('Go to home page'), 78 | ), 79 | ], 80 | ), 81 | ), 82 | ); 83 | } 84 | -------------------------------------------------------------------------------- /go_router/example/lib/state_restoration.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | 4 | void main() => runApp( 5 | const RootRestorationScope(restorationId: 'root', child: App()), 6 | ); 7 | 8 | class App extends StatefulWidget { 9 | const App({Key? key}) : super(key: key); 10 | 11 | static const title = 'GoRouter Example: State Restoration'; 12 | 13 | @override 14 | State createState() => _AppState(); 15 | } 16 | 17 | class _AppState extends State with RestorationMixin { 18 | @override 19 | String get restorationId => 'wrapper'; 20 | 21 | @override 22 | void restoreState(RestorationBucket? oldBucket, bool initialRestore) { 23 | // todo: implement restoreState for you app 24 | } 25 | 26 | @override 27 | Widget build(BuildContext context) => MaterialApp.router( 28 | routeInformationParser: _router.routeInformationParser, 29 | routerDelegate: _router.routerDelegate, 30 | title: App.title, 31 | restorationScopeId: 'app', 32 | ); 33 | 34 | final _router = GoRouter( 35 | routes: [ 36 | // restorationId set for the route automatically 37 | GoRoute( 38 | path: '/', 39 | builder: (context, state) => const Page1Screen(), 40 | ), 41 | 42 | // restorationId set for the route automatically 43 | GoRoute( 44 | path: '/page2', 45 | builder: (context, state) => const Page2Screen(), 46 | ), 47 | ], 48 | restorationScopeId: 'router', 49 | ); 50 | } 51 | 52 | class Page1Screen extends StatelessWidget { 53 | const Page1Screen({Key? key}) : super(key: key); 54 | 55 | @override 56 | Widget build(BuildContext context) => Scaffold( 57 | appBar: AppBar(title: const Text(App.title)), 58 | body: Center( 59 | child: Column( 60 | mainAxisAlignment: MainAxisAlignment.center, 61 | children: [ 62 | ElevatedButton( 63 | onPressed: () => context.go('/page2'), 64 | child: const Text('Go to page 2'), 65 | ), 66 | ], 67 | ), 68 | ), 69 | ); 70 | } 71 | 72 | class Page2Screen extends StatelessWidget { 73 | const Page2Screen({Key? key}) : super(key: key); 74 | 75 | @override 76 | Widget build(BuildContext context) => Scaffold( 77 | appBar: AppBar(title: const Text(App.title)), 78 | body: Center( 79 | child: Column( 80 | mainAxisAlignment: MainAxisAlignment.center, 81 | children: [ 82 | ElevatedButton( 83 | onPressed: () => context.go('/'), 84 | child: const Text('Go to home page'), 85 | ), 86 | ], 87 | ), 88 | ), 89 | ); 90 | } 91 | -------------------------------------------------------------------------------- /go_router/example/lib/sub_routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | 4 | import 'shared/data.dart'; 5 | 6 | void main() => runApp(App()); 7 | 8 | class App extends StatelessWidget { 9 | App({Key? key}) : super(key: key); 10 | 11 | static const title = 'GoRouter Example: Sub-routes'; 12 | 13 | @override 14 | Widget build(BuildContext context) => MaterialApp.router( 15 | routeInformationParser: _router.routeInformationParser, 16 | routerDelegate: _router.routerDelegate, 17 | title: title, 18 | ); 19 | 20 | final _router = GoRouter( 21 | routes: [ 22 | GoRoute( 23 | path: '/', 24 | builder: (context, state) => HomeScreen(families: Families.data), 25 | routes: [ 26 | GoRoute( 27 | path: 'family/:fid', 28 | builder: (context, state) => FamilyScreen( 29 | family: Families.family(state.params['fid']!), 30 | ), 31 | routes: [ 32 | GoRoute( 33 | path: 'person/:pid', 34 | builder: (context, state) { 35 | final family = Families.family(state.params['fid']!); 36 | final person = family.person(state.params['pid']!); 37 | 38 | return PersonScreen(family: family, person: person); 39 | }, 40 | ), 41 | ], 42 | ), 43 | ], 44 | ), 45 | ], 46 | ); 47 | } 48 | 49 | class HomeScreen extends StatelessWidget { 50 | const HomeScreen({required this.families, Key? key}) : super(key: key); 51 | final List families; 52 | 53 | @override 54 | Widget build(BuildContext context) => Scaffold( 55 | appBar: AppBar(title: const Text(App.title)), 56 | body: ListView( 57 | children: [ 58 | for (final f in families) 59 | ListTile( 60 | title: Text(f.name), 61 | onTap: () => context.go('/family/${f.id}'), 62 | ) 63 | ], 64 | ), 65 | ); 66 | } 67 | 68 | class FamilyScreen extends StatelessWidget { 69 | const FamilyScreen({required this.family, Key? key}) : super(key: key); 70 | final Family family; 71 | 72 | @override 73 | Widget build(BuildContext context) => Scaffold( 74 | appBar: AppBar(title: Text(family.name)), 75 | body: ListView( 76 | children: [ 77 | for (final p in family.people) 78 | ListTile( 79 | title: Text(p.name), 80 | onTap: () => context.go('/family/${family.id}/person/${p.id}'), 81 | ), 82 | ], 83 | ), 84 | ); 85 | } 86 | 87 | class PersonScreen extends StatelessWidget { 88 | const PersonScreen({required this.family, required this.person, Key? key}) 89 | : super(key: key); 90 | 91 | final Family family; 92 | final Person person; 93 | 94 | @override 95 | Widget build(BuildContext context) => Scaffold( 96 | appBar: AppBar(title: Text(person.name)), 97 | body: Text('${person.name} ${family.name} is ${person.age} years old'), 98 | ); 99 | } 100 | -------------------------------------------------------------------------------- /go_router/example/lib/url_strategy.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:go_router/go_router.dart'; 3 | 4 | void main() { 5 | // turn on the # in the URLs on the web (default) 6 | // GoRouter.setUrlPathStrategy(UrlPathStrategy.hash); 7 | 8 | // turn off the # in the URLs on the web 9 | // GoRouter.setUrlPathStrategy(UrlPathStrategy.path); 10 | 11 | runApp(App()); 12 | } 13 | 14 | class App extends StatelessWidget { 15 | App({Key? key}) : super(key: key); 16 | 17 | static const title = 'GoRouter Example: URL Path Strategy'; 18 | 19 | @override 20 | Widget build(BuildContext context) => MaterialApp.router( 21 | routeInformationParser: _router.routeInformationParser, 22 | routerDelegate: _router.routerDelegate, 23 | title: App.title, 24 | ); 25 | 26 | final _router = GoRouter( 27 | // turn off the # in the URLs on the web 28 | urlPathStrategy: UrlPathStrategy.path, 29 | 30 | routes: [ 31 | GoRoute( 32 | path: '/', 33 | builder: (context, state) => const Page1Screen(), 34 | ), 35 | GoRoute( 36 | path: '/page2', 37 | builder: (context, state) => const Page2Screen(), 38 | ), 39 | ], 40 | ); 41 | } 42 | 43 | class Page1Screen extends StatelessWidget { 44 | const Page1Screen({Key? key}) : super(key: key); 45 | 46 | @override 47 | Widget build(BuildContext context) => Scaffold( 48 | appBar: AppBar(title: const Text(App.title)), 49 | body: Center( 50 | child: Column( 51 | mainAxisAlignment: MainAxisAlignment.center, 52 | children: [ 53 | ElevatedButton( 54 | onPressed: () => context.go('/page2'), 55 | child: const Text('Go to page 2'), 56 | ), 57 | ], 58 | ), 59 | ), 60 | ); 61 | } 62 | 63 | class Page2Screen extends StatelessWidget { 64 | const Page2Screen({Key? key}) : super(key: key); 65 | 66 | @override 67 | Widget build(BuildContext context) => Scaffold( 68 | appBar: AppBar(title: const Text(App.title)), 69 | body: Center( 70 | child: Column( 71 | mainAxisAlignment: MainAxisAlignment.center, 72 | children: [ 73 | ElevatedButton( 74 | onPressed: () => context.go('/'), 75 | child: const Text('Go to home page'), 76 | ), 77 | ], 78 | ), 79 | ), 80 | ); 81 | } 82 | -------------------------------------------------------------------------------- /go_router/example/lib/widgets_app.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:go_router/go_router.dart'; 4 | 5 | void main() => runApp(App()); 6 | 7 | const blue = Color(0xFF2196F3); 8 | const white = Color(0xFFFFFFFF); 9 | 10 | class App extends StatelessWidget { 11 | App({Key? key}) : super(key: key); 12 | 13 | static const title = 'GoRouter Example: WidgetsApp'; 14 | 15 | @override 16 | Widget build(BuildContext context) => WidgetsApp.router( 17 | routeInformationParser: _router.routeInformationParser, 18 | routerDelegate: _router.routerDelegate, 19 | title: title, 20 | color: blue, 21 | textStyle: const TextStyle(color: blue), 22 | ); 23 | 24 | final _router = GoRouter( 25 | debugLogDiagnostics: true, 26 | routes: [ 27 | GoRoute( 28 | path: '/', 29 | builder: (context, state) => const Page1Screen(), 30 | ), 31 | GoRoute( 32 | path: '/page2', 33 | builder: (context, state) => const Page2Screen(), 34 | ), 35 | ], 36 | ); 37 | } 38 | 39 | class Page1Screen extends StatelessWidget { 40 | const Page1Screen({Key? key}) : super(key: key); 41 | 42 | @override 43 | Widget build(BuildContext context) => SafeArea( 44 | child: Center( 45 | child: Column( 46 | mainAxisAlignment: MainAxisAlignment.center, 47 | children: [ 48 | const Text( 49 | App.title, 50 | style: TextStyle(fontWeight: FontWeight.bold), 51 | ), 52 | const SizedBox(height: 16), 53 | Button( 54 | onPressed: () => context.go('/page2'), 55 | child: const Text( 56 | 'Go to page 2', 57 | style: TextStyle(color: white), 58 | ), 59 | ), 60 | ], 61 | ), 62 | ), 63 | ); 64 | } 65 | 66 | class Page2Screen extends StatelessWidget { 67 | const Page2Screen({Key? key}) : super(key: key); 68 | 69 | @override 70 | Widget build(BuildContext context) => SafeArea( 71 | child: Center( 72 | child: Column( 73 | mainAxisAlignment: MainAxisAlignment.center, 74 | children: [ 75 | const Text( 76 | App.title, 77 | style: TextStyle(fontWeight: FontWeight.bold), 78 | ), 79 | const SizedBox(height: 16), 80 | Button( 81 | onPressed: () => context.go('/'), 82 | child: const Text( 83 | 'Go to home page', 84 | style: TextStyle(color: white), 85 | ), 86 | ), 87 | ], 88 | ), 89 | ), 90 | ); 91 | } 92 | 93 | class Button extends StatelessWidget { 94 | const Button({ 95 | required this.onPressed, 96 | required this.child, 97 | Key? key, 98 | }) : super(key: key); 99 | 100 | final VoidCallback onPressed; 101 | final Widget child; 102 | 103 | @override 104 | Widget build(BuildContext context) => GestureDetector( 105 | onTap: onPressed, 106 | child: Container( 107 | padding: const EdgeInsets.all(8), 108 | color: blue, 109 | child: child, 110 | ), 111 | ); 112 | } 113 | -------------------------------------------------------------------------------- /go_router/example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/xcuserdata/ 7 | -------------------------------------------------------------------------------- /go_router/example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /go_router/example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /go_router/example/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import package_info_plus_macos 9 | import shared_preferences_macos 10 | import url_launcher_macos 11 | 12 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 13 | FLTPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FLTPackageInfoPlusPlugin")) 14 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) 15 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) 16 | } 17 | -------------------------------------------------------------------------------- /go_router/example/macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.12' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | end 35 | 36 | post_install do |installer| 37 | installer.pods_project.targets.each do |target| 38 | flutter_additional_macos_build_settings(target) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /go_router/example/macos/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FlutterMacOS (1.0.0) 3 | - shared_preferences_macos (0.0.1): 4 | - FlutterMacOS 5 | - url_launcher_macos (0.0.1): 6 | - FlutterMacOS 7 | 8 | DEPENDENCIES: 9 | - FlutterMacOS (from `Flutter/ephemeral`) 10 | - shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`) 11 | - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) 12 | 13 | EXTERNAL SOURCES: 14 | FlutterMacOS: 15 | :path: Flutter/ephemeral 16 | shared_preferences_macos: 17 | :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos 18 | url_launcher_macos: 19 | :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos 20 | 21 | SPEC CHECKSUMS: 22 | FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424 23 | shared_preferences_macos: 480ce071d0666e37cef23fe6c702293a3d21799e 24 | url_launcher_macos: 45af3d61de06997666568a7149c1be98b41c95d4 25 | 26 | PODFILE CHECKSUM: c7161fcf45d4fd9025dc0f48a76d6e64e52f8176 27 | 28 | COCOAPODS: 1.11.2 29 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 71 | 73 | 79 | 80 | 81 | 82 | 84 | 85 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = go_router_ex 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.builderUp 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /go_router/example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /go_router/example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: go_router_examples 2 | description: go_router examples 3 | version: 3.0.1 4 | publish_to: none 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | flutter: ">=1.17.0" 9 | 10 | dependencies: 11 | adaptive_dialog: ^1.2.0 12 | adaptive_navigation: ^0.0.4 13 | collection: ^1.15.0 14 | cupertino_icons: ^1.0.2 15 | flutter: 16 | sdk: flutter 17 | go_router: 18 | path: .. 19 | logging: ^1.0.0 20 | package_info_plus: ^1.3.0 21 | provider: ^5.0.0 22 | shared_preferences: ^2.0.11 23 | url_launcher: ^6.0.7 24 | 25 | dev_dependencies: 26 | all_lint_rules_community: ^0.0.4 27 | flutter_test: 28 | sdk: flutter 29 | 30 | flutter: 31 | uses-material-design: true 32 | -------------------------------------------------------------------------------- /go_router/example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/web/favicon.png -------------------------------------------------------------------------------- /go_router/example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /go_router/example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/csells/go_router/9b1087e4a0c22f3bff3272491b810b5e76d3b575/go_router/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /go_router/example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple", 3 | "short_name": "simple", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /go_router/lib/go_router.dart: -------------------------------------------------------------------------------- 1 | /// A declarative router for Flutter based on Navigation 2 supporting 2 | /// deep linking, data-driven routes and more 3 | library go_router; 4 | 5 | import 'package:flutter/widgets.dart'; 6 | 7 | import 'src/go_router.dart'; 8 | 9 | export 'src/custom_transition_page.dart'; 10 | export 'src/go_route.dart'; 11 | export 'src/go_router.dart'; 12 | export 'src/go_router_refresh_stream.dart'; 13 | export 'src/go_router_state.dart'; 14 | export 'src/typedefs.dart' show GoRouterPageBuilder, GoRouterRedirect; 15 | export 'src/url_path_strategy.dart'; 16 | 17 | /// Dart extension to add navigation function to a BuildContext object, e.g. 18 | /// context.go('/'); 19 | // NOTE: adding this here instead of in /src to work-around a Dart analyzer bug 20 | // and fix: https://github.com/csells/go_router/issues/116 21 | extension GoRouterHelper on BuildContext { 22 | /// Get a location from route name and parameters. 23 | String namedLocation( 24 | String name, { 25 | Map params = const {}, 26 | Map queryParams = const {}, 27 | }) => 28 | GoRouter.of(this) 29 | .namedLocation(name, params: params, queryParams: queryParams); 30 | 31 | /// Navigate to a location. 32 | void go(String location, {Object? extra}) => 33 | GoRouter.of(this).go(location, extra: extra); 34 | 35 | /// Navigate to a named route. 36 | void goNamed( 37 | String name, { 38 | Map params = const {}, 39 | Map queryParams = const {}, 40 | Object? extra, 41 | }) => 42 | GoRouter.of(this).goNamed( 43 | name, 44 | params: params, 45 | queryParams: queryParams, 46 | extra: extra, 47 | ); 48 | 49 | /// Push a location onto the page stack. 50 | void push(String location, {Object? extra}) => 51 | GoRouter.of(this).push(location, extra: extra); 52 | 53 | /// Navigate to a named route onto the page stack. 54 | void pushNamed( 55 | String name, { 56 | Map params = const {}, 57 | Map queryParams = const {}, 58 | Object? extra, 59 | }) => 60 | GoRouter.of(this).pushNamed( 61 | name, 62 | params: params, 63 | queryParams: queryParams, 64 | extra: extra, 65 | ); 66 | 67 | /// Pop the top page off the Navigator's page stack by calling 68 | /// [Navigator.pop]. 69 | void pop() => GoRouter.of(this).pop(); 70 | } 71 | -------------------------------------------------------------------------------- /go_router/lib/src/go_route_information_parser.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | /// GoRouter implementation of the RouteInformationParser base class 5 | class GoRouteInformationParser extends RouteInformationParser { 6 | /// for use by the Router architecture as part of the RouteInformationParser 7 | @override 8 | Future parseRouteInformation( 9 | RouteInformation routeInformation, 10 | ) => 11 | // Use [SynchronousFuture] so that the initial url is processed 12 | // synchronously and remove unwanted initial animations on deep-linking 13 | SynchronousFuture(Uri.parse(routeInformation.location!)); 14 | 15 | /// for use by the Router architecture as part of the RouteInformationParser 16 | @override 17 | RouteInformation restoreRouteInformation(Uri configuration) => 18 | RouteInformation(location: configuration.toString()); 19 | } 20 | -------------------------------------------------------------------------------- /go_router/lib/src/go_router_cupertino.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: diagnostic_describe_all_properties 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import '../go_router.dart'; 5 | 6 | /// Checks for CupertinoApp in the widget tree. 7 | bool isCupertinoApp(Element elem) => 8 | elem.findAncestorWidgetOfExactType() != null; 9 | 10 | /// Builds a Cupertino page. 11 | CupertinoPage pageBuilderForCupertinoApp({ 12 | required LocalKey key, 13 | required String? name, 14 | required Object? arguments, 15 | required String restorationId, 16 | required Widget child, 17 | }) => 18 | CupertinoPage( 19 | name: name, 20 | arguments: arguments, 21 | key: key, 22 | restorationId: restorationId, 23 | child: child, 24 | ); 25 | 26 | /// Default error page implementation for Cupertino. 27 | class GoRouterCupertinoErrorScreen extends StatelessWidget { 28 | /// Provide an exception to this page for it to be displayed. 29 | const GoRouterCupertinoErrorScreen(this.error, {Key? key}) : super(key: key); 30 | 31 | /// The exception to be displayed. 32 | final Exception? error; 33 | 34 | @override 35 | Widget build(BuildContext context) => CupertinoPageScaffold( 36 | navigationBar: 37 | const CupertinoNavigationBar(middle: Text('Page Not Found')), 38 | child: Center( 39 | child: Column( 40 | mainAxisAlignment: MainAxisAlignment.center, 41 | children: [ 42 | Text(error?.toString() ?? 'page not found'), 43 | CupertinoButton( 44 | onPressed: () => context.go('/'), 45 | child: const Text('Home'), 46 | ), 47 | ], 48 | ), 49 | ), 50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /go_router/lib/src/go_router_error_page.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: diagnostic_describe_all_properties 2 | 3 | import 'package:flutter/widgets.dart'; 4 | import '../go_router.dart'; 5 | 6 | /// Default error page implementation for WidgetsApp. 7 | class GoRouterErrorScreen extends StatelessWidget { 8 | /// Provide an exception to this page for it to be displayed. 9 | const GoRouterErrorScreen(this.error, {Key? key}) : super(key: key); 10 | 11 | /// The exception to be displayed. 12 | final Exception? error; 13 | 14 | static const _white = Color(0xFFFFFFFF); 15 | 16 | @override 17 | Widget build(BuildContext context) => SafeArea( 18 | child: Center( 19 | child: Column( 20 | mainAxisAlignment: MainAxisAlignment.center, 21 | children: [ 22 | const Text( 23 | 'Page Not Found', 24 | style: TextStyle(fontWeight: FontWeight.bold), 25 | ), 26 | const SizedBox(height: 16), 27 | Text(error?.toString() ?? 'page not found'), 28 | const SizedBox(height: 16), 29 | _Button( 30 | onPressed: () => context.go('/'), 31 | child: const Text( 32 | 'Go to home page', 33 | style: TextStyle(color: _white), 34 | ), 35 | ), 36 | ], 37 | ), 38 | ), 39 | ); 40 | } 41 | 42 | class _Button extends StatefulWidget { 43 | const _Button({ 44 | required this.onPressed, 45 | required this.child, 46 | Key? key, 47 | }) : super(key: key); 48 | 49 | final VoidCallback onPressed; 50 | final Widget child; 51 | 52 | @override 53 | State<_Button> createState() => _ButtonState(); 54 | } 55 | 56 | class _ButtonState extends State<_Button> { 57 | late final Color _color; 58 | 59 | @override 60 | void didChangeDependencies() { 61 | super.didChangeDependencies(); 62 | _color = (context as Element) 63 | .findAncestorWidgetOfExactType() 64 | ?.color ?? 65 | const Color(0xFF2196F3); // blue 66 | } 67 | 68 | @override 69 | Widget build(BuildContext context) => GestureDetector( 70 | onTap: widget.onPressed, 71 | child: Container( 72 | padding: const EdgeInsets.all(8), 73 | color: _color, 74 | child: widget.child, 75 | ), 76 | ); 77 | } 78 | -------------------------------------------------------------------------------- /go_router/lib/src/go_router_material.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: diagnostic_describe_all_properties 2 | 3 | import 'package:flutter/material.dart'; 4 | import '../go_router.dart'; 5 | 6 | /// Checks for MaterialApp in the widget tree. 7 | bool isMaterialApp(Element elem) => 8 | elem.findAncestorWidgetOfExactType() != null; 9 | 10 | /// Builds a Material page. 11 | MaterialPage pageBuilderForMaterialApp({ 12 | required LocalKey key, 13 | required String? name, 14 | required Object? arguments, 15 | required String restorationId, 16 | required Widget child, 17 | }) => 18 | MaterialPage( 19 | name: name, 20 | arguments: arguments, 21 | key: key, 22 | restorationId: restorationId, 23 | child: child, 24 | ); 25 | 26 | /// Default error page implementation for Material. 27 | class GoRouterMaterialErrorScreen extends StatelessWidget { 28 | /// Provide an exception to this page for it to be displayed. 29 | const GoRouterMaterialErrorScreen(this.error, {Key? key}) : super(key: key); 30 | 31 | /// The exception to be displayed. 32 | final Exception? error; 33 | 34 | @override 35 | Widget build(BuildContext context) => Scaffold( 36 | appBar: AppBar(title: const Text('Page Not Found')), 37 | body: Center( 38 | child: Column( 39 | mainAxisAlignment: MainAxisAlignment.center, 40 | children: [ 41 | SelectableText(error?.toString() ?? 'page not found'), 42 | TextButton( 43 | onPressed: () => context.go('/'), 44 | child: const Text('Home'), 45 | ), 46 | ], 47 | ), 48 | ), 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /go_router/lib/src/go_router_refresh_stream.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'go_router.dart'; 7 | 8 | /// This class can be used to make `refreshListenable` react to events in the 9 | /// the provided stream. This allows you to listen to stream based state 10 | /// management solutions like for example BLoC. 11 | /// 12 | /// {@tool snippet} 13 | /// Typical usage is as follows: 14 | /// 15 | /// ```dart 16 | /// GoRouter( 17 | /// refreshListenable: GoRouterRefreshStream(stream), 18 | /// ); 19 | /// ``` 20 | /// {@end-tool} 21 | class GoRouterRefreshStream extends ChangeNotifier { 22 | /// Creates a [GoRouterRefreshStream]. 23 | /// 24 | /// Every time the [stream] receives an event the [GoRouter] will refresh its 25 | /// current route. 26 | GoRouterRefreshStream(Stream stream) { 27 | notifyListeners(); 28 | _subscription = stream.asBroadcastStream().listen( 29 | (dynamic _) => notifyListeners(), 30 | ); 31 | } 32 | 33 | late final StreamSubscription _subscription; 34 | 35 | @override 36 | void dispose() { 37 | _subscription.cancel(); 38 | super.dispose(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /go_router/lib/src/go_router_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | 3 | import 'go_router_delegate.dart'; 4 | 5 | /// The route state during routing. 6 | class GoRouterState { 7 | /// Default constructor for creating route state during routing. 8 | GoRouterState( 9 | this._delegate, { 10 | required this.location, 11 | required this.subloc, 12 | required this.name, 13 | this.path, 14 | this.fullpath, 15 | this.params = const {}, 16 | this.queryParams = const {}, 17 | this.extra, 18 | this.error, 19 | ValueKey? pageKey, 20 | }) : pageKey = pageKey ?? 21 | ValueKey(error != null 22 | ? 'error' 23 | : fullpath != null && fullpath.isNotEmpty 24 | ? fullpath 25 | : subloc), 26 | assert((path ?? '').isEmpty == (fullpath ?? '').isEmpty); 27 | 28 | final GoRouterDelegate _delegate; 29 | 30 | /// The full location of the route, e.g. /family/f2/person/p1 31 | final String location; 32 | 33 | /// The location of this sub-route, e.g. /family/f2 34 | final String subloc; 35 | 36 | /// The optional name of the route. 37 | final String? name; 38 | 39 | /// The path to this sub-route, e.g. family/:fid 40 | final String? path; 41 | 42 | /// The full path to this sub-route, e.g. /family/:fid 43 | final String? fullpath; 44 | 45 | /// The parameters for this sub-route, e.g. {'fid': 'f2'} 46 | final Map params; 47 | 48 | /// The query parameters for the location, e.g. {'from': '/family/f2'} 49 | final Map queryParams; 50 | 51 | /// An extra object to pass along with the navigation. 52 | final Object? extra; 53 | 54 | /// The error associated with this sub-route. 55 | final Exception? error; 56 | 57 | /// A unique string key for this sub-route, e.g. ValueKey('/family/:fid') 58 | final ValueKey pageKey; 59 | 60 | /// Get a location from route name and parameters. 61 | /// This is useful for redirecting to a named location. 62 | String namedLocation( 63 | String name, { 64 | Map params = const {}, 65 | Map queryParams = const {}, 66 | }) => 67 | _delegate.namedLocation(name, params: params, queryParams: queryParams); 68 | } 69 | -------------------------------------------------------------------------------- /go_router/lib/src/inherited_go_router.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | import 'go_router.dart'; 5 | 6 | /// GoRouter implementation of InheritedWidget. 7 | /// 8 | /// Used for to find the current GoRouter in the widget tree. This is useful 9 | /// when routing from anywhere in your app. 10 | class InheritedGoRouter extends InheritedWidget { 11 | /// Default constructor for the inherited go router. 12 | const InheritedGoRouter({ 13 | required Widget child, 14 | required this.goRouter, 15 | Key? key, 16 | }) : super(child: child, key: key); 17 | 18 | /// The [GoRouter] that is made available to the widget tree. 19 | final GoRouter goRouter; 20 | 21 | /// Used by the Router architecture as part of the InheritedWidget. 22 | @override 23 | // ignore: prefer_expression_function_bodies 24 | bool updateShouldNotify(covariant InheritedWidget oldWidget) { 25 | // avoid rebuilding the widget tree if the router has not changed 26 | return false; 27 | } 28 | 29 | @override 30 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { 31 | super.debugFillProperties(properties); 32 | properties.add(DiagnosticsProperty('goRouter', goRouter)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /go_router/lib/src/logging.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:developer' as developer; 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:logging/logging.dart'; 5 | 6 | /// The logger for this package. 7 | final log = Logger('GoRouter'); 8 | 9 | StreamSubscription? _subscription; 10 | 11 | /// Forwards diagnostic messages to the dart:developer log() API. 12 | void setLogging({bool enabled = false}) { 13 | _subscription?.cancel(); 14 | if (!enabled) return; 15 | 16 | _subscription = log.onRecord.listen((e) { 17 | // use `dumpErrorToConsole` for severe messages to ensure that severe 18 | // exceptions are formatted consistently with other Flutter examples and 19 | // avoids printing duplicate exceptions 20 | if (e.level >= Level.SEVERE) { 21 | final error = e.error; 22 | FlutterError.dumpErrorToConsole( 23 | FlutterErrorDetails( 24 | exception: error is Exception ? error : Exception(error), 25 | stack: e.stackTrace, 26 | library: e.loggerName, 27 | context: ErrorDescription(e.message), 28 | ), 29 | ); 30 | } else { 31 | developer.log( 32 | e.message, 33 | time: e.time, 34 | sequenceNumber: e.sequenceNumber, 35 | level: e.level.value, 36 | name: e.loggerName, 37 | zone: e.zone, 38 | error: e.error, 39 | stackTrace: e.stackTrace, 40 | ); 41 | } 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /go_router/lib/src/path_strategy_nonweb.dart: -------------------------------------------------------------------------------- 1 | import 'url_path_strategy.dart'; 2 | 3 | /// no-op implementation of the URL path strategy for non-web target platforms 4 | void setUrlPathStrategyImpl(UrlPathStrategy strategy) { 5 | // no-op 6 | } 7 | -------------------------------------------------------------------------------- /go_router/lib/src/path_strategy_web.dart: -------------------------------------------------------------------------------- 1 | // from https://flutter.dev/docs/development/ui/navigation/url-strategies 2 | import 'package:flutter_web_plugins/flutter_web_plugins.dart'; 3 | 4 | import 'url_path_strategy.dart'; 5 | 6 | /// forwarding implementation of the URL path strategy for the web target 7 | /// platform 8 | void setUrlPathStrategyImpl(UrlPathStrategy strategy) { 9 | setUrlStrategy(strategy == UrlPathStrategy.path 10 | ? PathUrlStrategy() 11 | : const HashUrlStrategy()); 12 | } 13 | -------------------------------------------------------------------------------- /go_router/lib/src/typedefs.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import 'go_route_match.dart'; 4 | import 'go_router_state.dart'; 5 | 6 | /// Signature of a go router builder function with matchers. 7 | typedef GoRouterBuilderWithMatches = Widget Function( 8 | BuildContext context, 9 | Iterable matches, 10 | ); 11 | 12 | /// Signature of a go router builder function with navigator. 13 | typedef GoRouterBuilderWithNav = Widget Function( 14 | BuildContext context, 15 | GoRouterState state, 16 | Navigator navigator, 17 | ); 18 | 19 | /// The signature of the page builder callback for a matched GoRoute. 20 | typedef GoRouterPageBuilder = Page Function( 21 | BuildContext context, 22 | GoRouterState state, 23 | ); 24 | 25 | /// The signature of the widget builder callback for a matched GoRoute. 26 | typedef GoRouterWidgetBuilder = Widget Function( 27 | BuildContext context, 28 | GoRouterState state, 29 | ); 30 | 31 | /// The signature of the redirect callback. 32 | typedef GoRouterRedirect = String? Function(GoRouterState state); 33 | 34 | /// The signature of the navigatorBuilder callback. 35 | typedef GoRouterNavigatorBuilder = Widget Function( 36 | BuildContext context, 37 | GoRouterState state, 38 | Widget child, 39 | ); 40 | -------------------------------------------------------------------------------- /go_router/lib/src/url_path_strategy.dart: -------------------------------------------------------------------------------- 1 | /// The path strategy for use in GoRouter.setUrlPathStrategy. 2 | enum UrlPathStrategy { 3 | /// Use hash url strategy. 4 | hash, 5 | 6 | /// Use path url strategy. 7 | path, 8 | } 9 | -------------------------------------------------------------------------------- /go_router/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: go_router 2 | description: A declarative router for Flutter based on Navigation 2 supporting 3 | deep linking, data-driven routes and more 4 | version: 3.0.1 5 | repository: https://github.com/csells/go_router 6 | 7 | environment: 8 | sdk: ">=2.12.0 <3.0.0" 9 | flutter: ">=1.17.0" 10 | 11 | dependencies: 12 | collection: ^1.15.0 13 | flutter: 14 | sdk: flutter 15 | flutter_web_plugins: 16 | sdk: flutter 17 | logging: ^1.0.0 18 | path_to_regexp: ^0.4.0 19 | 20 | dev_dependencies: 21 | all_lint_rules_community: ^0.0.4 22 | flutter_test: 23 | sdk: flutter 24 | --------------------------------------------------------------------------------