网络知识 娱乐 Flutter基础(一)页面跳转

Flutter基础(一)页面跳转

一、介绍

在安卓里,界面(Activity)的跳转是通过startActivity来完成。而在Flutter里,把界面称作为一个个Route,他们的跳转是通过一个叫做路由管理(Navigator)的工具,负责页面之间的跳转,包括参数的传递接受和返回。Navigator的方法总的来说就两个,push(进栈),pop(出栈),跟Activity一样也会生成一个存放页面的栈。

二、页面跳转

首先,我们创建一个新的页面,如下

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("第二个页面"),
      ),
    );
  }
}
2.1 普通跳转

可以看到,在界面添加了跳转到TwoPageRoute页面的按钮。

TextButton(
  child: const Text("通过实例跳转"),
  onPressed: ()  {
   Navigator.push(context,
        MaterialPageRoute(builder: (context) {
      return const TwoPageRoute();
    }));
  },
)

提示1: 关于MaterialPageRoute可以理解为对路由界面的一个封装,继承于PageRoute,定义了页面之前跳转的动画,可以针对不同平台,实现与平台页面切换动画风格一致的路由切换动画。

提示2: 在Flutter中,如果代码是固定的,也就是写死的,编译器会建议加上const。如果你是通过值传递的,那自然就不能加const了。

2.2 参数跳转

界面的跳转中,经常都需要传递参数。下面我们传递一个String参数和一个Bean类

UserBean类

class UserBean{
   String name;
   int age;
   UserBean(this.name, this.age);
}

修改TwoPageRoute页面,增加两个参数,text和userBean。可以看到,在他们前面有个required关键字,意思是必传,这在很多Widget都会见到。

class TwoPageRoute extends StatelessWidget {
  const TwoPageRoute({Key? key,
    required this.text, // 接收一个text参数
    required this.userBean //接收一个userBean类
  }) : super(key: key);
  final String text;
  final UserBean userBean;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("第二个页面"),
      ),
      body: Center(
        child: Column(
          children: [
            Text("接受text值:$text"),
            Text("接受userBean值:${userBean.toString()}"),
          ],
        ),
      ),
    );
  }
}

修改TextButton中的传参

TextButton(
  child: const Text("通过实例跳转"),
  onPressed: ()  {
   Navigator.push(context,
        MaterialPageRoute(builder: (context) {
      return TwoPageRoute(
          text: "我是第一个页面", userBean: UserBean("小小虫", 18));
    }));
  },
)
2.2 接收参数返回

在Flutter,接受页面的返回参数,相比安卓会比较方便,如下:

TextButton(
  child: const Text("通过实例跳转"),
  onPressed: () async {
    var result = await Navigator.push(context,
        MaterialPageRoute(builder: (context) {
      return TwoPageRoute(
          text: "我是第一个页面", userBean: UserBean("小小虫", 18));
    }));
    print("路由返回值: $result");
  },
)

修改TwoPageRoute页面,添加一个按钮返回参数

TextButton(
    onPressed: () {
      Navigator.pop(context, "我是返回值");
    },
    child: const Text("关闭页面并返回值")
)

注意1: 关键词async和await,不然不会等待页面结束接受返回值。关键词async和await,不然不会等待页面结束接受返回值。

三、路由命名跳转

​ 在Flutter中,页面的跳转除了通过上面的方法外,还有一种通过路由命名跳转,类似于安卓的隐式跳转,安卓的如下:

<action android:name="com.example.SplashActivity" />

而在Flutter中,需要在MyApp中添加routes参数,如下:

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
      //注册路由表
      routes: {
        "custom_page": (context) =>  const CustomRoute(),
      },
    );
  }
}

增加一个TextButton,可以看到,路由命名的跳转需要通过pushNamed方法,支持arguments传参,是不是很熟悉的样子👀

TextButton(
  child: const Text("通过自定义命名路由跳转"),
  onPressed: () async {
    var result = await Navigator.pushNamed(context, "custom_page",
        arguments: "hello,我是通过自定义命名路由跳转");
    print("通过自定义命名路由跳转路由返回值: $result");
  },
)

然后修改CustomRoute页面,在build中获取参数,如下:

@override
Widget build(BuildContext context) {
  //获取路由参数
  var args=ModalRoute.of(context)?.settings.arguments;
  }

如果你想两种结合在一起,把TwoPageRoute也加到routes中,还是最好不要吧🤣

修改routes,增加two_page_route

routes: {
  "two_page_route": (context) {
    dynamic obj =  ModalRoute.of(context)!.settings.arguments;
    return TwoPageRoute(text: obj["text"],userBean:obj["user"] ,);
  },
}

修改参数,flutter的arguments的类型真的是很百变👍,感觉出问题的几率也会很高。

TextButton(
  child: const Text("通过自定义命名路由跳转"),
  onPressed: () async {
    var result = await Navigator.pushNamed(context, "two_page_route",
        arguments: {
          'text': '我是第一个页面',
          'user': UserBean("小小虫", 181),
        });
    print("通过自定义命名路由跳转路由返回值: $result");
  },
)

四、路由钩子

​ 所谓的路由钩子,其实就是一些路由相关的监听方法(监听就监听,还钩子)。

例如:

  • onGenerateRoute:通过命名路由打开是会被调用。注意!重点!重点!是对应的name没有在routes中有映射关系,那么就会执行onGenerateRoute钩子函数,也就是没有在routes中注册路由。
  • onUnknowRoute:在打开一个不存在的命名路由时会被调用
  • navigatorObservers:监听所有路由跳转动作

举几个栗子

onGenerateRoute使用,在MaterialApp中添加onGenerateRoute,如下:

@override
  Widget build(BuildContext context) {
    return MaterialApp(
      onGenerateRoute: (RouteSettings settings) {
        var routeName = settings.name;
        var isLogin = true;
        if(routeName == "two_page_route" && isLogin){
          return MaterialPageRoute(builder: (context) {
            return TwoPageRoute(text: "我是第一个页面", userBean: UserBean("小小虫", 18));
          });
        }else{
          //登录逻辑。。。
        }
      },
    );
  }
}

注意: 记得先把two_page_route从routes去掉!

navigatorObservers使用,在MaterialApp中添加navigatorObservers,如下:

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      //监听所有路由跳转动作,
      navigatorObservers: [
        MyNavigator()
      ],
    );
  }

新建一个MyNavigator类,如下:

import 'package:flutter/widgets.dart';

///导航栈的变化监听
class MyNavigator extends NavigatorObserver{
  @override
  void didPush(Route route, Route? previousRoute) {
    super.didPush(route, previousRoute);
	//监听每个路由进栈
  }

  @override
  void didPop(Route route, Route? previousRoute) {
    super.didPop(route, previousRoute);
    //监听每个路由出栈
  }
}