【Flutter】Navigator与go_router


前言

路由之间的跳转是任何前端开发绕不开的话题!


一、路由

  • Navigator是一个路由管理的组件
  • route一个屏幕,或者是指一个Page Widget

二、路由类型

1.静态路由(也叫命名路由)

代码如下(示例):
基础的页面直接的跳转,从homepage 跳转到 onepage,命名好对应的路由的名字,后续跳转直接填写路由的名字就可以跳转到指定的路由页面

void main() {
  runApp(
    MyApp(),
  );
}

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter route',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
      routes: {
        // 是个map 的形式,'/' 表示了默认路由,也就是主页面
        "/": (context) => const HomePage(),
        "/one": (context) => OnePage(),
      },
      initialRoute: '/',
    );
  }
}

//命名好之后,可以在跳转的时候直接调用名字:
Navigator.of(context).pushNamed("/one");//跳转到页面one 对应的OnePage()
Navigator.pushNamed(context, "new_page");

2.动态路由

代码如下(示例):
不需要注册,可以直接调用,和跳转到指定的页面

//MaterialPageRoute 是 android 风格页面切换
//CupertinoPageRoute 是ios 风格的页面切换
//

//方式一
Navigator.push(context, MaterialPageRoute(builder: (context){
  return OnePage();
}));

//方式二
Navigator.of(context).push(
  MaterialPageRoute(builder: (context){
    return OnePage();
  })
);

PageRouteBuilder:实现自定义页面切换的动画功能

Navigator.push(
    context,
    PageRouteBuilder(pageBuilder: (
      BuildContext context,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
    ) {
      return _TwoPage();
    }, transitionsBuilder: (BuildContext context,
        Animation<double> animation,
        Animation<double> secondaryAnimation,
        Widget child) {
      return SlideTransition(
        position: Tween(begin: Offset(-1, 0), end: Offset(0, 0))
            .animate(animation),
        child: child,
      );
    }));


Navigator

提示:Navigator是一个路由管理的组件

  • Future push(BuildContext context, Route route):将给定的路由入栈,返回值是一个Future对象,用以接收新路由出栈(即关闭)时的返回数据。
    exp:
    Navigator.push(context, MaterialPageRoute(builder: (context){
    return OnePage();
    }))
  • pop:出栈,顶出顶部的栈。Navigator.pop(context);返回上一个路由页面:
  • 其他的方法 pushName等
  • 在这里插入图片描述

路由传值的实现

动态路由跳转传递参数:


import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    MyApp(),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter route',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
      // routes: {
      //   // 是个map 的形式,'/' 表示了默认路由,也就是主页面
      //   "/": (context) => const HomePage(),
      // },
      // initialRoute: '/',
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // List<Widget> listWidget = [OnePage()];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('route 功能演示'),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            TextButton(
              onPressed: () async {
                var result = await Navigator.of(context)
                    .push(MaterialPageRoute(builder: (contest) {
                  return OnePage(
                    text: '我是传递进去的数值',
                  );
                }));
                print("路由返回值: $result");
              },
              child: const Text('跳转带参数'),
            ),
          ],
        ),
      ),
    );
  }
}

class OnePage extends StatelessWidget {
  OnePage({Key? key, required this.text}) : super(key: key);
  final String text;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('data'),
      ),
      body: Center(
        child: Container(
            child: Column(
          children: [
            Text(text),
            IconButton(
              onPressed: () => Navigator.pop(context, '我是返回的数值'),
              icon: const Icon(Icons.backpack),
            ),
          ],
        )),
      ),
    );
  }
}

静态路由页面跳转传递参数:
//Navigator.of(context).pushNamed("/onapge",arguments:{"argms":"这是传入A的参数"});

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    MyApp(),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter route',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      // home: HomePage(),
      routes: {
        // 是个map 的形式,'/' 表示了默认路由,也就是主页面
        "/": (context) => const HomePage(),
        '/onapge': (context) => OnePage(
              text: 'first title',
            )
      },
      initialRoute: '/',
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // List<Widget> listWidget = [OnePage()];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('route 功能演示'),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            TextButton(
              onPressed: () async {
                var result = await Navigator.of(context)
                    .pushNamed('/onapge', arguments: 'two title');
                print("路由返回值: $result");
              },
              child: const Text('跳转带参数'),
            ),
          ],
        ),
      ),
    );
  }
}

class OnePage extends StatelessWidget {
  OnePage({Key? key, required this.text}) : super(key: key);
  final String text;

  @override
  Widget build(BuildContext context) {
  //获取到传递进来的参数
    var args = ModalRoute.of(context)?.settings.arguments;
    return Scaffold(
      appBar: AppBar(
        title: Text('data'),
      ),
      body: Center(
        child: Container(
            child: Column(
          children: [
            Text(args.toString() ?? 'ddd'),
            IconButton(
              onPressed: () => Navigator.pop(context, '我是返回的数值'),
              icon: const Icon(Icons.backpack),
            ),
          ],
        )),
      ),
    );
  }
}


onGenerateRoute

可以通过onGenerateRoute控制路由页面跳转的权限,比如哪些页面需要登陆之后才能查看和查阅的,就可以在onGenerateRoute里面进行处理,避免在每个page 的页面里面进行判断处理,路由拦截的功能


## 抽离main.dart里面的路由使代码更加简单

```dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'myroute.dart';
void main() {
  runApp(
    MyApp(),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter route',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      initialRoute: MYRouter.initialRoute,
      routes: MYRouter.routes,
      onGenerateRoute: MYRouter.generateRoute,
      onUnknownRoute: MYRouter.unknownRoute,

    );
  }
}

class HomePage extends StatefulWidget {
    static const String routeName = "/homepage";
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // List<Widget> listWidget = [OnePage()];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('route 功能演示'),
      ),
      body: SingleChildScrollView(
        child: Column(
          children: [
            TextButton(
              onPressed: () {

              },
              child: const Text('跳转带参数'),
            ),
          ],
        ),
      ),
    );
  }
}


import 'package:flutter/material.dart';
import 'package:testcopy/main.dart';
import 'one.dart';
import 'two.dart';
import 'main.dart';

class MYRouter {
  static final Map<String, WidgetBuilder> routes = {
    One.routeName: (ctx) =>
        One(), // 记得在页面里面 声明静态变量 static const String routeName = "/";
    Two.routeName: (ctx) =>
        Two() //  声明静态变量 static const String routeName = "/about";
  };
  static const String initialRoute = HomePage.routeName;
  
  static final RouteFactory generateRoute = (settings) {
    //判断当前的路由使什么名字,路由页面是否需要登陆之后才能查看可以在这边做操作
    if (settings.name == NewPage.routeName) {
      return MaterialPageRoute(builder: (ctx) {
        return NewPage(settings.arguments);
      });
    }
    return null;
  };
  //路由跳转不到指定的路由页面的时候,就显示该缺省页面
  static final RouteFactory unknownRoute = (settings) {
    return MaterialPageRoute(builder: (ctx) {
      return UnkonwPage();
    });
  };
}


import 'package:flutter/material.dart';

class One extends StatefulWidget {
  static const String routeName = "/one";
  const One({Key? key}) : super(key: key);

  
  _OneState createState() => _OneState();
}

class _OneState extends State<One> {
  
  Widget build(BuildContext context) {
    return Container();
  }
}

import 'package:flutter/material.dart';

class Two extends StatefulWidget {
  static const String routeName = "/two";
  const Two({Key? key}) : super(key: key);

  
  _TwoState createState() => _TwoState();
}

class _TwoState extends State<Two> {
  
  Widget build(BuildContext context) {
    return Container();
  }
}


三.go_router

go_router是flutter.dev 官方推出的路由管理插件,非内置,有需要导入

push 和go的区别

  • push到的页面会被压入栈,将目标路由添加到现有路由之上,go会丢弃前page,替换为go到的页面
  • 假如在当前页面,点击A跳转到A页面,使用push。点击B跳转到B页面,使用GO。那么点击A的返回按键会回到刚才的页面,在B 页面点击返回不能直接,需要在backbutton 添加 go到主页面的功能

在这里插入图片描述
在这里插入图片描述

参数的传递 go_router

例子:

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

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

class App extends StatelessWidget {
  App({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routeInformationProvider: _router.routeInformationProvider,
      routeInformationParser: _router.routeInformationParser,
      routerDelegate: _router.routerDelegate,
      title: 'GoRouter Example',
      theme: ThemeData(primaryColor: Colors.cyanAccent),
    );
  }

  final GoRouter _router = GoRouter(
    // errorBuilder: (context, state) => ScreenA(state.error),//遇到错误的页面或者是没有路径的路由,会导入到当前页面
    initialLocation: '/',
    routes: <GoRoute>[
      GoRoute(
        path: '/sp',
        name: 'sp',
        builder: (BuildContext context, GoRouterState state) {
          // state.queryParams 接收用问号隔开的参数
          final query = state.queryParams['query'];
          return SearchPage(query: query!);
        },
      ),
      GoRoute(
        path: '/md/:id',
        name: 'md',
        builder: (BuildContext context, GoRouterState state) {
          // state.params 接收 `/` 隔开的参数(按位置)
          final movieid = state.params['id']!;
          return MovieDetailPage(
            id: movieid,
          );
        },
      ),
      GoRoute(
        name: 'setpage',
        path: '/setpage',
        builder: (BuildContext context, GoRouterState state) {
          return SettingPage();
        },
      ),
      GoRoute(
        path: '/',
        builder: (BuildContext context, GoRouterState state) {
          return const HomePage();
        },
      ),
    ],
  );
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('go_router: ^4.4.1'),
      ),
      body: Column(
        children: [
          ListTile(
            leading: const Icon(Icons.settings),
            title: const Text('setting'),
            onTap: () {
              context.push('/setpage');
            },
          ),
          ListTile(
            leading: const Icon(Icons.settings),
            title: const Text('search page'),
            onTap: () {
              context.push('/sp?query=flutter');
            },
          ),
          ListTile(
            leading: const Icon(Icons.settings),
            title: const Text('search page pushname'),
            onTap: () {
              GoRouter.of(context)
                  .pushNamed('sp', queryParams: {'query': 'look'});
            },
          ),
          ListTile(
            leading: const Icon(Icons.settings),
            title: const Text('movie  detail path'),
            onTap: () {
              GoRouter.of(context).push('/md/50000');
            },
          ),
          ListTile(
            leading: const Icon(Icons.settings),
            title: const Text('movie  detail named'),
            onTap: () {
              GoRouter.of(context).pushNamed(
                'md',
                params: {'id': '123456'},
              );
            },
          ),
        ],
      ),
    );
  }
}

class MovieDetailPage extends StatelessWidget {
  final String id;
  const MovieDetailPage({Key? key, required this.id}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Movie detail page'),
      ),
      body: Center(
        child: Text('当前ID 是 $id'),
      ),
    );
  }
}

class SettingPage extends StatelessWidget {
  const SettingPage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('setting page'),
      ),
    );
  }
}

class SearchPage extends StatelessWidget {
  final String query;
  const SearchPage({Key? key, required this.query}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('setting page'),
      ),
      body: Center(child: Text('search info is $query')),
    );
  }
}



路由之间传递参数

1.没有参数的路由之间的跳转

//定义GoRoute路由
	 GoRoute(
	   name: 'setpage',
	   path: '/setpage',
	   builder: (BuildContext context, GoRouterState state) {
	     return SettingPage();
	   },
	 ),
	 //需要跳转到该页面
	GoRouter.of(context).push('/setpage');
	GoRouter.of(context).pushNamed('setpage');
	context.push('/setpage');
	context.pushNamed('setpage');
	context.go('/setpage');
	context.goNamed('setpage');

2.通过在路由传递参数

//定义GoRoute路由
	 GoRoute(
	   name: 'setpage',
	   path: '/setpage',//传递的时候用?
	   builder: (BuildContext context, GoRouterState state) {
	   //读取参数传递进去
	   	final username= state.queryParams['username'];
	     return SettingPage(username:username);
	   },
	 ),
	 //需要跳转到该页面
	GoRouter.of(context).push('/setpage?usernam=JACK');
	context.push('/setpage?usernam=JACK');
	context.go('/setpage?usernam=JACK');
	GoRouter.of(context).pushNamed('setpage',queryParams{"username:username":"JACK"});
	context.goNamed('setpage',queryParams{"username:username":"JACK"});
	context.pushNamed('setpage',queryParams{"username:username":"JACK"});



3.在定义路由的时候 配置path 来传递参数

path_to_regexp包可以匹配相对应的path
path_to_regexp 包

//定义GoRoute路由
	 GoRoute(
	   name: 'setpage',
	   path: '/setpage/:username',//位置,如同url
	   builder: (BuildContext context, GoRouterState state) {
	   //读取参数传递进去
	   	final username= state.params['username'];
	     return SettingPage(username:username);
	   },
	 ),
	 //需要跳转到该页面
	GoRouter.of(context).push('/setpage/JACK');
	context.push('/setpage/JACK');
	context.go('/setpage/JACK');
	GoRouter.of(context).pushNamed('setpage',params{"username":"JACK"});
	context.goNamed('setpage',params{"username":"JACK"});
	context.pushNamed('setpage',params{"username":"JACK"});



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值