├── examples
├── single_base_route
│ ├── ios
│ │ ├── Flutter
│ │ │ ├── Debug.xcconfig
│ │ │ ├── Release.xcconfig
│ │ │ └── AppFrameworkInfo.plist
│ │ ├── Runner
│ │ │ ├── Runner-Bridging-Header.h
│ │ │ ├── Assets.xcassets
│ │ │ │ ├── LaunchImage.imageset
│ │ │ │ │ ├── LaunchImage.png
│ │ │ │ │ ├── LaunchImage@2x.png
│ │ │ │ │ ├── LaunchImage@3x.png
│ │ │ │ │ ├── README.md
│ │ │ │ │ └── Contents.json
│ │ │ │ └── AppIcon.appiconset
│ │ │ │ │ ├── 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-1024x1024@1x.png
│ │ │ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ │ │ └── Contents.json
│ │ │ ├── AppDelegate.swift
│ │ │ ├── Base.lproj
│ │ │ │ ├── Main.storyboard
│ │ │ │ └── LaunchScreen.storyboard
│ │ │ └── Info.plist
│ │ ├── Runner.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ └── Runner.xcodeproj
│ │ │ ├── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── android
│ │ ├── gradle.properties
│ │ ├── app
│ │ │ ├── src
│ │ │ │ ├── main
│ │ │ │ │ ├── res
│ │ │ │ │ │ ├── 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
│ │ │ │ │ │ │ └── styles.xml
│ │ │ │ │ │ └── drawable
│ │ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── kotlin
│ │ │ │ │ │ └── dev
│ │ │ │ │ │ │ └── krasnov
│ │ │ │ │ │ │ └── single_base_route
│ │ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ │ └── AndroidManifest.xml
│ │ │ │ ├── debug
│ │ │ │ │ └── AndroidManifest.xml
│ │ │ │ └── profile
│ │ │ │ │ └── AndroidManifest.xml
│ │ │ └── build.gradle
│ │ ├── gradle
│ │ │ └── wrapper
│ │ │ │ └── gradle-wrapper.properties
│ │ ├── settings.gradle
│ │ └── build.gradle
│ ├── navigation.png
│ ├── test_driver
│ │ ├── app.dart
│ │ ├── steps
│ │ │ ├── steps.dart
│ │ │ ├── open_song.dart
│ │ │ ├── open_artist.dart
│ │ │ ├── open_favorites.dart
│ │ │ ├── navigate_to_artist.dart
│ │ │ ├── text_appears.dart
│ │ │ ├── go_back.dart
│ │ │ └── title_is.dart
│ │ ├── app_test.dart
│ │ └── features
│ │ │ ├── navigate.feature
│ │ │ └── push_pop.feature
│ ├── README.md
│ ├── .metadata
│ ├── lib
│ │ ├── model.dart
│ │ ├── widgets
│ │ │ ├── splash_page.dart
│ │ │ ├── error_page.dart
│ │ │ ├── song_list_tile.dart
│ │ │ ├── favorites_page.dart
│ │ │ ├── artist_page.dart
│ │ │ ├── song_page.dart
│ │ │ └── library_page.dart
│ │ ├── deep_links.dart
│ │ ├── data.dart
│ │ └── main.dart
│ ├── pubspec.yaml
│ ├── .gitignore
│ └── pubspec.lock
└── multiple_base_routes
│ ├── ios
│ ├── Flutter
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── AppFrameworkInfo.plist
│ ├── Runner
│ │ ├── Runner-Bridging-Header.h
│ │ ├── Assets.xcassets
│ │ │ ├── LaunchImage.imageset
│ │ │ │ ├── LaunchImage.png
│ │ │ │ ├── LaunchImage@2x.png
│ │ │ │ ├── LaunchImage@3x.png
│ │ │ │ ├── README.md
│ │ │ │ └── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ ├── 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-1024x1024@1x.png
│ │ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ │ └── Contents.json
│ │ ├── AppDelegate.swift
│ │ ├── Base.lproj
│ │ │ ├── Main.storyboard
│ │ │ └── LaunchScreen.storyboard
│ │ └── Info.plist
│ ├── Runner.xcworkspace
│ │ └── contents.xcworkspacedata
│ └── Runner.xcodeproj
│ │ ├── project.xcworkspace
│ │ └── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
│ ├── android
│ ├── gradle.properties
│ ├── app
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── res
│ │ │ │ │ ├── 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
│ │ │ │ │ │ └── styles.xml
│ │ │ │ │ └── drawable
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── kotlin
│ │ │ │ │ └── dev
│ │ │ │ │ │ └── krasnov
│ │ │ │ │ │ └── multiple_base_routes
│ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── debug
│ │ │ │ └── AndroidManifest.xml
│ │ │ └── profile
│ │ │ │ └── AndroidManifest.xml
│ │ └── build.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ ├── settings.gradle
│ └── build.gradle
│ ├── test_driver
│ ├── steps
│ │ ├── steps.dart
│ │ ├── text_disappears.dart
│ │ ├── go_back.dart
│ │ └── title_is.dart
│ ├── app.dart
│ ├── app_test.dart
│ └── features
│ │ ├── bottom_navigation.feature
│ │ └── promise.feature
│ ├── navigation.png
│ ├── .metadata
│ ├── README.md
│ ├── lib
│ ├── model.dart
│ ├── widgets
│ │ ├── splash_page.dart
│ │ ├── error_page.dart
│ │ ├── song_list_tile.dart
│ │ ├── favorites_page.dart
│ │ ├── artist_page.dart
│ │ ├── login_page.dart
│ │ ├── song_page.dart
│ │ ├── authentication_page.dart
│ │ ├── user_page.dart
│ │ └── library_page.dart
│ ├── data.dart
│ ├── deep_links.dart
│ └── main.dart
│ ├── pubspec.yaml
│ ├── .gitignore
│ └── pubspec.lock
├── test
└── deep_link_navigation_test.dart
├── lib
├── deep_link_navigation.dart
└── src
│ ├── exceptions.dart
│ ├── pop_observer.dart
│ ├── default_page_loader.dart
│ ├── utils.dart
│ ├── deep_link.dart
│ ├── dispatchers.dart
│ ├── deep_link_material_app.dart
│ └── deep_link_navigator.dart
├── .metadata
├── example
├── pubspec.yaml
├── README.md
└── pubspec.lock
├── pubspec.yaml
├── LICENSE
├── CHANGELOG.md
├── .gitignore
├── pubspec.lock
└── README.md
/examples/single_base_route/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
--------------------------------------------------------------------------------
/examples/single_base_route/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 |
3 | android.enableR8=true
4 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 |
3 | android.enableR8=true
4 |
--------------------------------------------------------------------------------
/test/deep_link_navigation_test.dart:
--------------------------------------------------------------------------------
1 | // TODO: deep link navigator unit tests (mock native navigator)
2 | // navigating to same route
3 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/test_driver/steps/steps.dart:
--------------------------------------------------------------------------------
1 | export 'go_back.dart';
2 | export 'text_disappears.dart';
3 | export 'title_is.dart';
--------------------------------------------------------------------------------
/examples/single_base_route/navigation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/navigation.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/navigation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/navigation.png
--------------------------------------------------------------------------------
/examples/single_base_route/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/single_base_route/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/single_base_route/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/single_base_route/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/single_base_route/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/single_base_route/test_driver/app.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_driver/driver_extension.dart';
2 |
3 | import 'package:single_base_route/main.dart' as app;
4 |
5 | void main() {
6 | enableFlutterDriverExtension();
7 | app.main();
8 | }
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/test_driver/app.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_driver/driver_extension.dart';
2 | import 'package:multiple_base_routes/main.dart' as app;
3 |
4 | void main() {
5 | enableFlutterDriverExtension();
6 | app.main();
7 | }
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/lib/deep_link_navigation.dart:
--------------------------------------------------------------------------------
1 | library deep_link_navigation;
2 |
3 | export 'src/deep_link.dart';
4 | export 'src/deep_link_material_app.dart';
5 | export 'src/deep_link_navigator.dart';
6 | export 'src/dispatchers.dart';
7 | export 'src/exceptions.dart';
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/single_base_route/test_driver/steps/steps.dart:
--------------------------------------------------------------------------------
1 | export 'go_back.dart';
2 | export 'navigate_to_artist.dart';
3 | export 'open_artist.dart';
4 | export 'open_favorites.dart';
5 | export 'open_song.dart';
6 | export 'text_appears.dart';
7 | export 'title_is.dart';
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/single_base_route/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/HEAD/examples/multiple_base_routes/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/examples/single_base_route/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-4.10.2-all.zip
7 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/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-4.10.2-all.zip
7 |
--------------------------------------------------------------------------------
/examples/single_base_route/README.md:
--------------------------------------------------------------------------------
1 | # Single Base Route
2 |
3 | **This example demonstrates:**
4 | * Dispatchers with path-only deep links
5 | * Dispatchers with value deep links (ArtistDL, SongDL)
6 | * Cross-branch navigation (from favorite's song page to artist page)
7 | 
--------------------------------------------------------------------------------
/.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: cc949a8e8b9cf394b9290a8e80f87af3e207dce5
8 | channel: stable
9 |
10 | project_type: package
11 |
--------------------------------------------------------------------------------
/lib/src/exceptions.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 |
3 | /// Reserved exception thrown when a route didn't match the given navigation hierarchy.
4 | class RouteNotFound implements Exception {
5 | List route;
6 | RouteNotFound(this.route);
7 |
8 | @override
9 | String toString() => "Route not found: $route";
10 | }
--------------------------------------------------------------------------------
/examples/single_base_route/.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: cc949a8e8b9cf394b9290a8e80f87af3e207dce5
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/.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: cc949a8e8b9cf394b9290a8e80f87af3e207dce5
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/examples/single_base_route/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/single_base_route/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/single_base_route/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.
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/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.
--------------------------------------------------------------------------------
/examples/single_base_route/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/README.md:
--------------------------------------------------------------------------------
1 | # Multiple Base Routes
2 |
3 | **This example demonstrates:**
4 | * Everything from single base route example
5 | * Bottom navigation (library, favorites, user pages) persists across navigation
6 | * Login and error pages are full screen (hide bottom navigation)
7 | * Using the future result of push in user/authentication page
8 | * Custom `Authenticated` mixin ensures user is authenticated (LibraryDL, FavoritesDL, UserDL)
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/examples/single_base_route/lib/model.dart:
--------------------------------------------------------------------------------
1 | import 'package:meta/meta.dart';
2 |
3 | @immutable
4 | class Artist {
5 | final String id;
6 |
7 | final String name;
8 |
9 | final List songs;
10 |
11 | Artist(this.id, this.name, this.songs);
12 | }
13 |
14 | @immutable
15 | class Song {
16 | final String id;
17 |
18 | /// Foreign key for artist
19 | final String artistId;
20 |
21 | final String name;
22 |
23 | Song(this.id, this.artistId, this.name);
24 | }
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/model.dart:
--------------------------------------------------------------------------------
1 | import 'package:meta/meta.dart';
2 |
3 | @immutable
4 | class Artist {
5 | final String id;
6 |
7 | final String name;
8 |
9 | final List songs;
10 |
11 | Artist(this.id, this.name, this.songs);
12 | }
13 |
14 | @immutable
15 | class Song {
16 | final String id;
17 |
18 | /// Foreign key for artist
19 | final String artistId;
20 |
21 | final String name;
22 |
23 | Song(this.id, this.artistId, this.name);
24 | }
--------------------------------------------------------------------------------
/examples/single_base_route/android/app/src/main/kotlin/dev/krasnov/single_base_route/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package dev.krasnov.single_base_route
2 |
3 | import android.os.Bundle
4 |
5 | import io.flutter.app.FlutterActivity
6 | import io.flutter.plugins.GeneratedPluginRegistrant
7 |
8 | class MainActivity: FlutterActivity() {
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 | GeneratedPluginRegistrant.registerWith(this)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/single_base_route/test_driver/steps/open_song.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_driver/flutter_driver.dart';
2 | import 'package:flutter_gherkin/flutter_gherkin.dart';
3 | import 'package:gherkin/gherkin.dart';
4 |
5 | class OpenSong extends When1WithWorld {
6 | @override
7 | RegExp get pattern => RegExp(r"I open the song {string}");
8 |
9 | @override
10 | Future executeStep(String key) async => FlutterDriverUtils.tap(world.driver, find.byValueKey(key));
11 | }
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/app/src/main/kotlin/dev/krasnov/multiple_base_routes/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package dev.krasnov.multiple_base_routes
2 |
3 | import android.os.Bundle
4 |
5 | import io.flutter.app.FlutterActivity
6 | import io.flutter.plugins.GeneratedPluginRegistrant
7 |
8 | class MainActivity: FlutterActivity() {
9 | override fun onCreate(savedInstanceState: Bundle?) {
10 | super.onCreate(savedInstanceState)
11 | GeneratedPluginRegistrant.registerWith(this)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/examples/single_base_route/test_driver/steps/open_artist.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_driver/flutter_driver.dart';
2 | import 'package:flutter_gherkin/flutter_gherkin.dart';
3 | import 'package:gherkin/gherkin.dart';
4 |
5 | class OpenArtist extends When1WithWorld {
6 | @override
7 | RegExp get pattern => RegExp(r"I open the artist {string}");
8 |
9 | @override
10 | Future executeStep(String key) async => FlutterDriverUtils.tap(world.driver, find.byValueKey(key));
11 | }
--------------------------------------------------------------------------------
/examples/multiple_base_routes/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 |
--------------------------------------------------------------------------------
/examples/single_base_route/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 |
--------------------------------------------------------------------------------
/examples/single_base_route/lib/widgets/splash_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class SplashPage extends StatelessWidget {
4 | @override
5 | Widget build(BuildContext context) {
6 | return Scaffold(
7 | appBar: AppBar(
8 | title: Text(
9 | "splash",
10 | key: Key("title"),
11 | )
12 | ),
13 | body: IconButton(
14 | icon: Icon(Icons.autorenew),
15 | onPressed: () => null,
16 | ),
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/widgets/splash_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class SplashPage extends StatelessWidget {
4 | @override
5 | Widget build(BuildContext context) {
6 | return Scaffold(
7 | appBar: AppBar(
8 | title: Text(
9 | "splash",
10 | key: Key("title"),
11 | )
12 | ),
13 | body: IconButton(
14 | icon: Icon(Icons.autorenew),
15 | onPressed: () => null,
16 | ),
17 | );
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/single_base_route/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: single_base_route
2 | description: A new Flutter application.
3 | version: 1.0.0+1
4 |
5 | environment:
6 | sdk: ">=2.6.0 <3.0.0"
7 |
8 | dependencies:
9 | flutter:
10 | sdk: flutter
11 |
12 | deep_link_navigation:
13 | path: ../../
14 |
15 | dev_dependencies:
16 | flutter_test:
17 | sdk: flutter
18 |
19 | flutter_driver:
20 | sdk: flutter
21 |
22 | test: any
23 | flutter_gherkin: ^1.1.4
24 |
25 | flutter:
26 | uses-material-design: true
--------------------------------------------------------------------------------
/examples/single_base_route/test_driver/steps/open_favorites.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_gherkin/flutter_gherkin.dart';
2 | import 'package:gherkin/gherkin.dart';
3 | import 'package:flutter_driver/flutter_driver.dart';
4 |
5 | class OpenFavorites extends WhenWithWorld {
6 | @override
7 | RegExp get pattern => RegExp(r"I open my favorite songs");
8 |
9 | @override
10 | Future executeStep() async {
11 | await FlutterDriverUtils.tap(world.driver, find.byType("IconButton"));
12 | }
13 | }
--------------------------------------------------------------------------------
/examples/single_base_route/lib/widgets/error_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ErrorPage extends StatelessWidget {
4 | final Exception e;
5 |
6 | ErrorPage(this.e);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Scaffold(
11 | appBar: AppBar(
12 | title: Text(
13 | "ERROR",
14 | key: Key("title"),
15 | )
16 | ),
17 | body: Center(
18 | child: Text("$e"),
19 | ),
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/widgets/error_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class ErrorPage extends StatelessWidget {
4 | final Exception e;
5 |
6 | ErrorPage(this.e);
7 |
8 | @override
9 | Widget build(BuildContext context) {
10 | return Scaffold(
11 | appBar: AppBar(
12 | title: Text(
13 | "ERROR",
14 | key: Key("title"),
15 | )
16 | ),
17 | body: Center(
18 | child: Text("$e"),
19 | ),
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/single_base_route/test_driver/steps/navigate_to_artist.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_gherkin/flutter_gherkin.dart';
2 | import 'package:gherkin/gherkin.dart';
3 | import 'package:flutter_driver/flutter_driver.dart';
4 |
5 | class NavigateToArtist extends WhenWithWorld {
6 | @override
7 | RegExp get pattern => RegExp(r"I navigate to the song's artist");
8 |
9 | @override
10 | Future executeStep() async {
11 | await FlutterDriverUtils.tap(world.driver, find.text("Go to artist"));
12 | }
13 | }
--------------------------------------------------------------------------------
/examples/multiple_base_routes/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: multiple_base_routes
2 | description: A new Flutter application.
3 | version: 1.0.0+1
4 |
5 | environment:
6 | sdk: ">=2.6.0 <3.0.0"
7 |
8 | dependencies:
9 | flutter:
10 | sdk: flutter
11 |
12 | provider: ^4.0.1
13 | deep_link_navigation:
14 | path: ../../
15 |
16 | dev_dependencies:
17 | flutter_test:
18 | sdk: flutter
19 |
20 | flutter_driver:
21 | sdk: flutter
22 |
23 | test: any
24 | flutter_gherkin: ^1.1.4
25 |
26 | flutter:
27 | uses-material-design: true
--------------------------------------------------------------------------------
/examples/single_base_route/test_driver/steps/text_appears.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_gherkin/flutter_gherkin.dart';
2 | import 'package:gherkin/gherkin.dart';
3 | import 'package:flutter_driver/flutter_driver.dart';
4 |
5 | class TextAppears extends Then1WithWorld {
6 | @override
7 | RegExp get pattern => RegExp(r"the text {string} appears");
8 |
9 | @override
10 | Future executeStep(String input1) async {
11 |
12 | expect(await FlutterDriverUtils.isPresent(find.text(input1), world.driver), true);
13 | }
14 | }
--------------------------------------------------------------------------------
/examples/multiple_base_routes/test_driver/steps/text_disappears.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_gherkin/flutter_gherkin.dart';
2 | import 'package:gherkin/gherkin.dart';
3 | import 'package:flutter_driver/flutter_driver.dart';
4 |
5 | class TextDisappears extends Then1WithWorld {
6 | @override
7 | RegExp get pattern => RegExp(r"the text {string} disappears");
8 |
9 | @override
10 | Future executeStep(String input1) async {
11 | expect(await FlutterDriverUtils.isAbsent(world.driver, find.text(input1)), true);
12 | }
13 | }
--------------------------------------------------------------------------------
/examples/single_base_route/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/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 |
--------------------------------------------------------------------------------
/examples/single_base_route/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 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/examples/single_base_route/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
4 |
5 | def plugins = new Properties()
6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
7 | if (pluginsFile.exists()) {
8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
9 | }
10 |
11 | plugins.each { name, path ->
12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
13 | include ":$name"
14 | project(":$name").projectDir = pluginDirectory
15 | }
16 |
--------------------------------------------------------------------------------
/lib/src/pop_observer.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'package:deep_link_navigation/deep_link_navigation.dart';
4 |
5 | /// Notifies [DeepLinkNavigator] of a pop that occurred from the native Flutter navigator.
6 | /// Handles android back button, back arrow, and [DeepLinkNavigator.pop()] calls native pop.
7 | ///
8 | /// eg. Navigator(observers: [PopObserver()])
9 | class PopObserver extends NavigatorObserver {
10 | @override
11 | void didPop(Route route, Route previousRoute) => DeepLinkNavigator.of(route.navigator.context).notifyPopped();
12 | }
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: deep_link_navigation_example
2 | description: Deep link navigation for Flutter apps with an elegant configuation internally orchestrating a native Flutter navigator.
3 | version: 1.0.0
4 | author: Dennis Krasnov
5 | homepage: https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation
6 | repository: https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation
7 | issue_tracker: https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/issues
8 |
9 | environment:
10 | sdk: ">=2.0.0 <3.0.0"
11 |
12 | dependencies:
13 | deep_link_navigation:
14 | path: ../
--------------------------------------------------------------------------------
/examples/single_base_route/test_driver/steps/go_back.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_driver/flutter_driver.dart';
2 | import 'package:flutter_gherkin/flutter_gherkin.dart';
3 | import 'package:gherkin/gherkin.dart';
4 |
5 | class GoBack extends When1WithWorld {
6 | @override
7 | RegExp get pattern => RegExp(r"I go back {int} time(s)");
8 |
9 | @override
10 | Future executeStep(int times) async {
11 | for (var i = 0; i < times; i++) {
12 | await world.driver.tap(find.pageBack(), timeout: timeout);
13 | await FlutterDriverUtils.waitForFlutter(world.driver, timeout: timeout);
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/examples/multiple_base_routes/test_driver/steps/go_back.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_driver/flutter_driver.dart';
2 | import 'package:flutter_gherkin/flutter_gherkin.dart';
3 | import 'package:gherkin/gherkin.dart';
4 |
5 | class GoBack extends When1WithWorld {
6 | @override
7 | RegExp get pattern => RegExp(r"I go back {int} time(s)");
8 |
9 | @override
10 | Future executeStep(int times) async {
11 | for (var i = 0; i < times; i++) {
12 | await world.driver.tap(find.pageBack(), timeout: timeout);
13 | await FlutterDriverUtils.waitForFlutter(world.driver, timeout: timeout);
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/examples/single_base_route/lib/widgets/song_list_tile.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import 'package:single_base_route/deep_links.dart';
5 | import 'package:single_base_route/model.dart';
6 |
7 | class SongListTile extends StatelessWidget {
8 | final Song song;
9 |
10 | const SongListTile({Key key, this.song}) : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return ListTile(
15 | key: Key(song.name),
16 | leading: Icon(Icons.music_note),
17 | title: Text(song.name),
18 | onTap: () => DeepLinkNavigator.of(context).push(SongDL(song)),
19 | );
20 | }
21 | }
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/widgets/song_list_tile.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import 'package:multiple_base_routes/deep_links.dart';
5 | import 'package:multiple_base_routes/model.dart';
6 |
7 | class SongListTile extends StatelessWidget {
8 | final Song song;
9 |
10 | const SongListTile({Key key, this.song}) : super(key: key);
11 |
12 | @override
13 | Widget build(BuildContext context) {
14 | return ListTile(
15 | key: Key(song.name),
16 | leading: Icon(Icons.music_note),
17 | title: Text(song.name),
18 | onTap: () => DeepLinkNavigator.of(context).push(SongDL(song)),
19 | );
20 | }
21 | }
--------------------------------------------------------------------------------
/examples/single_base_route/lib/widgets/favorites_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'package:single_base_route/data.dart';
4 | import 'package:single_base_route/widgets/song_list_tile.dart';
5 |
6 | class FavoritesPage extends StatelessWidget {
7 | @override
8 | Widget build(BuildContext context) {
9 | return Scaffold(
10 | appBar: AppBar(
11 | title: Text(
12 | "My favorites (${Data.favoriteSongs.length})",
13 | key: Key("title"),
14 | ),
15 | ),
16 | body: ListView(
17 | children: [
18 | for (final song in Data.favoriteSongs)
19 | SongListTile(song: song),
20 | ],
21 | ),
22 | );
23 | }
24 | }
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/widgets/favorites_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'package:multiple_base_routes/data.dart';
4 | import 'package:multiple_base_routes/widgets/song_list_tile.dart';
5 |
6 | class FavoritesPage extends StatelessWidget {
7 | @override
8 | Widget build(BuildContext context) {
9 | return Scaffold(
10 | appBar: AppBar(
11 | title: Text(
12 | "My favorites (${Data.favoriteSongs.length})",
13 | key: Key("title"),
14 | ),
15 | ),
16 | body: ListView(
17 | children: [
18 | for (final song in Data.favoriteSongs)
19 | SongListTile(song: song),
20 | ],
21 | ),
22 | );
23 | }
24 | }
--------------------------------------------------------------------------------
/examples/single_base_route/lib/deep_links.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 |
3 | import 'package:single_base_route/model.dart';
4 |
5 | class LibraryDL extends DeepLink {
6 | LibraryDL() : super("library");
7 | }
8 |
9 | class FavoritesDL extends DeepLink {
10 | FavoritesDL() : super("favorites");
11 | }
12 |
13 | class ArtistDL extends ValueDeepLink {
14 | ArtistDL(Artist artist) : super("artist", artist, toString: (artist) => artist.id);
15 | }
16 |
17 | class SongDL extends ValueDeepLink {
18 | SongDL(Song song) : super("song", song, toString: (song) => song.id);
19 | }
20 |
21 | class ErrorDL extends ValueDeepLink {
22 | ErrorDL(E e) : super("error", e);
23 | }
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.2.71'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.2.1'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/examples/single_base_route/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.2.71'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.2.1'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | }
12 | }
13 |
14 | allprojects {
15 | repositories {
16 | google()
17 | jcenter()
18 | }
19 | }
20 |
21 | rootProject.buildDir = '../build'
22 | subprojects {
23 | project.buildDir = "${rootProject.buildDir}/${project.name}"
24 | }
25 | subprojects {
26 | project.evaluationDependsOn(':app')
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
32 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: deep_link_navigation
2 | description: Deep link navigation for Flutter apps with an elegant configuation internally orchestrating a native Flutter navigator.
3 | version: 1.3.1
4 | author: Dennis Krasnov
5 | homepage: https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation
6 | repository: https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation
7 | issue_tracker: https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/issues
8 |
9 | environment:
10 | sdk: ">=2.2.2 <3.0.0"
11 |
12 | dependencies:
13 | flutter:
14 | sdk: flutter
15 |
16 | provider: ^4.0.1
17 | after_layout: ^1.0.7+2
18 | merge_map: ^1.0.2
19 |
20 | dev_dependencies:
21 | flutter_test:
22 | sdk: flutter
23 |
24 | flutter:
25 |
26 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/test_driver/steps/title_is.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_gherkin/flutter_gherkin.dart';
2 | import 'package:gherkin/gherkin.dart';
3 | import 'package:flutter_driver/flutter_driver.dart';
4 |
5 | class TitleIs extends Then1WithWorld {
6 | @override
7 | RegExp get pattern => RegExp(r"the title is {string}");
8 |
9 | @override
10 | Future executeStep(String title) async {
11 | try {
12 | final text = await FlutterDriverUtils.getText(world.driver, find.byValueKey("title"));// .catchError(onError); default reporter message thing!
13 | expect(text, title);
14 | } catch (e) {
15 | await reporter.message("Step error '${pattern.pattern}': $e", MessageLevel.error);
16 | rethrow;
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/examples/single_base_route/test_driver/steps/title_is.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_gherkin/flutter_gherkin.dart';
2 | import 'package:gherkin/gherkin.dart';
3 | import 'package:flutter_driver/flutter_driver.dart';
4 |
5 | class TitleIs extends Then1WithWorld {
6 | @override
7 | RegExp get pattern => RegExp(r"the title is {string}");
8 |
9 | @override
10 | Future executeStep(String title) async {
11 | try {
12 | final text = await FlutterDriverUtils.getText(world.driver, find.byValueKey("title"));// .catchError(onError); default reporter message thing!
13 | expect(text, title);
14 | } catch (e) {
15 | await reporter.message("Step error '${pattern.pattern}': $e", MessageLevel.error);
16 | rethrow;
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/examples/single_base_route/lib/widgets/artist_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'package:single_base_route/model.dart';
4 | import 'package:single_base_route/widgets/song_list_tile.dart';
5 |
6 | class ArtistPage extends StatelessWidget {
7 | final Artist artist;
8 |
9 | const ArtistPage({Key key, this.artist}) : super(key: key);
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Scaffold(
14 | appBar: AppBar(
15 | title: Text(
16 | artist.name,
17 | key: Key("title"),
18 | ),
19 | ),
20 | body: ListView(
21 | children: [
22 | for (final song in artist.songs)
23 | SongListTile(song: song),
24 | ],
25 | ),
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/widgets/artist_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'package:multiple_base_routes/model.dart';
4 | import 'package:multiple_base_routes/widgets/song_list_tile.dart';
5 |
6 | class ArtistPage extends StatelessWidget {
7 | final Artist artist;
8 |
9 | const ArtistPage({Key key, this.artist}) : super(key: key);
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Scaffold(
14 | appBar: AppBar(
15 | title: Text(
16 | artist.name,
17 | key: Key("title"),
18 | ),
19 | ),
20 | body: ListView(
21 | children: [
22 | for (final song in artist.songs)
23 | SongListTile(song: song),
24 | ],
25 | ),
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/test_driver/app_test.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter_gherkin/flutter_gherkin.dart';
4 | import 'package:gherkin/gherkin.dart';
5 | import 'package:glob/glob.dart';
6 |
7 | import 'steps/steps.dart';
8 |
9 | Future main() {
10 | final config = FlutterTestConfiguration()
11 | ..features = [Glob(r"test_driver/features/**.feature")]
12 | ..reporters = [
13 | ProgressReporter(),
14 | TestRunSummaryReporter(),
15 | ]
16 | ..stepDefinitions = [GoBack(), TextDisappears(), TitleIs()]
17 | ..restartAppBetweenScenarios = true
18 | ..targetAppPath = "test_driver/app.dart"
19 | ..exitAfterTestRun = true;
20 | return GherkinRunner().execute(config);
21 | }
22 |
23 | // cd examples/multiple_base_routes
24 | // ~/Dev/flutter/bin/cache/dart-sdk/bin/dart test_driver/app_test.dart
25 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/widgets/login_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:provider/provider.dart';
4 |
5 | import '../data.dart';
6 |
7 | class LoginPage extends StatelessWidget {
8 | @override
9 | Widget build(BuildContext context) {
10 | return Scaffold(
11 | appBar: AppBar(
12 | title: Text(
13 | "Login",
14 | key: Key("title"),
15 | )
16 | ),
17 | body: Center(
18 | child: RaisedButton(
19 | key: Key("login"),
20 | child: Text("Login"),
21 | onPressed: () {
22 | Provider.of(context, listen: false).login();
23 | DeepLinkNavigator.of(context).replaceWithDefault();
24 | }
25 | ),
26 | ),
27 | );
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/examples/single_base_route/test_driver/app_test.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter_gherkin/flutter_gherkin.dart';
4 | import 'package:gherkin/gherkin.dart';
5 | import 'package:glob/glob.dart';
6 |
7 | import 'steps/steps.dart';
8 |
9 | Future main() {
10 | final config = FlutterTestConfiguration()
11 | ..features = [Glob(r"test_driver/features/**.feature")]
12 | ..reporters = [
13 | ProgressReporter(),
14 | TestRunSummaryReporter(),
15 | ]
16 | ..stepDefinitions = [GoBack(), NavigateToArtist(), OpenArtist(), OpenFavorites(), OpenSong(), TextAppears(), TitleIs()]
17 | ..restartAppBetweenScenarios = true
18 | ..targetAppPath = "test_driver/app.dart"
19 | ..exitAfterTestRun = true;
20 | return GherkinRunner().execute(config);
21 | }
22 |
23 | // cd examples/single_base_route
24 | // ~/Dev/flutter/bin/cache/dart-sdk/bin/dart test_driver/app_test.dart
--------------------------------------------------------------------------------
/lib/src/default_page_loader.dart:
--------------------------------------------------------------------------------
1 | import 'package:after_layout/after_layout.dart';
2 | import 'package:flutter/widgets.dart';
3 |
4 | import 'package:deep_link_navigation/deep_link_navigation.dart';
5 |
6 | /// Navigates to default route after first build.
7 | class DefaultRouteLoader extends StatefulWidget {
8 | /// The widget that is below this widget in the tree.
9 | ///
10 | /// {@macro flutter.widgets.child}
11 | final Widget child;
12 |
13 | const DefaultRouteLoader({Key key, this.child}) : super(key: key);
14 |
15 | @override
16 | _DefaultRouteLoaderState createState() => _DefaultRouteLoaderState();
17 | }
18 |
19 | class _DefaultRouteLoaderState extends State with AfterLayoutMixin {
20 | @override
21 | void afterFirstLayout(BuildContext context) => DeepLinkNavigator.of(context).replaceWithDefault();
22 |
23 | @override
24 | Widget build(BuildContext context) => widget.child;
25 | }
--------------------------------------------------------------------------------
/examples/single_base_route/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
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 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2019 Dennis Krasnov
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/examples/single_base_route/lib/data.dart:
--------------------------------------------------------------------------------
1 | import 'package:single_base_route/model.dart';
2 |
3 | /// Random placeholder data meant to be replaced with a database.
4 | class Data {
5 | static final artists = {
6 | "1234": Artist(
7 | "1234",
8 | "John Lennon",
9 | [
10 | Song(
11 | "4312",
12 | "1234",
13 | "Yesterday",
14 | ),
15 | Song(
16 | "4313",
17 | "1234",
18 | "Yellow Submarine",
19 | ),
20 | ],
21 | ),
22 | "1235": Artist(
23 | "1235",
24 | "Ludwig van beethoven",
25 | [
26 | Song(
27 | "6363",
28 | "1235",
29 | "Symphony No. 5",
30 | ),
31 | Song(
32 | "6364",
33 | "1235",
34 | "Symphony No. 9",
35 | ),
36 | ],
37 | ),
38 | };
39 |
40 | static final favoriteSongs = [
41 | artists["1234"].songs[0],
42 | artists["1235"].songs[0],
43 | artists["1235"].songs[1],
44 | ];
45 | }
--------------------------------------------------------------------------------
/examples/multiple_base_routes/test_driver/features/bottom_navigation.feature:
--------------------------------------------------------------------------------
1 | Feature: Deep link navigator bottom navigation
2 | Bottom navigation should appear on every screen and control multiple base routes.
3 |
4 | Scenario: Navigation goes to default page once authenticated
5 | Given the title is "Login"
6 | When I tap the "login" button
7 | Then the title is "Library"
8 |
9 | Scenario: Navigating though bottom navigation
10 | Given I tap the "login" button
11 | # Navigate to each screen
12 | When I tap the "favorites" button
13 | Then the title is "My favorites (3)"
14 | When I tap the "user" button
15 | Then the title is "User"
16 | When I tap the "library" button
17 | Then the title is "Library"
18 |
19 | Scenario: Error screen goes full screen
20 | Given I tap the "login" button
21 | When I tap the "Non-existant push" button
22 | Then the title is "ERROR"
23 | # Hide bottom navigation
24 | And the text "Library" disappears
25 | And the text "Favorites" disappears
26 | And the text "User" disappears
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | ## Examples
2 | ### [Single base route](https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/tree/master/examples/single_base_route)
3 | **This example demonstrates:**
4 | * Dispatchers with path-only deep links
5 | * Dispatchers with value deep links (ArtistDL, SongDL)
6 | * Cross-branch navigation (from favorite's song page to artist page)
7 | 
8 |
9 | ### [Multiple base routes](https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/tree/master/examples/multiple_base_routes)
10 | **This example demonstrates:**
11 | * Everything from single base route example
12 | * Bottom navigation (library, favorites, user pages) persists across navigation
13 | * Login and error pages are full screen (hide bottom navigation)
14 | * Using the future result of push in user/authentication page
15 | * Custom `Authenticated` mixin ensures user is authenticated (LibraryDL, FavoritesDL, UserDL)
16 | 
--------------------------------------------------------------------------------
/lib/src/utils.dart:
--------------------------------------------------------------------------------
1 | import 'dart:math' as math;
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | /// Used to compare how many deep links in a route match another route.
6 | int indexOfLastCommonElement(Iterable a, Iterable b) {
7 | int smallerLength = math.min(a.length, b.length);
8 | for (int i = 0; i < smallerLength; i++) {
9 | if (a.elementAt(i) != b.elementAt(i)) {
10 | return i;
11 | }
12 | }
13 | return smallerLength;
14 | }
15 |
16 | /// Page route transition without an animation.
17 | class NoAnimationPageRoute extends MaterialPageRoute {
18 | NoAnimationPageRoute({
19 | @required WidgetBuilder builder,
20 | RouteSettings settings,
21 | bool maintainState = true,
22 | bool fullscreenDialog = false, // TODO makes 'x' icon instead of '<-'
23 | }) : super(
24 | builder: builder,
25 | maintainState: maintainState,
26 | settings: settings,
27 | fullscreenDialog: fullscreenDialog
28 | );
29 |
30 | @override
31 | Widget buildTransitions(BuildContext context, Animation animation, Animation secondaryAnimation, Widget child) => child;
32 | }
--------------------------------------------------------------------------------
/examples/single_base_route/test_driver/features/navigate.feature:
--------------------------------------------------------------------------------
1 | Feature: Deep link navigator direct navigation
2 | Routes should be accessed from any other route.
3 |
4 | Scenario: Navigation from artists' song page to artist page
5 | Given I open the artist "John Lennon"
6 | And I open the song "Yesterday"
7 | And the title is "Yesterday"
8 | When I navigate to the song's artist
9 | Then the title is "John Lennon"
10 | # Once like normal
11 | Then I go back 1 time
12 | And the title is "Library"
13 |
14 | Scenario: Navigation from favorites' song page to artist page
15 | Given I open my favorite songs
16 | And I open the song "Yesterday"
17 | And the title is "Yesterday"
18 | When I navigate to the song's artist
19 | Then the title is "John Lennon"
20 | # Once instead of twice
21 | Then I go back 1 time
22 | And the title is "Library"
23 |
24 | Scenario: Navigation to an unknown route shows error page
25 | Given the title is "Library"
26 | When I tap the "Non-existant navigate" button
27 | Then the title is "ERROR"
28 | And the text "Route not found: [library, song/6363]" appears
29 | Then I go back 1 time
30 | And the title is "Library"
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [1.3.1] - 2019/12/09
2 |
3 | * Breaking change: Changed path (string) to route (List) in configuration
4 | * Breaking change: navigation and subNavigation no longer takes context as argument, no function for path
5 | * Updated documentation and examples
6 |
7 | ## [1.2.1] - 2019/11/16
8 |
9 | * Removed context from exception mapping
10 |
11 | ## [1.1.1] - 2019/11/15
12 |
13 | * Updated configuration syntax to allow passing value deeper in hierarchy
14 | * Added a future return for DeepLinkNavigator.push
15 | * Added promise route for multi_base_routes example, added to UI test
16 | * Refactored and documented project
17 | * Added readme documentation with diagrams and examples
18 |
19 | ## [1.0.1] - 2019/11/14
20 |
21 | * Completely overhauled configuration syntax
22 | * Simplified deep link processing logic
23 | * Generalized exception handling by mapping exceptions to routes
24 | * Remove unnecessary exception dispatchers
25 | * Updated examples with new configuration syntax
26 | * Fixed examples' deep link typing
27 | * Checked in multiple_base_route example configuration files
28 |
29 | ## [0.1.2] - 2019/11/06
30 |
31 | * Made defaultRoute field optional
32 |
33 | ## [0.1.1] - 2019/11/02
34 |
35 | * Initial release
36 |
--------------------------------------------------------------------------------
/examples/single_base_route/lib/widgets/song_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import 'package:single_base_route/data.dart';
5 | import 'package:single_base_route/deep_links.dart';
6 | import 'package:single_base_route/model.dart';
7 |
8 | class SongPage extends StatelessWidget {
9 | final Song song;
10 |
11 | const SongPage({Key key, this.song}) : super(key: key);
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Scaffold(
16 | appBar: AppBar(
17 | title: Text(
18 | song.name,
19 | key: Key("title")
20 | ),
21 | ),
22 | body: Center(
23 | child: Column(
24 | mainAxisAlignment: MainAxisAlignment.center,
25 | children: [
26 | Icon(
27 | Icons.music_video,
28 | size: 200,
29 | ),
30 | RaisedButton(
31 | child: Text("Go to artist"),
32 | onPressed: () => DeepLinkNavigator.of(context).navigateTo([
33 | LibraryDL(),
34 | ArtistDL(Data.artists[song.artistId]),
35 | ]),
36 | ),
37 | ],
38 | ),
39 | ),
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/test_driver/features/promise.feature:
--------------------------------------------------------------------------------
1 | Feature: Deep link navigator push promises
2 | A push should be able to return a future result from a pop.
3 |
4 | Scenario: Cancelling (un)authentication does nothing when already authenticated
5 | Given I tap the "login" button
6 | And I tap the "user" button
7 | And I tap the "authentication chooser" button
8 | And the title is "Authentication"
9 | When I go back 1 time
10 | And I tap the "favorites" button
11 | Then the title is "My favorites (3)"
12 |
13 | Scenario: Authenticating does nothing when already authenticated
14 | Given I tap the "login" button
15 | And I tap the "user" button
16 | And I tap the "authentication chooser" button
17 | And the title is "Authentication"
18 | When I tap the "authenticate" button
19 | And I tap the "favorites" button
20 | Then the title is "My favorites (3)"
21 |
22 | Scenario: Unauthenticating redirects to login screen on navigation attempt
23 | Given I tap the "login" button
24 | And I tap the "user" button
25 | And I tap the "authentication chooser" button
26 | And the title is "Authentication"
27 | When I tap the "unauthenticate" button
28 | And I tap the "favorites" button
29 | Then the title is "Login"
30 |
31 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/widgets/song_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import 'package:multiple_base_routes/data.dart';
5 | import 'package:multiple_base_routes/deep_links.dart';
6 | import 'package:multiple_base_routes/model.dart';
7 |
8 | class SongPage extends StatelessWidget {
9 | final Song song;
10 |
11 | const SongPage({Key key, this.song}) : super(key: key);
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Scaffold(
16 | appBar: AppBar(
17 | title: Text(
18 | song.name,
19 | key: Key("title")
20 | ),
21 | ),
22 | body: Center(
23 | child: Column(
24 | mainAxisAlignment: MainAxisAlignment.center,
25 | children: [
26 | Icon(
27 | Icons.music_video,
28 | size: 200,
29 | ),
30 | RaisedButton(
31 | child: Text("Go to artist"),
32 | onPressed: () => DeepLinkNavigator.of(context).navigateTo([
33 | LibraryDL(),
34 | ArtistDL(Data.artists[song.artistId]),
35 | ]),
36 | ),
37 | ],
38 | ),
39 | ),
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/widgets/authentication_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:provider/provider.dart';
4 |
5 | import 'package:multiple_base_routes/data.dart';
6 |
7 | class AuthenticationPage extends StatelessWidget {
8 | @override
9 | Widget build(BuildContext context) {
10 | final isAuthenticated = Provider.of(context).authenticated;
11 |
12 | return Scaffold(
13 | appBar: AppBar(
14 | title: Text(
15 | "Authentication",
16 | key: Key("title"),
17 | ),
18 | ),
19 | body: Center(
20 | child: Column(
21 | mainAxisAlignment: MainAxisAlignment.center,
22 | children: [
23 | Text("You're currently ${isAuthenticated ? "" : "un"}authenticated."),
24 | SizedBox(height: 10),
25 | RaisedButton(
26 | key: Key("authenticate"),
27 | child: Text("Authenticate"),
28 | onPressed: () => DeepLinkNavigator.of(context).pop(true),
29 | ),
30 | SizedBox(height: 10),
31 | RaisedButton(
32 | key: Key("unauthenticate"),
33 | child: Text("Unuthenticate"),
34 | onPressed: () => DeepLinkNavigator.of(context).pop(false),
35 | ),
36 | ],
37 | ),
38 | ),
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/data.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'package:multiple_base_routes/model.dart';
4 |
5 | /// Dummy state management.
6 | class AuthenticationService with ChangeNotifier {
7 | bool _authenticated = false;
8 | bool get authenticated => _authenticated;
9 |
10 | void login() {
11 | _authenticated = true;
12 | notifyListeners();
13 | }
14 |
15 | void logout() {
16 | _authenticated = false;
17 | notifyListeners();
18 | }
19 | }
20 |
21 | /// Random placeholder data meant to be replaced with a database.
22 | class Data {
23 | static final artists = {
24 | "1234": Artist(
25 | "1234",
26 | "John Lennon",
27 | [
28 | Song(
29 | "4312",
30 | "1234",
31 | "Yesterday",
32 | ),
33 | Song(
34 | "4313",
35 | "1234",
36 | "Yellow Submarine",
37 | ),
38 | ],
39 | ),
40 | "1235": Artist(
41 | "1235",
42 | "Ludwig van beethoven",
43 | [
44 | Song(
45 | "6363",
46 | "1235",
47 | "Symphony No. 5",
48 | ),
49 | Song(
50 | "6364",
51 | "1235",
52 | "Symphony No. 9",
53 | ),
54 | ],
55 | ),
56 | };
57 |
58 | static final favoriteSongs = [
59 | artists["1234"].songs[0],
60 | artists["1235"].songs[0],
61 | artists["1235"].songs[1],
62 | ];
63 | }
--------------------------------------------------------------------------------
/examples/single_base_route/test_driver/features/push_pop.feature:
--------------------------------------------------------------------------------
1 | Feature: Deep link navigator push and pop
2 | Pages should be pushed onto and popped from the navigator stack.
3 |
4 | Scenario: Navigation starts with the default route
5 | Then the title is "Library"
6 |
7 | Scenario: Pushing to and popping from favorites page
8 | Given I open my favorite songs
9 | And the title is "My favorites (3)"
10 | When I go back 1 time
11 | Then the title is "Library"
12 |
13 | Scenario: Pushing to and popping favorites' song page
14 | Given I open my favorite songs
15 | And I open the song "Yesterday"
16 | And the title is "Yesterday"
17 | When I go back 1 time
18 | Then the title is "My favorites (3)"
19 |
20 | Scenario: Pushing to and popping artist page
21 | Given I open the artist "John Lennon"
22 | And the title is "John Lennon"
23 | When I go back 1 time
24 | Then the title is "Library"
25 |
26 | Scenario: Pushing to and popping artist's song page
27 | Given I open the artist "John Lennon"
28 | And I open the song "Yesterday"
29 | And the title is "Yesterday"
30 | When I go back 1 time
31 | Then the title is "John Lennon"
32 |
33 | Scenario: Pushing to an unknown route shows error page
34 | Given the title is "Library"
35 | When I tap the "Non-existant push" button
36 | Then the title is "ERROR"
37 | And the text "Route not found: [library, song/4312]" appears
38 | Then I go back 1 time
39 | And the title is "Library"
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/widgets/user_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:provider/provider.dart';
4 |
5 | import 'package:multiple_base_routes/data.dart';
6 | import 'package:multiple_base_routes/deep_links.dart';
7 |
8 | class UserPage extends StatelessWidget {
9 | @override
10 | Widget build(BuildContext context) {
11 | return Scaffold(
12 | appBar: AppBar(
13 | title: Text(
14 | "User",
15 | key: Key("title"),
16 | ),
17 | ),
18 | body: Center(
19 | child: RaisedButton(
20 | key: Key("authentication chooser"),
21 | child: Text("Choose authentication status"),
22 | onPressed: () async {
23 | final isNowAuthenticated = await DeepLinkNavigator.of(context).push(AuthenticationDL());
24 |
25 | if (isNowAuthenticated != null) {
26 | if (isNowAuthenticated) {
27 | Provider.of(context, listen: false).login();
28 | Scaffold.of(context).showSnackBar(SnackBar(
29 | content: Text("You're already authenticated, nothing should change")
30 | ));
31 | }
32 | else {
33 | Provider.of(context, listen: false).logout();
34 | Scaffold.of(context).showSnackBar(SnackBar(
35 | content: Text("Try navigating to a page that requires authentication")
36 | ));
37 | }
38 | }
39 | },
40 | ),
41 | ),
42 | );
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/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 |
--------------------------------------------------------------------------------
/examples/single_base_route/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 |
--------------------------------------------------------------------------------
/examples/single_base_route/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 | single_base_route
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 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/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 | multiple_base_routes
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 |
--------------------------------------------------------------------------------
/.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 | .packages
28 | .pub-cache/
29 | .pub/
30 | build/
31 |
32 | # Android related
33 | **/android/**/gradle-wrapper.jar
34 | **/android/.gradle
35 | **/android/captures/
36 | **/android/gradlew
37 | **/android/gradlew.bat
38 | **/android/local.properties
39 | **/android/**/GeneratedPluginRegistrant.java
40 |
41 | # iOS/XCode related
42 | **/ios/**/*.mode1v3
43 | **/ios/**/*.mode2v3
44 | **/ios/**/*.moved-aside
45 | **/ios/**/*.pbxuser
46 | **/ios/**/*.perspectivev3
47 | **/ios/**/*sync/
48 | **/ios/**/.sconsign.dblite
49 | **/ios/**/.tags*
50 | **/ios/**/.vagrant/
51 | **/ios/**/DerivedData/
52 | **/ios/**/Icon?
53 | **/ios/**/Pods/
54 | **/ios/**/.symlinks/
55 | **/ios/**/profile
56 | **/ios/**/xcuserdata
57 | **/ios/.generated/
58 | **/ios/Flutter/App.framework
59 | **/ios/Flutter/Flutter.framework
60 | **/ios/Flutter/Generated.xcconfig
61 | **/ios/Flutter/app.flx
62 | **/ios/Flutter/app.zip
63 | **/ios/Flutter/flutter_assets/
64 | **/ios/Flutter/flutter_export_environment.sh
65 | **/ios/ServiceDefinitions.json
66 | **/ios/Runner/GeneratedPluginRegistrant.*
67 |
68 | # Exceptions to above rules.
69 | !**/ios/**/default.mode1v3
70 | !**/ios/**/default.mode2v3
71 | !**/ios/**/default.pbxuser
72 | !**/ios/**/default.perspectivev3
73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
74 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/widgets/library_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import 'package:multiple_base_routes/data.dart';
5 | import 'package:multiple_base_routes/deep_links.dart';
6 |
7 | class LibraryPage extends StatelessWidget {
8 | @override
9 | Widget build(BuildContext context) {
10 | return Scaffold(
11 | appBar: AppBar(
12 | title: Text(
13 | "Library",
14 | key: Key("title"),
15 | ),
16 | ),
17 | body: ListView(
18 | children: [
19 | for (final artist in Data.artists.values)
20 | ListTile(
21 | key: Key(artist.name),
22 | leading: CircleAvatar(child: Icon(Icons.person)),
23 | title: Text(artist.name),
24 | subtitle: Text("${artist.songs.length} songs"),
25 | onTap: () => DeepLinkNavigator.of(context).push(ArtistDL(artist)),
26 | ),
27 | Divider(),
28 | ListTile(
29 | key: Key("Non-existant push"),
30 | leading: CircleAvatar(child: Icon(Icons.bug_report)),
31 | title: Text("Non-existant push"),
32 | // Route doesn't exist
33 | onTap: () => DeepLinkNavigator.of(context).push(SongDL(Data.favoriteSongs[0])),
34 | ),
35 | ListTile(
36 | key: Key("Non-existant navigate"),
37 | leading: CircleAvatar(child: Icon(Icons.bug_report)),
38 | title: Text("Non-existant navigate"),
39 | // Route doesn't exist
40 | onTap: () => DeepLinkNavigator.of(context).navigateTo([LibraryDL(), SongDL(Data.favoriteSongs[1])]),
41 | )
42 | ],
43 | ),
44 | );
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/.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 | .packages
28 | .pub-cache/
29 | .pub/
30 | /build/
31 |
32 | # Android related
33 | **/android/**/gradle-wrapper.jar
34 | **/android/.gradle
35 | **/android/captures/
36 | **/android/gradlew
37 | **/android/gradlew.bat
38 | **/android/local.properties
39 | **/android/**/GeneratedPluginRegistrant.java
40 |
41 | # iOS/XCode related
42 | **/ios/**/*.mode1v3
43 | **/ios/**/*.mode2v3
44 | **/ios/**/*.moved-aside
45 | **/ios/**/*.pbxuser
46 | **/ios/**/*.perspectivev3
47 | **/ios/**/*sync/
48 | **/ios/**/.sconsign.dblite
49 | **/ios/**/.tags*
50 | **/ios/**/.vagrant/
51 | **/ios/**/DerivedData/
52 | **/ios/**/Icon?
53 | **/ios/**/Pods/
54 | **/ios/**/.symlinks/
55 | **/ios/**/profile
56 | **/ios/**/xcuserdata
57 | **/ios/.generated/
58 | **/ios/Flutter/App.framework
59 | **/ios/Flutter/Flutter.framework
60 | **/ios/Flutter/Generated.xcconfig
61 | **/ios/Flutter/app.flx
62 | **/ios/Flutter/app.zip
63 | **/ios/Flutter/flutter_assets/
64 | **/ios/Flutter/flutter_export_environment.sh
65 | **/ios/ServiceDefinitions.json
66 | **/ios/Runner/GeneratedPluginRegistrant.*
67 |
68 | # Exceptions to above rules.
69 | !**/ios/**/default.mode1v3
70 | !**/ios/**/default.mode2v3
71 | !**/ios/**/default.pbxuser
72 | !**/ios/**/default.perspectivev3
73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
74 |
--------------------------------------------------------------------------------
/examples/single_base_route/.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 | .packages
28 | .pub-cache/
29 | .pub/
30 | /build/
31 |
32 | # Android related
33 | **/android/**/gradle-wrapper.jar
34 | **/android/.gradle
35 | **/android/captures/
36 | **/android/gradlew
37 | **/android/gradlew.bat
38 | **/android/local.properties
39 | **/android/**/GeneratedPluginRegistrant.java
40 |
41 | # iOS/XCode related
42 | **/ios/**/*.mode1v3
43 | **/ios/**/*.mode2v3
44 | **/ios/**/*.moved-aside
45 | **/ios/**/*.pbxuser
46 | **/ios/**/*.perspectivev3
47 | **/ios/**/*sync/
48 | **/ios/**/.sconsign.dblite
49 | **/ios/**/.tags*
50 | **/ios/**/.vagrant/
51 | **/ios/**/DerivedData/
52 | **/ios/**/Icon?
53 | **/ios/**/Pods/
54 | **/ios/**/.symlinks/
55 | **/ios/**/profile
56 | **/ios/**/xcuserdata
57 | **/ios/.generated/
58 | **/ios/Flutter/App.framework
59 | **/ios/Flutter/Flutter.framework
60 | **/ios/Flutter/Generated.xcconfig
61 | **/ios/Flutter/app.flx
62 | **/ios/Flutter/app.zip
63 | **/ios/Flutter/flutter_assets/
64 | **/ios/Flutter/flutter_export_environment.sh
65 | **/ios/ServiceDefinitions.json
66 | **/ios/Runner/GeneratedPluginRegistrant.*
67 |
68 | # Exceptions to above rules.
69 | !**/ios/**/default.mode1v3
70 | !**/ios/**/default.mode2v3
71 | !**/ios/**/default.pbxuser
72 | !**/ios/**/default.perspectivev3
73 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
74 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/deep_links.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 | import 'package:flutter/cupertino.dart';
3 |
4 | import 'package:multiple_base_routes/model.dart';
5 | import 'package:provider/provider.dart';
6 |
7 | import 'data.dart';
8 |
9 | /// Custom mixins
10 |
11 | mixin FullScreen on DeepLink {}
12 |
13 | mixin Authenticated on DeepLink {
14 | @override
15 | void onDispatch(BuildContext context) {
16 | // Get state from context or global/static variable
17 | final isAuthenticated = Provider.of(context, listen: false).authenticated;
18 |
19 | // Throw custom exception
20 | if (!isAuthenticated) {
21 | throw Unauthenticated();
22 | }
23 | }
24 | }
25 |
26 | /// Custom exceptions
27 |
28 | class Unauthenticated implements Exception {
29 | @override
30 | String toString() => "Unauthenticated :(";
31 | }
32 |
33 | /// Deep links
34 |
35 | class LoginDL extends DeepLink with FullScreen {
36 | LoginDL() : super("login");
37 | }
38 |
39 | class LibraryDL extends DeepLink with Authenticated {
40 | LibraryDL() : super("library");
41 | }
42 |
43 | class FavoritesDL extends DeepLink with Authenticated {
44 | FavoritesDL() : super("favorites");
45 | }
46 |
47 | class UserDL extends DeepLink with Authenticated {
48 | UserDL() : super("user");
49 | }
50 |
51 | class AuthenticationDL extends DeepLink {
52 | AuthenticationDL() : super("authentication");
53 | }
54 |
55 | class ArtistDL extends ValueDeepLink {
56 | ArtistDL(Artist artist) : super("artist", artist, toString: (artist) => artist.id);
57 | }
58 |
59 | class SongDL extends ValueDeepLink {
60 | SongDL(Song song) : super("song", song, toString: (song) => song.id);
61 | }
62 |
63 | class ErrorDL extends ValueDeepLink with FullScreen {
64 | ErrorDL(E e) : super("error", e);
65 | }
--------------------------------------------------------------------------------
/examples/single_base_route/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
13 |
20 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/lib/src/deep_link.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/widgets.dart';
2 |
3 | /// Base class for all deep links.
4 | ///
5 | /// eg. final deepLink = XYZDeepLink();
6 | @immutable
7 | abstract class DeepLink {
8 | /// Human-readable representation of deep link.
9 | final String path;
10 |
11 | DeepLink(this.path);
12 |
13 | /// Mixins on [DeepLink] may override [onDispatch] to perform validation that throws custom errors.
14 | /// May validate against state accessed through context, or defined as a global/static variable.
15 | /// Does nothing by default.
16 | void onDispatch(BuildContext context) {}
17 |
18 | @override
19 | String toString() => path;
20 | }
21 |
22 | /// Deep link that also stores typed data.
23 | /// May override [toString()] to provide human-readable data representation.
24 | ///
25 | /// eg. final deepLink = XYZValueDeepLink(42);
26 | /// eg. class SongDL extends ValueDeepLink {
27 | /// SongDL(Song song) : super("song", song, toString: (song) => song.id);
28 | /// }
29 | abstract class ValueDeepLink extends DeepLink {
30 | final T data;
31 |
32 | ValueDeepLink(String path, this.data, {String Function(T) toString})
33 | : super("$path/${toString != null ? toString(data) : data}");
34 | }
35 |
36 | /// Value deep link that base64 encodes its [toString] representation.
37 | /// Shouldn't manually override toString().
38 | ///
39 | /// eg. final deepLink = XYZBase64DeepLink(data);
40 | /// TODO: test this deep link
41 | //import 'dart:convert';
42 | //abstract class Base64DeepLink extends ValueDeepLink {
43 | // /// Converts [original] to base64 representation.
44 | // static String _base64Encoded(String original) {
45 | // final bytes = utf8.encode(original);
46 | // return base64.encode(bytes);
47 | // }
48 | //
49 | // Base64DeepLink(String path, String data) : super(path, data, toString: (data) => _base64Encoded(data));
50 | //}
--------------------------------------------------------------------------------
/examples/multiple_base_routes/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
9 |
13 |
20 |
24 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/examples/single_base_route/lib/widgets/library_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import 'package:single_base_route/data.dart';
5 | import 'package:single_base_route/deep_links.dart';
6 |
7 | class LibraryPage extends StatelessWidget {
8 | @override
9 | Widget build(BuildContext context) {
10 | return Scaffold(
11 | appBar: AppBar(
12 | title: Text(
13 | "Library",
14 | key: Key("title"),
15 | ),
16 | actions: [
17 | IconButton(
18 | icon: Icon(Icons.favorite),
19 | onPressed: () {
20 | DeepLinkNavigator.of(context).push(FavoritesDL());
21 | },
22 | )
23 | ],
24 | ),
25 | body: ListView(
26 | children: [
27 | for (final artist in Data.artists.values)
28 | ListTile(
29 | key: Key(artist.name),
30 | leading: CircleAvatar(child: Icon(Icons.person)),
31 | title: Text(artist.name),
32 | subtitle: Text("${artist.songs.length} songs"),
33 | onTap: () => DeepLinkNavigator.of(context).push(ArtistDL(artist)),
34 | ),
35 | Divider(),
36 | ListTile(
37 | key: Key("Non-existant push"),
38 | leading: CircleAvatar(child: Icon(Icons.bug_report)),
39 | title: Text("Non-existant push"),
40 | // Route doesn't exist
41 | onTap: () => DeepLinkNavigator.of(context).push(SongDL(Data.favoriteSongs[0])),
42 | ),
43 | ListTile(
44 | key: Key("Non-existant navigate"),
45 | leading: CircleAvatar(child: Icon(Icons.bug_report)),
46 | title: Text("Non-existant navigate"),
47 | // Route doesn't exist
48 | onTap: () => DeepLinkNavigator.of(context).navigateTo([LibraryDL(), SongDL(Data.favoriteSongs[1])]),
49 | )
50 | ],
51 | ),
52 | );
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/examples/single_base_route/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import 'package:single_base_route/deep_links.dart';
5 | import 'package:single_base_route/model.dart';
6 | import 'package:single_base_route/widgets/artist_page.dart';
7 | import 'package:single_base_route/widgets/error_page.dart';
8 | import 'package:single_base_route/widgets/favorites_page.dart';
9 | import 'package:single_base_route/widgets/library_page.dart';
10 | import 'package:single_base_route/widgets/song_page.dart';
11 | import 'package:single_base_route/widgets/splash_page.dart';
12 |
13 | void main() => runApp(MusicApp());
14 |
15 | class MusicApp extends StatelessWidget {
16 | @override
17 | Widget build(BuildContext context) => DeepLinkMaterialApp(
18 | // This is where the magic happens
19 | navigation: Dispatcher()
20 | ..path(
21 | (route) => LibraryPage(),
22 | subNavigation: Dispatcher()
23 | ..value(
24 | (artist, route) => ArtistPage(artist: artist),
25 | subNavigation: (artist) => Dispatcher()
26 | ..song()
27 | )
28 | ..path(
29 | (route) => FavoritesPage(),
30 | subNavigation: Dispatcher()
31 | ..song()
32 | )
33 | ..value>((exception, route) => ErrorPage(exception)),
34 | )
35 | // Exception handling mappings and route dispatchers are specified independently
36 | ..exception((exception, route) => [LibraryDL(), ErrorDL(exception)]),
37 | defaultRoute: [LibraryDL()],
38 | splashScreen: SplashPage(),
39 | // Non-navigation related fields are still available
40 | themeMode: ThemeMode.light,
41 | );
42 | }
43 |
44 | /// Reusing code through static extension methods.
45 | extension DispatcherExtensions on Dispatcher {
46 | void song() => value((song, route) => SongPage(song: song));
47 | }
--------------------------------------------------------------------------------
/example/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | after_layout:
5 | dependency: transitive
6 | description:
7 | name: after_layout
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "1.0.7+2"
11 | collection:
12 | dependency: transitive
13 | description:
14 | name: collection
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "1.14.11"
18 | deep_link_navigation:
19 | dependency: "direct main"
20 | description:
21 | path: ".."
22 | relative: true
23 | source: path
24 | version: "1.3.1"
25 | flutter:
26 | dependency: transitive
27 | description: flutter
28 | source: sdk
29 | version: "0.0.0"
30 | merge_map:
31 | dependency: transitive
32 | description:
33 | name: merge_map
34 | url: "https://pub.dartlang.org"
35 | source: hosted
36 | version: "1.0.2"
37 | meta:
38 | dependency: transitive
39 | description:
40 | name: meta
41 | url: "https://pub.dartlang.org"
42 | source: hosted
43 | version: "1.1.8"
44 | nested:
45 | dependency: transitive
46 | description:
47 | name: nested
48 | url: "https://pub.dartlang.org"
49 | source: hosted
50 | version: "0.0.4"
51 | provider:
52 | dependency: transitive
53 | description:
54 | name: provider
55 | url: "https://pub.dartlang.org"
56 | source: hosted
57 | version: "4.0.1"
58 | sky_engine:
59 | dependency: transitive
60 | description: flutter
61 | source: sdk
62 | version: "0.0.99"
63 | typed_data:
64 | dependency: transitive
65 | description:
66 | name: typed_data
67 | url: "https://pub.dartlang.org"
68 | source: hosted
69 | version: "1.1.6"
70 | vector_math:
71 | dependency: transitive
72 | description:
73 | name: vector_math
74 | url: "https://pub.dartlang.org"
75 | source: hosted
76 | version: "2.0.8"
77 | sdks:
78 | dart: ">=2.2.2 <3.0.0"
79 | flutter: ">=1.12.1"
80 |
--------------------------------------------------------------------------------
/examples/single_base_route/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 28
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 |
39 | defaultConfig {
40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
41 | applicationId "dev.krasnov.single_base_route"
42 | minSdkVersion 16
43 | targetSdkVersion 28
44 | versionCode flutterVersionCode.toInteger()
45 | versionName flutterVersionName
46 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
47 | }
48 |
49 | buildTypes {
50 | release {
51 | // TODO: Add your own signing config for the release build.
52 | // Signing with the debug keys for now, so `flutter run --release` works.
53 | signingConfig signingConfigs.debug
54 | }
55 | }
56 | }
57 |
58 | flutter {
59 | source '../..'
60 | }
61 |
62 | dependencies {
63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
64 | testImplementation 'junit:junit:4.12'
65 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
66 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
67 | }
68 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/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 28
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 |
39 | defaultConfig {
40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
41 | applicationId "dev.krasnov.multiple_base_routes"
42 | minSdkVersion 16
43 | targetSdkVersion 28
44 | versionCode flutterVersionCode.toInteger()
45 | versionName flutterVersionName
46 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
47 | }
48 |
49 | buildTypes {
50 | release {
51 | // TODO: Add your own signing config for the release build.
52 | // Signing with the debug keys for now, so `flutter run --release` works.
53 | signingConfig signingConfigs.debug
54 | }
55 | }
56 | }
57 |
58 | flutter {
59 | source '../..'
60 | }
61 |
62 | dependencies {
63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
64 | testImplementation 'junit:junit:4.12'
65 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
66 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
67 | }
68 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/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 |
--------------------------------------------------------------------------------
/examples/single_base_route/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 |
--------------------------------------------------------------------------------
/lib/src/dispatchers.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/widgets.dart';
2 |
3 | import 'package:deep_link_navigation/src/deep_link.dart';
4 |
5 | /// Widget for route.
6 | typedef PathBuilder = Widget Function(List route);
7 |
8 | /// Widget for route with [value].
9 | typedef ValueBuilder = Widget Function(T value, List route);
10 |
11 | /// Route mapping for [exception].
12 | typedef ErrorMapping = List Function(Exception exception, List route);
13 |
14 | /// Dispatcher for this level of navigation with [value].
15 | typedef NavigationValueBuilder = Dispatcher Function(T value);
16 |
17 | /// A non-leaf node in the navigation hierarchy tree.
18 | class Dispatcher {
19 | /// Internal representation of widget builders.
20 | /// Values must be dynamic since function parameter types don't upcast.
21 | /// Ideally, the value type would be `ValueBuilder`.
22 | Map _routeBuilders = {};
23 | Map get routeBuilders => _routeBuilders;
24 |
25 | /// Internal representation of error mappings.
26 | Map _errorMappers = {};
27 | Map get errorMappers => _errorMappers;
28 |
29 | /// Internal representation of deep link navigation hierarchy.
30 | /// Ideally the value type would be `NavigationValueBuilder`.
31 | Map _subNavigations = {};
32 | Map get subNavigations => _subNavigations;
33 |
34 | /// Add a path widget builder to this level of hierarchy.
35 | void path(
36 | PathBuilder builder,
37 | {Dispatcher subNavigation}
38 | ) {
39 | assert(DL != dynamic, "A deep link type must be specified.");
40 | assert(!routeBuilders.containsKey(DL), "A widget builder for ${DL.runtimeType} has already beed defined.");
41 | assert(builder != null);
42 |
43 | _routeBuilders[DL] = (_, route) => builder(route);
44 |
45 | if (subNavigation != null) {
46 | _subNavigations[DL] = (_) => subNavigation;
47 | }
48 | }
49 |
50 | /// Add a value widget builder to this level of hierarchy.
51 | void value>(
52 | ValueBuilder builder,
53 | {NavigationValueBuilder subNavigation}
54 | ) {
55 | assert(T != dynamic, "Data type must be specified.");
56 | assert(DL != dynamic, "A deep link type must be specified.");
57 | assert(!routeBuilders.containsKey(DL), "A widget builder for ${DL.runtimeType} has already beed defined.");
58 | assert(builder != null);
59 |
60 | _routeBuilders[DL] = builder;
61 |
62 | if (subNavigation != null) {
63 | _subNavigations[DL] = subNavigation;
64 | }
65 | }
66 |
67 | /// Add a exception mapping to this level of hierarchy.
68 | void exception(ErrorMapping mapper) {
69 | assert(E != dynamic, "An error type must be specified.");
70 | assert(!routeBuilders.containsKey(E), "An error mapping for ${E.runtimeType} has already beed defined.");
71 | assert(mapper != null);
72 |
73 | _errorMappers[E] = mapper;
74 | }
75 | }
--------------------------------------------------------------------------------
/examples/multiple_base_routes/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 |
--------------------------------------------------------------------------------
/examples/single_base_route/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 |
--------------------------------------------------------------------------------
/examples/single_base_route/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 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/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 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:deep_link_navigation/deep_link_navigation.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:provider/provider.dart';
4 |
5 | import 'package:multiple_base_routes/data.dart';
6 | import 'package:multiple_base_routes/deep_links.dart';
7 | import 'package:multiple_base_routes/model.dart';
8 | import 'package:multiple_base_routes/widgets/artist_page.dart';
9 | import 'package:multiple_base_routes/widgets/authentication_page.dart';
10 | import 'package:multiple_base_routes/widgets/error_page.dart';
11 | import 'package:multiple_base_routes/widgets/favorites_page.dart';
12 | import 'package:multiple_base_routes/widgets/library_page.dart';
13 | import 'package:multiple_base_routes/widgets/login_page.dart';
14 | import 'package:multiple_base_routes/widgets/song_page.dart';
15 | import 'package:multiple_base_routes/widgets/splash_page.dart';
16 | import 'package:multiple_base_routes/widgets/user_page.dart';
17 |
18 | void main() => runApp(
19 | // State management of your choice
20 | ChangeNotifierProvider(
21 | create: (_) => AuthenticationService(),
22 | child: MusicApp(),
23 | )
24 | );
25 |
26 | /// [DeepLink]s associated with the bottom navigation.
27 | final bottomNavigationDeepLinks = [LibraryDL(), FavoritesDL(), UserDL()];
28 |
29 | /// Current index of bottom navigation based on [currentRoute].
30 | int currentIndex(List currentRoute) {
31 | final index = bottomNavigationDeepLinks.indexOf(currentRoute?.first);
32 | return index != -1 ? index : 0;
33 | }
34 |
35 | class MusicApp extends StatelessWidget {
36 | @override
37 | Widget build(BuildContext context) => DeepLinkMaterialApp(
38 | // This is where the magic happens
39 | navigation: Dispatcher()
40 | // RouteNotFound error page
41 | ..exception((exception, route) => [ErrorDL(exception)])
42 | ..value>((exception, route) => ErrorPage(exception))
43 | // Unauthenticated login page
44 | ..exception((exception, route) => [LoginDL()])
45 | ..path((path) => LoginPage())
46 | // The rest of the app
47 | ..path(
48 | (route) => LibraryPage(),
49 | subNavigation: Dispatcher()
50 | ..value(
51 | (artist, route) => ArtistPage(artist: artist),
52 | subNavigation: (artist) => Dispatcher()
53 | ..song()
54 | ),
55 | )
56 | ..path(
57 | (route) => FavoritesPage(),
58 | subNavigation: Dispatcher()
59 | ..song()
60 | )
61 | ..path(
62 | (route) => UserPage(),
63 | subNavigation: Dispatcher()..path((route) => AuthenticationPage()),
64 | ),
65 | defaultRoute: [LibraryDL()],
66 | splashScreen: SplashPage(),
67 | // Optionally specify always visible widgets
68 | childBuilder: (BuildContext context, DeepLinkNavigator deepLinkNavigator, Widget child) => Scaffold(
69 | body: child,
70 | // Don't show bottom navigation while [currentRoute] is null, or any deep list is [FullScreen]
71 | bottomNavigationBar: deepLinkNavigator.currentRoute?.any((dl) => dl is FullScreen) ?? true ? null : BottomNavigationBar(
72 | currentIndex: currentIndex(deepLinkNavigator.currentRoute),
73 | onTap: (int index) => deepLinkNavigator.navigateTo([bottomNavigationDeepLinks[index]]),
74 | items: [
75 | BottomNavigationBarItem(title: Text("Library"), icon: Icon(Icons.queue_music, key: Key("library"))),
76 | BottomNavigationBarItem(title: Text("Favorites"), icon: Icon(Icons.favorite, key: Key("favorites"))),
77 | BottomNavigationBarItem(title: Text("User"), icon: Icon(Icons.person, key: Key("user"))),
78 | ],
79 | ),
80 | ),
81 | // Non-navigation related fields are still available
82 | themeMode: ThemeMode.light,
83 | );
84 | }
85 |
86 | /// Reusing code through static extension methods.
87 | extension DispatcherExtensions on Dispatcher {
88 | void song() => value((song, route) => SongPage(song: song));
89 | }
--------------------------------------------------------------------------------
/lib/src/deep_link_material_app.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/cupertino.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:provider/provider.dart';
4 |
5 | import 'package:deep_link_navigation/deep_link_navigation.dart';
6 | import 'package:deep_link_navigation/src/default_page_loader.dart';
7 | import 'package:deep_link_navigation/src/pop_observer.dart';
8 |
9 | // TODO: create versions for material, ios, etc
10 |
11 | /// Extension of [MaterialApp] adds deep-linking functionality by orchestrating a native flutter navigator.
12 | class DeepLinkMaterialApp extends MaterialApp {
13 | /// Internal instance of a native flutter Navigator.
14 | static final _navigatorKey = GlobalKey();
15 |
16 | /// Top-level dispatcher to represent deep link navigation hierarchy.
17 | final Dispatcher navigation;
18 |
19 | /// {@macro flutter.widgets.widgetsApp.builder}
20 | ///
21 | /// Material specific features such as [showDialog] and [showMenu], and widgets
22 | /// such as [Tooltip], [PopupMenuButton], also require a [Navigator] to properly
23 | /// function.
24 | ///
25 | /// Efficiently rebuilds sandwiched widgets on every meaningful change to [DeepLinkNavigator].
26 | final Widget Function(BuildContext context, DeepLinkNavigator deepLinkNavigator, Widget child) childBuilder;
27 |
28 | /// Widget to display while waiting for first build to complete.
29 | /// Defaults to empty container.
30 | final Widget splashScreen;
31 |
32 | /// Initial route of application.
33 | /// [RouteNotFound] exception defaults back to this route.
34 | /// If null, must manually navigate from the splash screen.
35 | /// If specified, must contain at least one deep link.
36 | final List defaultRoute;
37 |
38 | /// Creates a MaterialApp.
39 | ///
40 | /// At least one of [home], [routes], [onGenerateRoute], or [builder] must be
41 | /// non-null. If only [routes] is given, it must include an entry for the
42 | /// [Navigator.defaultRouteName] (`/`), since that is the route used when the
43 | /// application is launched with an intent that specifies an otherwise
44 | /// unsupported route.
45 | ///
46 | /// This class creates an instance of [WidgetsApp].
47 | ///
48 | /// The boolean arguments, [routes], and [navigatorObservers], must not be null.
49 | DeepLinkMaterialApp({
50 | Key key,
51 | // GlobalKey navigatorKey,
52 | List navigatorObservers = const [],
53 | String title = '',
54 | GenerateAppTitle onGenerateTitle,
55 | Color color,
56 | ThemeData theme,
57 | ThemeData darkTheme,
58 | ThemeMode themeMode = ThemeMode.system,
59 | Locale locale,
60 | Iterable> localizationsDelegates,
61 | LocaleListResolutionCallback localeListResolutionCallback,
62 | LocaleResolutionCallback localeResolutionCallback,
63 | Iterable supportedLocales = const [Locale('en', 'US')],
64 | bool debugShowMaterialGrid = false,
65 | bool showPerformanceOverlay = false,
66 | bool checkerboardRasterCacheImages = false,
67 | bool checkerboardOffscreenLayers = false,
68 | bool showSemanticsDebugger = false,
69 | bool debugShowCheckedModeBanner = true,
70 | // Custom fields
71 | @required this.navigation,
72 | this.childBuilder,
73 | this.splashScreen,
74 | this.defaultRoute,
75 | }) : super(
76 | key: key,
77 | navigatorKey: _navigatorKey,
78 | home: splashScreen ?? Container(),
79 | navigatorObservers: [
80 | PopObserver(),
81 | ...navigatorObservers
82 | ],
83 | // Provider for easy access through ListenableProvider.of(context) and rebuild capabilities
84 | builder: (BuildContext context, Widget child) => ListenableProvider(
85 | create: (BuildContext context) => DeepLinkNavigator(
86 | navigatorKey: _navigatorKey,
87 | navigation: navigation,
88 | defaultRoute: defaultRoute,
89 | ),
90 | dispose: (BuildContext context, DeepLinkNavigator value) => null,
91 | // Inline extraction of [DeepLinkNavigator] instance
92 | child: Consumer(
93 | builder: (BuildContext context, DeepLinkNavigator value, Widget child) => childBuilder != null
94 | ? childBuilder(context, value, DefaultRouteLoader(child: child))
95 | : DefaultRouteLoader(child: child),
96 | child: child,
97 | ),
98 | ),
99 | title: title,
100 | onGenerateTitle: onGenerateTitle,
101 | color: color,
102 | theme: theme,
103 | darkTheme: darkTheme,
104 | themeMode: themeMode,
105 | locale: locale,
106 | localizationsDelegates: localizationsDelegates,
107 | localeListResolutionCallback: localeListResolutionCallback,
108 | localeResolutionCallback: localeResolutionCallback,
109 | supportedLocales: supportedLocales,
110 | debugShowMaterialGrid: debugShowMaterialGrid,
111 | showPerformanceOverlay: showPerformanceOverlay,
112 | checkerboardRasterCacheImages: checkerboardRasterCacheImages,
113 | checkerboardOffscreenLayers: checkerboardOffscreenLayers,
114 | showSemanticsDebugger: showSemanticsDebugger,
115 | debugShowCheckedModeBanner: debugShowCheckedModeBanner,
116 | );
117 | }
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | after_layout:
5 | dependency: "direct main"
6 | description:
7 | name: after_layout
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "1.0.7+2"
11 | archive:
12 | dependency: transitive
13 | description:
14 | name: archive
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.0.11"
18 | args:
19 | dependency: transitive
20 | description:
21 | name: args
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "1.5.2"
25 | async:
26 | dependency: transitive
27 | description:
28 | name: async
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "2.4.0"
32 | boolean_selector:
33 | dependency: transitive
34 | description:
35 | name: boolean_selector
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "1.0.5"
39 | charcode:
40 | dependency: transitive
41 | description:
42 | name: charcode
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.1.2"
46 | collection:
47 | dependency: transitive
48 | description:
49 | name: collection
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.14.11"
53 | convert:
54 | dependency: transitive
55 | description:
56 | name: convert
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "2.1.1"
60 | crypto:
61 | dependency: transitive
62 | description:
63 | name: crypto
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "2.1.3"
67 | flutter:
68 | dependency: "direct main"
69 | description: flutter
70 | source: sdk
71 | version: "0.0.0"
72 | flutter_test:
73 | dependency: "direct dev"
74 | description: flutter
75 | source: sdk
76 | version: "0.0.0"
77 | image:
78 | dependency: transitive
79 | description:
80 | name: image
81 | url: "https://pub.dartlang.org"
82 | source: hosted
83 | version: "2.1.4"
84 | matcher:
85 | dependency: transitive
86 | description:
87 | name: matcher
88 | url: "https://pub.dartlang.org"
89 | source: hosted
90 | version: "0.12.6"
91 | merge_map:
92 | dependency: "direct main"
93 | description:
94 | name: merge_map
95 | url: "https://pub.dartlang.org"
96 | source: hosted
97 | version: "1.0.2"
98 | meta:
99 | dependency: transitive
100 | description:
101 | name: meta
102 | url: "https://pub.dartlang.org"
103 | source: hosted
104 | version: "1.1.8"
105 | nested:
106 | dependency: transitive
107 | description:
108 | name: nested
109 | url: "https://pub.dartlang.org"
110 | source: hosted
111 | version: "0.0.4"
112 | path:
113 | dependency: transitive
114 | description:
115 | name: path
116 | url: "https://pub.dartlang.org"
117 | source: hosted
118 | version: "1.6.4"
119 | pedantic:
120 | dependency: transitive
121 | description:
122 | name: pedantic
123 | url: "https://pub.dartlang.org"
124 | source: hosted
125 | version: "1.8.0+1"
126 | petitparser:
127 | dependency: transitive
128 | description:
129 | name: petitparser
130 | url: "https://pub.dartlang.org"
131 | source: hosted
132 | version: "2.4.0"
133 | provider:
134 | dependency: "direct main"
135 | description:
136 | name: provider
137 | url: "https://pub.dartlang.org"
138 | source: hosted
139 | version: "4.0.1"
140 | quiver:
141 | dependency: transitive
142 | description:
143 | name: quiver
144 | url: "https://pub.dartlang.org"
145 | source: hosted
146 | version: "2.0.5"
147 | sky_engine:
148 | dependency: transitive
149 | description: flutter
150 | source: sdk
151 | version: "0.0.99"
152 | source_span:
153 | dependency: transitive
154 | description:
155 | name: source_span
156 | url: "https://pub.dartlang.org"
157 | source: hosted
158 | version: "1.5.5"
159 | stack_trace:
160 | dependency: transitive
161 | description:
162 | name: stack_trace
163 | url: "https://pub.dartlang.org"
164 | source: hosted
165 | version: "1.9.3"
166 | stream_channel:
167 | dependency: transitive
168 | description:
169 | name: stream_channel
170 | url: "https://pub.dartlang.org"
171 | source: hosted
172 | version: "2.0.0"
173 | string_scanner:
174 | dependency: transitive
175 | description:
176 | name: string_scanner
177 | url: "https://pub.dartlang.org"
178 | source: hosted
179 | version: "1.0.5"
180 | term_glyph:
181 | dependency: transitive
182 | description:
183 | name: term_glyph
184 | url: "https://pub.dartlang.org"
185 | source: hosted
186 | version: "1.1.0"
187 | test_api:
188 | dependency: transitive
189 | description:
190 | name: test_api
191 | url: "https://pub.dartlang.org"
192 | source: hosted
193 | version: "0.2.11"
194 | typed_data:
195 | dependency: transitive
196 | description:
197 | name: typed_data
198 | url: "https://pub.dartlang.org"
199 | source: hosted
200 | version: "1.1.6"
201 | vector_math:
202 | dependency: transitive
203 | description:
204 | name: vector_math
205 | url: "https://pub.dartlang.org"
206 | source: hosted
207 | version: "2.0.8"
208 | xml:
209 | dependency: transitive
210 | description:
211 | name: xml
212 | url: "https://pub.dartlang.org"
213 | source: hosted
214 | version: "3.5.0"
215 | sdks:
216 | dart: ">=2.4.0 <3.0.0"
217 | flutter: ">=1.12.1"
218 |
--------------------------------------------------------------------------------
/lib/src/deep_link_navigator.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 |
3 | import 'package:flutter/material.dart';
4 | import 'package:merge_map/merge_map.dart';
5 | import 'package:provider/provider.dart' show Provider;
6 |
7 | import 'package:deep_link_navigation/deep_link_navigation.dart';
8 | import 'package:deep_link_navigation/src/utils.dart';
9 | import 'package:deep_link_navigation/src/dispatchers.dart';
10 |
11 | /// Internal state which orchestrates native navigator though [navigatorKey].
12 | class DeepLinkNavigator with ChangeNotifier {
13 | /// {@macro flutter.widgets.widgetsApp.navigatorKey}
14 | final GlobalKey navigatorKey;
15 |
16 | /// Top-level dispatcher to represent deep link navigation hierarchy.
17 | final Dispatcher navigation;
18 |
19 | /// Initial route of application.
20 | /// [RouteNotFound] exception defaults back to this route.
21 | /// If null, must manually navigate from the splash screen.
22 | /// If specified, must contain at least one deep link.
23 | final List defaultRoute;
24 |
25 | /// Current location within navigation hierarchy.
26 | List _currentRoute;
27 | List get currentRoute => _currentRoute;
28 |
29 | /// Previous location within navigation hierarchy.
30 | List _previousRoute;
31 | List get previousRoute => _previousRoute;
32 |
33 | /// Only return promises and animate transitions when pushing.
34 | bool _actionWasPush = false;
35 | bool get actionWasPush => _actionWasPush;
36 |
37 | /// Whether currently handling pops for a deep link navigation.
38 | /// Typical pop handling should be temporarily disabled.
39 | bool _poppingForDeepNavigation = false;
40 |
41 | DeepLinkNavigator({
42 | @required this.navigatorKey,
43 | @required this.navigation,
44 | this.defaultRoute
45 | }) :
46 | assert(navigation != null),
47 | assert(defaultRoute == null || defaultRoute.isNotEmpty);
48 |
49 | /// Handle a significant change of the current route.
50 | /// Optionally handles [promise] for pushes.
51 | @optionalTypeArgs
52 | Future _handleRouteChanged([Future promise]) {
53 | handlePop();
54 |
55 | // Mutable route built up level-by-level
56 | var accumulatedRoute = [];
57 |
58 | // Deep link dispatcher at the next level of navigation hierarchy
59 | var nextDispatcher = navigation;
60 |
61 | // Whether this is the last level of navigation hierarchy
62 | // This doesn't affect the base (first level) navigation
63 | var endOfNavigationHierarchy = false;
64 |
65 | // Mutable exception handling uses deepest level possible
66 | var errors = {};
67 |
68 | try {
69 | // Trace each deep link to reach current route
70 | for (final deepLink in currentRoute) {
71 | // Throw if further navigation can't be found
72 | if (endOfNavigationHierarchy) {
73 | throw(RouteNotFound(currentRoute));
74 | }
75 |
76 | // Deep link dispatcher at this level of navigation hierarchy
77 | final currentDispatcher = nextDispatcher;
78 | assert(currentDispatcher.routeBuilders.isNotEmpty);
79 |
80 | // Attempt to go deeper in navigation
81 | if (currentDispatcher.subNavigations.containsKey(deepLink.runtimeType)) {
82 | nextDispatcher = currentDispatcher.subNavigations[deepLink.runtimeType](deepLink is ValueDeepLink ? deepLink.data : null);
83 | assert(nextDispatcher != null);
84 | }
85 | else {
86 | endOfNavigationHierarchy = true;
87 | }
88 |
89 | // Merge and override error mappings
90 | errors = mergeMap([errors, currentDispatcher.errorMappers]);
91 | assert(errors.containsKey(RouteNotFound), "Must specify `exception` on base navigation builder.");
92 |
93 | // Attempt to find deep link
94 | try {
95 | if (!currentDispatcher.routeBuilders.containsKey(deepLink.runtimeType)) {
96 | throw(RouteNotFound(currentRoute));
97 | }
98 |
99 | // Custom logic can throw custom exceptions
100 | deepLink.onDispatch(navigatorKey.currentContext);
101 | } catch (_) {
102 | // Set current route to represent real navigator state on failure
103 | // Otherwise, handleRouteChanged would pop too many times
104 | _currentRoute = accumulatedRoute;
105 | rethrow;
106 | }
107 |
108 | // Append deep link to accumulated route
109 | accumulatedRoute.add(deepLink);
110 |
111 | // Only return push promise on last deep link
112 | if (actionWasPush && deepLink == currentRoute.last) {
113 | return _pushNavigatorIfNecessary(deepLink, currentDispatcher, accumulatedRoute);
114 | }
115 | _pushNavigatorIfNecessary(deepLink, currentDispatcher, accumulatedRoute);
116 | }
117 | } on RouteNotFound catch(exception) {
118 | final route = errors[exception.runtimeType](exception, List.from(accumulatedRoute));
119 | assert(route != null && route.isNotEmpty);
120 |
121 | navigateTo(route);
122 | } on Exception catch(exception) {
123 | if (!errors.containsKey(exception.runtimeType)) rethrow;
124 |
125 | final route = errors[exception.runtimeType](exception, List.from(accumulatedRoute));
126 | assert(route != null && route.isNotEmpty);
127 |
128 | navigateTo(route);
129 | }
130 | return null;
131 | }
132 |
133 | /// Pops until lowest common ancestor route.
134 | void handlePop() {
135 | _poppingForDeepNavigation = true;
136 | var _timesDidNotPop = 0;
137 | for (final deepLink in previousRoute.sublist(commonElementsInRoutes).reversed) {
138 | // Allow base routes to be conceptually popped once since they're replaced instead of pushed
139 | if (navigatorKey.currentState.canPop()) {
140 | navigatorKey.currentState.pop();
141 | } else assert(++_timesDidNotPop <= 1, "$deepLink caused a second pop to a base route");
142 | }
143 | _poppingForDeepNavigation = false;
144 | }
145 |
146 | /// Pushes widgets for deep links for deep links that didn't exist in previous route.
147 | @optionalTypeArgs
148 | Future _pushNavigatorIfNecessary(DeepLink deepLink, Dispatcher currentDispatcher, List accumulatedRoute) {
149 | if (previousRoute.isEmpty || currentRoute.sublist(commonElementsInRoutes).contains(deepLink)) {
150 | // Widget that corresponds with deep link
151 | final widget = currentDispatcher.routeBuilders[deepLink.runtimeType](
152 | deepLink is ValueDeepLink ? deepLink.data : null,
153 | accumulatedRoute,
154 | );
155 | assert(widget != null);
156 |
157 | // Animate only push (and thus pop) transitions
158 | final pageRoute = actionWasPush
159 | ? MaterialPageRoute(builder: (BuildContext context) => widget)
160 | : NoAnimationPageRoute(builder: (BuildContext context) => widget);
161 |
162 | // Replace root level pages
163 | final action = accumulatedRoute.length == 1
164 | ? (pageRoute) => navigatorKey.currentState.pushReplacement(pageRoute)
165 | : (pageRoute) => navigatorKey.currentState.push(pageRoute);
166 |
167 | // Reuse native navigator's completer
168 | return action(pageRoute);
169 | }
170 | return null;
171 | }
172 |
173 | /// Number of common deep links in [previousRoute] and [currentRoute].
174 | int get commonElementsInRoutes => indexOfLastCommonElement(previousRoute ?? [], currentRoute);
175 |
176 | /// Pushes the given [deepLink] onto the navigator.
177 | /// Optionally returns a [T] value.
178 | @optionalTypeArgs
179 | Future push(DeepLink deepLink) async {
180 | _previousRoute = List.from(currentRoute);
181 | currentRoute.add(deepLink);
182 | _actionWasPush = true;
183 |
184 | notifyListeners();
185 | return _handleRouteChanged();
186 | }
187 |
188 | /// Navigates to a specific route anywhere in the navigation hierarchy.
189 | void navigateTo(List route) {
190 | _previousRoute = List.from(currentRoute ?? []);
191 | _currentRoute = List.from(route);
192 | _actionWasPush = false;
193 |
194 | notifyListeners();
195 | _handleRouteChanged();
196 | }
197 |
198 | /// Resets navigation to user's default page.
199 | /// Does nothing if [defaultRoute] is null.
200 | void replaceWithDefault() {
201 | if (defaultRoute != null) {
202 | navigateTo(defaultRoute);
203 | }
204 | }
205 |
206 | /// Pop the top-most deep link off the navigator that most tightly encloses the given context.
207 | @optionalTypeArgs
208 | bool pop([T result]) => navigatorKey.currentState.pop(result);
209 |
210 | /// Handles deep link popping logic.
211 | /// Notified exclusively from native navigator's [PopObserver].
212 | void notifyPopped() {
213 | // Only processes if not currently popping routes for [navigateTo].
214 | if (!_poppingForDeepNavigation) {
215 | _previousRoute = List.from(currentRoute);
216 | currentRoute.removeLast();
217 |
218 | notifyListeners();
219 | }
220 | }
221 |
222 | /// Method that allows widgets to access [DeepLinkNavigator] as their `BuildContext`
223 | /// contains a [DeepLinkNavigator] instance.
224 | ///
225 | /// TODO: other examples using dart code notation
226 | /// ```dart
227 | /// DeepLinkNavigator.of(context);
228 | /// ```
229 | static DeepLinkNavigator of(BuildContext context) {
230 | try {
231 | return Provider.of(context, listen: false);
232 | } on Object catch (_) {
233 | throw FlutterError(
234 | """
235 | DeepLinkNavigator.of() called with a context that does not contain a DeepLinkNavigator.
236 | No ancestor could be found starting from the context that was passed to DeepLinkNavigator.of().
237 | This can happen if the context you used comes from a widget above the DeepLinkRouter.
238 | """,
239 | );
240 | }
241 | }
242 |
243 | /// URL-friendly representation of current route.
244 | @override
245 | String toString() => currentRoute.join("/");
246 | }
247 |
248 | // TODO: add on google analytics listener on callback 'routeChanged'
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Flutter Deep Link Navigation
2 |
3 | [](https://pub.dev/packages/deep_link_navigation)
4 | [](https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation)
5 | [](https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/blob/master/LICENSE)
6 | [](https://github.com/Solido/awesome-flutter#navigation)
7 |
8 |
9 |
10 |
11 |
12 |
13 | Provides an elegant abstraction for complete deep linking navigation in Flutter.
14 |
15 | This package **only provides deep linking for internal navigation**. Any external platform-level deep linking solution can optionally be used in conjuction with this package.
16 |
17 | The target audience of the documentation is experienced Flutter developers.
18 |
19 | ## Motivation
20 | There's nothing wrong with not using deep links for internal navigation.
21 |
22 | Partially implementing deep links would either have **limited benefits** or be **extremely complicated** (not to mention confusing).
23 |
24 | Hence why if you decide to use deep links, it makes sense to exclusively use deep links.
25 |
26 | **If you try to implement complete deep linking yourself here's some of the issues you'll run into:**
27 | * Widgets become coupled with their place in the navigation hierarchy
28 | - Becomes an issue if you want to reuse a page
29 | - No amount of fancy iteration or recursion will help
30 | * It's difficult to 'pass down' deep link values used in higher levels
31 | - Given the hierarchy is `Artist` --> `Song`
32 | - Being able to do `(artist) => ArtistPage(artist)` and later `(song) => SongPage(artist, song)`
33 | - I actually published a [rest-like router package](https://pub.dev/packages/rest_router) while trying to solve this issue
34 | * How do you represent deep links internally?
35 | - How to keep configuration as terse as possible?
36 | - How to represent current route in a string-friendly format (eg. for analytics, debugging)
37 | - How to serialize route to be used with a platform-level deep linking solution
38 | - How to handle native navigator pops (eg. back button)?
39 | - How to handle a route that doesn't exist (or any other exception that occurs during dispatch)?
40 | * How do you integrate custom logic for validating deep link navigation?
41 | - Ideally provide context to be able to access state management
42 | - eg. certain subtrees of the navigation hierarchy are available only for subscribed or authenticated users
43 |
44 | **TL;DR**
45 |
46 | I separated the navigation system from [Diet Driven](https://github.com/Dennis-Krasnov/Diet-Driven) (shameless plug, [please hire me](https://denniskrasnov.com/)) into its own package and published it.
47 |
48 | This package provides a solution for all the aforementioned difficulties.
49 |
50 | ## Examples
51 | ### [Single base route](https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/tree/master/examples/single_base_route)
52 | **This example demonstrates:**
53 | * Dispatchers with path-only deep links
54 | * Dispatchers with value deep links (ArtistDL, SongDL)
55 | * Exception handling (RouteNotFoundDL)
56 | * Cross-branch navigation (from favorite's song page to artist page)
57 | 
58 |
59 | ### [Multiple base routes](https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/tree/master/examples/multiple_base_routes)
60 | **This example demonstrates:**
61 | * Everything from single base route example
62 | * Bottom navigation (library, favorites, user pages) persists across navigation
63 | * Login and error pages are full screen (hide bottom navigation)
64 | * Using the asynchronous result of push (AuthenticationDL from UserDL)
65 | * Custom `Authenticated` mixin ensures user is authenticated (LibraryDL, FavoritesDL, UserDL)
66 | 
67 |
68 | ## Configuration
69 | #### Deep links
70 | `DeepLink` is base unit of routes, a deep link is mapped to a `Widget` by a `Dispatcher`.
71 |
72 | Deep links may be reused in different levels of the navigation hierarchy.
73 |
74 | A route is the full location of a page, represented by `List`.
75 |
76 | `path` is the string representation of the route aka `route.join("/")`.
77 |
78 | **Path**
79 | ```dart
80 | class LibraryDL extends DeepLink {
81 | LibraryDL() : super("library");
82 | }
83 | ```
84 |
85 | **Value**
86 |
87 | Deep links can also store data, value dispatchers are strongly typed.
88 | ```dart
89 | class SongDL extends ValueDeepLink {
90 | SongDL(Song song) : super("song", song);
91 | }
92 |
93 | class SongDL extends ValueDeepLink {
94 | // Override toString
95 | SongDL(Song song) : super("song", song, toString: (song) => song.id);
96 | }
97 | ```
98 |
99 | **Mixin for sake of inheritence**
100 |
101 | This could also be achieved by implementing an abstract class.
102 |
103 | See use in *Child builder* section.
104 | ```dart
105 | mixin FullScreen on DeepLink {}
106 |
107 | class LoginDL extends DeepLink with FullScreen {
108 | LoginDL() : super("login");
109 | }
110 | ```
111 |
112 | **Mixin with logic**
113 | ```dart
114 | mixin Authenticated on DeepLink {
115 | @override
116 | void onDispatch(BuildContext context) {
117 | // Get state from context or global/static variable
118 | final isAuthenticated = Provider.of(context, listen: false).authenticated;
119 |
120 | // Throw custom exception
121 | if (!isAuthenticated) {
122 | throw Unauthenticated();
123 | }
124 | }
125 | }
126 |
127 | // ...
128 |
129 | navigation: (context) => Dispatcher()
130 | // Unauthenticated login page
131 | ..exception((exception, route) => [LoginDL()])
132 | ..path((route) => LoginPage()),
133 | ```
134 |
135 | #### Application
136 | Use `DeepLinkMaterialApp` instead of using Flutter's `MaterialApp`.
137 |
138 | This replaces native navigation options with their deep link counterparts.
139 |
140 | At most one deep link of a type can exist on a dispatcher.
141 |
142 | **Deep link material app**
143 | ```dart
144 | DeepLinkMaterialApp(
145 | navigation: (context) => Dispatcher() // see next section ...
146 | defaultRoute: [LibraryDL()], // if ommited, the splash screen is shown until explicit navigation
147 | splashScreen: SplashPage(),
148 | childBuilder: // see child builder section ...
149 |
150 | // Non-navigation related fields are still available
151 | themeMode: ThemeMode.light,
152 | // ...
153 | );
154 | ```
155 |
156 | **Path dispatcher**
157 | ```dart
158 | ..path((route) => LoginPage()),
159 | ```
160 |
161 | **Value dispatcher**
162 | ```dart
163 | ..value((song, route) => SongPage(song: song)),
164 | ```
165 |
166 | **Sub navigation**
167 |
168 | ```dart
169 | ..path(
170 | (route) => LibraryPage(),
171 | subNavigation: Dispatcher() // ...
172 | ),
173 |
174 | ..value(
175 | (song, route) => SongPage(song: song),
176 | subNavigation: (song) => Dispatcher() // song may be used from this point onward
177 | ),
178 | ```
179 |
180 | **Exception mapping**
181 |
182 | Exceptions that are thrown while running through the navigation hierarchy are mapped to routes.
183 |
184 | `..exception` **MUST** be defined on the base-level dispatcher.
185 |
186 | If multiple mappings of the same type are found thoughout the hierarchy, the deep-most mapping is used.
187 | ```dart
188 | ..exception((exception, route) => [ErrorDL(exception)])
189 | ```
190 |
191 | #### Child builder
192 | The widget specified in `childBuilder` is rebuilt when the route in `deepLinkNavigator` changes.
193 | ```dart
194 | /// [DeepLink]s associated with the bottom navigation.
195 | final bottomNavigationDeepLinks = [LibraryDL(), FavoritesDL(), UserDL()];
196 |
197 | /// Current index of bottom navigation based on [currentRoute].
198 | int currentIndex(List currentRoute) {
199 | final index = bottomNavigationDeepLinks.indexOf(currentRoute?.first);
200 | return index != -1 ? index : 0;
201 | }
202 |
203 | /// ...
204 |
205 | childBuilder: (BuildContext context, DeepLinkNavigator deepLinkNavigator, Widget child) => Scaffold(
206 | body: child,
207 | // Don't show bottom navigation while [currentRoute] is null, or any deep list is [FullScreen]
208 | bottomNavigationBar: deepLinkNavigator.currentRoute?.any((dl) => dl is FullScreen) ?? true ? null : BottomNavigationBar(
209 | currentIndex: currentIndex(deepLinkNavigator.currentRoute),
210 | onTap: (int index) => deepLinkNavigator.navigateTo([bottomNavigationDeepLinks[index]]),
211 | items: [
212 | BottomNavigationBarItem(title: Text("Library"), icon: Icon(Icons.queue_music)),
213 | BottomNavigationBarItem(title: Text("Favorites"), icon: Icon(Icons.favorite)),
214 | BottomNavigationBarItem(title: Text("User"), icon: Icon(Icons.person)),
215 | ],
216 | ),
217 | )
218 | ```
219 |
220 | #### In-app navigation
221 | `DeepLinkNavigator` mirrors `Navigator`'s interface as much as possible (including push and pop futures).
222 |
223 | All methods internally orchestrate a native flutter navigator.
224 |
225 | **Push a deep link**
226 | ```dart
227 | await DeepLinkNavigator.of(context).push(ArtistDL(...));
228 | ```
229 |
230 | **Pop a value**
231 | ```dart
232 | DeepLinkNavigator.of(context).pop(...);
233 |
234 | // or
235 |
236 | Navigator.of(context).pop(...);
237 | ```
238 |
239 | **Navigate to specific route**
240 | ```dart
241 | DeepLinkNavigator.of(context).navigateTo([
242 | LibraryDL(),
243 | ArtistDL(...),
244 | ]);
245 | ```
246 |
247 | **Return to default route (if any)**
248 | ```dart
249 | DeepLinkNavigator.of(context).replaceWithDefault();
250 | ```
251 |
252 | **TODO: Throw exception to be caught by mapper**
253 | ```dart
254 | // TODO DeepLinkNavigator.of(context).throw(Exception(...));
255 | ```
256 |
257 | **TODO: Access deep link navigator from anywhere**
258 | ```dart
259 | // TODO DeepLinkNavigator()...
260 | ```
261 |
262 | **TODO: Page transitions**
263 | ```dart
264 | // await DeepLinkNavigator.of(context).push(
265 | // ArtistDL(...),
266 | // transition: ...,
267 | // duration: ...,
268 | // );
269 |
270 | // Possibly:
271 | // await DeepLinkNavigator.of(context).fadeIn(...);
272 | ```
273 |
274 | ## Platform deep links
275 | // TODO: serialize `List`
276 |
277 | ## Limitations
278 | * Must **FULLY** specify **ALL** generic types since this is how deep links are matched internally
279 | * Please look very carefully when debugging
280 | * Fails by throwing `RouteNotFound` if the route doesn't exist
281 | * Fails by entering infinite recursion if `RouteNotFound` maps to a route that doesn't exist
282 | * Can't currently define arbitrarily deep navigation hierarchies (think Spotify)
283 | * Can't store separate persisted navigation states for a multi-base route application (think Instagram)
284 |
285 | ## What's left to do
286 |
287 | [ ] Custom/predefined page transitions
288 |
289 | [ ] Access deep link navigator from anywhere using static method and factory pattern
290 |
291 | [ ] Assert `RouteNotFound` dispatcher exists by running through navigation tree
292 |
293 | [ ] Unit test deep link navigator logic
294 |
295 | [ ] Cupertino and Widget apps
296 |
297 | [ ] Explicit exception throwing
298 |
299 | [ ] Platform deep links example + documentation
300 |
301 | [ ] Route changed callback for analytics, etc
--------------------------------------------------------------------------------
/examples/single_base_route/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | after_layout:
5 | dependency: transitive
6 | description:
7 | name: after_layout
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "1.0.7+2"
11 | analyzer:
12 | dependency: transitive
13 | description:
14 | name: analyzer
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "0.36.4"
18 | archive:
19 | dependency: transitive
20 | description:
21 | name: archive
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "2.0.11"
25 | args:
26 | dependency: transitive
27 | description:
28 | name: args
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.5.2"
32 | async:
33 | dependency: transitive
34 | description:
35 | name: async
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "2.4.0"
39 | boolean_selector:
40 | dependency: transitive
41 | description:
42 | name: boolean_selector
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.0.5"
46 | charcode:
47 | dependency: transitive
48 | description:
49 | name: charcode
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.1.2"
53 | collection:
54 | dependency: transitive
55 | description:
56 | name: collection
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "1.14.11"
60 | convert:
61 | dependency: transitive
62 | description:
63 | name: convert
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "2.1.1"
67 | coverage:
68 | dependency: transitive
69 | description:
70 | name: coverage
71 | url: "https://pub.dartlang.org"
72 | source: hosted
73 | version: "0.13.3+3"
74 | crypto:
75 | dependency: transitive
76 | description:
77 | name: crypto
78 | url: "https://pub.dartlang.org"
79 | source: hosted
80 | version: "2.1.3"
81 | csslib:
82 | dependency: transitive
83 | description:
84 | name: csslib
85 | url: "https://pub.dartlang.org"
86 | source: hosted
87 | version: "0.16.1"
88 | deep_link_navigation:
89 | dependency: "direct main"
90 | description:
91 | path: "../.."
92 | relative: true
93 | source: path
94 | version: "1.3.1"
95 | file:
96 | dependency: transitive
97 | description:
98 | name: file
99 | url: "https://pub.dartlang.org"
100 | source: hosted
101 | version: "5.1.0"
102 | flutter:
103 | dependency: "direct main"
104 | description: flutter
105 | source: sdk
106 | version: "0.0.0"
107 | flutter_driver:
108 | dependency: "direct dev"
109 | description: flutter
110 | source: sdk
111 | version: "0.0.0"
112 | flutter_gherkin:
113 | dependency: "direct dev"
114 | description:
115 | name: flutter_gherkin
116 | url: "https://pub.dartlang.org"
117 | source: hosted
118 | version: "1.1.4"
119 | flutter_test:
120 | dependency: "direct dev"
121 | description: flutter
122 | source: sdk
123 | version: "0.0.0"
124 | front_end:
125 | dependency: transitive
126 | description:
127 | name: front_end
128 | url: "https://pub.dartlang.org"
129 | source: hosted
130 | version: "0.1.19"
131 | fuchsia_remote_debug_protocol:
132 | dependency: transitive
133 | description: flutter
134 | source: sdk
135 | version: "0.0.0"
136 | gherkin:
137 | dependency: transitive
138 | description:
139 | name: gherkin
140 | url: "https://pub.dartlang.org"
141 | source: hosted
142 | version: "1.1.3"
143 | glob:
144 | dependency: transitive
145 | description:
146 | name: glob
147 | url: "https://pub.dartlang.org"
148 | source: hosted
149 | version: "1.2.0"
150 | html:
151 | dependency: transitive
152 | description:
153 | name: html
154 | url: "https://pub.dartlang.org"
155 | source: hosted
156 | version: "0.14.0+3"
157 | http:
158 | dependency: transitive
159 | description:
160 | name: http
161 | url: "https://pub.dartlang.org"
162 | source: hosted
163 | version: "0.12.0+2"
164 | http_multi_server:
165 | dependency: transitive
166 | description:
167 | name: http_multi_server
168 | url: "https://pub.dartlang.org"
169 | source: hosted
170 | version: "2.1.0"
171 | http_parser:
172 | dependency: transitive
173 | description:
174 | name: http_parser
175 | url: "https://pub.dartlang.org"
176 | source: hosted
177 | version: "3.1.3"
178 | image:
179 | dependency: transitive
180 | description:
181 | name: image
182 | url: "https://pub.dartlang.org"
183 | source: hosted
184 | version: "2.1.4"
185 | intl:
186 | dependency: transitive
187 | description:
188 | name: intl
189 | url: "https://pub.dartlang.org"
190 | source: hosted
191 | version: "0.16.0"
192 | io:
193 | dependency: transitive
194 | description:
195 | name: io
196 | url: "https://pub.dartlang.org"
197 | source: hosted
198 | version: "0.3.3"
199 | js:
200 | dependency: transitive
201 | description:
202 | name: js
203 | url: "https://pub.dartlang.org"
204 | source: hosted
205 | version: "0.6.1+1"
206 | json_rpc_2:
207 | dependency: transitive
208 | description:
209 | name: json_rpc_2
210 | url: "https://pub.dartlang.org"
211 | source: hosted
212 | version: "2.1.0"
213 | kernel:
214 | dependency: transitive
215 | description:
216 | name: kernel
217 | url: "https://pub.dartlang.org"
218 | source: hosted
219 | version: "0.3.19"
220 | logging:
221 | dependency: transitive
222 | description:
223 | name: logging
224 | url: "https://pub.dartlang.org"
225 | source: hosted
226 | version: "0.11.3+2"
227 | matcher:
228 | dependency: transitive
229 | description:
230 | name: matcher
231 | url: "https://pub.dartlang.org"
232 | source: hosted
233 | version: "0.12.6"
234 | merge_map:
235 | dependency: transitive
236 | description:
237 | name: merge_map
238 | url: "https://pub.dartlang.org"
239 | source: hosted
240 | version: "1.0.2"
241 | meta:
242 | dependency: transitive
243 | description:
244 | name: meta
245 | url: "https://pub.dartlang.org"
246 | source: hosted
247 | version: "1.1.8"
248 | mime:
249 | dependency: transitive
250 | description:
251 | name: mime
252 | url: "https://pub.dartlang.org"
253 | source: hosted
254 | version: "0.9.6+3"
255 | multi_server_socket:
256 | dependency: transitive
257 | description:
258 | name: multi_server_socket
259 | url: "https://pub.dartlang.org"
260 | source: hosted
261 | version: "1.0.2"
262 | nested:
263 | dependency: transitive
264 | description:
265 | name: nested
266 | url: "https://pub.dartlang.org"
267 | source: hosted
268 | version: "0.0.4"
269 | node_interop:
270 | dependency: transitive
271 | description:
272 | name: node_interop
273 | url: "https://pub.dartlang.org"
274 | source: hosted
275 | version: "1.0.3"
276 | node_io:
277 | dependency: transitive
278 | description:
279 | name: node_io
280 | url: "https://pub.dartlang.org"
281 | source: hosted
282 | version: "1.0.1+2"
283 | node_preamble:
284 | dependency: transitive
285 | description:
286 | name: node_preamble
287 | url: "https://pub.dartlang.org"
288 | source: hosted
289 | version: "1.4.8"
290 | package_config:
291 | dependency: transitive
292 | description:
293 | name: package_config
294 | url: "https://pub.dartlang.org"
295 | source: hosted
296 | version: "1.1.0"
297 | package_resolver:
298 | dependency: transitive
299 | description:
300 | name: package_resolver
301 | url: "https://pub.dartlang.org"
302 | source: hosted
303 | version: "1.0.10"
304 | path:
305 | dependency: transitive
306 | description:
307 | name: path
308 | url: "https://pub.dartlang.org"
309 | source: hosted
310 | version: "1.6.4"
311 | pedantic:
312 | dependency: transitive
313 | description:
314 | name: pedantic
315 | url: "https://pub.dartlang.org"
316 | source: hosted
317 | version: "1.8.0+1"
318 | petitparser:
319 | dependency: transitive
320 | description:
321 | name: petitparser
322 | url: "https://pub.dartlang.org"
323 | source: hosted
324 | version: "2.4.0"
325 | platform:
326 | dependency: transitive
327 | description:
328 | name: platform
329 | url: "https://pub.dartlang.org"
330 | source: hosted
331 | version: "2.2.1"
332 | pool:
333 | dependency: transitive
334 | description:
335 | name: pool
336 | url: "https://pub.dartlang.org"
337 | source: hosted
338 | version: "1.4.0"
339 | process:
340 | dependency: transitive
341 | description:
342 | name: process
343 | url: "https://pub.dartlang.org"
344 | source: hosted
345 | version: "3.0.12"
346 | provider:
347 | dependency: transitive
348 | description:
349 | name: provider
350 | url: "https://pub.dartlang.org"
351 | source: hosted
352 | version: "4.0.1"
353 | pub_semver:
354 | dependency: transitive
355 | description:
356 | name: pub_semver
357 | url: "https://pub.dartlang.org"
358 | source: hosted
359 | version: "1.4.2"
360 | quiver:
361 | dependency: transitive
362 | description:
363 | name: quiver
364 | url: "https://pub.dartlang.org"
365 | source: hosted
366 | version: "2.0.5"
367 | shelf:
368 | dependency: transitive
369 | description:
370 | name: shelf
371 | url: "https://pub.dartlang.org"
372 | source: hosted
373 | version: "0.7.5"
374 | shelf_packages_handler:
375 | dependency: transitive
376 | description:
377 | name: shelf_packages_handler
378 | url: "https://pub.dartlang.org"
379 | source: hosted
380 | version: "1.0.4"
381 | shelf_static:
382 | dependency: transitive
383 | description:
384 | name: shelf_static
385 | url: "https://pub.dartlang.org"
386 | source: hosted
387 | version: "0.2.8"
388 | shelf_web_socket:
389 | dependency: transitive
390 | description:
391 | name: shelf_web_socket
392 | url: "https://pub.dartlang.org"
393 | source: hosted
394 | version: "0.2.3"
395 | sky_engine:
396 | dependency: transitive
397 | description: flutter
398 | source: sdk
399 | version: "0.0.99"
400 | source_map_stack_trace:
401 | dependency: transitive
402 | description:
403 | name: source_map_stack_trace
404 | url: "https://pub.dartlang.org"
405 | source: hosted
406 | version: "1.1.5"
407 | source_maps:
408 | dependency: transitive
409 | description:
410 | name: source_maps
411 | url: "https://pub.dartlang.org"
412 | source: hosted
413 | version: "0.10.8"
414 | source_span:
415 | dependency: transitive
416 | description:
417 | name: source_span
418 | url: "https://pub.dartlang.org"
419 | source: hosted
420 | version: "1.5.5"
421 | stack_trace:
422 | dependency: transitive
423 | description:
424 | name: stack_trace
425 | url: "https://pub.dartlang.org"
426 | source: hosted
427 | version: "1.9.3"
428 | stream_channel:
429 | dependency: transitive
430 | description:
431 | name: stream_channel
432 | url: "https://pub.dartlang.org"
433 | source: hosted
434 | version: "2.0.0"
435 | string_scanner:
436 | dependency: transitive
437 | description:
438 | name: string_scanner
439 | url: "https://pub.dartlang.org"
440 | source: hosted
441 | version: "1.0.5"
442 | term_glyph:
443 | dependency: transitive
444 | description:
445 | name: term_glyph
446 | url: "https://pub.dartlang.org"
447 | source: hosted
448 | version: "1.1.0"
449 | test:
450 | dependency: "direct dev"
451 | description:
452 | name: test
453 | url: "https://pub.dartlang.org"
454 | source: hosted
455 | version: "1.9.4"
456 | test_api:
457 | dependency: transitive
458 | description:
459 | name: test_api
460 | url: "https://pub.dartlang.org"
461 | source: hosted
462 | version: "0.2.11"
463 | test_core:
464 | dependency: transitive
465 | description:
466 | name: test_core
467 | url: "https://pub.dartlang.org"
468 | source: hosted
469 | version: "0.2.15"
470 | typed_data:
471 | dependency: transitive
472 | description:
473 | name: typed_data
474 | url: "https://pub.dartlang.org"
475 | source: hosted
476 | version: "1.1.6"
477 | vector_math:
478 | dependency: transitive
479 | description:
480 | name: vector_math
481 | url: "https://pub.dartlang.org"
482 | source: hosted
483 | version: "2.0.8"
484 | vm_service:
485 | dependency: transitive
486 | description:
487 | name: vm_service
488 | url: "https://pub.dartlang.org"
489 | source: hosted
490 | version: "2.2.0"
491 | vm_service_client:
492 | dependency: transitive
493 | description:
494 | name: vm_service_client
495 | url: "https://pub.dartlang.org"
496 | source: hosted
497 | version: "0.2.6+2"
498 | watcher:
499 | dependency: transitive
500 | description:
501 | name: watcher
502 | url: "https://pub.dartlang.org"
503 | source: hosted
504 | version: "0.9.7+12"
505 | web_socket_channel:
506 | dependency: transitive
507 | description:
508 | name: web_socket_channel
509 | url: "https://pub.dartlang.org"
510 | source: hosted
511 | version: "1.1.0"
512 | xml:
513 | dependency: transitive
514 | description:
515 | name: xml
516 | url: "https://pub.dartlang.org"
517 | source: hosted
518 | version: "3.5.0"
519 | yaml:
520 | dependency: transitive
521 | description:
522 | name: yaml
523 | url: "https://pub.dartlang.org"
524 | source: hosted
525 | version: "2.2.0"
526 | sdks:
527 | dart: ">=2.6.0 <3.0.0"
528 | flutter: ">=1.12.1"
529 |
--------------------------------------------------------------------------------
/examples/multiple_base_routes/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | after_layout:
5 | dependency: transitive
6 | description:
7 | name: after_layout
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "1.0.7+2"
11 | analyzer:
12 | dependency: transitive
13 | description:
14 | name: analyzer
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "0.36.4"
18 | archive:
19 | dependency: transitive
20 | description:
21 | name: archive
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "2.0.11"
25 | args:
26 | dependency: transitive
27 | description:
28 | name: args
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "1.5.2"
32 | async:
33 | dependency: transitive
34 | description:
35 | name: async
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "2.4.0"
39 | boolean_selector:
40 | dependency: transitive
41 | description:
42 | name: boolean_selector
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.0.5"
46 | charcode:
47 | dependency: transitive
48 | description:
49 | name: charcode
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.1.2"
53 | collection:
54 | dependency: transitive
55 | description:
56 | name: collection
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "1.14.11"
60 | convert:
61 | dependency: transitive
62 | description:
63 | name: convert
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "2.1.1"
67 | coverage:
68 | dependency: transitive
69 | description:
70 | name: coverage
71 | url: "https://pub.dartlang.org"
72 | source: hosted
73 | version: "0.13.3+3"
74 | crypto:
75 | dependency: transitive
76 | description:
77 | name: crypto
78 | url: "https://pub.dartlang.org"
79 | source: hosted
80 | version: "2.1.3"
81 | csslib:
82 | dependency: transitive
83 | description:
84 | name: csslib
85 | url: "https://pub.dartlang.org"
86 | source: hosted
87 | version: "0.16.1"
88 | deep_link_navigation:
89 | dependency: "direct main"
90 | description:
91 | path: "../.."
92 | relative: true
93 | source: path
94 | version: "1.3.1"
95 | file:
96 | dependency: transitive
97 | description:
98 | name: file
99 | url: "https://pub.dartlang.org"
100 | source: hosted
101 | version: "5.1.0"
102 | flutter:
103 | dependency: "direct main"
104 | description: flutter
105 | source: sdk
106 | version: "0.0.0"
107 | flutter_driver:
108 | dependency: "direct dev"
109 | description: flutter
110 | source: sdk
111 | version: "0.0.0"
112 | flutter_gherkin:
113 | dependency: "direct dev"
114 | description:
115 | name: flutter_gherkin
116 | url: "https://pub.dartlang.org"
117 | source: hosted
118 | version: "1.1.4"
119 | flutter_test:
120 | dependency: "direct dev"
121 | description: flutter
122 | source: sdk
123 | version: "0.0.0"
124 | front_end:
125 | dependency: transitive
126 | description:
127 | name: front_end
128 | url: "https://pub.dartlang.org"
129 | source: hosted
130 | version: "0.1.19"
131 | fuchsia_remote_debug_protocol:
132 | dependency: transitive
133 | description: flutter
134 | source: sdk
135 | version: "0.0.0"
136 | gherkin:
137 | dependency: transitive
138 | description:
139 | name: gherkin
140 | url: "https://pub.dartlang.org"
141 | source: hosted
142 | version: "1.1.3"
143 | glob:
144 | dependency: transitive
145 | description:
146 | name: glob
147 | url: "https://pub.dartlang.org"
148 | source: hosted
149 | version: "1.2.0"
150 | html:
151 | dependency: transitive
152 | description:
153 | name: html
154 | url: "https://pub.dartlang.org"
155 | source: hosted
156 | version: "0.14.0+3"
157 | http:
158 | dependency: transitive
159 | description:
160 | name: http
161 | url: "https://pub.dartlang.org"
162 | source: hosted
163 | version: "0.12.0+2"
164 | http_multi_server:
165 | dependency: transitive
166 | description:
167 | name: http_multi_server
168 | url: "https://pub.dartlang.org"
169 | source: hosted
170 | version: "2.1.0"
171 | http_parser:
172 | dependency: transitive
173 | description:
174 | name: http_parser
175 | url: "https://pub.dartlang.org"
176 | source: hosted
177 | version: "3.1.3"
178 | image:
179 | dependency: transitive
180 | description:
181 | name: image
182 | url: "https://pub.dartlang.org"
183 | source: hosted
184 | version: "2.1.4"
185 | intl:
186 | dependency: transitive
187 | description:
188 | name: intl
189 | url: "https://pub.dartlang.org"
190 | source: hosted
191 | version: "0.16.0"
192 | io:
193 | dependency: transitive
194 | description:
195 | name: io
196 | url: "https://pub.dartlang.org"
197 | source: hosted
198 | version: "0.3.3"
199 | js:
200 | dependency: transitive
201 | description:
202 | name: js
203 | url: "https://pub.dartlang.org"
204 | source: hosted
205 | version: "0.6.1+1"
206 | json_rpc_2:
207 | dependency: transitive
208 | description:
209 | name: json_rpc_2
210 | url: "https://pub.dartlang.org"
211 | source: hosted
212 | version: "2.1.0"
213 | kernel:
214 | dependency: transitive
215 | description:
216 | name: kernel
217 | url: "https://pub.dartlang.org"
218 | source: hosted
219 | version: "0.3.19"
220 | logging:
221 | dependency: transitive
222 | description:
223 | name: logging
224 | url: "https://pub.dartlang.org"
225 | source: hosted
226 | version: "0.11.3+2"
227 | matcher:
228 | dependency: transitive
229 | description:
230 | name: matcher
231 | url: "https://pub.dartlang.org"
232 | source: hosted
233 | version: "0.12.6"
234 | merge_map:
235 | dependency: transitive
236 | description:
237 | name: merge_map
238 | url: "https://pub.dartlang.org"
239 | source: hosted
240 | version: "1.0.2"
241 | meta:
242 | dependency: transitive
243 | description:
244 | name: meta
245 | url: "https://pub.dartlang.org"
246 | source: hosted
247 | version: "1.1.8"
248 | mime:
249 | dependency: transitive
250 | description:
251 | name: mime
252 | url: "https://pub.dartlang.org"
253 | source: hosted
254 | version: "0.9.6+3"
255 | multi_server_socket:
256 | dependency: transitive
257 | description:
258 | name: multi_server_socket
259 | url: "https://pub.dartlang.org"
260 | source: hosted
261 | version: "1.0.2"
262 | nested:
263 | dependency: transitive
264 | description:
265 | name: nested
266 | url: "https://pub.dartlang.org"
267 | source: hosted
268 | version: "0.0.4"
269 | node_interop:
270 | dependency: transitive
271 | description:
272 | name: node_interop
273 | url: "https://pub.dartlang.org"
274 | source: hosted
275 | version: "1.0.3"
276 | node_io:
277 | dependency: transitive
278 | description:
279 | name: node_io
280 | url: "https://pub.dartlang.org"
281 | source: hosted
282 | version: "1.0.1+2"
283 | node_preamble:
284 | dependency: transitive
285 | description:
286 | name: node_preamble
287 | url: "https://pub.dartlang.org"
288 | source: hosted
289 | version: "1.4.8"
290 | package_config:
291 | dependency: transitive
292 | description:
293 | name: package_config
294 | url: "https://pub.dartlang.org"
295 | source: hosted
296 | version: "1.1.0"
297 | package_resolver:
298 | dependency: transitive
299 | description:
300 | name: package_resolver
301 | url: "https://pub.dartlang.org"
302 | source: hosted
303 | version: "1.0.10"
304 | path:
305 | dependency: transitive
306 | description:
307 | name: path
308 | url: "https://pub.dartlang.org"
309 | source: hosted
310 | version: "1.6.4"
311 | pedantic:
312 | dependency: transitive
313 | description:
314 | name: pedantic
315 | url: "https://pub.dartlang.org"
316 | source: hosted
317 | version: "1.8.0+1"
318 | petitparser:
319 | dependency: transitive
320 | description:
321 | name: petitparser
322 | url: "https://pub.dartlang.org"
323 | source: hosted
324 | version: "2.4.0"
325 | platform:
326 | dependency: transitive
327 | description:
328 | name: platform
329 | url: "https://pub.dartlang.org"
330 | source: hosted
331 | version: "2.2.1"
332 | pool:
333 | dependency: transitive
334 | description:
335 | name: pool
336 | url: "https://pub.dartlang.org"
337 | source: hosted
338 | version: "1.4.0"
339 | process:
340 | dependency: transitive
341 | description:
342 | name: process
343 | url: "https://pub.dartlang.org"
344 | source: hosted
345 | version: "3.0.12"
346 | provider:
347 | dependency: "direct main"
348 | description:
349 | name: provider
350 | url: "https://pub.dartlang.org"
351 | source: hosted
352 | version: "4.0.1"
353 | pub_semver:
354 | dependency: transitive
355 | description:
356 | name: pub_semver
357 | url: "https://pub.dartlang.org"
358 | source: hosted
359 | version: "1.4.2"
360 | quiver:
361 | dependency: transitive
362 | description:
363 | name: quiver
364 | url: "https://pub.dartlang.org"
365 | source: hosted
366 | version: "2.0.5"
367 | shelf:
368 | dependency: transitive
369 | description:
370 | name: shelf
371 | url: "https://pub.dartlang.org"
372 | source: hosted
373 | version: "0.7.5"
374 | shelf_packages_handler:
375 | dependency: transitive
376 | description:
377 | name: shelf_packages_handler
378 | url: "https://pub.dartlang.org"
379 | source: hosted
380 | version: "1.0.4"
381 | shelf_static:
382 | dependency: transitive
383 | description:
384 | name: shelf_static
385 | url: "https://pub.dartlang.org"
386 | source: hosted
387 | version: "0.2.8"
388 | shelf_web_socket:
389 | dependency: transitive
390 | description:
391 | name: shelf_web_socket
392 | url: "https://pub.dartlang.org"
393 | source: hosted
394 | version: "0.2.3"
395 | sky_engine:
396 | dependency: transitive
397 | description: flutter
398 | source: sdk
399 | version: "0.0.99"
400 | source_map_stack_trace:
401 | dependency: transitive
402 | description:
403 | name: source_map_stack_trace
404 | url: "https://pub.dartlang.org"
405 | source: hosted
406 | version: "1.1.5"
407 | source_maps:
408 | dependency: transitive
409 | description:
410 | name: source_maps
411 | url: "https://pub.dartlang.org"
412 | source: hosted
413 | version: "0.10.8"
414 | source_span:
415 | dependency: transitive
416 | description:
417 | name: source_span
418 | url: "https://pub.dartlang.org"
419 | source: hosted
420 | version: "1.5.5"
421 | stack_trace:
422 | dependency: transitive
423 | description:
424 | name: stack_trace
425 | url: "https://pub.dartlang.org"
426 | source: hosted
427 | version: "1.9.3"
428 | stream_channel:
429 | dependency: transitive
430 | description:
431 | name: stream_channel
432 | url: "https://pub.dartlang.org"
433 | source: hosted
434 | version: "2.0.0"
435 | string_scanner:
436 | dependency: transitive
437 | description:
438 | name: string_scanner
439 | url: "https://pub.dartlang.org"
440 | source: hosted
441 | version: "1.0.5"
442 | term_glyph:
443 | dependency: transitive
444 | description:
445 | name: term_glyph
446 | url: "https://pub.dartlang.org"
447 | source: hosted
448 | version: "1.1.0"
449 | test:
450 | dependency: "direct dev"
451 | description:
452 | name: test
453 | url: "https://pub.dartlang.org"
454 | source: hosted
455 | version: "1.9.4"
456 | test_api:
457 | dependency: transitive
458 | description:
459 | name: test_api
460 | url: "https://pub.dartlang.org"
461 | source: hosted
462 | version: "0.2.11"
463 | test_core:
464 | dependency: transitive
465 | description:
466 | name: test_core
467 | url: "https://pub.dartlang.org"
468 | source: hosted
469 | version: "0.2.15"
470 | typed_data:
471 | dependency: transitive
472 | description:
473 | name: typed_data
474 | url: "https://pub.dartlang.org"
475 | source: hosted
476 | version: "1.1.6"
477 | vector_math:
478 | dependency: transitive
479 | description:
480 | name: vector_math
481 | url: "https://pub.dartlang.org"
482 | source: hosted
483 | version: "2.0.8"
484 | vm_service:
485 | dependency: transitive
486 | description:
487 | name: vm_service
488 | url: "https://pub.dartlang.org"
489 | source: hosted
490 | version: "2.2.0"
491 | vm_service_client:
492 | dependency: transitive
493 | description:
494 | name: vm_service_client
495 | url: "https://pub.dartlang.org"
496 | source: hosted
497 | version: "0.2.6+2"
498 | watcher:
499 | dependency: transitive
500 | description:
501 | name: watcher
502 | url: "https://pub.dartlang.org"
503 | source: hosted
504 | version: "0.9.7+12"
505 | web_socket_channel:
506 | dependency: transitive
507 | description:
508 | name: web_socket_channel
509 | url: "https://pub.dartlang.org"
510 | source: hosted
511 | version: "1.1.0"
512 | xml:
513 | dependency: transitive
514 | description:
515 | name: xml
516 | url: "https://pub.dartlang.org"
517 | source: hosted
518 | version: "3.5.0"
519 | yaml:
520 | dependency: transitive
521 | description:
522 | name: yaml
523 | url: "https://pub.dartlang.org"
524 | source: hosted
525 | version: "2.2.0"
526 | sdks:
527 | dart: ">=2.6.0 <3.0.0"
528 | flutter: ">=1.12.1"
529 |
--------------------------------------------------------------------------------