开源软件名称(OpenSource Name):Milad-Akarie/auto_route_library开源软件地址(OpenSource Url):https://github.com/Milad-Akarie/auto_route_library开源编程语言(OpenSource Language):Dart 99.6%开源软件介绍(OpenSource Introduction):
IntroductionWhat is AutoRoute?It’s a Flutter navigation package, it allows for strongly-typed arguments passing, effortless deep-linking and it uses code generation to simplify routes setup, with that being said it requires a minimal amount of code to generate everything needed for navigation inside of your App. Why AutoRoute?If your App requires deep-linking or guarded routes or just a clean routing setup you'll need to use named/generated routes and you’ll end up writing a lot of boilerplate code for mediator argument classes, checking for required arguments, extracting arguments and a bunch of other stuff. AutoRoute does all that for you and much more. Installationdependencies:
auto_route: [latest-version]
dev_dependencies:
auto_route_generator: [latest-version]
build_runner: Setup And UsageCreate a placeholder class and annotate it with Note: Unless you want to generate a part of file (.gr.dart) The name of the router must be prefixed with $ so we have a generated class with the same name minus the $.
// @CupertinoAutoRouter
// @AdaptiveAutoRouter
// @CustomAutoRouter
@MaterialAutoRouter(
replaceInRouteName: 'Page,Route',
routes: <AutoRoute>[
AutoRoute(page: BookListPage, initial: true),
AutoRoute(page: BookDetailsPage),
],
)
class $AppRouter {} Using part builder ( New in version 3.0.0+)To generate a part-of file instead of a stand alone part 'app_router.gr.dart';
@MaterialAutoRouter(
replaceInRouteName: 'Page,Route',
routes: <AutoRoute>[
AutoRoute(page: BookListPage, initial: true),
AutoRoute(page: BookDetailsPage),
],
)
// extend the generated private router
class AppRouter extends _$AppRouter{} Tip: You can Shorten auto-generated route names from e.g. BookListPageRoute to BookListRoute using the replaceInRouteName argument. Now simply run the generatorUse the [watch] flag to watch the files' system for edits and rebuild as necessary.
if you want the generator to run one time and exits use
Finalize the setupafter you run the generator your router class will be generated, hook it up with MaterialApp. // assuing this is the root widget of your App
class App extends StatelessWidget {
// make sure you don't initiate your router
// inside of the build function.
final _appRouter = AppRouter();
@override
Widget build(BuildContext context){
return MaterialApp.router(
routerDelegate: _appRouter.delegate(),
routeInformationParser: _appRouter.defaultRouteParser(),
);
}
} Generated RoutesA class BookListRoute extends PageRouteInfo {
const BookListRoute() : super(name, path: '/books');
static const String name = 'BookListRoute';
} if the declared route has children AutoRoute will add a children parameter to its constructor to be used in nested navigation. more on that here. class UserRoute extends PageRouteInfo {
UserRoute({List<PagerouteInfo> children}) :
super(
name,
path: '/user/:id',
initialChildren: children);
static const String name = 'UserRoute';
} Navigating Between Screens
// get the scoped router by calling
AutoRouter.of(context)
// or using the extension
context.router
// adds a new entry to the pages stack
router.push(const BooksListRoute())
// or by using using paths
router.pushNamed('/books')
// removes last entry in stack and pushs provided route
// if last entry == provided route page will just be updated
router.replace(const BooksListRoute())
// or by using using paths
router.replaceNamed('/books')
// pops until provided route, if it already exists in stack
// else adds it to the stack (good for web Apps).
router.navigate(const BooksListRoute())
// or by using using paths
router.navigateNamed('/books')
// on Web it calls window.history.back();
// on Native it navigates you back
// to the previous location
router.navigateBack();
// adds a list of routes to the pages stack at once
router.pushAll([
BooksListRoute(),
BookDetailsRoute(id:1),
]);
// This's like providing a completely new stack as it rebuilds the stack
// with the list of passed routes
// entires might just update if already exist
router.replaceAll([
LoginRoute()
]);
// pops the last page unless stack has 1 entry
context.router.pop();
// keeps poping routes until predicate is satisfied
context.router.popUntil((route) => route.name == 'HomeRoute');
// a simplifed version of the above line
context.router.popUntilRouteWithName('HomeRoute');
// pops all routes down to the root
context.router.popUntilRoot();
// removes the top most page in stack even if it's the last
// remove != pop, it doesn't respect WillPopScopes it just
// removes the entry.
context.router.removeLast();
// removes any route in stack that satisfis the predicate
// this works exactly like removing items from a regualar List
// <PageRouteInfo>[...].removeWhere((r)=>)
context.router.removeWhere((route) => );
// you can also use the common helper methods from context extension to navigate
context.pushRoute(const BooksListRoute());
context.replaceRoute(const BooksListRoute());
context.navigateTo(const BooksListRoute());
context.navigateNamedTo('/books');
context.navigateBack();
context.popRoute(); Passing ArgumentsThat's the fun part! AutoRoute automatically detects and handles your page arguments for you, the generated route object will deliver all the arguments your page needs including path/query params. e.g. The following page widget will take an argument of type class BookDetailsPage extends StatelessWidget {
const BookDetailsPage({required this.book});
final Book book;
... Note: Default values are respected. Required fields are also respected and handled properly. The generated
Note: all args are generated as named parameters regardless of their original type. Returning ResultsYou can return results by either using the pop completer or by passing a callback function as an argument the same way you'd pass an object. 1 - Using the pop completer var result = await router.push(LoginRoute()); then inside of your router.pop(true); as you'd notice we did not specify the result type, we're playing with dynamic values here, which can be risky and I personally don't recommend it. To avoid working with dynamic values we specify what type of results we expect our page to return, which is a AutoRoute<bool>(page: LoginPage), we push and specify the type of results we're expecting var result = await router.push<bool>(LoginRoute()); and of course we pop with the same type router.pop<bool>(true); 2- Passing a callback function as an argument. class BookDetailsPage extends StatelessWidget {
const BookDetailsRoute({this.book, required this.onRateBook});
final Book book;
final void Function(int) onRateBook;
... The generated context.router.push(
BookDetailsRoute(
book: book,
onRateBook: (rating) {
// handle result
}),
); if you're finishing with results make sure you call the callback function as you pop the page onRateBook(RESULT);
context.router.pop(); Note: Default values are respected. Required fields are also respected and handled properly. Nested NavigationNested navigation means building an inner router inside of a page of another router, for example in the below diagram users page is built inside of dashboard page. defining nested routes is as easy as populating the children field of the parent route. In the following example @MaterialAutoRouter(
replaceInRouteName: 'Page,Route',
routes: <AutoRoute>[
AutoRoute(
path: '/dashboard',
page: DashboardPage,
children: [
AutoRoute(path: 'users', page: UsersPage),
AutoRoute(path: 'posts', page: PostsPage),
AutoRoute(path: 'settings', page: SettingsPage),
],
),
AutoRoute(path: '/login', page: LoginPage)
],
)
class $AppRouter {} To render/build nested routes we need an class DashboardPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Row(
children: [
Column(
children: [
NavLink(label: 'Users', destination: const UsersRoute()),
NavLink(label: 'Posts', destination: const PostsRoute()),
NavLink(label: 'Settings', destination: const SettingsRoute()),
],
),
Expanded(
// nested routes will be rendered here
child: AutoRouter(),
)
],
);
}
} Now if we navigate to What if want to show one of the child pages at AutoRoute(
path: '/dashboard',
page: DashboardPage,
children: [
AutoRoute(path: '', page: UsersPage),
//The same thing can be done using the initial flag
//AutoRoute(page: UsersPage,initial: true),
AutoRoute(path: 'posts', page: PostsPage),
],
), or by using a AutoRoute(
path: '/dashboard',
page: DashboardPage,
children: [
RedirectRoute(path: '', redirectTo: 'users'),
AutoRoute(path: 'users', page: UsersPage),
AutoRoute(path: 'posts', page: PostsPage),
],
), which can be simplified to the following where AutoRoute(
path: '/dashboard',
page: DashboardPage,
children: [
// RedirectRoute(path: '', redirectTo: 'users'),
AutoRoute(path: 'users', page: UsersPage, initial: true),
AutoRoute(path: 'posts', page: PostsPage),
],
), Things to keep in mind when implementing nested navigation1- Each router manages it's own pages stack. Tab NavigationIf you're working with flutter mobile you're most likely to implement tabs navigation, that's why in the previous example we used an Now we can try to implement our tabs using an
Let's change the previous example to use tab navigation. Notice that we're not going to change anything in our routes declaration map, we still have a dashboard page that has tree nested children, users, posts and settings. class DashboardPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AutoTabsRouter(
// list of your tab routes
// routes used here must be declaraed as children
// routes of /dashboard
routes: const [
UsersRoute(),
PostsRoute(),
SettingsRoute(),
],
builder: (context, child, animation) {
// obtain the scoped TabsRouter controller using context
final tabsRouter = AutoTabsRouter.of(context);
// Here we're building our Scaffold inside of AutoTabsRouter
// to access the tabsRouter controller provided in this context
//
//alterntivly you could use a global key
return Scaffold(
body: FadeTransition(
opacity: animation,
// the passed child is techinaclly our animated selected-tab page
child: child,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: tabsRouter.activeIndex,
onTap: (index) {
// here we switch between tabs
tabsRouter.setActiveIndex(index);
},
items: [
BottomNavigationBarItem(label: 'Users',...),
BottomNavigationBarItem(label: 'Posts',...),
BottomNavigationBarItem(label: 'Settings',...),
],
));
},
);
}
} if you think the above setup is a bit messy you could use the shipped-in class DashboardPage extends StatelessWidget {
@override
Widget build(context) {
@override
Widget build(context) {
return AutoTabsScaffold(
routes: const [
UsersRoute(),
PostsRoute(),
SettingsRoute(),
],
bottomNavigationBuilder: (_,tabsRouter) {
return BottomNavigationBar(
currentIndex: tabsRouter.activeIndex,
onTap: tabsRouter.setActiveIndex
items: [
BottomNavigationBarItem(label: 'Users',...),
BottomNavigationBarItem(label: 'Posts',...),
BottomNavigationBarItem(label: 'Settings',...),
],
)),
}
);
} Using PageViewUse the AutoTabsRouter.pageView(
routes: [
BooksTab(),
ProfileTab(),
SettingsTab(),
],
builder: (context, child, _) {
return Scaffold(
appBar: AppBar(
title: Text(context.topRoute.name),
leading: AutoLeadingButton()),
body: child,
bottomNavigationBar: BottomNavigationBar(
currentIndex: tabsRouter.activeIndex,
onTap: tabsRouter.setActiveIndex
items: [
BottomNavigationBarItem(label: 'Books',...),
BottomNavigationBarItem(label: 'Profile',...),
BottomNavigationBarItem(label: 'Settings',...),
],
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论