[Flutter-33] Route & Navigator

Route & Navigator

Route:一个页面要想被路由统一管理,必须包装为一个Route。
但是Route是一个抽象类,所以它是不能实例化的
MaterialPageRoute -> PageRoute -> ModalRoute -> TransitionRoute -> OverlayRoute -> Route

1: Navigator

Navigator:管理所有的Route的Widget,通过一个Stack来进行管理的

那么我们开发中需要手动去创建一个Navigator吗?

    并不需要,我们开发中使用的MaterialApp、CupertinoApp、WidgetsApp它们默认是有插入Navigator的
    所以,我们在需要的时候,只需要直接使用即可

Navigator.of(context)

1.1: Navigator有几个最常见的方法:

// 路由跳转:传入一个路由对象
Future<T> push<T extends Object>(Route<T> route)

// 路由跳转:传入一个名称(命名路由)
Future<T> pushNamed<T extends Object>(
  String routeName, {
    Object arguments,
  })

// 路由返回:可以传入一个参数
bool pop<T extends Object>([ T result ])

2: 路由的使用

  • 1-测试push跳转(无参数)
  • 2-测试push跳转(有参数)
  • 3-测试push跳转(有参数)+Pop返回监听 一
  • 4-测试push跳转(有参数)+Pop返回监听 二
  • 5-测试pushName跳转(无参数)(命名路由)
  • 6-测试pushName跳转(有参数)(命名路由)
  • 7-测试onGenerateRoute+传参数

// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_route_navigator/tabbar/home.dart';
import 'package:flutter_route_navigator/tabbar/profile.dart';
import 'package:flutter_route_navigator/push/route_name.dart';
import 'package:flutter_route_navigator/push/test_push_route.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Content2();
  }
}

class Content extends StatefulWidget {
  @override
  _ContentState createState() => _ContentState();
}

class _ContentState extends State<Content> {
  var _selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Route')),
        body: IndexedStack(
          index: _selectedIndex,
          children: [
            Home(),
            Profile(),
          ],
        ),
        bottomNavigationBar: BottomNavigationBar(
          type: BottomNavigationBarType.fixed,
          currentIndex: _selectedIndex,
          items: [
            _createItem(Icons.home, '首页'),
            _createItem(Icons.center_focus_strong, '我的'),
          ],
          onTap: (index) {
            print('index: $index');
            setState(() {
              _selectedIndex = index;
            });
          },
        ),
      ),
    );
  }

  BottomNavigationBarItem _createItem(IconData? icon, String lable) {
    return BottomNavigationBarItem(
      icon: Icon(icon, size: 20),
      label: lable,
    );
  }
}

class Content2 extends StatefulWidget {
  @override
  _Content2State createState() => _Content2State();
}

class _Content2State extends State<Content2> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'route',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        splashColor: Colors.transparent,
      ),
      initialRoute: '/main',
      routes: {
        RouteName.mainRouteName: (context) => Main(),
        RouteName.homeRouteName: (context) => Home(),
        RouteName.profileRouteName: (context) => Profile(),
        RouteName.pushNameByRouteName: (context) => PushNameByRoute(),
        RouteName.pushNameByRouteAndParamesName: (context) =>
            PushNameByRouteAndParames(),
        // onGenerateRoute不需要这里映射关系
      },
    );
  }
}

class Main extends StatefulWidget {
  static const String routeName = '/main';

  @override
  _MainState createState() => _MainState();
}

class _MainState extends State<Main> {
  var _selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Route')),
      body: IndexedStack(
        index: _selectedIndex,
        children: [
          Home(),
          Profile(),
        ],
      ),
      bottomNavigationBar: BottomNavigationBar(
        type: BottomNavigationBarType.fixed,
        currentIndex: _selectedIndex,
        items: [
          _createItem(Icons.home, '首页'),
          _createItem(Icons.center_focus_strong, '我的'),
        ],
        onTap: (index) {
          print('index: $index');
          setState(() {
            _selectedIndex = index;
          });
        },
      ),
    );
  }

  BottomNavigationBarItem _createItem(IconData? icon, String lable) {
    return BottomNavigationBarItem(
      icon: Icon(icon, size: 20),
      label: lable,
    );
  }
}


// home.dart
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_route_navigator/push/generate_route.dart';
import 'package:flutter_route_navigator/push/route_name.dart';
import 'package:flutter_route_navigator/push/test_push_route.dart';

class Home extends StatefulWidget {
  static const String routeName = '/home';

  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  String _home2Arg = '/';
  String _home3Arg = '/';
  String _home4Arg = '/';
  String _home6Arg = '/';
  String _home7Arg = '/';

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        // 测试1
        ListTile(
          title: Text('1-测试push跳转(无参数)'),
          subtitle: Text('接收pop参数: /'),
          onTap: () {
            Navigator.of(context).push(MaterialPageRoute(builder: (context) {
              return PushByRoute();
            }));
          },
        ),

        // 测试2
        ListTile(
          title: Text('2-测试push跳转(有参数)'),
          subtitle: Text('接收pop参数: $_home2Arg'),
          onTap: () {
            Navigator.of(context).push(MaterialPageRoute(builder: (context) {
              return PushByRouteAndParames('参数2');
            })).then((value) {
              setState(() {
                _home2Arg = value;
              });
            });
            // 跳转过去清空变量值
            setState(() {
              _home2Arg = '/';
            });
          },
        ),

        // 测试3
        ListTile(
          title: Text('3-测试push跳转(有参数)+Pop返回监听 一'),
          subtitle: Text('接收pop 返回来的参数: $_home3Arg'),
          onTap: () {
            Navigator.of(context).push(MaterialPageRoute(builder: (context) {
              return PushByRouteAndParamesPopListen1('参数3');
            })).then((value) {
              setState(() {
                _home3Arg = value;
              });
            });
            // 跳转过去清空变量值
            setState(() {
              _home3Arg = '/';
            });
          },
        ),

        // 测试4
        ListTile(
          title: Text('4-测试push跳转(有参数)+Pop返回监听 二'),
          subtitle: Text('接收pop 返回来的参数: $_home4Arg'),
          onTap: () {
            Navigator.of(context).push(MaterialPageRoute(builder: (context) {
              return PushByRouteAndParamesPopListen2('参数4');
            })).then((value) {
              setState(() {
                _home4Arg = value;
              });
            });
            // 跳转过去清空变量值
            setState(() {
              _home4Arg = '/';
            });
          },
        ),

        // 测试5
        ListTile(
          title: Text('5-测试pushName跳转(无参数)(命名路由)'),
          subtitle: Text('接收pop参数: /'),
          onTap: () {
            Navigator.of(context).pushNamed(RouteName.pushNameByRouteName);
          },
        ),

        // 测试6
        ListTile(
          title: Text('6-测试pushName跳转(有参数)(命名路由)'),
          subtitle: Text('接收pop参数: $_home6Arg'),
          onTap: () {
            Navigator.of(context)
                .pushNamed(RouteName.pushNameByRouteAndParamesName,
                    arguments: '参数6')
                .then((value) {
              setState(() {
                _home6Arg = value.toString();
              });
            });
            setState(() {
              _home6Arg = '/';
            });
          },
        ),

        // 测试7
        ListTile(
          title: Text('7-测试onGenerateRoute+传参数'),
          subtitle: Text('接收pop的参数: $_home7Arg'),
          onTap: () {
            final settings = RouteSettings(
              name: RouteName.generateRouteName,
              arguments: '参数7',
            );
            Navigator.of(context)
                .push(GenerateRoute.onGenerateRoute(settings)!)
                .then((value) {
              setState(() {
                _home7Arg = value;
              });
            });

            setState(() {
              _home7Arg = '/';
            });
          },
        ),
      ],
    );
  }
}

class Home1 extends StatelessWidget {
  static const String routeName = '/home';

  @override
  Widget build(BuildContext context) {
    return Container(
      alignment: Alignment(0, 0),
      child: ElevatedButton(
        child: Text('-> 跳转到首页详情detail page'),
        onPressed: () {
          print('click-Elevated');

          /* 方式1
          final route = MaterialPageRoute(builder: (context) {
            return HomeDetail('100');
          });
          Navigator.of(context).push(route).then((value) => {
                print('接收返回的数据: \'$value\''),
              });
          */

          /* 方式2

          */
          _tapPushName(context);
        },
      ),
    );
  }

  _tapPushName(BuildContext context) {
    Navigator.of(context)
        .pushNamed('/detail', arguments: '我是参数来自home页面')
        .then((value) => {
              print('接收参数通过name: $value'),
            });
  }
}

// 1. 路由管理:

/* Route
> 一个页面要想被路由统一管理】、必须包装成一个Route
> 但是Route是一个抽象类】不能被实例化

  MaterialPageRoute
  > 事实上MaterialPageRoute并不是Route的直接子类
  MaterialPageRoute在不同的平台有不同的表现
  > 【上下】对Android平台,打开一个页面会从屏幕底部滑动到屏幕的顶部,关闭页面时从顶部滑动到底部消失
  > 【左右】对iOS平台,打开一个页面会从屏幕右侧滑动到屏幕的左侧,关闭页面时从左侧滑动到右侧消失
  >  当然,iOS平台我们也可以使用CupertinoPageRoute

  MaterialPageRoute -> PageRoute -> ModalRoute -> TransitionRoute -> OverlayRoute -> Route
*/

/* Navigator
> 管理所有Route的Widget、通过一个Stack来管理的。

  那么我们开发中需要手动去创建一个Navigator吗?
  - 并不需要,我们开发中使用的MaterialApp、CupertinoApp、WidgetsApp它们默认是有插入Navigator的
  - 所以,我们在需要的时候,只需要直接使用即可:
  Navigator.of(context)

  Navigator 几个常见的方法:
  - 路由跳转: 传入一个路由对象
  Future<T> push<T extends object>(Route<T> route)

  - 路由跳转:传入一个名称(命名路由)
  Future<T> pushNamed<T extends object>(String route, {Object arguments})

  - 路由返回:可以传入一个参数
  bool pop<T extends Object>( [T result] )
*/

// 2. 路由使用

/*
需求1: 
  1. 从首页跳转到详情页
  2. 从详情页返回到首页


需求2: 
  1. 基于需求1 + 传递参数


路由返回的处理:
  - 如果用户是点击左上角的返回箭头返回上一页的话、我们怎么处理:
    - 方法1: 我们自定义返回箭头的监听
    - 方法2: 给Scaffold包裹一层 WillPopScope
      WillPopScope
        - WillPopScope 有一个回调函数onWillPop、当我们点击返回按钮时会执行
        - 这个函数要求有一个Future的返回值:
          - true:那么系统会自动帮我们执行pop操作
          - false:系统不再执行pop操作,需要我们自己来执行

    注意:
      两个方法实现有一个小差异:方法1 还会保留侧滑功能; 方法2会禁用侧滑功能、侧滑功能失效。


*/

// route_name.dart
class RouteName {
  static const String homeRouteName = '/home';
  static const String mainRouteName = '/main';
  static const String detailRouteName = '/detail';
  static const String profileRouteName = '/profile';
  static const String pushNameByRouteName = '/pushNameByRoute';
  static const String pushNameByRouteAndParamesName =
      '/pushNameByRouteAndParames';
  static const String generateRouteName = '/generateRouteName';
}
// test_push_route.dart
import 'package:flutter/material.dart';

/* Pop1: 无参数

*/
class PushByRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('测试push1'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.of(context).pop();
          },
          child: Text('/'),
        ),
      ),
    );
  }
}

/* Pop2: 有参数

*/
class PushByRouteAndParames extends StatelessWidget {
  final String arg;
  PushByRouteAndParames(this.arg);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('测试push2'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.of(context).pop('pop2');
          },
          child: Text('$arg'),
        ),
      ),
    );
  }
}

/* Pop3: 有参数 + 监听Pop
- AppBar.leading

*/
class PushByRouteAndParamesPopListen1 extends StatelessWidget {
  final String arg;
  PushByRouteAndParamesPopListen1(this.arg);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('测试push3'),
        leading: IconButton(
          onPressed: () {
            Navigator.of(context).pop('pop-3-pop');
          },
          icon: Icon(Icons.arrow_back_ios),
        ),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.of(context).pop('pop3');
          },
          child: Text('$arg'),
        ),
      ),
    );
  }
}

/* Pop4: 有参数 + 监听Pop2
- WillPopScope
这个函数要求有一个Future的返回值:

    true:那么系统会自动帮我们执行pop操作
    false:系统不再执行pop操作,需要我们自己来执行
*/
class PushByRouteAndParamesPopListen2 extends StatelessWidget {
  final String arg;
  PushByRouteAndParamesPopListen2(this.arg);

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        Navigator.of(context).pop('pop-4-pop-WillPopScope');
        return Future.value(false);
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text('测试push4'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () {
              Navigator.of(context).pop('pop4');
            },
            child: Text('$arg'),
          ),
        ),
      ),
    );
  }
}

class PushNameByRoute extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('测试pushName-5'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Navigator.of(context).pop();
          },
          child: Text('/'),
        ),
      ),
    );
  }
}

class PushNameByRouteAndParames extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // PushName 获取传来的参数
    final args = ModalRoute.of(context)?.settings.arguments.toString();

    return WillPopScope(
      onWillPop: () {
        Navigator.of(context).pop('pop-6-pop');
        return Future.value(false);
      },
      child: Scaffold(
        appBar: AppBar(
          title: Text('测试pushName-6'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: () {
              Navigator.of(context).pop('pop6');
            },
            child: Text('$args'),
          ),
        ),
      ),
    );
  }
}
// generate_route
import 'package:flutter/material.dart';
import 'package:flutter_route_navigator/push/route_name.dart';

/* onGenerateRoute
- onGenerateRoute不需要这里映射关系

*/
class GenerateRoute {
  static MaterialPageRoute? onGenerateRoute(RouteSettings settings) {
    final routeName = settings.name;
    final arg = settings.arguments.toString();

    switch (routeName) {
      case RouteName.generateRouteName:
        return MaterialPageRoute(
          builder: (context) {
            return TestGenerateRoute(arg);
          },
        );

      default:
        return null;
    }
  }
}

class TestGenerateRoute extends StatelessWidget {
  final String message;
  TestGenerateRoute(this.message);

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () {
        Navigator.of(context).pop('pop-7-pop');
        return Future.value(false);
      },
      child: Scaffold(
        appBar: AppBar(title: Text('Generate Route Test')),
        body: Center(
          child: GestureDetector(
            onTap: () {
              Navigator.of(context).pop('pop-7');
            },
            child: Text('$message'),
          ),
        ),
      ),
    );
  }
}
posted @ 2021-08-18 17:46  comefromchina  阅读(127)  评论(0)    收藏  举报