flutter 路由全解

本教程前半部分属于入门,后半部分属于进阶

1.routes 和navigator 的关系 ?

大部分应用程序都包含多个页面,并希望用户能从当前屏幕平滑过渡到另一个屏幕。移动应用程序通常通过被称为“屏幕”或“页面”的全屏元素来显示内容Flutter 中,这些元素被称为路由(Route),它们由导航器(Navigator)控件管理。导航器管理着路由对象的堆栈并提供管理堆栈的方法,如 Navigator.pushNavigator.pop,通过路由对象的进出栈来使用户从一个页面跳转到另一个页面。

2. 怎么使用 ?

(1)push方法

设置一个构造函数 (第二个page的初始方法)

 final String title;
 Page({this.title});

跳转传值

   Navigator.push(context,
        MaterialPageRoute(builder: (context) => Page(title: "题目")));

返回

   Navigator.pop(context);
(2) pushName方法

在初始化APP的地方MaterialApp,设置一个路由列表

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(  // 这个是标记
      home: MainPage(),
      debugShowCheckedModeBanner: false,
      routes: {  // 就是它
        '/page':(context) => Page()
       '/home':(context) => Home()
      },
    );
  }
}

在需要跳转的时候

    Navigator.pushNamed(context, '/page');

这里有一个问题就是使用 Navigator.pushNamed 时无法直接给新页面传参数,目前官方还没有标准解决方案,我知道的方案是在 onGenerateRoute 回调方法和arguments参数设置。

完整事例

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(

             //监听导航routes 数据  PassArguments
      onGenerateRoute: (settings) {

//对比路由名称
        if (settings.name == PassArgumentsScreen.routeName) {
          final ScreenArguments args = settings.arguments;

           //返回  MaterialPageRoute 
          return MaterialPageRoute(
            builder: (context) {
              return PassArgumentsScreen(
                title: args.title,
                message: args.message,
              );
            },
          );
        }
      },
      title: 'Navigation with Arguments',
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Screen'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
           
                //设置一个button
            RaisedButton(
              child: Text("Navigate to screen that extracts arguments"),
              onPressed: () {
              

             // 设置传值参数  arguments  /settings

                Navigator.push(
                  context,
                  MaterialPageRoute(
                    builder: (context) => ExtractArgumentsScreen(),
             
                    settings: RouteSettings(
                      arguments: ScreenArguments(
                        'Extract Arguments Screen',
                        'This message is extracted in the build method.',
                      ),
                    ),
                  ),
                );
              },
            ),
       
   
            RaisedButton(
              child: Text("Navigate to a named that accepts arguments"),
              onPressed: () {
              

                Navigator.pushNamed(
                  context,
                  PassArgumentsScreen.routeName,
                  arguments: ScreenArguments(
                    'Accept Arguments Screen',
                    'This message is extracted in the onGenerateRoute function.',
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

// push界面
class ExtractArgumentsScreen extends StatelessWidget {
  static const routeName = '/extractArguments';

  @override
  Widget build(BuildContext context) {

 final ScreenArguments args = ModalRoute.of(context).settings.arguments;

    return Scaffold(
      appBar: AppBar(
        title: Text(args.title),
      ),
      body: Center(
        child: Text(args.message),
      ),
    );
  }
}

//接受界面
class PassArgumentsScreen extends StatelessWidget {
  static const routeName = '/passArguments';

  final String title;
  final String message;


  // MaterialApp widget.
  const PassArgumentsScreen({
    Key key,
    @required this.title,
    @required this.message,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Text(message),
      ),
    );
  }
}

// 设置任意参数
class ScreenArguments {
  final String title;
  final String message;

  ScreenArguments(this.title, this.message);
}

看懂了上面的原理当然可以简化了。
定义要接受参数的界面的时候

// 定义接受的构造函数
final arguments;
Page({this.arguments};

在上一界面传值的时候

Navigator.pushNamed(context,'/search',argument:{
    'id':'1123';   // 参数随便写
    'name':'sunny'
})

当然路由配置的时候要这样

final routes = {
    'page':(context,{arguments})=>Page(arguments:arguments);
     'page':(context,{arguments})=>Page(arguments:arguments);
     'page':(context,{arguments})=>Page(arguments:arguments);
     'page':(context,{arguments})=>Page(arguments:arguments);  
}
final  String  name = setting.name;
final  Function  pagContentBuilder = this.routes[name];

当然你也可以吧参数和路由名称都封装到一个类中去 。说说思路,全局单利路由文件设置好 ,设置路由对象 ,对象的个数和名称提前固定 ,可以根据名称随时更改需要传值的参数 。 在界面跳转前更改参数 ,传入参数 ,在界面跳转后接受数据。
面对逆向的时候还可以加上加密措施和代码混淆

//Routes.dart
import 'pa/material'  import '所有界面'
final routes = {
    'page':(context,{arguments})=>Page(arguments:arguments);
     'page':(context,{arguments})=>Page(arguments:arguments);
     'page':(context,{arguments})=>Page(arguments:arguments);
     'page':(context,{arguments})=>Page(arguments:arguments);  
}
// 生成路由回调的的固定写法 
var onGenerateroute = (RounteSettings setting){
   final  String  name = setting.name;
   final  Function  pagContentBuilder = routes[name];
   if (pagContentBuilder != null){
      if(setting.arguments != null){

        final  Route route = MaterialpageRoute(
         buider:(context) => 
               PageContentBuilder(context,arguments:setting.arguments);
         )
         return route;
      }else{
           final  Route route = MaterialpageRoute(
         buider:(context) => 
               PageContentBuilder(context);
         )
         return route;
      }

   }
 
}


3.路由的命名规范 ?

树状结构命名法

'\' #主页
'\home' #主页 下 home界面
'\my' #主页 下 my界面
'\home\name' #主页 下 home界面 下name界面

3.initialRoute 的作用 ?

initialRoute 同样是MaterialApp 的属性 ,也就是相当于iOS里面的appdelegate部分,在全局只能设置一次 。 在routes里面设置的路由都可以通过initialRoute来设置,也就是第一次启动app时候,我们的初始界面是那个。

路由的返回功能

Navigator.of(context).pop();

返回根路由/ 替换路由 在跳转的时候使用替换路由,在返回的时候会直接返回到根目录

Navigator.of(context).pushReplacementNamed('/firstPage');

或者直接返回根的代码/ 没有的话直接更改界面 。

Navigator.of(context).pushAndRemoveUntil(
    // 中间的界面置为空,直到我们返回的页面。
    newMaterialPageRutes(builder:context)=>new Tab bar(index : 1),
    (route)=>route==null;
)

(🔥🔥🔥看到这里的都很厉害,给你们点终极福利)🔥🔥🔥

fluro的封装使用

国外都流行用这个现在,下面是最权威的封装

1设置一个全局的router,

形式多样,单例都可以,此处就放到Application类中了。

//application.dart
import 'package:fluro/fluro.dart';

class Application {
  static Router router;
}
2. 设置router 信息

引入handle文件,具体界面目录

//routers.dart
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'package:~/route_handlers.dart';

class Routes {
  static String root = '/';
//添加路由命名
  static String chatDetail = '/chat';

  static void configureRoutes(Router router) {
    router.notFoundHandler = new Handler(
      handlerFunc: (BuildContext context, Map<String, List<String>> params) {
        print('Route was not found.');
      }
    );
    router.define(root, handler: rootHandler);
//一条一条往后加处理方式
    router.define(chatDetail, handler: chatDetailHandler);
  }
}
3. 设置routerHandle 信息

具体每个页面怎么处理都在这个文件中

//route_handlers.dart
import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'package:flutter_whatsapp/src/home.dart';


// 一条一条往后加,此处没做任何处理
var rootHandler = new Handler(
  handlerFunc: (BuildContext context, Map<String, List<String>> params) {
    return new Home();
  }
);

 //此处解析了一个参数profileId
var chatDetailHandler = new Handler(
    handlerFunc: (BuildContext context, Map<String, List<String>> params) {
      int profileId = int.tryParse(params['profileId']?.first) ?? null;
// 此处调用构造方法
      return new DetailChatScreen(
        id: profileId,  
      );
    }
);

4.使用

MaterialApp中初始化


import 'package:fluro/fluro.dart';
import 'package:flutter/material.dart';
import 'package:config/application.dart';
import 'package:config/routes.dart';
import 'package:/src/home.dart';
import 'package:/src/values/colors.dart';



class FlutteredApp extends StatefulWidget {
  @override
  _FlutteredAppState createState() => _FlutteredAppState();
}

class _FlutteredAppState extends State<FlutteredApp> {

  _FlutteredAppState() {
    final router = new Router();
    Routes.configureRoutes(router);
    Application.router = router;
  }

  /// Default theme.
  static final ThemeData _defaultTheme = new ThemeData(
    primaryColor: primaryColor,
    accentColor: Colors.white,
    scaffoldBackgroundColor: scaffoldBgColor,
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'setting',
      theme: _defaultTheme,
      onGenerateRoute: Application.router.generator,
    );
  }
}

调用

Application.router.navigateTo(
      context,
      "${Router.chatDetail}?profileId=${id}",
      transition: TransitionType.inFromRight,
    );

返回

  Navigator.of(context).pop();

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容