Flutter利用註解生成可自定義的路由

Repositorygit

route_generator是什麼

這是一個簡單的 Flutter 路由生成庫,只須要少許的代碼,而後利用註解配合源代碼生成,自動生成路由表,省去手工管理路由代碼的煩惱。github

特性

  • 自定義路由名稱
  • 自定義路由動畫
  • 自定義路由參數
  • 自定義路由邏輯
  • 支持從pubspec.yaml文件中的依賴包自動生成路由代碼

依賴

dependencies:
  # Your other regular dependencies here
 route_annotation: ^0.2.0

dev_dependencies:
  # Your other dev_dependencies here
 build_runner: ^1.5.0
 route_generator: ^0.1.4
複製代碼

生成代碼

  • 單次構建app

    在項目根目錄中運行flutter pub run build_runner build,能夠在須要時爲項目生成路由代碼。這會觸發一次性構建,該構建遍歷源文件,選擇相關文件,併爲它們生成必要的路由代碼。雖然這很方便,但若是您沒必要每次在模型類中進行更改時都必須手動構建,那麼你能夠選擇持續構建。less

  • 持續構建ide

    在項目根目錄中運行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 這個註解用來標識一個自定義路由頁面的過渡時長
router new Router()相同
page new RoutePage()相同
initialPage new RoutePage(isInitialRoute: true)相同
routeField new RouteField()相同
routeBuilder new PageRouteBuilderFuntcion()相同
pageBuilder new RoutePageBuilderFunction()相同
transitionBuilder new RouteTransitionBuilderFunction()相同
transitionDuration new 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,
    );
  }
}
複製代碼

定義路由頁面

@initialPage
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold();
  }
}

複製代碼

定義路由頁面參數

  • 對於單個參數ui

    @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();
      }
    }
    複製代碼

    導航this

    Navigator.of(context).pushNamed(
      ROUTE_ONE_ARGUMENT_PAGE,
      arguments: "title is empty",
    );
    複製代碼

    注意事項:spa

    對於單個參數的路由,利用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)

這種方法自定義路由的優先級最高,若是同時存在多種自定義路由選擇,該種方案最早被選擇。

@page
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)

這種方法自定義路由的優先級較低,若是同時存在多種自定義路由選擇,則按優先級從大到小選擇。

@page
class CustomRoutePage extends StatelessWidget {
  @pageBuilder
  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)

這種方法自定義路由的優先級最低,若是同時存在多種自定義路由選擇,則按優先級從大到小選擇。

@page
class CustomRoutePage extends StatelessWidget {
  // pageBuilder註解代表這個方法用來定義如何返回RoutePage
  // 它是可選的
  @pageBuilder
  static Widget buildPage(BuildContext context, Animation animation,
          Animation secondaryAnimation, RouteSettings settings) =>
      CustomRoutePage();

  // transitionBuilder註解代表這個方法用來定義如何應用動畫過渡
  // 它是可選的
  @transitionBuilder
  static Widget buildTransitions(
          BuildContext context,
          Animation<double> animation,
          Animation<double> secondaryAnimation,
          Widget child,
          RouteSettings settings) =>
      child;

  // transitionDuration註解代表這個字段用來定義頁面過渡時常長,默認值爲300 milliseconds
  // 它是可選的
  @transitionDuration
  static Duration transitionDurations = Duration(milliseconds: 400);

  ...

}
複製代碼

它會生成以下代碼:

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.transitionDurations,
      ),
};
複製代碼

從pubspec.yaml文件中的依賴包自動生成路由代碼

  1. 在須要支持從pubspec.yaml文件中的依賴包自動生成路由代碼的項目根目錄下,新建build.yaml文件,若是已經存在,則跳過這一步。

  2. 在文件中添加如下內容:

    # 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部分請合併。

  3. 從新運行build_runner command便可

獲取更詳細信息,請參閱example

關於build_runner watch模式下的問題

  • 須要瞭解的是:pubspec.yaml dependencies packages 不支持watch模式持續生成路由代碼(第一次生成依然是有效的),可是你任然能夠在當前的application啓用watch模式。後期考慮支持。

  • 因爲BuildStep不支持同一文件的不一樣輸出,即對於每個文件,它的輸出文件是限定了的,因此watch模式下,若是你修改了註解信息,那麼你可能須要使Route註解所在的文件刷新一次(必須使文件出現改動,而且保存,例如添加空行),纔會從新生成xxx.route.dart。正在盡力解決,目前方案須要手動刷新一次,若是你們有更好的方案,歡迎提出。

注意事項

  • 只容許有一個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 'package:example_library/example_library.dart';
import 'package:example/custom_route_name_page.dart';
import 'package:example/custom_route_page.dart';
import 'package:example/home_page.dart';
import 'package:example/one_arguement_page.dart';
import 'package:example/two_arguement_page.dart';
import 'package:example/second_page.dart';

const ROUTE_LIBRARY_PAGE = 'library_page';
const ROUTE_CUSTOM = 'custom';
const ROUTE_CUSTOM_ROUTE_PAGE = 'custom_route_page';
const ROUTE_HOME = '/';
const ROUTE_ONE_ARGUMENT_PAGE = 'one_argument_page';
const ROUTE_TWO_ARGUMENT_PAGE = 'two_argument_page';
const ROUTE_SECOND_PAGE = 'second_page';

RouteFactory onGenerateRoute = (settings) => Map.fromEntries([
      ..._libraryPage.entries,
      ..._custom.entries,
      ..._customRoutePage.entries,
      ..._home.entries,
      ..._oneArgumentPage.entries,
      ..._twoArgumentPage.entries,
      ..._secondPage.entries,
    ])[settings.name](settings);

Map<String, RouteFactory> _libraryPage = <String, RouteFactory>{
  'library_page': (RouteSettings settings) => MaterialPageRoute(
        builder: (BuildContext context) => LibraryPage(),
      ),
};
Map<String, RouteFactory> _custom = <String, RouteFactory>{
  'custom': (RouteSettings settings) => MaterialPageRoute(
        builder: (BuildContext context) => CustomRoutePageName(),
      ),
};
Map<String, RouteFactory> _customRoutePage = CustomRoutePage.route;
Map<String, RouteFactory> _home = <String, RouteFactory>{
  '/': (RouteSettings settings) => MaterialPageRoute(
        builder: (BuildContext context) => HomePage(),
      ),
};
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) {
          final arguments = settings.arguments as Map<String, dynamic>;
          return TwoArgumentPage(
            title: arguments['title'],
            subTitle: arguments['subTitle'],
          );
        },
      ),
};
Map<String, RouteFactory> _secondPage = <String, RouteFactory>{
  'second_page': (RouteSettings settings) => MaterialPageRoute(
        builder: (BuildContext context) => SecondPage(),
      ),
};

複製代碼

常見問題

  • 沒有生成路由文件

    請檢查是否添加了Router註解

  • 路由生成不完整

    請嘗試運行如下命令:

    • flutter pub run build_runner clean
    • flutter pub run build_runner build

Example

獲取更詳細信息,請參閱example

相關文章
相關標籤/搜索