Flutter 导航系列 ---- Navigator 1.0

349 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情

我们在Navigator 1.0 的时候使用 Flutter,你可能会使用Navigator并且熟悉以下概念:

  • Navigator— 管理一堆 Route 对象的小部件。
  • Route — 由 一个表示 Navigator的屏幕的对象管理,通常由MaterialPageRoute.

在 Navigator 2.0 之前,使用命名路由匿名路由Routes将其推送和弹出到Navigator' 堆栈。接下来的部分是对这两种方法的简要回顾。

匿名路由

大多数移动应用程序会在彼此的顶部显示屏幕,就像堆栈一样。在 Flutter 中,这很容易通过使用Navigator.

MaterialApp并且CupertinoApp已经Navigator在引擎盖下使用了。您可以使用 访问导航器Navigator.of()或使用显示新屏幕Navigator.push(),然后使用以下命令返回上一个屏幕Navigator.pop()

import 'package:flutter/material.dart';

void main() {
  runApp(Nav2App());
}

class Nav2App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: FlatButton(
          child: Text('View Details'),
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) {
                return DetailScreen();
              }),
            );
          },
        ),
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: FlatButton(
          child: Text('Pop!'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

push()被调用时,DetailScreen小部件被放置在小部件的顶部。前一个屏幕 HomeScreen仍然是小部件树的一部分,因此State与其关联的任何对象都会在DetailScreen可见时保留。

命名路线

Flutter 还支持命名路由,它们在routes参数 onMaterialApp或中定义CupertinoApp

import 'package:flutter/material.dart';

void main() {
  runApp(Nav2App());
}

class Nav2App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      routes: {
        '/': (context) => HomeScreen(),
        '/details': (context) => DetailScreen(),
      },
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: FlatButton(
          child: Text('View Details'),
          onPressed: () {
            Navigator.pushNamed(
              context,
              '/details',
            );
          },
        ),
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Center(
        child: FlatButton(
          child: Text('Pop!'),
          onPressed: () {
            Navigator.pop(context);
          },
        ),
      ),
    );
  }
}

这些路由必须是预定义的。尽管您可以将参数传递给命名路由,但您无法解析路由本身的参数。例如,如果应用程序在 Web 上运行,则无法解析来自/details/:id

使用 onGenerateRoute 的高级命名路由

处理命名路由的一种更灵活的方法是使用onGenerateRoute. 此 API 使您能够处理所有路径:

onGenerateRoute: (settings) {
  // Handle '/'
  if (settings.name == '/') {
    return MaterialPageRoute(builder: (context) => HomeScreen());
  }
  
  // Handle '/details/:id'
  var uri = Uri.parse(settings.name);
  if (uri.pathSegments.length == 2 &&
      uri.pathSegments.first == 'details') {
    var id = uri.pathSegments[1];
    return MaterialPageRoute(builder: (context) => DetailScreen(id: id));
  }
  
  return MaterialPageRoute(builder: (context) => UnknownScreen());
},

这里,settings是 的一个实例RouteSettings。name 和 arguments 字段Navigator.pushNamed是调用时提供的值,或者initialRoute设置为什么。