- 2019/08/09 更新
- route_generator是什麼
- 特性
- 依賴
- 生成程式碼
- route_annotation
- 程式碼示例
- 自定義路由(優先順序:3)
- 自定義路由(優先順序:2)
- 自定義路由(優先順序:1)
- 注意事項
- 最終生成程式碼
- 常見問題
- Example
2019/08/09 更新
從pubspec.yaml檔案中的依賴包自動生成路由程式碼
感謝@法的空間網友的建議,新的版本支援從pubspec.yaml檔案中的依賴包自動生成路由程式碼了。 啟用方法如下:
-
在需要支援從pubspec.yaml檔案中的依賴包自動生成路由程式碼的專案根目錄下,新建build.yaml檔案,如果已經存在,則跳過這一步。
-
在檔案中新增以下內容:
# If you are sure that you only run `flutter pub run build_runner build`, # and don't run `flutter pub run build_runner watch`, then you can enable # the following comment out content. # targets: # $default: # builders: # route_generator|route_collector: # enabled: false # If you also want to enable source code generation for the packages of # dependencies in the pubspec.yaml, I think the following is what you need. builders: route_collector_all_packages: import: 'package:route_generator/builder.dart' builder_factories: ['routeCollectorAllPackages'] build_extensions: { '.dart': ['.collector_all_packages.dart'] } auto_apply: all_packages runs_before: ["route_generator|route_builder"] build_to: cache 複製程式碼
注意相同key部分請合併。
-
重新執行build_runner command即可
獲取更詳細資訊,請參閱example
關於build_runner watch模式下的問題
-
需要了解的是:pubspec.yaml dependencies packages 不支援watch模式持續生成路由程式碼(第一次生成依然是有效的),但是你任然可以在當前的application啟用watch模式。後期考慮支援。
-
由於BuildStep不支援同一檔案的不同輸出,即對於每一個檔案,它的輸出檔案是限定了的,所以watch模式下,如果你修改了註解資訊,那麼你可能需要使Route註解所在的檔案重新整理一次(必須使檔案出現改動,並且儲存,例如新增空行),才會重新生成xxx.route.dart。正在盡力解決,目前方案需要手動重新整理一次,如果大家有更好的方案,歡迎提出。
route_generator是什麼
這是一個簡單的 Flutter 路由生成庫,只需要少量的程式碼,然後利用註解配合原始碼生成,自動生成路由表,省去手工管理路由程式碼的煩惱。
特性
- 自定義路由名稱
- 自定義路由動畫
- 自定義路由引數
- 自定義路由邏輯
- 支援從pubspec.yaml檔案中的依賴包自動生成路由程式碼
依賴
dependencies:
# Your other regular dependencies here
route_annotation: ^0.1.0
dev_dependencies:
# Your other dev_dependencies here
build_runner: ^1.5.0
route_generator: ^0.1.2
複製程式碼
生成程式碼
-
單次構建
在專案根目錄中執行
flutter pub run build_runner build
,可以在需要時為專案生成路由程式碼。這會觸發一次性構建,該構建遍歷原始檔,選擇相關檔案,併為它們生成必要的路由程式碼。雖然這很方便,但如果您不必每次在模型類中進行更改時都必須手動構建,那麼你可以選擇持續構建。 -
持續構建
在專案根目錄中執行
flutter pub run build_runner watch
來啟動watcher,它可以使我們的原始碼生成過程更加方便。它會監視專案檔案中的更改,並在需要時自動構建必要的檔案。
route_annotation
annotation | description |
---|---|
Router |
此註解用來標誌某個為 Flutter App 的類,並以此生成相應的路由程式碼 |
RoutePage |
此註解用來註解一個路由頁面 |
RouteParameter |
一個用來標誌頁面引數的註解,只為可選引數設計。用於 RoutePage 。 |
RouteField |
此註解用來標誌一個完全自定義的路由,被註解的物件必須作為路由頁面類靜態欄位 |
PageRouteBuilderFuntcion |
這個註解用來標識一個路由頁面的 RouteFactory 靜態方法 |
RoutePageBuilderFunction |
這個註解用來標識一個路由頁面的 RoutePageBuilder 靜態方法 |
RouteTransitionBuilderFunction |
這個註解用來標識一個路由頁面的 TransitionBuilder 靜態方法 |
RouteTransitionDurationField |
這個註解用來標識一個自定義路由頁面的過渡時長 |
程式碼示例
定義路由 App
@Router()
class DemoApp extends StatefulWidget {
@override
_DemoAppState createState() => _DemoAppState();
}
class _DemoAppState extends State<DemoApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: "/",
onGenerateRoute: onGenerateRoute,
);
}
}
複製程式碼
定義路由頁面
// isInitialRoute為true表示它將作為initial page
@RoutePage(isInitialRoute: true)
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold();
}
}
複製程式碼
定義路由頁面引數
-
對於單個引數
@RoutePage(params: [RouteParameter("title")]) class OneArgumentPage extends StatelessWidget { final String title; const OneArgumentPage({Key key, this.title}) : super(key: key); @override Widget build(BuildContext context) { return Container(); } } 複製程式碼
導航
Navigator.of(context).pushNamed( ROUTE_ONE_ARGUMENT_PAGE, arguments: "title is empty", ); 複製程式碼
注意事項:
對於單個引數的路由,利用Navigator進行導航的時候
arguments
即為原始引數。 -
對於多個引數
@RoutePage(params: [RouteParameter("title"), RouteParameter("subTitle")]) class TwoArgumentPage extends StatelessWidget { final String title; final String subTitle; TwoArgumentPage({this.title, Key key, this.subTitle}) : super(key: key); @override Widget build(BuildContext context) { return Scaffold(); } } 複製程式碼
導航
Navigator.of(context).pushNamed( ROUTE_TWO_ARGUMENT_PAGE, arguments: { "title": _titleController.text.isNotEmpty ? _titleController.text : "title is empty", "subTitle": _subTitleController.text.isNotEmpty ? _subTitleController.text : "sub title is empty", }, ); 複製程式碼
注意事項:
對於多個引數的路由,利用Navigator進行導航的時候
arguments
必須為Map<string,dynamic>
。
如果你不需要自定義路由,以下部分,你可以什麼都不用新增,就讓route_generator為你自動生成相關程式碼吧!
自定義路由(優先順序:3)
這種方法自定義路由的優先順序最高,如果同時存在多種自定義路由選擇,該種方案最先被選擇。
@RoutePage()
class CustomRoutePage extends StatelessWidget {
@RouteField()
static Map<String, RouteFactory> route = <String, RouteFactory>{
'custom_route': (RouteSettings settings) =>
MaterialPageRoute(builder: (BuildContext context) => CustomRoutePage()),
'alias_route': (RouteSettings settings) => PageRouteBuilder(
pageBuilder: (BuildContext context, Animation animation,
Animation secondaryAnimation) =>
CustomRoutePage(),
),
};
...
}
複製程式碼
它會生成如下程式碼:
Map<String, RouteFactory> _customRoutePage = CustomRoutePage.route;
複製程式碼
自定義路由(優先順序:2)
這種方法自定義路由的優先順序較低,如果同時存在多種自定義路由選擇,則按優先順序從大到小選擇。
@RoutePage()
class CustomRoutePage extends StatelessWidget {
@PageRouteBuilderFuntcion()
static Route buildPageRoute(RouteSettings settings) => PageRouteBuilder(
pageBuilder: (BuildContext context, Animation animation,
Animation secondaryAnimation) =>
CustomRoutePage(),
);
...
}
複製程式碼
它會生成如下程式碼:
Map<String, RouteFactory> _customRoutePage = <String, RouteFactory>{
'custom_route_page': CustomRoutePage.buildPageRoute,
};
複製程式碼
自定義路由(優先順序:1)
這種方法自定義路由的優先順序最低,如果同時存在多種自定義路由選擇,則按優先順序從大到小選擇。
@RoutePage()
class CustomRoutePage extends StatelessWidget {
// RoutePageBuilderFunction註解表明這個方法用來定義如何返回RoutePage
// 它是可選的
@RoutePageBuilderFunction()
static Widget buildPage(BuildContext context, Animation animation,
Animation secondaryAnimation, RouteSettings settings) =>
CustomRoutePage();
// RouteTransitionBuilderFunction註解表明這個方法用來定義如何應用動畫過渡
// 它是可選的
@RouteTransitionBuilderFunction()
static Widget buildTransitions(
BuildContext context,
Animation<double> animation,
Animation<double> secondaryAnimation,
Widget child,
RouteSettings settings) =>
child;
// RouteTransitionDurationField註解表明這個欄位用來定義頁面過渡時常長,預設值為300 milliseconds
// 它是可選的
@RouteTransitionDurationField()
static Duration transitionDuration = Duration(milliseconds: 400);
...
}
複製程式碼
它會生成如下程式碼:
Map<String, RouteFactory> _customRoutePage = <String, RouteFactory>{
'custom_route_page': (RouteSettings settings) => PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) =>
CustomRoutePage(),
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
CustomRoutePage.buildTransitions(
context, animation, secondaryAnimation, child, settings),
transitionDuration: CustomRoutePage.transitionDuration,
),
};
複製程式碼
注意事項
- 只允許有一個initalRoute
- initalRoute會忽略自定義路由名,但會生成名為
ROUTE_HOME
的路由名稱常量。 - 所有自定義路由method或getter必須定義在路由所在類,且必須為static所修飾的和非私有的。
最終生成程式碼
最終生成的檔名為FILENAME.route.dart 其中FILENAME是被Router註解的App類所在的檔名。
// GENERATED CODE - DO NOT MODIFY BY HAND
// **************************************************************************
// RouteGenerator
// **************************************************************************
import 'package:flutter/material.dart';
import 'home_page.dart';
import 'custom_route_page.dart';
import 'custom_route_name_page.dart';
import 'second_page.dart';
import 'one_arguement_page.dart';
import 'two_arguement_page.dart';
const ROUTE_HOME = '/';
const ROUTE_CUSTOM_ROUTE_PAGE = 'custom_route_page';
const ROUTE_CUSTOM = 'custom';
const ROUTE_SECOND_PAGE = 'second_page';
const ROUTE_ONE_ARGUMENT_PAGE = 'one_argument_page';
const ROUTE_TWO_ARGUMENT_PAGE = 'two_argument_page';
RouteFactory onGenerateRoute = (settings) => Map.fromEntries([
..._home.entries,
..._customRoutePage.entries,
..._custom.entries,
..._secondPage.entries,
..._oneArgumentPage.entries,
..._twoArgumentPage.entries,
])[settings.name](settings);
Map<String, RouteFactory> _home = <String, RouteFactory>{
'/': (RouteSettings settings) => MaterialPageRoute(
builder: (BuildContext context) => HomePage(),
),
};
Map<String, RouteFactory> _customRoutePage = <String, RouteFactory>{
'custom_route_page': (RouteSettings settings) => PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) =>
CustomRoutePage.buildPage(
context, animation, secondaryAnimation, settings),
transitionsBuilder: (context, animation, secondaryAnimation, child) =>
CustomRoutePage.buildTransitions(
context, animation, secondaryAnimation, child, settings),
transitionDuration: CustomRoutePage.transitionDuration,
),
};
Map<String, RouteFactory> _custom = <String, RouteFactory>{
'custom': (RouteSettings settings) => MaterialPageRoute(
builder: (BuildContext context) => CustomRoutePageName(),
),
};
Map<String, RouteFactory> _secondPage = <String, RouteFactory>{
'second_page': (RouteSettings settings) => MaterialPageRoute(
builder: (BuildContext context) => SecondPage(),
),
};
Map<String, RouteFactory> _oneArgumentPage = <String, RouteFactory>{
'one_argument_page': (RouteSettings settings) => MaterialPageRoute(
builder: (BuildContext context) =>
OneArgumentPage(title: settings.arguments),
),
};
Map<String, RouteFactory> _twoArgumentPage = <String, RouteFactory>{
'two_argument_page': (RouteSettings settings) => MaterialPageRoute(
builder: (BuildContext context) => TwoArgumentPage(
title: (settings.arguments as Map<String, dynamic>)['title'],
subTitle:
(settings.arguments as Map<String, dynamic>)['subTitle'],
),
),
};
複製程式碼
常見問題
-
沒有生成路由檔案
請檢查是否新增了Router註解
Example
獲取更詳細資訊,請參閱example