美文网首页
Flutter路由官方文档学习笔记

Flutter路由官方文档学习笔记

作者: 悯农 | 来源:发表于2019-08-25 02:39 被阅读0次

    在Flutter中,screen和pages都叫做路由(routes)。一个route就是一个widget,使用Navigator导航到新的一个route。

    1.导航到新的页面并返回

    两个route之间的导航由以下步骤:

    • 创建两个route
    • 使用Navigator.push()导航到第二个route
    • 使用Navigator.pop()返回到第一个route

    1.1 创建两个routes

    class FirstRoute extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('First Route'),
          ),
          body: Center(
            child: RaisedButton(
              child: Text('Open route'),
              onPressed: () {
                // Navigate to second route when tapped.
              },
            ),
          ),
        );
      }
    }
    
    class SecondRoute extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Second Route"),
          ),
          body: Center(
            child: RaisedButton(
              onPressed: () {
                // Navigate back to first route when tapped.
              },
              child: Text('Go back!'),
            ),
          ),
        );
      }
    }
    

    1.2 使用Navigator.push()导航到第二个route

    // Within the `FirstRoute` widget
    onPressed: () {
      Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => SecondRoute()),
      );
    }
    

    1.3 使用Navigator.pop()返回到第一个route

    // Within the SecondRoute widget
    onPressed: () {
      Navigator.pop(context);
    }
    

    2. 使用命名routes进行导航

    上面通过使用创建route并将其推送到导航器来导航到新的页面。

    如果你的应用中需要由很多地方导航到同一个页面,如果在使用以上的方法那么代码就会重复。解决办法就是定义命名路由,通过命名路由进行导航。

    使用命名路由步骤:

    • 创建两个页面
    • 定义routes
    • 使用Navigator.pushNamed()导航到第二个页面
    • 使用Navigator.pop()返回到第一个页面

    2.1 创建两个页面

    代码同上1.1

    2.2 定义routes

    通过MaterialApp构造函数提供的附加属性来定义routes:initialRoutes和routes它本身。

    initialRoutes的属性定义应用程序从哪个route开始。
    routes属性定义可用的命名路由以及导航到这些路由时要构建的widget。

    MaterialApp(
      // Start the app with the "/" named route. In this case, the app starts
      // on the FirstScreen widget.
      initialRoute: '/',
      routes: {
        // When navigating to the "/" route, build the FirstScreen widget.
        '/': (context) => FirstScreen(),
        // When navigating to the "/second" route, build the SecondScreen widget.
        '/second': (context) => SecondScreen(),
      },
    );
    

    2.3 使用Navigator.pushNamed()导航到第二个页面

    // Within the `FirstScreen` widget
    onPressed: () {
      // Navigate to the second screen using a named route.
      Navigator.pushNamed(context, '/second');
    }
    

    2.4 使用Navigator.pop()返回到第一个页面

    代码同上1.3

    3.将参数传递给命名路由

    Navigator提供了使用公共标识符从应用程序的任何地方传递给命名路由。

    使用Navigator.pushNamed()方法的arguments参数完成参数传递。

    以下步骤将参数传递给命名路由并使用ModalRoute.of()和onGenerateRoute()读取参数:

    • 定义需要传递的参数。
    • 创建一个提取参数的widget。
    • 在路由表中注册widget。
    • 导航到widget。

    3.1 定义需要传递的参数

    // You can pass any object to the arguments parameter.
    // In this example, create a class that contains a customizable
    // title and message.
    class ScreenArguments {
      final String title;
      final String message;
    
      ScreenArguments(this.title, this.message);
    }
    

    3.2 创建一个提取参数的widget

    创建一个widget从ScreenArguments获取并显示title和message。要访问ScreenArguments,请使用ModalRoute.of()方法。此方法返回带有参数的当前路由。

    // A widget that extracts the necessary arguments from the ModalRoute.
    class ExtractArgumentsScreen extends StatelessWidget {
      static const routeName = '/extractArguments';
    
      @override
      Widget build(BuildContext context) {
        // Extract the arguments from the current ModalRoute settings and cast
        // them as ScreenArguments.
        final ScreenArguments args = ModalRoute.of(context).settings.arguments;
    
        return Scaffold(
          appBar: AppBar(
            title: Text(args.title),
          ),
          body: Center(
            child: Text(args.message),
          ),
        );
      }
    }
    

    3.3 在路由表中注册widget

    MaterialApp(
      routes: {
        ExtractArgumentsScreen.routeName: (context) => ExtractArgumentsScreen(),
      },     
    );
    

    3.4 导航到widget

    // A button that navigates to a named route. The named route
    // extracts the arguments by itself.
    RaisedButton(
      child: Text("Navigate to screen that extracts arguments"),
      onPressed: () {
        // When the user taps the button, navigate to the specific route
        // and provide the arguments as part of the RouteSettings.
        Navigator.pushNamed(
          context,
          ExtractArgumentsScreen.routeName,
          arguments: ScreenArguments(
            'Extract Arguments Screen',
            'This message is extracted in the build method.',
          ),
        );
      },
    );
    

    使用onGenerateRoute提取参数

    也可以在onGenerateRoute()函数中提取参数并将它们传递给widget,而不是直接在窗口小部件中提取参数。
    onGenerateRoute()函数根据给定的RouteSettings创建正确的路由。

    MaterialApp(
      // Provide a function to handle named routes. Use this function to
      // identify the named route being pushed, and create the correct
      // screen.
      onGenerateRoute: (settings) {
        // If you push the PassArguments route
        if (settings.name == PassArgumentsScreen.routeName) {
          // Cast the arguments to the correct type: ScreenArguments.
          final ScreenArguments args = settings.arguments;
    
          // Then, extract the required data from the arguments and
          // pass the data to the correct screen.
          return MaterialPageRoute(
            builder: (context) {
              return PassArgumentsScreen(
                title: args.title,
                message: args.message,
              );
            },
          );
        }
      },
    );
    

    4. 带数据从页面返回

    有些时候我们需要从打开页面带数据返回到导航页面。

    使用Navigator.pop()方法带回数据的步骤:

    • 定义主屏幕
    • 添加一个启动选择屏幕的按钮
    • 使用两个按钮显示选择屏幕
    • 点击按钮时,关闭选择屏幕
    • 在主屏幕上显示选择的snackbar

    4.1 定义主屏幕

    class HomeScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Returning Data Demo'),
          ),
          // Create the SelectionButton widget in the next step.
          body: Center(child: SelectionButton()),
        );
      }
    }
    

    4.2 添加一个启动选择屏幕的按钮

    创建SelectionButton按钮,它将做以下两件事:

    • 点击时打开选中页面
    • 等待选中页面返回结果
    class SelectionButton extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return RaisedButton(
          onPressed: () {
            _navigateAndDisplaySelection(context);
          },
          child: Text('Pick an option, any option!'),
        );
      }
    
      // A method that launches the SelectionScreen and awaits the
      // result from Navigator.pop.
      _navigateAndDisplaySelection(BuildContext context) async {
        // Navigator.push returns a Future that completes after calling
        // Navigator.pop on the Selection Screen.
        final result = await Navigator.push(
          context,
          // Create the SelectionScreen in the next step.
          MaterialPageRoute(builder: (context) => SelectionScreen()),
        );
      }
    }
    

    4.3 使用两个按钮显示选择屏幕

    现在,构建一个包含两个按钮的选择屏幕。当用户点击按钮时,该应用关闭选择屏幕并让主屏幕知道点击了哪个按钮。

    class SelectionScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Pick an option'),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: RaisedButton(
                    onPressed: () {
                      // Pop here with "Yep"...
                    },
                    child: Text('Yep!'),
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: RaisedButton(
                    onPressed: () {
                      // Pop here with "Nope"
                    },
                    child: Text('Nope.'),
                  ),
                )
              ],
            ),
          ),
        );
      }
    }
    

    4.4 点击按钮时,关闭选择屏幕

    现在,更新两个按钮的onPressed()回调。要将数据返回到第一个屏幕,请使用Navigator.pop()方法,该方法接受名为result的可选第二个参数。任何结果都会在SelectionButton中返回到Future。

    Yep button
    RaisedButton(
      onPressed: () {
        // The Yep button returns "Yep!" as the result.
        Navigator.pop(context, 'Yep!');
      },
      child: Text('Yep!'),
    );
    
    Nope button
    RaisedButton(
      onPressed: () {
        // The Nope button returns "Nope!" as the result.
        Navigator.pop(context, 'Nope!');
      },
      child: Text('Nope!'),
    );
    

    4.5 在主屏幕上显示选择的snackbar

    现在您正在启动选择屏幕并等待结果,您将需要对返回的信息执行某些操作。

    _navigateAndDisplaySelection(BuildContext context) async {
      final result = await Navigator.push(
        context,
        MaterialPageRoute(builder: (context) => SelectionScreen()),
      );
    
      // After the Selection Screen returns a result, hide any previous snackbars
      // and show the new result.
      Scaffold.of(context)
        ..removeCurrentSnackBar()
        ..showSnackBar(SnackBar(content: Text("$result")));
    }
    

    5. 将数据发送到新页面

    通常,您不仅要导航到新屏幕,还要将数据传递到屏幕。例如,您可能希望传递有关已点击的项目的信息

    请记住:屏幕只是小部件。在此示例中,创建待办事项列表。轻触待办事项时,导航到显示有关待办事项信息的新屏幕(widget)。此配方使用以下步骤:

    • 定义待办事项类
    • 显示待办事项列表
    • 创建一个可以显示待办事项信息的详细信息屏幕
    • 导航并将数据传递到详细信息屏幕

    5.1 定义待办事项类

    首先,您需要一种简单的方式来表示待办事项。对于此示例,请创建一个包含两个数据的类:标题和说明。

    class Todo {
      final String title;
      final String description;
    
      Todo(this.title, this.description);
    }
    

    5.2 显示待办事项列表

    其次,显示待办事项列表。在此示例中,生成20个待办事项并使用ListView显示它们。

    生成待办事项列表
    final todos = List<Todo>.generate(
      20,
      (i) => Todo(
            'Todo $i',
            'A description of what needs to be done for Todo $i',
          ),
    );
    
    使用ListView显示待办事项列表
    ListView.builder(
      itemCount: todos.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(todos[index].title),
        );
      },
    );
    

    5.3 创建一个可以显示待办事项信息的详细信息屏幕

    现在,创建第二个屏幕。屏幕标题包含待办事项的标题,屏幕正文显示说明。

    class DetailScreen extends StatelessWidget {
      // Declare a field that holds the Todo.
      final Todo todo;
    
      // In the constructor, require a Todo.
      DetailScreen({Key key, @required this.todo}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        // Use the Todo to create the UI.
        return Scaffold(
          appBar: AppBar(
            title: Text(todo.title),
          ),
          body: Padding(
            padding: EdgeInsets.all(16.0),
            child: Text(todo.description),
          ),
        );
      }
    }
    

    5.4 导航并将数据传递到详细信息屏幕

    有了DetailScreen,您就可以执行导航了。在此示例中,当用户点击列表中的待办事项时,导航到DetailScreen。将待办事项传递给DetailScreen。

    ListView.builder(
      itemCount: todos.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(todos[index].title),
          // When a user taps the ListTile, navigate to the DetailScreen.
          // Notice that you're not only creating a DetailScreen, you're
          // also passing the current todo to it.
          onTap: () {
            Navigator.push(
              context,
              MaterialPageRoute(
                builder: (context) => DetailScreen(todo: todos[index]),
              ),
            );
          },
        );
      },
    );
    

    参考
    flutter.dev Navigation
    flutter_route sample

    相关文章

      网友评论

          本文标题:Flutter路由官方文档学习笔记

          本文链接:https://www.haomeiwen.com/subject/enwcectx.html