Flutter入门-路由

路由

Flutter使用路由来定义页面之间的跳转,类似Vue。Flutter使用Navigator组件来管理路由导航。

方法:

  • Navigator.push,跳转
  • Navigator.pop, 返回上一级,如果是对话框,会关闭对话框。

普通路由

使用场景:在一些小型项目中推荐使用。

核心代码

跳转

 Navigator.of(context).push(MaterialPageRoute(
                builder: (context) => const SearchPage(),
              ));
 //传值             
 Navigator.of(context).push(MaterialPageRoute(
                builder: (context) => FormPage(title: "我是传递过来的值"),
              ));              

返回或关闭

 Navigator.of(context).pop();

演示案例

1、主页面

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  
  Widget build(BuildContext context) {
    return Column(
      children: [
        //按钮1,使用漂浮按钮,设置点击事件,点击的时候,push到另一个页面
        ElevatedButton(
            onPressed: () {
              Navigator.of(context).push(MaterialPageRoute(
                builder: (context) => const SearchPage(),
              ));
            },
            child: const Text('跳转到搜索页面')),

        //按钮2,跳转传值,通过构造函数传值。
        ElevatedButton(
            onPressed: () {
              Navigator.of(context).push(MaterialPageRoute(
                builder: (context) => FormPage(title: "我是传递过来的值"),
              ));
            },
            child: const Text('跳转到表单页面并传值')),
      ],
    );
  }
}

2、SearchPage,不传值

使用Scaffold,跳转过来的页面都会自带返回箭头,点击就会执行pop。

class SearchPage extends StatelessWidget {
  const SearchPage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("我是搜索页面"),
      ),
      body: const Text("搜索页面内容区域"),
    );
  }
}

3、FormPage,传值
使用Scaffold,跳转过来的页面都会自带返回箭头,点击就会执行pop

class FormPage extends StatelessWidget {
  String title;
  //设置默认值,没有传值将会使用默认值
  FormPage({super.key, this.title = "表单"});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: ListView(
        children: const [
          ListTile(
            title: Text("item标题"),
            subtitle: Text("item描述"),
          ),
        ],
      ),
      //也可以手动添加返回按钮,使用floatingActionButton
      floatingActionButton: FloatingActionButton(
        child: const Text('返回'),
        onPressed: () {
          Navigator.of(context).pop();
        },
      ),
    );
  }
}

命名路由

如果是大型项目,路由比较多,我们希望可以统一管理所有的路由,就可以使用命名路由。

基本使用

1、定义命名路由

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: Tabs(),
      routes: {
        '/form': (context) => FormPage(),
        '/search': (context) => SearchPage(),
         
      },
    );
  }
}

路由的名称,我们也可以根据模块进行区分

  • /user/login
  • /user_login
  • user_login

怎样都可以。

2、页面跳转

直接通过 Navigator.pushNamed()进行页面跳转。

return Column(
  children: [
    ElevatedButton(
        onPressed: () {
          //Navigator.of(context).push(MaterialPageRoute(builder: (context) => SearchPage()));
          Navigator.pushNamed(context, '/search');
        },
        child: Text('跳转到搜索页面')),
    ElevatedButton(
        onPressed: () {
          // Navigator.of(context).push(MaterialPageRoute(builder: (context) {
          //   //return FormPage(title: "我是传递过来的值");
          //   return FormPage();
          // }));
          Navigator.pushNamed(context, '/form');
        },
        child: Text('跳转到表单页面并传值')),
  ],

命名路由传值

1、定义路由,改造后

class MyApp extends StatelessWidget {

    //1、抽取路由定义
  final routes = {
    '/search': (context) => SearchPage(),
    '/form': (context, {arguments}) => FormPage(arguments: arguments),
  };

  
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Welcome to Flutter',
        theme: ThemeData(primarySwatch: Colors.blue),
        home: Tabs(),

        //2、路由监听,类似拦截器的效果,固定写法,固定代码
        onGenerateRoute: (RouteSettings settings) {
          //统一处理,进行了非空检查,也就是末尾添加感叹号
          final String name = settings.name!;
          final Function pageContentBuilder = this.routes[name]!;

          if (pageContentBuilder != null) {
            //如果有值就传值
            if (settings.arguments != null) {
              final Route route = MaterialPageRoute(
                  builder: (context) => pageContentBuilder(context,
                      arguments: settings.arguments));
              return route;
            } else {
              //没有值直接跳转
              final Route route = MaterialPageRoute(
                  builder: (context) => pageContentBuilder(context));
              return route;
            }
          }
        });
  }
}

2、传递数据

return Column(
  children: [
    ElevatedButton(
        onPressed: () {
        //命名路由,不传值
          Navigator.pushNamed(context, '/search');
        },
        child: Text('跳转到搜索页面')),
    ElevatedButton(
        onPressed: () {
          //命名路由,不传值
          //Navigator.pushNamed(context, '/form');
          
          //命名路由,传值写法
          Navigator.pushNamed(context, '/form', arguments: {
            "title": "表单",
            "name": "章三",
            "desc": "我是描述内容",
          });
        },
        child: Text('跳转到表单页面并传值')),
  ],
);

3、接收数据

class FormPage extends StatelessWidget {
  //接收传递的参数
  final arguments;
  FormPage({this.arguments});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        //取出传递过来的数据
        title: Text(arguments != null ? arguments['title'] : "default"),
      ),
      body: ListView(
        children: [
        //取出传递过来的数据
          ListTile(
            title: Text(arguments != null ? arguments['name'] : "default"),
            subtitle: Text(arguments != null ? arguments['desc'] : 'default'),
          ),
        ],
      ),
      //手动添加返回按钮
      floatingActionButton: FloatingActionButton(
        child: Text('返回'),
        onPressed: () {
          Navigator.of(context).pop();
        },
      ),
    );
  }
}

路由替换

不使用路由替换

ElevatedButton(
  onPressed: () {
    //这种方式跳转,下一页返回的时候直接返回到这一页
    Navigator.pushNamed(context, '/register2');
  },
  child: const Text("下一步")),

使用路由替换

ElevatedButton(
  onPressed: () {
    //这种方式跳转,下一页返回返回到当前页的上一页。
    Navigator.pushReplacementNamed(context, '/register2');
  },
  child: const Text("下一步")),

区别:

  • 不使用路由替换,跳转到下一页之后,点击返回按钮,返回到当前页。
  • 使用路由替换,跳转到下一页之后,点击返回按钮,返回到当前页的上一页。
返回根路径

假设注册分好几步,注册成功后,直接跳转到首页。通过Navigator.pushAndRemoveUntil可以直接跳转到指定的路由,并清空route

ElevatedButton(
  onPressed: () {
    Navigator.pushAndRemoveUntil(
      context,
      MaterialPageRoute(builder: (context) => const Tabs(index: 2)),
      (route) => route == null,
    );
  },
  child: const Text("完成注册")),

抽取路由文件

//配置路由
final routes = {
  '/': (context) => const Tabs(),
  '/search': (context) => const SearchPage(),
  '/form': (context, {arguments}) => FormPage(arguments: arguments),
  '/product': (context) => const ProductPage(),
  '/productInfo': (context, {arguments}) => ProductInfoPage(arguments: arguments),
  '/login': (context) => const LoginPage(),
  '/register1': (context) => const RegisterFirstPage(),
  '/register2': (context) => const RegisterSecondPage(),
  '/register3': (context) => const RegisterThirdPage(),
  '/userCenter': (context) => const UserCenterPage(),
};

/*
 *  这个方法是固定写法,功能就像是一个拦截器。 
 */
var onGenerateRoute = (RouteSettings settings) {
  //统一处理,进行了非空检查,也就是末尾添加感叹号
  final String name = settings.name!;
  debugPrint("访问的路由地址名称=$name");
  final Function pageContentBuilder = routes[name]!;

  if (settings.arguments != null) {
    final Route route = MaterialPageRoute(builder: (context) => pageContentBuilder(context, arguments: settings.arguments));
    return route;
  } else {
    //没有值直接跳转
    final Route route = MaterialPageRoute(builder: (context) => pageContentBuilder(context));
    return route;
  }
};