From ab8dafec73c860ed6930c21c45c82bc6d8b63d39 Mon Sep 17 00:00:00 2001 From: Adrian Axente Date: Mon, 10 Feb 2020 00:31:01 +0200 Subject: [PATCH 1/8] - implemented custom transition support at route definition level - added built in custom transitions - added custom transition extension for avoid boilerplate - updated the readme file --- README.md | 20 ++ example/pubspec.lock | 4 +- examples/multiple_base_routes/lib/main.dart | 128 ++++++++----- lib/deep_link_navigation.dart | 4 +- lib/src/deep_link_navigator.dart | 14 +- lib/src/deep_link_transition.dart | 188 +++++++++++++++++++ lib/src/deep_link_transition_extensions.dart | 170 +++++++++++++++++ lib/src/dispatchers.dart | 46 ++++- pubspec.lock | 2 +- pubspec.yaml | 4 +- 10 files changed, 513 insertions(+), 67 deletions(-) create mode 100644 lib/src/deep_link_transition.dart create mode 100644 lib/src/deep_link_transition_extensions.dart diff --git a/README.md b/README.md index f79976b..ab639e3 100644 --- a/README.md +++ b/README.md @@ -156,11 +156,31 @@ DeepLinkMaterialApp( **Path dispatcher** ```dart ..path((route) => LoginPage()), + +..pathRoute((path) => MaterialPageRoute(builder: (_) => LoginPage())), // with a custom page route or a custom PageRouteBuilder that implement custom route transitions + +..pathRoute( + (route) => PageTransition( + type: PageTransitionType.fade, + child: LoginPage(), + ), + ), // with custom PageTransition, an implementation of PageRouteBuilder provided by this package + +..pathRoute((route) => LoginPage().fadeTransition()) // with one of the custom transition extensions provided by this package +.. ``` **Value dispatcher** ```dart ..value((song, route) => SongPage(song: song)), + +.valueRoute((song, route) => MaterialPageRoute(builder: (_) => SongPage(song: song))), // with a custom page route or a custom PageRouteBuilder that implement custom route transitions + +..valueRoute( + (artist, route) => ArtistPage(artist: artist).scaleTransition( + alignment: Alignment.center, + duration: Duration(milliseconds: 800), + )) // with one of the custom transition extensions provided by this package ``` **Sub navigation** diff --git a/example/pubspec.lock b/example/pubspec.lock index 35c1827..cdca28e 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -21,7 +21,7 @@ packages: path: ".." relative: true source: path - version: "1.3.1" + version: "1.4.0" flutter: dependency: transitive description: flutter @@ -75,5 +75,5 @@ packages: source: hosted version: "2.0.8" sdks: - dart: ">=2.2.2 <3.0.0" + dart: ">=2.7.0 <3.0.0" flutter: ">=1.12.1" diff --git a/examples/multiple_base_routes/lib/main.dart b/examples/multiple_base_routes/lib/main.dart index 7017536..1f474ea 100644 --- a/examples/multiple_base_routes/lib/main.dart +++ b/examples/multiple_base_routes/lib/main.dart @@ -16,12 +16,11 @@ import 'package:multiple_base_routes/widgets/splash_page.dart'; import 'package:multiple_base_routes/widgets/user_page.dart'; void main() => runApp( - // State management of your choice - ChangeNotifierProvider( - create: (_) => AuthenticationService(), - child: MusicApp(), - ) -); + // State management of your choice + ChangeNotifierProvider( + create: (_) => AuthenticationService(), + child: MusicApp(), + )); /// [DeepLink]s associated with the bottom navigation. final bottomNavigationDeepLinks = [LibraryDL(), FavoritesDL(), UserDL()]; @@ -35,55 +34,80 @@ int currentIndex(List currentRoute) { class MusicApp extends StatelessWidget { @override Widget build(BuildContext context) => DeepLinkMaterialApp( - // This is where the magic happens - navigation: Dispatcher() - // RouteNotFound error page - ..exception((exception, route) => [ErrorDL(exception)]) - ..value>((exception, route) => ErrorPage(exception)) - // Unauthenticated login page - ..exception((exception, route) => [LoginDL()]) - ..path((path) => LoginPage()) - // The rest of the app - ..path( - (route) => LibraryPage(), - subNavigation: Dispatcher() - ..value( - (artist, route) => ArtistPage(artist: artist), - subNavigation: (artist) => Dispatcher() - ..song() + // This is where the magic happens + navigation: Dispatcher() + // RouteNotFound error page + ..exception( + (exception, route) => [ErrorDL(exception)]) + ..value>( + (exception, route) => ErrorPage(exception)) + // Unauthenticated login page + ..exception((exception, route) => [LoginDL()]) + ..pathRoute((path) => MaterialPageRoute( + builder: (_) => LoginPage(), + )) + // The rest of the app + ..pathRoute( + (route) => PageTransition( + type: PageTransitionType.fade, + child: LibraryPage(), + ), + subNavigation: Dispatcher() + ..valueRoute( + (artist, route) => ArtistPage(artist: artist).scaleTransition( + alignment: Alignment.center, + duration: Duration(milliseconds: 800), + ), + subNavigation: (artist) => Dispatcher()..song(), + ), + ) + ..pathRoute( + (route) => FavoritesPage().rotateTransition( + duration: Duration(milliseconds: 500), + curve: Curves.bounceInOut, + alignment: Alignment.center, + ), + subNavigation: Dispatcher()..song()) + ..pathRoute( + (route) => UserPage().customTransition(PageTransitionType.upToDown), + subNavigation: Dispatcher() + ..path((route) => AuthenticationPage()), ), - ) - ..path( - (route) => FavoritesPage(), - subNavigation: Dispatcher() - ..song() - ) - ..path( - (route) => UserPage(), - subNavigation: Dispatcher()..path((route) => AuthenticationPage()), - ), - defaultRoute: [LibraryDL()], - splashScreen: SplashPage(), - // Optionally specify always visible widgets - childBuilder: (BuildContext context, DeepLinkNavigator deepLinkNavigator, Widget child) => Scaffold( - body: child, - // Don't show bottom navigation while [currentRoute] is null, or any deep list is [FullScreen] - bottomNavigationBar: deepLinkNavigator.currentRoute?.any((dl) => dl is FullScreen) ?? true ? null : BottomNavigationBar( - currentIndex: currentIndex(deepLinkNavigator.currentRoute), - onTap: (int index) => deepLinkNavigator.navigateTo([bottomNavigationDeepLinks[index]]), - items: [ - BottomNavigationBarItem(title: Text("Library"), icon: Icon(Icons.queue_music, key: Key("library"))), - BottomNavigationBarItem(title: Text("Favorites"), icon: Icon(Icons.favorite, key: Key("favorites"))), - BottomNavigationBarItem(title: Text("User"), icon: Icon(Icons.person, key: Key("user"))), - ], - ), - ), - // Non-navigation related fields are still available - themeMode: ThemeMode.light, - ); + defaultRoute: [LibraryDL()], + splashScreen: SplashPage(), + // Optionally specify always visible widgets + childBuilder: (BuildContext context, + DeepLinkNavigator deepLinkNavigator, Widget child) => + Scaffold( + body: child, + // Don't show bottom navigation while [currentRoute] is null, or any deep list is [FullScreen] + bottomNavigationBar: deepLinkNavigator.currentRoute + ?.any((dl) => dl is FullScreen) ?? + true + ? null + : BottomNavigationBar( + currentIndex: currentIndex(deepLinkNavigator.currentRoute), + onTap: (int index) => deepLinkNavigator + .navigateTo([bottomNavigationDeepLinks[index]]), + items: [ + BottomNavigationBarItem( + title: Text("Library"), + icon: Icon(Icons.queue_music, key: Key("library"))), + BottomNavigationBarItem( + title: Text("Favorites"), + icon: Icon(Icons.favorite, key: Key("favorites"))), + BottomNavigationBarItem( + title: Text("User"), + icon: Icon(Icons.person, key: Key("user"))), + ], + ), + ), + // Non-navigation related fields are still available + themeMode: ThemeMode.light, + ); } /// Reusing code through static extension methods. extension DispatcherExtensions on Dispatcher { void song() => value((song, route) => SongPage(song: song)); -} \ No newline at end of file +} diff --git a/lib/deep_link_navigation.dart b/lib/deep_link_navigation.dart index 8a8f586..48ac730 100644 --- a/lib/deep_link_navigation.dart +++ b/lib/deep_link_navigation.dart @@ -4,4 +4,6 @@ export 'src/deep_link.dart'; export 'src/deep_link_material_app.dart'; export 'src/deep_link_navigator.dart'; export 'src/dispatchers.dart'; -export 'src/exceptions.dart'; \ No newline at end of file +export 'src/exceptions.dart'; +export 'src/deep_link_transition.dart'; +export 'src/deep_link_transition_extensions.dart'; \ No newline at end of file diff --git a/lib/src/deep_link_navigator.dart b/lib/src/deep_link_navigator.dart index 931be72..9e60182 100644 --- a/lib/src/deep_link_navigator.dart +++ b/lib/src/deep_link_navigator.dart @@ -147,17 +147,19 @@ class DeepLinkNavigator with ChangeNotifier { @optionalTypeArgs Future _pushNavigatorIfNecessary(DeepLink deepLink, Dispatcher currentDispatcher, List accumulatedRoute) { if (previousRoute.isEmpty || currentRoute.sublist(commonElementsInRoutes).contains(deepLink)) { - // Widget that corresponds with deep link - final widget = currentDispatcher.routeBuilders[deepLink.runtimeType]( + // Widget or Route that corresponds with deep link + final widgetOrRoute = currentDispatcher.routeBuilders[deepLink.runtimeType]( deepLink is ValueDeepLink ? deepLink.data : null, accumulatedRoute, ); - assert(widget != null); + assert(widgetOrRoute != null); // Animate only push (and thus pop) transitions - final pageRoute = actionWasPush - ? MaterialPageRoute(builder: (BuildContext context) => widget) - : NoAnimationPageRoute(builder: (BuildContext context) => widget); + final pageRoute = widgetOrRoute is Route + ? widgetOrRoute + : actionWasPush + ? MaterialPageRoute(builder: (BuildContext context) => widgetOrRoute) + : NoAnimationPageRoute(builder: (BuildContext context) => widgetOrRoute); // Replace root level pages final action = accumulatedRoute.length == 1 diff --git a/lib/src/deep_link_transition.dart b/lib/src/deep_link_transition.dart new file mode 100644 index 0000000..31b39c7 --- /dev/null +++ b/lib/src/deep_link_transition.dart @@ -0,0 +1,188 @@ +import 'package:flutter/material.dart'; + +enum PageTransitionType { + fade, + rightToLeft, + leftToRight, + upToDown, + downToUp, + scale, + rotate, + size, + rightToLeftWithFade, + leftToRightWithFade, +} + +class PageTransition extends PageRouteBuilder { + final Widget child; + final PageTransitionType type; + final Curve curve; + final Alignment alignment; + final Duration duration; + + PageTransition({ + Key key, + @required this.child, + @required this.type, + this.curve = Curves.linear, + this.alignment, + this.duration = const Duration(milliseconds: 300), + }) : super( + pageBuilder: (BuildContext context, Animation animation, + Animation secondaryAnimation) { + return child; + }, + transitionDuration: duration, + transitionsBuilder: (BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child) { + switch (type) { + case PageTransitionType.fade: + return FadeTransition(opacity: animation, child: child); + break; + case PageTransitionType.rightToLeft: + return SlideTransition( + transformHitTests: false, + position: new Tween( + begin: const Offset(1.0, 0.0), + end: Offset.zero, + ).animate(animation), + child: new SlideTransition( + position: new Tween( + begin: Offset.zero, + end: const Offset(-1.0, 0.0), + ).animate(secondaryAnimation), + child: child, + ), + ); + break; + case PageTransitionType.leftToRight: + return SlideTransition( + transformHitTests: false, + position: Tween( + begin: const Offset(-1.0, 0.0), + end: Offset.zero, + ).animate(animation), + child: new SlideTransition( + position: new Tween( + begin: Offset.zero, + end: const Offset(1.0, 0.0), + ).animate(secondaryAnimation), + child: child, + ), + ); + break; + case PageTransitionType.upToDown: + return SlideTransition( + transformHitTests: false, + position: Tween( + begin: const Offset(0.0, -1.0), + end: Offset.zero, + ).animate(animation), + child: new SlideTransition( + position: new Tween( + begin: Offset.zero, + end: const Offset(0.0, 1.0), + ).animate(secondaryAnimation), + child: child, + ), + ); + break; + case PageTransitionType.downToUp: + return SlideTransition( + transformHitTests: false, + position: Tween( + begin: const Offset(0.0, 1.0), + end: Offset.zero, + ).animate(animation), + child: new SlideTransition( + position: new Tween( + begin: Offset.zero, + end: const Offset(0.0, -1.0), + ).animate(secondaryAnimation), + child: child, + ), + ); + break; + case PageTransitionType.scale: + return ScaleTransition( + alignment: alignment, + scale: CurvedAnimation( + parent: animation, + curve: Interval( + 0.00, + 0.50, + curve: curve, + ), + ), + child: child, + ); + break; + case PageTransitionType.rotate: + return new RotationTransition( + alignment: alignment, + turns: animation, + child: new ScaleTransition( + alignment: alignment, + scale: animation, + child: FadeTransition( + opacity: animation, + child: child, + ), + ), + ); + break; + case PageTransitionType.size: + return Align( + alignment: alignment, + child: SizeTransition( + sizeFactor: CurvedAnimation( + parent: animation, + curve: curve, + ), + child: child, + ), + ); + break; + case PageTransitionType.rightToLeftWithFade: + return SlideTransition( + position: Tween( + begin: const Offset(1.0, 0.0), + end: Offset.zero, + ).animate(animation), + child: FadeTransition( + opacity: animation, + child: SlideTransition( + position: Tween( + begin: Offset.zero, + end: const Offset(-1.0, 0.0), + ).animate(secondaryAnimation), + child: child, + ), + ), + ); + break; + case PageTransitionType.leftToRightWithFade: + return SlideTransition( + position: Tween( + begin: const Offset(-1.0, 0.0), + end: Offset.zero, + ).animate(animation), + child: FadeTransition( + opacity: animation, + child: SlideTransition( + position: Tween( + begin: Offset.zero, + end: const Offset(1.0, 0.0), + ).animate(secondaryAnimation), + child: child, + ), + ), + ); + break; + default: + return FadeTransition(opacity: animation, child: child); + } + }); +} diff --git a/lib/src/deep_link_transition_extensions.dart b/lib/src/deep_link_transition_extensions.dart new file mode 100644 index 0000000..43891d3 --- /dev/null +++ b/lib/src/deep_link_transition_extensions.dart @@ -0,0 +1,170 @@ +import 'package:flutter/material.dart'; +import 'package:deep_link_navigation/src/deep_link_transition.dart'; + +extension PageTransitionExtension on Widget { + PageTransition fadeTransition({ + Key key, + Curve curve = Curves.linear, + Alignment alignment, + Duration duration = const Duration(milliseconds: 300), + }) => + PageTransition( + key: key, + type: PageTransitionType.fade, + curve: curve, + alignment: alignment, + duration: duration, + child: this, + ); + + PageTransition rightToLeftTransition({ + Key key, + Curve curve = Curves.linear, + Alignment alignment, + Duration duration = const Duration(milliseconds: 300), + }) => + PageTransition( + key: key, + type: PageTransitionType.rightToLeft, + curve: curve, + alignment: alignment, + duration: duration, + child: this, + ); + + PageTransition leftToRightTransition({ + Key key, + Curve curve = Curves.linear, + Alignment alignment, + Duration duration = const Duration(milliseconds: 300), + }) => + PageTransition( + key: key, + type: PageTransitionType.leftToRight, + curve: curve, + alignment: alignment, + duration: duration, + child: this, + ); + + PageTransition upToDownTransition({ + Key key, + Curve curve = Curves.linear, + Alignment alignment, + Duration duration = const Duration(milliseconds: 300), + }) => + PageTransition( + key: key, + type: PageTransitionType.upToDown, + curve: curve, + alignment: alignment, + duration: duration, + child: this, + ); + + PageTransition downToUpTransition({ + Key key, + Curve curve = Curves.linear, + Alignment alignment, + Duration duration = const Duration(milliseconds: 300), + }) => + PageTransition( + key: key, + type: PageTransitionType.downToUp, + curve: curve, + alignment: alignment, + duration: duration, + child: this, + ); + + PageTransition scaleTransition({ + Key key, + Curve curve = Curves.linear, + Alignment alignment, + Duration duration = const Duration(milliseconds: 300), + }) => + PageTransition( + key: key, + type: PageTransitionType.scale, + curve: curve, + alignment: alignment, + duration: duration, + child: this, + ); + + PageTransition rotateTransition({ + Key key, + Curve curve = Curves.linear, + Alignment alignment, + Duration duration = const Duration(milliseconds: 300), + }) => + PageTransition( + key: key, + type: PageTransitionType.rotate, + curve: curve, + alignment: alignment, + duration: duration, + child: this, + ); + + PageTransition sizeTransition({ + Key key, + Curve curve = Curves.linear, + Alignment alignment, + Duration duration = const Duration(milliseconds: 300), + }) => + PageTransition( + key: key, + type: PageTransitionType.size, + curve: curve, + alignment: alignment, + duration: duration, + child: this, + ); + + PageTransition rightToLeftWithFadeTransition({ + Key key, + Curve curve = Curves.linear, + Alignment alignment, + Duration duration = const Duration(milliseconds: 300), + }) => + PageTransition( + key: key, + type: PageTransitionType.rightToLeftWithFade, + curve: curve, + alignment: alignment, + duration: duration, + child: this, + ); + + PageTransition leftToRightWithFadeTransition({ + Key key, + Curve curve = Curves.linear, + Alignment alignment, + Duration duration = const Duration(milliseconds: 300), + }) => + PageTransition( + key: key, + type: PageTransitionType.leftToRightWithFade, + curve: curve, + alignment: alignment, + duration: duration, + child: this, + ); + + PageTransition customTransition( + PageTransitionType type, { + Key key, + Curve curve = Curves.linear, + Alignment alignment, + Duration duration = const Duration(milliseconds: 300), + }) => + PageTransition( + key: key, + type: type, + curve: curve, + alignment: alignment, + duration: duration, + child: this, + ); +} diff --git a/lib/src/dispatchers.dart b/lib/src/dispatchers.dart index bcdf2a2..6ce6041 100644 --- a/lib/src/dispatchers.dart +++ b/lib/src/dispatchers.dart @@ -1,13 +1,20 @@ +import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:deep_link_navigation/src/deep_link.dart'; -/// Widget for route. +/// Widget for deeplink route. typedef PathBuilder = Widget Function(List route); -/// Widget for route with [value]. +/// Custom route for deeplink route +typedef PathRouteBuilder = Route Function(List route); + +/// Widget for deeplink route with [value]. typedef ValueBuilder = Widget Function(T value, List route); +/// Custom route for deeplink route with [value]. +typedef ValueRouteBuilder = Route Function(T value, List route); + /// Route mapping for [exception]. typedef ErrorMapping = List Function(Exception exception, List route); @@ -37,7 +44,23 @@ class Dispatcher { {Dispatcher subNavigation} ) { assert(DL != dynamic, "A deep link type must be specified."); - assert(!routeBuilders.containsKey(DL), "A widget builder for ${DL.runtimeType} has already beed defined."); + assert(!routeBuilders.containsKey(DL), "A path builder for ${DL.runtimeType} has already beed defined."); + assert(builder != null); + + _routeBuilders[DL] = (_, route) => builder(route); + + if (subNavigation != null) { + _subNavigations[DL] = (_) => subNavigation; + } + } + + /// Add a path route builder to this level of hierarchy. + void pathRoute
( + PathRouteBuilder builder, + {Dispatcher subNavigation} + ) { + assert(DL != dynamic, "A deep link type must be specified."); + assert(!routeBuilders.containsKey(DL), "A route path builder for ${DL.runtimeType} has already beed defined."); assert(builder != null); _routeBuilders[DL] = (_, route) => builder(route); @@ -64,6 +87,23 @@ class Dispatcher { } } + /// Add a value route builder to this level of hierarchy. + void valueRoute>( + ValueRouteBuilder builder, + {NavigationValueBuilder subNavigation} + ) { + assert(T != dynamic, "Data type must be specified."); + assert(DL != dynamic, "A deep link type must be specified."); + assert(!routeBuilders.containsKey(DL), "A widget builder for ${DL.runtimeType} has already beed defined."); + assert(builder != null); + + _routeBuilders[DL] = builder; + + if (subNavigation != null) { + _subNavigations[DL] = subNavigation; + } + } + /// Add a exception mapping to this level of hierarchy. void exception(ErrorMapping mapper) { assert(E != dynamic, "An error type must be specified."); diff --git a/pubspec.lock b/pubspec.lock index dbba1bb..8936d7f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -213,5 +213,5 @@ packages: source: hosted version: "3.5.0" sdks: - dart: ">=2.4.0 <3.0.0" + dart: ">=2.7.0 <3.0.0" flutter: ">=1.12.1" diff --git a/pubspec.yaml b/pubspec.yaml index 8d8208b..c12e05e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,13 +1,13 @@ name: deep_link_navigation description: Deep link navigation for Flutter apps with an elegant configuation internally orchestrating a native Flutter navigator. -version: 1.3.1 +version: 1.4.0 author: Dennis Krasnov homepage: https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation repository: https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation issue_tracker: https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/issues environment: - sdk: ">=2.2.2 <3.0.0" + sdk: ">=2.7.0 <3.0.0" dependencies: flutter: From b964d95182e25d948edbbdbd9a3e76ee274f0283 Mon Sep 17 00:00:00 2001 From: Adrian Axente Date: Mon, 10 Feb 2020 00:36:24 +0200 Subject: [PATCH 2/8] Update README.md --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ab639e3..f1afdff 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,9 @@ DeepLinkMaterialApp( ```dart ..path((route) => LoginPage()), -..pathRoute((path) => MaterialPageRoute(builder: (_) => LoginPage())), // with a custom page route or a custom PageRouteBuilder that implement custom route transitions +..pathRoute((path) => MaterialPageRoute( + builder: (_) => LoginPage())), // with a custom page route or + a custom PageRouteBuilder that implement custom route transitions ..pathRoute( (route) => PageTransition( @@ -166,7 +168,8 @@ DeepLinkMaterialApp( ), ), // with custom PageTransition, an implementation of PageRouteBuilder provided by this package -..pathRoute((route) => LoginPage().fadeTransition()) // with one of the custom transition extensions provided by this package +..pathRoute((route) => LoginPage().fadeTransition()) // with one of the custom transition extensions +provided by this package .. ``` @@ -174,7 +177,9 @@ DeepLinkMaterialApp( ```dart ..value((song, route) => SongPage(song: song)), -.valueRoute((song, route) => MaterialPageRoute(builder: (_) => SongPage(song: song))), // with a custom page route or a custom PageRouteBuilder that implement custom route transitions +.valueRoute((song, route) => MaterialPageRoute( + builder: (_) => SongPage(song: song))), // with a custom page route + or a custom PageRouteBuilder that implement custom route transitions ..valueRoute( (artist, route) => ArtistPage(artist: artist).scaleTransition( @@ -318,4 +323,4 @@ DeepLinkNavigator.of(context).replaceWithDefault(); [ ] Platform deep links example + documentation -[ ] Route changed callback for analytics, etc \ No newline at end of file +[ ] Route changed callback for analytics, etc From 64ae3e80ecfb24eeaf3d71c58cdaa6d414431ace Mon Sep 17 00:00:00 2001 From: Adrian Axente Date: Mon, 10 Feb 2020 00:40:37 +0200 Subject: [PATCH 3/8] Update README.md --- README.md | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index f1afdff..17d7903 100644 --- a/README.md +++ b/README.md @@ -158,8 +158,7 @@ DeepLinkMaterialApp( ..path((route) => LoginPage()), ..pathRoute((path) => MaterialPageRoute( - builder: (_) => LoginPage())), // with a custom page route or - a custom PageRouteBuilder that implement custom route transitions + builder: (_) => LoginPage())), // with a custom page route or a custom PageRouteBuilder that implement custom route transitions ..pathRoute( (route) => PageTransition( @@ -168,8 +167,7 @@ DeepLinkMaterialApp( ), ), // with custom PageTransition, an implementation of PageRouteBuilder provided by this package -..pathRoute((route) => LoginPage().fadeTransition()) // with one of the custom transition extensions -provided by this package +..pathRoute((route) => LoginPage().fadeTransition()) // with one of the custom transition extensions provided by this package .. ``` @@ -178,8 +176,7 @@ provided by this package ..value((song, route) => SongPage(song: song)), .valueRoute((song, route) => MaterialPageRoute( - builder: (_) => SongPage(song: song))), // with a custom page route - or a custom PageRouteBuilder that implement custom route transitions + builder: (_) => SongPage(song: song))), // with a custom page route or a custom PageRouteBuilder that implement custom route transitions ..valueRoute( (artist, route) => ArtistPage(artist: artist).scaleTransition( From 79224e0fd6a6e5965890fc3fffe1b29a049ee8aa Mon Sep 17 00:00:00 2001 From: Adrian Axente Date: Tue, 11 Feb 2020 23:50:51 +0200 Subject: [PATCH 4/8] - implemented transitions as a named parameter to push, navigateTo, replaceWithDefault - removed pathRoute and valueRoute functions now path and value have a named parameter: transition - updated the multiple_base_routes example --- example/pubspec.lock | 2 +- examples/multiple_base_routes/lib/main.dart | 37 +++-- .../lib/widgets/library_page.dart | 17 ++- lib/deep_link_navigation.dart | 2 +- lib/src/deep_link_navigator.dart | 138 ++++++++++++------ lib/src/deep_link_transition.dart | 30 ++-- ...nsions.dart => deep_link_transitions.dart} | 113 +++++++------- lib/src/dispatchers.dart | 76 ++++------ pubspec.lock | 2 +- pubspec.yaml | 2 +- 10 files changed, 232 insertions(+), 187 deletions(-) rename lib/src/{deep_link_transition_extensions.dart => deep_link_transitions.dart} (58%) diff --git a/example/pubspec.lock b/example/pubspec.lock index cdca28e..b6d8ba8 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -75,5 +75,5 @@ packages: source: hosted version: "2.0.8" sdks: - dart: ">=2.7.0 <3.0.0" + dart: ">=2.2.2 <3.0.0" flutter: ">=1.12.1" diff --git a/examples/multiple_base_routes/lib/main.dart b/examples/multiple_base_routes/lib/main.dart index 1f474ea..7795c6e 100644 --- a/examples/multiple_base_routes/lib/main.dart +++ b/examples/multiple_base_routes/lib/main.dart @@ -43,33 +43,38 @@ class MusicApp extends StatelessWidget { (exception, route) => ErrorPage(exception)) // Unauthenticated login page ..exception((exception, route) => [LoginDL()]) - ..pathRoute((path) => MaterialPageRoute( - builder: (_) => LoginPage(), - )) + ..path( + (route) => LoginPage(), + transition: (widget) => MaterialPageRoute(builder: (_) => widget), + ) // The rest of the app - ..pathRoute( - (route) => PageTransition( - type: PageTransitionType.fade, + ..path( + (route) => LibraryPage(), + transition: (widget) => DeepLinkTransition( + type: DeepLinkTransitionType.fade, child: LibraryPage(), ), subNavigation: Dispatcher() - ..valueRoute( - (artist, route) => ArtistPage(artist: artist).scaleTransition( + ..value( + (artist, route) => ArtistPage(artist: artist), + transition: DeepLinkTransitions.scaleTransition( alignment: Alignment.center, duration: Duration(milliseconds: 800), ), subNavigation: (artist) => Dispatcher()..song(), ), ) - ..pathRoute( - (route) => FavoritesPage().rotateTransition( - duration: Duration(milliseconds: 500), - curve: Curves.bounceInOut, - alignment: Alignment.center, - ), + ..path((route) => FavoritesPage(), + transition: DeepLinkTransitions.rotateTransition( + duration: Duration(milliseconds: 500), + curve: Curves.bounceInOut, + alignment: Alignment.center, + ), subNavigation: Dispatcher()..song()) - ..pathRoute( - (route) => UserPage().customTransition(PageTransitionType.upToDown), + ..path( + (route) => UserPage(), + transition: DeepLinkTransitions.customTransition( + DeepLinkTransitionType.upToDown), subNavigation: Dispatcher() ..path((route) => AuthenticationPage()), ), diff --git a/examples/multiple_base_routes/lib/widgets/library_page.dart b/examples/multiple_base_routes/lib/widgets/library_page.dart index 31734d3..a7d1ecb 100644 --- a/examples/multiple_base_routes/lib/widgets/library_page.dart +++ b/examples/multiple_base_routes/lib/widgets/library_page.dart @@ -22,7 +22,16 @@ class LibraryPage extends StatelessWidget { leading: CircleAvatar(child: Icon(Icons.person)), title: Text(artist.name), subtitle: Text("${artist.songs.length} songs"), - onTap: () => DeepLinkNavigator.of(context).push(ArtistDL(artist)), + onTap: () => DeepLinkNavigator.of(context).push( + ArtistDL(artist), + transition: artist.name.startsWith('Ludwig') + ? DeepLinkTransitions.rotateTransition( + duration: Duration(milliseconds: 500), + curve: Curves.bounceInOut, + alignment: Alignment.center, + ) + : null, + ), ), Divider(), ListTile( @@ -30,14 +39,16 @@ class LibraryPage extends StatelessWidget { leading: CircleAvatar(child: Icon(Icons.bug_report)), title: Text("Non-existant push"), // Route doesn't exist - onTap: () => DeepLinkNavigator.of(context).push(SongDL(Data.favoriteSongs[0])), + onTap: () => DeepLinkNavigator.of(context) + .push(SongDL(Data.favoriteSongs[0])), ), ListTile( key: Key("Non-existant navigate"), leading: CircleAvatar(child: Icon(Icons.bug_report)), title: Text("Non-existant navigate"), // Route doesn't exist - onTap: () => DeepLinkNavigator.of(context).navigateTo([LibraryDL(), SongDL(Data.favoriteSongs[1])]), + onTap: () => DeepLinkNavigator.of(context) + .navigateTo([LibraryDL(), SongDL(Data.favoriteSongs[1])]), ) ], ), diff --git a/lib/deep_link_navigation.dart b/lib/deep_link_navigation.dart index 48ac730..f128e35 100644 --- a/lib/deep_link_navigation.dart +++ b/lib/deep_link_navigation.dart @@ -6,4 +6,4 @@ export 'src/deep_link_navigator.dart'; export 'src/dispatchers.dart'; export 'src/exceptions.dart'; export 'src/deep_link_transition.dart'; -export 'src/deep_link_transition_extensions.dart'; \ No newline at end of file +export 'src/deep_link_transitions.dart'; \ No newline at end of file diff --git a/lib/src/deep_link_navigator.dart b/lib/src/deep_link_navigator.dart index 9e60182..ddd1c03 100644 --- a/lib/src/deep_link_navigator.dart +++ b/lib/src/deep_link_navigator.dart @@ -38,18 +38,18 @@ class DeepLinkNavigator with ChangeNotifier { /// Typical pop handling should be temporarily disabled. bool _poppingForDeepNavigation = false; - DeepLinkNavigator({ - @required this.navigatorKey, - @required this.navigation, - this.defaultRoute - }) : - assert(navigation != null), - assert(defaultRoute == null || defaultRoute.isNotEmpty); + DeepLinkNavigator( + {@required this.navigatorKey, + @required this.navigation, + this.defaultRoute}) + : assert(navigation != null), + assert(defaultRoute == null || defaultRoute.isNotEmpty); /// Handle a significant change of the current route. /// Optionally handles [promise] for pushes. @optionalTypeArgs - Future _handleRouteChanged([Future promise]) { + Future _handleRouteChanged( + [PathTransitionBuilder transition, Future promise]) { handlePop(); // Mutable route built up level-by-level @@ -70,7 +70,7 @@ class DeepLinkNavigator with ChangeNotifier { for (final deepLink in currentRoute) { // Throw if further navigation can't be found if (endOfNavigationHierarchy) { - throw(RouteNotFound(currentRoute)); + throw (RouteNotFound(currentRoute)); } // Deep link dispatcher at this level of navigation hierarchy @@ -78,22 +78,26 @@ class DeepLinkNavigator with ChangeNotifier { assert(currentDispatcher.routeBuilders.isNotEmpty); // Attempt to go deeper in navigation - if (currentDispatcher.subNavigations.containsKey(deepLink.runtimeType)) { - nextDispatcher = currentDispatcher.subNavigations[deepLink.runtimeType](deepLink is ValueDeepLink ? deepLink.data : null); + if (currentDispatcher.subNavigations + .containsKey(deepLink.runtimeType)) { + nextDispatcher = + currentDispatcher.subNavigations[deepLink.runtimeType]( + deepLink is ValueDeepLink ? deepLink.data : null); assert(nextDispatcher != null); - } - else { + } else { endOfNavigationHierarchy = true; } // Merge and override error mappings errors = mergeMap([errors, currentDispatcher.errorMappers]); - assert(errors.containsKey(RouteNotFound), "Must specify `exception` on base navigation builder."); + assert(errors.containsKey(RouteNotFound), + "Must specify `exception` on base navigation builder."); // Attempt to find deep link try { - if (!currentDispatcher.routeBuilders.containsKey(deepLink.runtimeType)) { - throw(RouteNotFound(currentRoute)); + if (!currentDispatcher.routeBuilders + .containsKey(deepLink.runtimeType)) { + throw (RouteNotFound(currentRoute)); } // Custom logic can throw custom exceptions @@ -110,22 +114,34 @@ class DeepLinkNavigator with ChangeNotifier { // Only return push promise on last deep link if (actionWasPush && deepLink == currentRoute.last) { - return _pushNavigatorIfNecessary(deepLink, currentDispatcher, accumulatedRoute); + return _pushNavigatorIfNecessary( + deepLink, + currentDispatcher, + accumulatedRoute, + transition, + ); } - _pushNavigatorIfNecessary(deepLink, currentDispatcher, accumulatedRoute); + _pushNavigatorIfNecessary( + deepLink, + currentDispatcher, + accumulatedRoute, + deepLink == currentRoute.last ? transition : null, + ); } - } on RouteNotFound catch(exception) { - final route = errors[exception.runtimeType](exception, List.from(accumulatedRoute)); + } on RouteNotFound catch (exception) { + final route = + errors[exception.runtimeType](exception, List.from(accumulatedRoute)); assert(route != null && route.isNotEmpty); - navigateTo(route); - } on Exception catch(exception) { + navigateTo(route, transition: transition); + } on Exception catch (exception) { if (!errors.containsKey(exception.runtimeType)) rethrow; - final route = errors[exception.runtimeType](exception, List.from(accumulatedRoute)); + final route = + errors[exception.runtimeType](exception, List.from(accumulatedRoute)); assert(route != null && route.isNotEmpty); - navigateTo(route); + navigateTo(route, transition: transition); } return null; } @@ -134,37 +150,55 @@ class DeepLinkNavigator with ChangeNotifier { void handlePop() { _poppingForDeepNavigation = true; var _timesDidNotPop = 0; - for (final deepLink in previousRoute.sublist(commonElementsInRoutes).reversed) { + for (final deepLink + in previousRoute.sublist(commonElementsInRoutes).reversed) { // Allow base routes to be conceptually popped once since they're replaced instead of pushed if (navigatorKey.currentState.canPop()) { navigatorKey.currentState.pop(); - } else assert(++_timesDidNotPop <= 1, "$deepLink caused a second pop to a base route"); + } else + assert(++_timesDidNotPop <= 1, + "$deepLink caused a second pop to a base route"); } _poppingForDeepNavigation = false; } /// Pushes widgets for deep links for deep links that didn't exist in previous route. @optionalTypeArgs - Future _pushNavigatorIfNecessary(DeepLink deepLink, Dispatcher currentDispatcher, List accumulatedRoute) { - if (previousRoute.isEmpty || currentRoute.sublist(commonElementsInRoutes).contains(deepLink)) { - // Widget or Route that corresponds with deep link - final widgetOrRoute = currentDispatcher.routeBuilders[deepLink.runtimeType]( + Future _pushNavigatorIfNecessary( + DeepLink deepLink, + Dispatcher currentDispatcher, + List accumulatedRoute, [ + PathTransitionBuilder transitionBuilder, + ]) { + if (previousRoute.isEmpty || + currentRoute.sublist(commonElementsInRoutes).contains(deepLink)) { + // Widget that corresponds with deep link + final widget = currentDispatcher.routeBuilders[deepLink.runtimeType]( deepLink is ValueDeepLink ? deepLink.data : null, accumulatedRoute, ); - assert(widgetOrRoute != null); + assert(widget != null); + + // TransitionBuilder that coresponds with deep link + final finalTransitionBuilder = transitionBuilder != null + ? transitionBuilder + : currentDispatcher.transitionBuilders + .containsKey(deepLink.runtimeType) + ? currentDispatcher.transitionBuilders[deepLink.runtimeType] + : null; // Animate only push (and thus pop) transitions - final pageRoute = widgetOrRoute is Route - ? widgetOrRoute - : actionWasPush - ? MaterialPageRoute(builder: (BuildContext context) => widgetOrRoute) - : NoAnimationPageRoute(builder: (BuildContext context) => widgetOrRoute); + final pageRoute = finalTransitionBuilder != null + ? finalTransitionBuilder(widget) + : actionWasPush + ? MaterialPageRoute(builder: (BuildContext context) => widget) + : NoAnimationPageRoute( + builder: (BuildContext context) => widget); // Replace root level pages final action = accumulatedRoute.length == 1 - ? (pageRoute) => navigatorKey.currentState.pushReplacement(pageRoute) - : (pageRoute) => navigatorKey.currentState.push(pageRoute); + ? (pageRoute) => navigatorKey.currentState.pushReplacement(pageRoute) + : (pageRoute) => navigatorKey.currentState.push(pageRoute); // Reuse native navigator's completer return action(pageRoute); @@ -173,41 +207,51 @@ class DeepLinkNavigator with ChangeNotifier { } /// Number of common deep links in [previousRoute] and [currentRoute]. - int get commonElementsInRoutes => indexOfLastCommonElement(previousRoute ?? [], currentRoute); + int get commonElementsInRoutes => + indexOfLastCommonElement(previousRoute ?? [], currentRoute); /// Pushes the given [deepLink] onto the navigator. /// Optionally returns a [T] value. @optionalTypeArgs - Future push(DeepLink deepLink) async { + Future push( + DeepLink deepLink, { + PathTransitionBuilder transition, + }) async { _previousRoute = List.from(currentRoute); currentRoute.add(deepLink); _actionWasPush = true; notifyListeners(); - return _handleRouteChanged(); + return _handleRouteChanged(transition); } /// Navigates to a specific route anywhere in the navigation hierarchy. - void navigateTo(List route) { + void navigateTo( + List route, { + PathTransitionBuilder transition, + }) { _previousRoute = List.from(currentRoute ?? []); _currentRoute = List.from(route); _actionWasPush = false; notifyListeners(); - _handleRouteChanged(); + _handleRouteChanged(transition); } /// Resets navigation to user's default page. /// Does nothing if [defaultRoute] is null. - void replaceWithDefault() { + void replaceWithDefault({ + PathTransitionBuilder transition, + }) { if (defaultRoute != null) { - navigateTo(defaultRoute); + navigateTo(defaultRoute, transition: transition); } } /// Pop the top-most deep link off the navigator that most tightly encloses the given context. @optionalTypeArgs - bool pop([T result]) => navigatorKey.currentState.pop(result); + bool pop([T result]) => + navigatorKey.currentState.pop(result); /// Handles deep link popping logic. /// Notified exclusively from native navigator's [PopObserver]. @@ -247,4 +291,4 @@ class DeepLinkNavigator with ChangeNotifier { String toString() => currentRoute.join("/"); } -// TODO: add on google analytics listener on callback 'routeChanged' \ No newline at end of file +// TODO: add on google analytics listener on callback 'routeChanged' diff --git a/lib/src/deep_link_transition.dart b/lib/src/deep_link_transition.dart index 31b39c7..f117d53 100644 --- a/lib/src/deep_link_transition.dart +++ b/lib/src/deep_link_transition.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -enum PageTransitionType { +enum DeepLinkTransitionType { fade, rightToLeft, leftToRight, @@ -13,14 +13,14 @@ enum PageTransitionType { leftToRightWithFade, } -class PageTransition extends PageRouteBuilder { +class DeepLinkTransition extends PageRouteBuilder { final Widget child; - final PageTransitionType type; + final DeepLinkTransitionType type; final Curve curve; final Alignment alignment; final Duration duration; - PageTransition({ + DeepLinkTransition({ Key key, @required this.child, @required this.type, @@ -38,10 +38,10 @@ class PageTransition extends PageRouteBuilder { Animation secondaryAnimation, Widget child) { switch (type) { - case PageTransitionType.fade: - return FadeTransition(opacity: animation, child: child); + case DeepLinkTransitionType.fade: + return FadeTransition(opacity: animation, child: child); break; - case PageTransitionType.rightToLeft: + case DeepLinkTransitionType.rightToLeft: return SlideTransition( transformHitTests: false, position: new Tween( @@ -57,7 +57,7 @@ class PageTransition extends PageRouteBuilder { ), ); break; - case PageTransitionType.leftToRight: + case DeepLinkTransitionType.leftToRight: return SlideTransition( transformHitTests: false, position: Tween( @@ -73,7 +73,7 @@ class PageTransition extends PageRouteBuilder { ), ); break; - case PageTransitionType.upToDown: + case DeepLinkTransitionType.upToDown: return SlideTransition( transformHitTests: false, position: Tween( @@ -89,7 +89,7 @@ class PageTransition extends PageRouteBuilder { ), ); break; - case PageTransitionType.downToUp: + case DeepLinkTransitionType.downToUp: return SlideTransition( transformHitTests: false, position: Tween( @@ -105,7 +105,7 @@ class PageTransition extends PageRouteBuilder { ), ); break; - case PageTransitionType.scale: + case DeepLinkTransitionType.scale: return ScaleTransition( alignment: alignment, scale: CurvedAnimation( @@ -119,7 +119,7 @@ class PageTransition extends PageRouteBuilder { child: child, ); break; - case PageTransitionType.rotate: + case DeepLinkTransitionType.rotate: return new RotationTransition( alignment: alignment, turns: animation, @@ -133,7 +133,7 @@ class PageTransition extends PageRouteBuilder { ), ); break; - case PageTransitionType.size: + case DeepLinkTransitionType.size: return Align( alignment: alignment, child: SizeTransition( @@ -145,7 +145,7 @@ class PageTransition extends PageRouteBuilder { ), ); break; - case PageTransitionType.rightToLeftWithFade: + case DeepLinkTransitionType.rightToLeftWithFade: return SlideTransition( position: Tween( begin: const Offset(1.0, 0.0), @@ -163,7 +163,7 @@ class PageTransition extends PageRouteBuilder { ), ); break; - case PageTransitionType.leftToRightWithFade: + case DeepLinkTransitionType.leftToRightWithFade: return SlideTransition( position: Tween( begin: const Offset(-1.0, 0.0), diff --git a/lib/src/deep_link_transition_extensions.dart b/lib/src/deep_link_transitions.dart similarity index 58% rename from lib/src/deep_link_transition_extensions.dart rename to lib/src/deep_link_transitions.dart index 43891d3..cc93f53 100644 --- a/lib/src/deep_link_transition_extensions.dart +++ b/lib/src/deep_link_transitions.dart @@ -1,170 +1,171 @@ +import 'package:deep_link_navigation/src/dispatchers.dart'; import 'package:flutter/material.dart'; import 'package:deep_link_navigation/src/deep_link_transition.dart'; -extension PageTransitionExtension on Widget { - PageTransition fadeTransition({ +class DeepLinkTransitions { + static PathTransitionBuilder fadeTransition({ Key key, Curve curve = Curves.linear, Alignment alignment, Duration duration = const Duration(milliseconds: 300), - }) => - PageTransition( + }) => (child) => + DeepLinkTransition( key: key, - type: PageTransitionType.fade, + type: DeepLinkTransitionType.fade, curve: curve, alignment: alignment, duration: duration, - child: this, + child: child, ); - PageTransition rightToLeftTransition({ + static PathTransitionBuilder rightToLeftTransition({ Key key, Curve curve = Curves.linear, Alignment alignment, Duration duration = const Duration(milliseconds: 300), - }) => - PageTransition( + }) => (child) => + DeepLinkTransition( key: key, - type: PageTransitionType.rightToLeft, + type: DeepLinkTransitionType.rightToLeft, curve: curve, alignment: alignment, duration: duration, - child: this, + child: child, ); - PageTransition leftToRightTransition({ + static PathTransitionBuilder leftToRightTransition({ Key key, Curve curve = Curves.linear, Alignment alignment, Duration duration = const Duration(milliseconds: 300), - }) => - PageTransition( + }) => (child) => + DeepLinkTransition( key: key, - type: PageTransitionType.leftToRight, + type: DeepLinkTransitionType.leftToRight, curve: curve, alignment: alignment, duration: duration, - child: this, + child: child, ); - PageTransition upToDownTransition({ + static PathTransitionBuilder upToDownTransition({ Key key, Curve curve = Curves.linear, Alignment alignment, Duration duration = const Duration(milliseconds: 300), - }) => - PageTransition( + }) => (child) => + DeepLinkTransition( key: key, - type: PageTransitionType.upToDown, + type: DeepLinkTransitionType.upToDown, curve: curve, alignment: alignment, duration: duration, - child: this, + child: child, ); - PageTransition downToUpTransition({ + static PathTransitionBuilder downToUpTransition({ Key key, Curve curve = Curves.linear, Alignment alignment, Duration duration = const Duration(milliseconds: 300), - }) => - PageTransition( + }) => (child) => + DeepLinkTransition( key: key, - type: PageTransitionType.downToUp, + type: DeepLinkTransitionType.downToUp, curve: curve, alignment: alignment, duration: duration, - child: this, + child: child, ); - PageTransition scaleTransition({ + static PathTransitionBuilder scaleTransition({ Key key, Curve curve = Curves.linear, Alignment alignment, Duration duration = const Duration(milliseconds: 300), - }) => - PageTransition( + }) => (child) => + DeepLinkTransition( key: key, - type: PageTransitionType.scale, + type: DeepLinkTransitionType.scale, curve: curve, alignment: alignment, duration: duration, - child: this, + child: child, ); - PageTransition rotateTransition({ + static PathTransitionBuilder rotateTransition({ Key key, Curve curve = Curves.linear, Alignment alignment, Duration duration = const Duration(milliseconds: 300), - }) => - PageTransition( + }) => (child) => + DeepLinkTransition( key: key, - type: PageTransitionType.rotate, + type: DeepLinkTransitionType.rotate, curve: curve, alignment: alignment, duration: duration, - child: this, + child: child, ); - PageTransition sizeTransition({ + static PathTransitionBuilder sizeTransition({ Key key, Curve curve = Curves.linear, Alignment alignment, Duration duration = const Duration(milliseconds: 300), - }) => - PageTransition( + }) => (child) => + DeepLinkTransition( key: key, - type: PageTransitionType.size, + type: DeepLinkTransitionType.size, curve: curve, alignment: alignment, duration: duration, - child: this, + child: child, ); - PageTransition rightToLeftWithFadeTransition({ + static PathTransitionBuilder rightToLeftWithFadeTransition({ Key key, Curve curve = Curves.linear, Alignment alignment, Duration duration = const Duration(milliseconds: 300), - }) => - PageTransition( + }) => (child) => + DeepLinkTransition( key: key, - type: PageTransitionType.rightToLeftWithFade, + type: DeepLinkTransitionType.rightToLeftWithFade, curve: curve, alignment: alignment, duration: duration, - child: this, + child: child, ); - PageTransition leftToRightWithFadeTransition({ + static PathTransitionBuilder leftToRightWithFadeTransition({ Key key, Curve curve = Curves.linear, Alignment alignment, Duration duration = const Duration(milliseconds: 300), - }) => - PageTransition( + }) => (child) => + DeepLinkTransition( key: key, - type: PageTransitionType.leftToRightWithFade, + type: DeepLinkTransitionType.leftToRightWithFade, curve: curve, alignment: alignment, duration: duration, - child: this, + child: child, ); - PageTransition customTransition( - PageTransitionType type, { + static PathTransitionBuilder customTransition( + DeepLinkTransitionType type, { Key key, Curve curve = Curves.linear, Alignment alignment, Duration duration = const Duration(milliseconds: 300), - }) => - PageTransition( + }) => (child) => + DeepLinkTransition( key: key, type: type, curve: curve, alignment: alignment, duration: duration, - child: this, + child: child, ); } diff --git a/lib/src/dispatchers.dart b/lib/src/dispatchers.dart index 6ce6041..0888443 100644 --- a/lib/src/dispatchers.dart +++ b/lib/src/dispatchers.dart @@ -3,20 +3,18 @@ import 'package:flutter/widgets.dart'; import 'package:deep_link_navigation/src/deep_link.dart'; +/// Path Transition builder for path +typedef PathTransitionBuilder = Route Function(Widget widget); + /// Widget for deeplink route. typedef PathBuilder = Widget Function(List route); -/// Custom route for deeplink route -typedef PathRouteBuilder = Route Function(List route); - /// Widget for deeplink route with [value]. typedef ValueBuilder = Widget Function(T value, List route); -/// Custom route for deeplink route with [value]. -typedef ValueRouteBuilder = Route Function(T value, List route); - /// Route mapping for [exception]. -typedef ErrorMapping = List Function(Exception exception, List route); +typedef ErrorMapping = List Function( + Exception exception, List route); /// Dispatcher for this level of navigation with [value]. typedef NavigationValueBuilder = Dispatcher Function(T value); @@ -29,6 +27,12 @@ class Dispatcher { Map _routeBuilders = {}; Map get routeBuilders => _routeBuilders; + /// Internal representation of transition builders. + /// Values must be dynamic since function parameter types don't upcast. + /// Ideally, the value type would be `PathTransitionBuilder`. + Map _transitionBuilders = {}; + Map get transitionBuilders => _transitionBuilders; + /// Internal representation of error mappings. Map _errorMappers = {}; Map get errorMappers => _errorMappers; @@ -40,30 +44,20 @@ class Dispatcher { /// Add a path widget builder to this level of hierarchy. void path
( - PathBuilder builder, - {Dispatcher subNavigation} - ) { + PathBuilder builder, { + PathTransitionBuilder transition, + Dispatcher subNavigation, + }) { assert(DL != dynamic, "A deep link type must be specified."); - assert(!routeBuilders.containsKey(DL), "A path builder for ${DL.runtimeType} has already beed defined."); + assert(!routeBuilders.containsKey(DL), + "A path builder for ${DL.runtimeType} has already beed defined."); assert(builder != null); _routeBuilders[DL] = (_, route) => builder(route); - if (subNavigation != null) { - _subNavigations[DL] = (_) => subNavigation; + if (transition != null) { + transitionBuilders[DL] = transition; } - } - - /// Add a path route builder to this level of hierarchy. - void pathRoute
( - PathRouteBuilder builder, - {Dispatcher subNavigation} - ) { - assert(DL != dynamic, "A deep link type must be specified."); - assert(!routeBuilders.containsKey(DL), "A route path builder for ${DL.runtimeType} has already beed defined."); - assert(builder != null); - - _routeBuilders[DL] = (_, route) => builder(route); if (subNavigation != null) { _subNavigations[DL] = (_) => subNavigation; @@ -72,32 +66,21 @@ class Dispatcher { /// Add a value widget builder to this level of hierarchy. void value>( - ValueBuilder builder, - {NavigationValueBuilder subNavigation} - ) { + ValueBuilder builder, { + PathTransitionBuilder transition, + NavigationValueBuilder subNavigation, + }) { assert(T != dynamic, "Data type must be specified."); assert(DL != dynamic, "A deep link type must be specified."); - assert(!routeBuilders.containsKey(DL), "A widget builder for ${DL.runtimeType} has already beed defined."); + assert(!routeBuilders.containsKey(DL), + "A widget builder for ${DL.runtimeType} has already beed defined."); assert(builder != null); _routeBuilders[DL] = builder; - if (subNavigation != null) { - _subNavigations[DL] = subNavigation; + if (transition != null) { + transitionBuilders[DL] = transition; } - } - - /// Add a value route builder to this level of hierarchy. - void valueRoute>( - ValueRouteBuilder builder, - {NavigationValueBuilder subNavigation} - ) { - assert(T != dynamic, "Data type must be specified."); - assert(DL != dynamic, "A deep link type must be specified."); - assert(!routeBuilders.containsKey(DL), "A widget builder for ${DL.runtimeType} has already beed defined."); - assert(builder != null); - - _routeBuilders[DL] = builder; if (subNavigation != null) { _subNavigations[DL] = subNavigation; @@ -107,9 +90,10 @@ class Dispatcher { /// Add a exception mapping to this level of hierarchy. void exception(ErrorMapping mapper) { assert(E != dynamic, "An error type must be specified."); - assert(!routeBuilders.containsKey(E), "An error mapping for ${E.runtimeType} has already beed defined."); + assert(!routeBuilders.containsKey(E), + "An error mapping for ${E.runtimeType} has already beed defined."); assert(mapper != null); _errorMappers[E] = mapper; } -} \ No newline at end of file +} diff --git a/pubspec.lock b/pubspec.lock index 8936d7f..dbba1bb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -213,5 +213,5 @@ packages: source: hosted version: "3.5.0" sdks: - dart: ">=2.7.0 <3.0.0" + dart: ">=2.4.0 <3.0.0" flutter: ">=1.12.1" diff --git a/pubspec.yaml b/pubspec.yaml index c12e05e..4bc121a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ repository: https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation issue_tracker: https://github.com/Dennis-Krasnov/Flutter-Deep-Link-Navigation/issues environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=2.2.2 <3.0.0" dependencies: flutter: From 966d39c9d9d62f39d7ce0097332e535241c58b3d Mon Sep 17 00:00:00 2001 From: Adrian Axente Date: Wed, 12 Feb 2020 00:03:16 +0200 Subject: [PATCH 5/8] - removed unnecessary transition suffix from transition convenient methods --- examples/multiple_base_routes/lib/main.dart | 6 ++--- .../lib/widgets/library_page.dart | 2 +- lib/src/deep_link_transitions.dart | 22 +++++++++---------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/multiple_base_routes/lib/main.dart b/examples/multiple_base_routes/lib/main.dart index 7795c6e..623ad7a 100644 --- a/examples/multiple_base_routes/lib/main.dart +++ b/examples/multiple_base_routes/lib/main.dart @@ -57,7 +57,7 @@ class MusicApp extends StatelessWidget { subNavigation: Dispatcher() ..value( (artist, route) => ArtistPage(artist: artist), - transition: DeepLinkTransitions.scaleTransition( + transition: DeepLinkTransitions.scale( alignment: Alignment.center, duration: Duration(milliseconds: 800), ), @@ -65,7 +65,7 @@ class MusicApp extends StatelessWidget { ), ) ..path((route) => FavoritesPage(), - transition: DeepLinkTransitions.rotateTransition( + transition: DeepLinkTransitions.rotate( duration: Duration(milliseconds: 500), curve: Curves.bounceInOut, alignment: Alignment.center, @@ -73,7 +73,7 @@ class MusicApp extends StatelessWidget { subNavigation: Dispatcher()..song()) ..path( (route) => UserPage(), - transition: DeepLinkTransitions.customTransition( + transition: DeepLinkTransitions.custom( DeepLinkTransitionType.upToDown), subNavigation: Dispatcher() ..path((route) => AuthenticationPage()), diff --git a/examples/multiple_base_routes/lib/widgets/library_page.dart b/examples/multiple_base_routes/lib/widgets/library_page.dart index a7d1ecb..198dfff 100644 --- a/examples/multiple_base_routes/lib/widgets/library_page.dart +++ b/examples/multiple_base_routes/lib/widgets/library_page.dart @@ -25,7 +25,7 @@ class LibraryPage extends StatelessWidget { onTap: () => DeepLinkNavigator.of(context).push( ArtistDL(artist), transition: artist.name.startsWith('Ludwig') - ? DeepLinkTransitions.rotateTransition( + ? DeepLinkTransitions.rotate( duration: Duration(milliseconds: 500), curve: Curves.bounceInOut, alignment: Alignment.center, diff --git a/lib/src/deep_link_transitions.dart b/lib/src/deep_link_transitions.dart index cc93f53..34c2dba 100644 --- a/lib/src/deep_link_transitions.dart +++ b/lib/src/deep_link_transitions.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:deep_link_navigation/src/deep_link_transition.dart'; class DeepLinkTransitions { - static PathTransitionBuilder fadeTransition({ + static PathTransitionBuilder fade({ Key key, Curve curve = Curves.linear, Alignment alignment, @@ -18,7 +18,7 @@ class DeepLinkTransitions { child: child, ); - static PathTransitionBuilder rightToLeftTransition({ + static PathTransitionBuilder rightToLeft({ Key key, Curve curve = Curves.linear, Alignment alignment, @@ -33,7 +33,7 @@ class DeepLinkTransitions { child: child, ); - static PathTransitionBuilder leftToRightTransition({ + static PathTransitionBuilder leftToRight({ Key key, Curve curve = Curves.linear, Alignment alignment, @@ -48,7 +48,7 @@ class DeepLinkTransitions { child: child, ); - static PathTransitionBuilder upToDownTransition({ + static PathTransitionBuilder upToDown({ Key key, Curve curve = Curves.linear, Alignment alignment, @@ -63,7 +63,7 @@ class DeepLinkTransitions { child: child, ); - static PathTransitionBuilder downToUpTransition({ + static PathTransitionBuilder downToUp({ Key key, Curve curve = Curves.linear, Alignment alignment, @@ -78,7 +78,7 @@ class DeepLinkTransitions { child: child, ); - static PathTransitionBuilder scaleTransition({ + static PathTransitionBuilder scale({ Key key, Curve curve = Curves.linear, Alignment alignment, @@ -93,7 +93,7 @@ class DeepLinkTransitions { child: child, ); - static PathTransitionBuilder rotateTransition({ + static PathTransitionBuilder rotate({ Key key, Curve curve = Curves.linear, Alignment alignment, @@ -108,7 +108,7 @@ class DeepLinkTransitions { child: child, ); - static PathTransitionBuilder sizeTransition({ + static PathTransitionBuilder size({ Key key, Curve curve = Curves.linear, Alignment alignment, @@ -123,7 +123,7 @@ class DeepLinkTransitions { child: child, ); - static PathTransitionBuilder rightToLeftWithFadeTransition({ + static PathTransitionBuilder rightToLeftWithFade({ Key key, Curve curve = Curves.linear, Alignment alignment, @@ -138,7 +138,7 @@ class DeepLinkTransitions { child: child, ); - static PathTransitionBuilder leftToRightWithFadeTransition({ + static PathTransitionBuilder leftToRightWithFade({ Key key, Curve curve = Curves.linear, Alignment alignment, @@ -153,7 +153,7 @@ class DeepLinkTransitions { child: child, ); - static PathTransitionBuilder customTransition( + static PathTransitionBuilder custom( DeepLinkTransitionType type, { Key key, Curve curve = Curves.linear, From c3bae467e91670572807068a0b40db8eb9bf423b Mon Sep 17 00:00:00 2001 From: Adrian Axente Date: Wed, 12 Feb 2020 00:08:11 +0200 Subject: [PATCH 6/8] - small correction .. --- examples/multiple_base_routes/lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/multiple_base_routes/lib/main.dart b/examples/multiple_base_routes/lib/main.dart index 623ad7a..094406a 100644 --- a/examples/multiple_base_routes/lib/main.dart +++ b/examples/multiple_base_routes/lib/main.dart @@ -52,7 +52,7 @@ class MusicApp extends StatelessWidget { (route) => LibraryPage(), transition: (widget) => DeepLinkTransition( type: DeepLinkTransitionType.fade, - child: LibraryPage(), + child: widget, ), subNavigation: Dispatcher() ..value( From 3fdeabb3dd1bba8081a0759cac1f88c7a1f6fbc9 Mon Sep 17 00:00:00 2001 From: Adrian Axente Date: Wed, 12 Feb 2020 00:24:47 +0200 Subject: [PATCH 7/8] Update README.md --- README.md | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 17d7903..b35032e 100644 --- a/README.md +++ b/README.md @@ -157,17 +157,22 @@ DeepLinkMaterialApp( ```dart ..path((route) => LoginPage()), -..pathRoute((path) => MaterialPageRoute( - builder: (_) => LoginPage())), // with a custom page route or a custom PageRouteBuilder that implement custom route transitions +..pathRoute((path) => LoginPage(), + transition: (widget) => MaterialPageRoute(builder: (_) => widget), +), // with a custom page route or a custom PageRouteBuilder that implement custom route transitions -..pathRoute( - (route) => PageTransition( +..path( + (route) => LoginPage(), + transition: (widget) => DeepLinkTransition( type: PageTransitionType.fade, - child: LoginPage(), + child: widget, ), - ), // with custom PageTransition, an implementation of PageRouteBuilder provided by this package + ), // with custom DeepLinkTransition, an implementation of PageRouteBuilder provided by this package -..pathRoute((route) => LoginPage().fadeTransition()) // with one of the custom transition extensions provided by this package +..pathRoute( + (route) => LoginPage(), + transition: DeepLinkTransitions.fade(), +) // with one of the custom transition shorthand builder functions provided by this package .. ``` @@ -175,14 +180,18 @@ DeepLinkMaterialApp( ```dart ..value((song, route) => SongPage(song: song)), -.valueRoute((song, route) => MaterialPageRoute( - builder: (_) => SongPage(song: song))), // with a custom page route or a custom PageRouteBuilder that implement custom route transitions +.valueRoute( + (song, route) => SongPage(song: song), + transition: (widget) => MaterialPageRoute(builder: (_) => widget), +), // with a custom page route or a custom PageRouteBuilder that implement custom route transitions ..valueRoute( - (artist, route) => ArtistPage(artist: artist).scaleTransition( + (artist, route) => ArtistPage(artist: artist), + transition: DeepLinkTransition.scale( alignment: Alignment.center, duration: Duration(milliseconds: 800), - )) // with one of the custom transition extensions provided by this package + ), +) // with one of the custom transition shorthand builder functions provided by this package ``` **Sub navigation** @@ -247,6 +256,10 @@ All methods internally orchestrate a native flutter navigator. **Push a deep link** ```dart await DeepLinkNavigator.of(context).push(ArtistDL(...)); + +await DeepLinkNavigator.of(context).push( + ArtistDL(...), + transition: ....); //with transition ``` **Pop a value** @@ -264,11 +277,19 @@ DeepLinkNavigator.of(context).navigateTo([ LibraryDL(), ArtistDL(...), ]); + +DeepLinkNavigator.of(context).navigateTo([ + LibraryDL(), + ArtistDL(...), +], +transition: ....); //with transition ``` **Return to default route (if any)** ```dart DeepLinkNavigator.of(context).replaceWithDefault(); + +DeepLinkNavigator.of(context).replaceWithDefault(transition: ...); //with transition ``` **TODO: Throw exception to be caught by mapper** @@ -306,8 +327,6 @@ DeepLinkNavigator.of(context).replaceWithDefault(); ## What's left to do -[ ] Custom/predefined page transitions - [ ] Access deep link navigator from anywhere using static method and factory pattern [ ] Assert `RouteNotFound` dispatcher exists by running through navigation tree From 3ad95a503d0bcbcb64528015a625c644a6bf8236 Mon Sep 17 00:00:00 2001 From: Adrian Axente Date: Wed, 12 Feb 2020 00:28:11 +0200 Subject: [PATCH 8/8] Update README.md --- README.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/README.md b/README.md index b35032e..1b61be6 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,7 @@ DeepLinkMaterialApp( ..valueRoute( (artist, route) => ArtistPage(artist: artist), - transition: DeepLinkTransition.scale( + transition: DeepLinkTransitions.scale( alignment: Alignment.center, duration: Duration(milliseconds: 800), ), @@ -302,18 +302,6 @@ DeepLinkNavigator.of(context).replaceWithDefault(transition: ...); //with transi // TODO DeepLinkNavigator()... ``` -**TODO: Page transitions** -```dart -// await DeepLinkNavigator.of(context).push( -// ArtistDL(...), -// transition: ..., -// duration: ..., -// ); - -// Possibly: -// await DeepLinkNavigator.of(context).fadeIn(...); -``` - ## Platform deep links // TODO: serialize `List`