前言

一个APP往往是由很多个页面组成的,单独的一个页面在安卓里面称为Activity,IOS称为ViewController,在Flutter里面仅仅是一个Widget。本文讲解Flutter的路由,Flutter内的路由组件有NavigatorRouter 。简单的可以用Navigator,更复杂的可以用Router。主要学习两个页面之间的跳转和传参,以及跨屏动画。

简单路由

在Flutter中,Navigator维护了一个堆栈,用来管理页面路由。可以通过Navigator.push()Navigator.pop()来压栈和出栈。

跳转

在第一个页面添加一个按钮,回调函数如下:

onPressed: () {
  Navigator.push(
    context,
    MaterialPageRoute(builder: (context) => SecondRoute()),
  );
}

返回

在第二个页面SecondRoute添加一个返回按钮,回调如下:

onPressed: () {
  Navigator.pop(context);
}

命名路由

相对于上面的简单路由,命名路由可以让你在APP多个地方跳转同一个页面时避免代码重复。可以向下面一样创健路由表:

void main() {
  runApp(
    MaterialApp(
      title: 'Named Routes Demo',
      initialRoute: '/',
      routes: {
        '/': (context) => const FirstScreen(),
        '/second': (context) => const SecondScreen(),
      },
    ),
  );
}

跳转

在第一个页面添加一个按钮,回调函数如下:

onPressed: () {
  Navigator.pushNamed(context, '/second');
}

返回

在第二个页面SecondScreen添加一个返回按钮,回调如下:

onPressed: () {
  Navigator.pop(context);
}

带参数传递的命名路由

根据解析参数的主体不同,可以分为两种方式。一种是由ExtractArgumentsScreen组件根据传过来的arguments自己解析出参数;第二种是在MaterialApp下提供的onGenerateRoute函数里进行解析,完成以后作为构造函数的参数传给跳转目标组件,这样目标组件不用做任何特殊处理。

组件自己解析

  1. 发送
    如下代码,跳转按钮的回调添加一个arguments参数,arguments允许你传任何类型的对象,所以可以自定义一个参数对象使用。
 onPressed: () {
   Navigator.pushNamed(
     context,
     ExtractArgumentsScreen.routeName,
     arguments: ScreenArguments(
       'Extract Arguments Screen',
       'This message is extracted in the build method.',
     ),
   );
 },

参数对象类型如下,包含两个String类型的标题和信息。

class ScreenArguments {
  final String title;
  final String message;
  ScreenArguments(this.title, this.message);
}
  1. 解析
    这时候在目标组件里面就可以按照以下方式拿到传过来的arguments,并且转换成ScreenArguments类型。
final args = ModalRoute.of(context)!.settings.arguments as ScreenArguments;

onGenerateRoute()函数解析

  1. 发送
    发送还是一样的,添加一个arguments参数。
 onPressed: () {
   Navigator.pushNamed(
     context,
     PassArgumentsScreen.routeName,
     arguments: ScreenArguments(
       'Accept Arguments Screen',
       'This message is extracted in the onGenerateRoute '
           'function.',
     ),
   );
 },
  1. 解析
    如下,首先如果本次跳转的目标组件名字正确,就获取arguments 参数并且转换成ScreenArguments类型。
      onGenerateRoute: (settings) {
        if (settings.name == PassArgumentsScreen.routeName) {
          final args = settings.arguments as ScreenArguments;
          return MaterialPageRoute(
            builder: (context) {
              return PassArgumentsScreen(
                title: args.title,
                message: args.message,
              );
            },
          );
        }
        assert(false, 'Need to implement ${settings.name}');
        return null;
      },

从页面返回参数

有时候第一个页面需要知道用户进入第二个页面以后操作了什么,这种情形就需要第二个页面返回一个值,告诉第一个页面:用户在我这里做了什么。下面的例子展示了如何让第一个页面知道用户在第二个页面点击了Yep还是Nope。
第二个页面加两个按钮,回调分别如下:

//第一个:
onPressed: () {
  Navigator.pop(context, 'Yep!');//带着参数Yep返回上一个页面
},
//第二个
onPressed: () {
  Navigator.pop(context, 'Nope.');
},

如果在耗时操作前面添加await字段,则在此处阻塞,等耗时操作返回后继续往下运行。

第一个页面的跳转按钮回调如下:跳转以后第一个页面就阻塞阻塞了,等从第二个页面返回后取到result值,然后继续执行下面的代码。

onPressed: () {
   final result = await Navigator.push(
     context,
     MaterialPageRoute(builder: (context) => const SelectionScreen()),
   );
   //以下代码级联写法,先移除旧的SnackBar(),再显示新的SnackBar()
   ScaffoldMessenger.of(context)
     ..removeCurrentSnackBar()
     ..showSnackBar(SnackBar(content: Text('$result')));
 }
},

从页面传递参数

我们之前讲过,再Flutter中,每一个页面就是一个组件,假设我要从商品List页面,跳转到Detail页面,Detail页面需要知道用户是通过点击哪个商品id跳转过来的,方便展示详情信息。List在跳转时可以把id作为Detail的构造函数参数传给Detail.

onTap: () {
  Navigator.push(
    context,
    MaterialPageRoute(
      builder: (context) => DetailScreen(todo: todos[index]),
    ),
  );

Detail页面只需要在构造函数添加一个参数,从此参数里面取值,无需特殊处理。

class DetailScreen extends StatelessWidget {
  // In the constructor, require a Todo.
  const DetailScreen({Key? key, required this.todo}) : super(key: key);

  // Declare a field that holds the Todo.
  final Todo todo;

  @override
  Widget build(BuildContext context) {
    // Use the Todo to create the UI.
    return Scaffold(
      appBar: AppBar(
        title: Text(todo.title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Text(todo.description),
      ),
    );
  }
}

跨屏动效

且看一下这个动效:
这是两个页面,但是这个跨屏动态效果将两个页面的两个窗口关联在一起,可以引导用户的关注点。

在这里插入图片描述
这个特效使用了一个组件如下:组件会自动关联

Hero(
  tag: 'imageHero',
  child: Image.network(
    'https://image.haier.com/cn/cooling/W020210723365974095796_60.png',
 ),
);
Logo

智屏生态联盟致力于大屏生态发展,利用大屏快应用技术降低开发者开发、发布大屏应用门槛

更多推荐