喜迎
春节

路由详解


Flutter中的路由即页面跳转,通过Navigator组件管理路由导航并提供管理堆栈的方法,例如Navigator.push和Navigator.pop。

1 普通路由基本使用

# myflutter(项目名)目录结构如下
# 修改“自定义底部导航”的“实现页面切换”代码
# 新增form.dart、search.dart
- pages/
  - tabs/
    - user.dart
    - home.dart【🖊】
    - setting.dart
    - category.dart【🖊】
  - tabs.dart【🖊】
  - form.dart【+】
  - search.dart【+】
- main.dart

1-1 tabs.dart

import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';

class Tabs extends StatefulWidget {
  const Tabs({Key? key}) : super(key: key);

  @override
  State<Tabs> createState() => _TabsState();
}

class _TabsState extends State<Tabs> {
  int _currentIndex = 0;
  final List<Widget> _pages = const [
    HomePage(),
    CategoryPage(),
    SettingPage(),
    UserPage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('普通路由基本使用')),
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        // 选中的颜色
        fixedColor: Colors.red,
        // 配置底部菜单大小
        iconSize: 40,
        // 4个以上菜单需配置此项
        type: BottomNavigationBarType.fixed,
        // 默认选中分类(第二个)
        // currentIndex: 1,
        currentIndex: _currentIndex,
        onTap: (index) {
          // 获取点击的索引值
          // print(index);
          // 注意调用setState
          setState(() {
            _currentIndex = index;
          });
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'),
          // 没有type菜单图标会被挤掉
          BottomNavigationBarItem(icon: Icon(Icons.people), label: '用户'),
        ],
      ),
    );
  }
}

1-2 form.dart

import 'package:flutter/material.dart';

class FormPage extends StatefulWidget {
  const FormPage({Key? key}) : super(key: key);

  @override
  State<FormPage> createState() => _FormPageState();
}

class _FormPageState extends State<FormPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('表单')),
      body: const Center(child: Text('表单页面')),
    );
  }
}

1-3 home.dart

import '../form.dart';
import '../search.dart';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

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

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: () {
              // 跳转路由
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (BuildContext context) {
                    // 首页跳转搜索页面
                    return const SearchPage();
                  },
                ),
              );
            },
            child: const Text('搜索'),
          ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              // 跳转路由
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (BuildContext context) {
                    // 首页跳转表单页面
                    return const FormPage();
                  },
                ),
              );
            },
            child: const Text('跳转到表单页面'),
          ),
        ],
      ),
    );
  }
}

1-4 search.dart

import 'package:flutter/material.dart';

class SearchPage extends StatefulWidget {
  const SearchPage({Key? key}) : super(key: key);

  @override
  State<SearchPage> createState() => _SearchPageState();
}

class _SearchPageState extends State<SearchPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('搜索')),
      body: const Center(child: Text('搜索页面')),
    );
  }
}

1-5 category.dart

import '../search.dart';
import 'package:flutter/material.dart';

class CategoryPage extends StatefulWidget {
  const CategoryPage({Key? key}) : super(key: key);

  @override
  State<CategoryPage> createState() => _CategoryPageState();
}

class _CategoryPageState extends State<CategoryPage> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: () {
              // 跳转路由
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (BuildContext context) {
                    // 分类跳转搜索页面
                    return const SearchPage();
                  },
                ),
              );
            },
            child: const Text('搜索'),
          ),
        ],
      ),
    );
  }
}

2 普通路由跳转传值

# myflutter(项目名)目录结构如下,修改“普通路由基本使用”代码,新增news.dart
- pages/
  - tabs/
    - user.dart
    - home.dart【🖊】
    - setting.dart
    - category.dart
  - tabs.dart【🖊】
  - news.dart【+】
  - form.dart
  - search.dart
- main.dart

2-1 tabs.dart

import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';

class Tabs extends StatefulWidget {
  const Tabs({Key? key}) : super(key: key);

  @override
  State<Tabs> createState() => _TabsState();
}

class _TabsState extends State<Tabs> {
  int _currentIndex = 0;
  final List<Widget> _pages = const [
    HomePage(),
    CategoryPage(),
    SettingPage(),
    UserPage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('普通路由跳转传值')),
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        // 选中的颜色
        fixedColor: Colors.red,
        // 配置底部菜单大小
        iconSize: 40,
        // 4个以上菜单需配置此项
        type: BottomNavigationBarType.fixed,
        // 默认选中分类(第二个)
        // currentIndex: 1,
        currentIndex: _currentIndex,
        onTap: (index) {
          // 获取点击的索引值
          // print(index);
          // 注意调用setState
          setState(() {
            _currentIndex = index;
          });
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'),
          // 没有type菜单图标会被挤掉
          BottomNavigationBarItem(icon: Icon(Icons.people), label: '用户'),
        ],
      ),
    );
  }
}

2-2 news.dart

import 'package:flutter/material.dart';

// 新闻页面接收上个页面传来的参数
class NewsPage extends StatefulWidget {
  final String title;
  final int aid;
  const NewsPage({Key? key, this.title = '新闻', required this.aid})
    : super(key: key);

  @override
  State<NewsPage> createState() => _NewsPageState();
}

class _NewsPageState extends State<NewsPage> {
  @override
  void initState() {
    super.initState();
    print(widget.aid);
    print(widget.title);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      floatingActionButton: FloatingActionButton(
        // 返回到上一页
        onPressed: () {
          Navigator.pop(context);
        },
        child: const Icon(Icons.home),
      ),
      // 获取NewsPage里面定义的title
      appBar: AppBar(title: Text(widget.title)),
      body: const Center(child: Text('新闻页面')),
    );
  }
}

2-3 home.dart

import '../news.dart';
import '../form.dart';
import '../search.dart';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

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

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: () {
              // 跳转路由
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (BuildContext context) {
                    // 首页跳转搜索页面
                    return const SearchPage();
                  },
                ),
              );
            },
            child: const Text('搜索'),
          ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              // 跳转路由
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (BuildContext context) {
                    // 首页跳转表单页面
                    return const FormPage();
                  },
                ),
              );
            },
            child: const Text('跳转到表单页面'),
          ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              // 跳转路由
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (BuildContext context) {
                    return const NewsPage(title: '我是标题', aid: 12);
                  },
                ),
              );
            },
            child: const Text('跳转传值'),
          ),
        ],
      ),
    );
  }
}

3 命名路由基本使用

# myflutter(项目名)目录结构如下,修改“普通路由跳转传值”代码
- pages/
  - tabs/
    - user.dart
    - home.dart【🖊】
    - setting.dart
    - category.dart【🖊】
  - tabs.dart【🖊】
  - news.dart【🖊】
  - form.dart
  - search.dart
- main.dart【🖊】

3-1 tabs.dart

import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';

class Tabs extends StatefulWidget {
  const Tabs({Key? key}) : super(key: key);

  @override
  State<Tabs> createState() => _TabsState();
}

class _TabsState extends State<Tabs> {
  int _currentIndex = 0;
  final List<Widget> _pages = const [
    HomePage(),
    CategoryPage(),
    SettingPage(),
    UserPage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('命名路由基本使用')),
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        // 选中的颜色
        fixedColor: Colors.red,
        // 配置底部菜单大小
        iconSize: 40,
        // 4个以上菜单需配置此项
        type: BottomNavigationBarType.fixed,
        // 默认选中分类(第二个)
        // currentIndex: 1,
        currentIndex: _currentIndex,
        onTap: (index) {
          // 获取点击的索引值
          // print(index);
          // 注意调用setState
          setState(() {
            _currentIndex = index;
          });
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'),
          // 没有type菜单图标会被挤掉
          BottomNavigationBarItem(icon: Icon(Icons.people), label: '用户'),
        ],
      ),
    );
  }
}

3-2 main.dart

import './pages/tabs.dart';
import './pages/news.dart';
import './pages/form.dart';
import './pages/search.dart';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      // home: const Tabs(),
      // 初始化路由
      initialRoute: '/',
      // 配置路由
      routes: {
        '/': (context) => const Tabs(),
        '/news': (context) => const NewsPage(),
        '/form': (context) => const FormPage(),
        '/search': (context) {
          return const SearchPage();
        },
      },
    );
  }
}

3-3 news.dart

import 'package:flutter/material.dart';

class NewsPage extends StatefulWidget {
  const NewsPage({Key? key}) : super(key: key);

  @override
  State<NewsPage> createState() => _NewsPageState();
}

class _NewsPageState extends State<NewsPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('新闻')),
      body: const Center(child: Text('新闻页面')),
    );
  }
}

3-4 home.dart

import '../search.dart';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

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

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: () {
              // 跳转路由
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (BuildContext context) {
                    // 首页跳转搜索页面
                    return const SearchPage();
                  },
                ),
              );
            },
            child: const Text('基本路由跳转,引入组件search.dart'),
          ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              Navigator.pushNamed(context, '/news');
            },
            child: const Text('命令路由跳转,main.dart中配置路由'),
          ),
        ],
      ),
    );
  }
}

3-5 category.dart

import 'package:flutter/material.dart';

class CategoryPage extends StatefulWidget {
  const CategoryPage({Key? key}) : super(key: key);

  @override
  State<CategoryPage> createState() => _CategoryPageState();
}

class _CategoryPageState extends State<CategoryPage> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: () {
              Navigator.pushNamed(context, '/search');
            },
            child: const Text('命名路由跳转到search'),
          ),
        ],
      ),
    );
  }
}

4 命名路由传值操作

  • 命名路由传值需将路由配置移到外面,首先定义Map类型的routes,调用onGenerateRoute处理。
  • 然后在对应的页面进行接收传值,在页面中定义arguments用于传值,最后再跳转页面实现传参。
# myflutter(项目名)目录结构如下,修改“命名路由基本使用”代码,并添加shop.dart文件
- pages/
  - tabs/
    - user.dart
    - home.dart【🖊】
    - setting.dart
    - category.dart
  - tabs.dart【🖊】
  - news.dart
  - form.dart【🖊】
  - shop.dart【+】
  - search.dart
- main.dart【🖊】

4-1 tabs.dart

import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';

class Tabs extends StatefulWidget {
  const Tabs({Key? key}) : super(key: key);

  @override
  State<Tabs> createState() => _TabsState();
}

class _TabsState extends State<Tabs> {
  int _currentIndex = 0;
  final List<Widget> _pages = const [
    HomePage(),
    CategoryPage(),
    SettingPage(),
    UserPage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('命名路由传值操作')),
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        // 选中的颜色
        fixedColor: Colors.red,
        // 配置底部菜单大小
        iconSize: 40,
        // 4个以上菜单需配置此项
        type: BottomNavigationBarType.fixed,
        // 默认选中分类(第二个)
        // currentIndex: 1,
        currentIndex: _currentIndex,
        onTap: (index) {
          // 获取点击的索引值
          // print(index);
          // 注意调用setState
          setState(() {
            _currentIndex = index;
          });
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'),
          // 没有type菜单图标会被挤掉
          BottomNavigationBarItem(icon: Icon(Icons.people), label: '用户'),
        ],
      ),
    );
  }
}

4-2 shop.dart

import 'package:flutter/material.dart';

class ShopPage extends StatefulWidget {
  // 接收命名路由跳转传值
  final Map arguments;
  const ShopPage({Key? key, required this.arguments}) : super(key: key);

  @override
  State<ShopPage> createState() => _ShopPageState();
}

class _ShopPageState extends State<ShopPage> {
  @override
  void initState() {
    super.initState();
    print(widget.arguments);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('商店')),
      body: const Center(child: Text('商店页面')),
    );
  }
}

4-3 form.dart

import 'package:flutter/material.dart';

// 其他页面跳转到form页面
class FormPage extends StatefulWidget {
  // 进行命名路由传值
  final Map arguments;
  const FormPage({Key? key, required this.arguments}) : super(key: key);

  @override
  State<FormPage> createState() => _FormPageState();
}

class _FormPageState extends State<FormPage> {
  @override
  void initState() {
    super.initState();
    // 打印输出
    print(widget.arguments);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('表单')),
      body: const Center(child: Text('表单页面')),
    );
  }
}

4-4 main.dart

import './pages/tabs.dart';
import './pages/news.dart';
import './pages/form.dart';
import './pages/shop.dart';
import './pages/search.dart';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // 定义一个Map类型的路由
  Map routes = {
    '/': (context) => const Tabs(),
    '/news': (context) => const NewsPage(),
    '/form': (context, {arguments}) => FormPage(arguments: arguments),
    '/shop': (context, {arguments}) {
      return ShopPage(arguments: arguments);
    },
    '/search': (context) {
      return const SearchPage();
    },
  };

  MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      // home: const Tabs(),
      // 初始化路由
      initialRoute: '/',
      // 固定写法,配置onGenerateRoute
      onGenerateRoute: (RouteSettings settings) {
        print(settings);
        // “/news”或“/search”
        print(settings.name);
        print(settings.arguments);

        // 统一处理
        final String? name = settings.name;
        final Function? pageContentBuilder = 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;
          }
        }
        return null;
      },
    );
  }
}

4-5 home.dart

import '../search.dart';
import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

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

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: () {
              // 跳转路由
              Navigator.of(context).push(
                MaterialPageRoute(
                  builder: (BuildContext context) {
                    // 首页跳转搜索页面
                    return const SearchPage();
                  },
                ),
              );
            },
            child: const Text('基本路由跳转,引入组件search.dart'),
          ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              Navigator.pushNamed(context, '/news');
            },
            child: const Text('命令路由跳转,main.dart中配置路由'),
          ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              Navigator.pushNamed(
                context,
                '/form',
                arguments: {'title': '命名路由传值', 'aid': 20},
              );
            },
            child: const Text('命令路由传值操作,跳转到form页面'),
          ),
          const SizedBox(height: 20),
          ElevatedButton(
            onPressed: () {
              Navigator.pushNamed(
                context,
                '/shop',
                arguments: {'title': '命名路由传值', 'aid': 30},
              );
            },
            child: const Text('命令路由传值操作,跳转到shop页面'),
          ),
        ],
      ),
    );
  }
}

5 命名路由代码抽离

# myflutter(项目名)目录结构如下,修改“命名路由传值操作”代码,新增routes.dart
- pages/
  - tabs/
    - user.dart
    - home.dart
    - setting.dart
    - category.dart
  - tabs.dart【🖊】
  - news.dart
  - form.dart
  - shop.dart
  - search.dart
- routes
  - routes.dart【+】
- main.dart【🖊】

5-1 tabs.dart

import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';

class Tabs extends StatefulWidget {
  const Tabs({Key? key}) : super(key: key);

  @override
  State<Tabs> createState() => _TabsState();
}

class _TabsState extends State<Tabs> {
  int _currentIndex = 0;
  final List<Widget> _pages = const [
    HomePage(),
    CategoryPage(),
    SettingPage(),
    UserPage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('命名路由代码抽离')),
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        // 选中的颜色
        fixedColor: Colors.red,
        // 配置底部菜单大小
        iconSize: 40,
        // 4个以上菜单需配置此项
        type: BottomNavigationBarType.fixed,
        // 默认选中分类(第二个)
        // currentIndex: 1,
        currentIndex: _currentIndex,
        onTap: (index) {
          // 获取点击的索引值
          // print(index);
          // 注意调用setState
          setState(() {
            _currentIndex = index;
          });
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'),
          // 没有type菜单图标会被挤掉
          BottomNavigationBarItem(icon: Icon(Icons.people), label: '用户'),
        ],
      ),
    );
  }
}

5-2 main.dart

import './routes/routes.dart';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      // home: const Tabs(),
      // 初始化路由
      initialRoute: '/',
      // 固定写法,配置onGenerateRoute
      onGenerateRoute: onGenerateRoute,
    );
  }
}

5-3 routes.dart

import '../pages/tabs.dart';
import '../pages/news.dart';
import '../pages/form.dart';
import '../pages/shop.dart';
import '../pages/search.dart';
import 'package:flutter/material.dart';

// 定义一个Map类型的路由
Map routes = {
  '/': (context) => const Tabs(),
  '/news': (context) => const NewsPage(),
  '/form': (context, {arguments}) => FormPage(arguments: arguments),
  '/shop': (context, {arguments}) {
    return ShopPage(arguments: arguments);
  },
  '/search': (context) {
    return const SearchPage();
  },
};

// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
  print(settings);
  // “/news”或“/search”
  print(settings.name);
  print(settings.arguments);

  // 统一处理
  final String? name = settings.name;
  final Function? pageContentBuilder = 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;
    }
  }
  return null;
};

6 返回到上一级路由

# myflutter(项目名)目录结构如下
- pages/
  - tabs/
    - user.dart
    - home.dart
    - setting.dart
    - category.dart
  - user/
    - login.dart
  - tabs.dart
- routes
  - routes.dart
- main.dart

6-1 tabs.dart

import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';

class Tabs extends StatefulWidget {
  const Tabs({Key? key}) : super(key: key);

  @override
  State<Tabs> createState() => _TabsState();
}

class _TabsState extends State<Tabs> {
  int _currentIndex = 0;
  final List<Widget> _pages = const [
    HomePage(),
    CategoryPage(),
    SettingPage(),
    UserPage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('返回到上一级路由')),
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        // 选中的颜色
        fixedColor: Colors.red,
        // 配置底部菜单大小
        iconSize: 40,
        // 4个以上菜单需配置此项
        type: BottomNavigationBarType.fixed,
        // 默认选中分类(第二个)
        // currentIndex: 1,
        currentIndex: _currentIndex,
        onTap: (index) {
          // 获取点击的索引值
          // print(index);
          // 注意调用setState
          setState(() {
            _currentIndex = index;
          });
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'),
          // 没有type菜单图标会被挤掉
          BottomNavigationBarItem(icon: Icon(Icons.people), label: '用户'),
        ],
      ),
    );
  }
}

6-2 user.dart

import 'package:flutter/material.dart';

class UserPage extends StatefulWidget {
  const UserPage({Key? key}) : super(key: key);

  @override
  State<UserPage> createState() => _UserPageState();
}

class _UserPageState extends State<UserPage> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: () {
              print('执行跳转');
              Navigator.pushNamed(context, '/login');
            },
            child: const Text('登录'),
          ),
          const SizedBox(height: 40),
          ElevatedButton(onPressed: () {}, child: const Text('注册')),
        ],
      ),
    );
  }
}

6-3 main.dart

import './routes/routes.dart';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      // home: const Tabs(),
      // 初始化路由
      initialRoute: '/',
      // 固定写法,配置onGenerateRoute
      onGenerateRoute: onGenerateRoute,
    );
  }
}

6-4 login.dart

import 'package:flutter/material.dart';

class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('登录页面')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('登录跳转演示,执行登录后返回到上一个页面'),
            const SizedBox(height: 40),
            ElevatedButton(
              onPressed: () {
                // 返回到上一级页面
                Navigator.of(context).pop();
              },
              child: const Text('执行登录'),
            ),
          ],
        ),
      ),
    );
  }
}

6-5 home.dart

import 'package:flutter/material.dart';

class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

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

class _HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
    return const Center(child: Text('首页展示'));
  }
}

6-6 routes.dart

import '../pages/tabs.dart';
import '../pages/user/login.dart';
import 'package:flutter/material.dart';

// 定义一个Map类型的路由
Map routes = {
  '/': (context) => const Tabs(),
  '/login': (context) => const LoginPage(),
};

// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
  // 统一处理
  final String? name = settings.name;
  final Function? pageContentBuilder = 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;
    }
  }
  return null;
};

6-7 setting.dart

import 'package:flutter/material.dart';

class SettingPage extends StatefulWidget {
  const SettingPage({Key? key}) : super(key: key);

  @override
  State<SettingPage> createState() => _SettingPageState();
}

class _SettingPageState extends State<SettingPage> {
  @override
  Widget build(BuildContext context) {
    return const Center(child: Text('系统设置'));
  }
}

6-8 category.dart

import 'package:flutter/material.dart';

class CategoryPage extends StatefulWidget {
  const CategoryPage({Key? key}) : super(key: key);

  @override
  State<CategoryPage> createState() => _CategoryPageState();
}

class _CategoryPageState extends State<CategoryPage> {
  @override
  Widget build(BuildContext context) {
    return const Center(child: Text('分类页面'));
  }
}

7 Flutter中替换路由

  • 比如:registerFirst > registerSecond > registerThird > registerFirst。
  • registerSecond页,通过pushReplacementNamed()跳转到registerThird页。
  • Navigator.of(context).pushReplacementNamed('/registerSecond');
  • registerThird页,通过点击registerThird的返回按钮,返回registerFirst页。
# myflutter(项目名)目录结构如下,修改“返回到上一级路由”代码
# 新增registerFirst.dart、registerSecond.dart、registerThird.dart
- pages/
  - tabs/
    - user.dart【🖊】
    - home.dart
    - setting.dart
    - category.dart
  - user/
    - login.dart
    - registerFirst.dart【+】
    - registerSecond.dart【+】
    - registerThird.dart【+】
  - tabs.dart【🖊】
- routes
  - routes.dart【🖊】
- main.dart

7-1 tabs.dart

import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';

class Tabs extends StatefulWidget {
  const Tabs({Key? key}) : super(key: key);

  @override
  State<Tabs> createState() => _TabsState();
}

class _TabsState extends State<Tabs> {
  int _currentIndex = 0;
  final List<Widget> _pages = const [
    HomePage(),
    CategoryPage(),
    SettingPage(),
    UserPage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Flutter中替换路由')),
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        // 选中的颜色
        fixedColor: Colors.red,
        // 配置底部菜单大小
        iconSize: 40,
        // 4个以上菜单需配置此项
        type: BottomNavigationBarType.fixed,
        // 默认选中分类(第二个)
        // currentIndex: 1,
        currentIndex: _currentIndex,
        onTap: (index) {
          // 获取点击的索引值
          // print(index);
          // 注意调用setState
          setState(() {
            _currentIndex = index;
          });
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'),
          // 没有type菜单图标会被挤掉
          BottomNavigationBarItem(icon: Icon(Icons.people), label: '用户'),
        ],
      ),
    );
  }
}

7-2 user.dart

import 'package:flutter/material.dart';

class UserPage extends StatefulWidget {
  const UserPage({Key? key}) : super(key: key);

  @override
  State<UserPage> createState() => _UserPageState();
}

class _UserPageState extends State<UserPage> {
  @override
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          ElevatedButton(
            onPressed: () {
              print('执行跳转');
              Navigator.pushNamed(context, '/login');
            },
            child: const Text('登录'),
          ),
          const SizedBox(height: 40),
          ElevatedButton(
            onPressed: () {
              Navigator.pushNamed(context, '/registerFirst');
            },
            child: const Text('注册'),
          ),
        ],
      ),
    );
  }
}

7-3 routes.dart

import '../pages/tabs.dart';
import '../pages/user/login.dart';
import 'package:flutter/material.dart';
import '../pages/user/registerThird.dart';
import '../pages/user/registerFirst.dart';
import '../pages/user/registerSecond.dart';

// 定义一个Map类型的路由
Map routes = {
  '/': (context) => const Tabs(),
  '/login': (context) => const LoginPage(),
  '/registerThird': (context) => const RegisterThirdPage(),
  '/registerFirst': (context) => const RegisterFirstPage(),
  '/registerSecond': (context) => const RegisterSecondPage(),
};

// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
  // 统一处理
  final String? name = settings.name;
  final Function? pageContentBuilder = 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;
    }
  }
  return null;
};

7-4 registerFirst.dart

import 'package:flutter/material.dart';

class RegisterFirstPage extends StatefulWidget {
  const RegisterFirstPage({Key? key}) : super(key: key);

  @override
  State<RegisterFirstPage> createState() => _RegisterFirstPageState();
}

class _RegisterFirstPageState extends State<RegisterFirstPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('注册第一步')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('注册第一步'),
            const SizedBox(height: 40),
            ElevatedButton(
              onPressed: () {
                Navigator.pushNamed(context, '/registerSecond');
              },
              child: const Text('下一步'),
            ),
          ],
        ),
      ),
    );
  }
}

7-5 registerThird.dart

import 'package:flutter/material.dart';

class RegisterThirdPage extends StatefulWidget {
  const RegisterThirdPage({Key? key}) : super(key: key);

  @override
  State<RegisterThirdPage> createState() => _RegisterThirdPageState();
}

class _RegisterThirdPageState extends State<RegisterThirdPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('注册第三步')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('注册第三步'),
            const SizedBox(height: 40),
            ElevatedButton(onPressed: () {}, child: const Text('完成注册')),
          ],
        ),
      ),
    );
  }
}

7-6 registerSecond.dart

import 'package:flutter/material.dart';

class RegisterSecondPage extends StatefulWidget {
  const RegisterSecondPage({Key? key}) : super(key: key);

  @override
  State<RegisterSecondPage> createState() => _RegisterSecondPageState();
}

class _RegisterSecondPageState extends State<RegisterSecondPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('注册第二步')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('注册第二步'),
            const SizedBox(height: 40),
            ElevatedButton(
              onPressed: () {
                // 命名路由跳转
                // Navigator.pushNamed(context, '/registerThird');
                // 替换路由跳转
                Navigator.of(context).pushReplacementNamed('/registerThird');
              },
              child: const Text('下一步'),
            ),
          ],
        ),
      ),
    );
  }
}

8 返回到根路由方法

  • 用户中心 > registerFirst > registerSecond > registerThird > 用户中心。
  • registerThird注册成功后返回用户中心,此时需要用返回到根路由的方法。
# myflutter(项目名)目录结构如下,修改“Flutter中替换路由”代码
- pages/
  - tabs/
    - user.dart
    - home.dart
    - setting.dart
    - category.dart
  - user/
    - login.dart
    - registerFirst.dart
    - registerSecond.dart
    - registerThird.dart【🖊】
  - tabs.dart【🖊】
- routes
  - routes.dart
- main.dart【🖊】

8-1 tabs.dart

import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';

class Tabs extends StatefulWidget {
  final int index;
  const Tabs({Key? key, this.index = 0}) : super(key: key);

  @override
  State<Tabs> createState() => _TabsState();
}

class _TabsState extends State<Tabs> {
  late int _currentIndex;
  @override
  void initState() {
    super.initState();
    _currentIndex = widget.index;
  }

  final List<Widget> _pages = const [
    HomePage(),
    CategoryPage(),
    SettingPage(),
    UserPage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('返回到根路由方法')),
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        // 选中的颜色
        fixedColor: Colors.red,
        // 配置底部菜单大小
        iconSize: 40,
        // 4个以上菜单需配置此项
        type: BottomNavigationBarType.fixed,
        // 默认选中分类(第二个)
        // currentIndex: 1,
        currentIndex: _currentIndex,
        onTap: (index) {
          // 获取点击的索引值
          // print(index);
          // 注意调用setState
          setState(() {
            _currentIndex = index;
          });
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'),
          // 没有type菜单图标会被挤掉
          BottomNavigationBarItem(icon: Icon(Icons.people), label: '用户'),
        ],
      ),
    );
  }
}

8-2 main.dart

import './routes/routes.dart';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      // home: const Tabs(),
      // 初始化路由,首次运行加载登录页
      initialRoute: '/login',
      // 固定写法,配置onGenerateRoute
      onGenerateRoute: onGenerateRoute,
    );
  }
}

8-3 registerThird.dart

import '../tabs.dart';
import 'package:flutter/material.dart';

class RegisterThirdPage extends StatefulWidget {
  const RegisterThirdPage({Key? key}) : super(key: key);

  @override
  State<RegisterThirdPage> createState() => _RegisterThirdPageState();
}

class _RegisterThirdPageState extends State<RegisterThirdPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('注册第三步')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text('注册第三步'),
            const SizedBox(height: 40),
            ElevatedButton(
              onPressed: () {
                // 返回到根页面
                Navigator.of(context).pushAndRemoveUntil(
                  MaterialPageRoute(
                    builder: (BuildContext context) {
                      // index指定返回的页面
                      return const Tabs(index: 3);
                    },
                  ),
                  (route) => false,
                );
              },
              child: const Text('完成注册'),
            ),
          ],
        ),
      ),
    );
  }
}

9 同风格的路由跳转

  • MaterialPageRoute组件可以使用和平台风格一致的路由切换动画。
  • 例如在iOS上会左右滑动切换,而在Android上则会上下滑动切换。
  • CupertinoPageRoute是Cupertino组件库提供的iOS风格的路由切换组件。
  • 如果在Android上也想使用左右切换风格,可以使用CupertinoPageRoute。
  • 在iOS中appBarTheme的标题是自动居中的,而在Android中是自动居左的。
# myflutter(项目名)目录结构如下,修改“返回到根路由方法”代码
- pages/
  - tabs/
    - user.dart
    - home.dart
    - setting.dart
    - category.dart
  - user/
    - login.dart
    - registerFirst.dart
    - registerSecond.dart
    - registerThird.dart
  - tabs.dart【🖊】
- routes
  - routes.dart【🖊】
- main.dart【🖊】

9-1 tabs.dart

import './tabs/user.dart';
import './tabs/home.dart';
import './tabs/setting.dart';
import './tabs/category.dart';
import 'package:flutter/material.dart';

class Tabs extends StatefulWidget {
  final int index;
  const Tabs({Key? key, this.index = 0}) : super(key: key);

  @override
  State<Tabs> createState() => _TabsState();
}

class _TabsState extends State<Tabs> {
  late int _currentIndex;
  @override
  void initState() {
    super.initState();
    _currentIndex = widget.index;
  }

  final List<Widget> _pages = const [
    HomePage(),
    CategoryPage(),
    SettingPage(),
    UserPage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('同风格的路由跳转')),
      body: _pages[_currentIndex],
      bottomNavigationBar: BottomNavigationBar(
        // 选中的颜色
        fixedColor: Colors.red,
        // 配置底部菜单大小
        iconSize: 40,
        // 4个以上菜单需配置此项
        type: BottomNavigationBarType.fixed,
        // 默认选中分类(第二个)
        // currentIndex: 1,
        currentIndex: _currentIndex,
        onTap: (index) {
          // 获取点击的索引值
          // print(index);
          // 注意调用setState
          setState(() {
            _currentIndex = index;
          });
        },
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home), label: '首页'),
          BottomNavigationBarItem(icon: Icon(Icons.category), label: '分类'),
          BottomNavigationBarItem(icon: Icon(Icons.settings), label: '设置'),
          // 没有type菜单图标会被挤掉
          BottomNavigationBarItem(icon: Icon(Icons.people), label: '用户'),
        ],
      ),
    );
  }
}

9-2 main.dart

import './routes/routes.dart';
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // 去掉右上角debug图标
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        // 全局配置主题
        appBarTheme: const AppBarTheme(
          // 主题标题全部居中显示
          centerTitle: true,
        ),
      ),
      // home: const Tabs(),
      // 初始化路由,首次运行加载登录页
      initialRoute: '/login',
      // 固定写法,配置onGenerateRoute
      onGenerateRoute: onGenerateRoute,
    );
  }
}

9-3 routes.dart

import '../pages/tabs.dart';
import '../pages/user/login.dart';
// 配置iOS风格的路由,引入库
import 'package:flutter/cupertino.dart';
// import 'package:flutter/material.dart';
import '../pages/user/registerThird.dart';
import '../pages/user/registerFirst.dart';
import '../pages/user/registerSecond.dart';

// 定义一个Map类型的路由
Map routes = {
  '/': (context) => const Tabs(),
  '/login': (context) => const LoginPage(),
  '/registerThird': (context) => const RegisterThirdPage(),
  '/registerFirst': (context) => const RegisterFirstPage(),
  '/registerSecond': (context) => const RegisterSecondPage(),
};

// 配置onGenerateRoute,固定写法,该方法相当于一个中间件,可以做权限判断
// ignore: prefer_function_declarations_over_variables
var onGenerateRoute = (RouteSettings settings) {
  // 统一处理
  final String? name = settings.name;
  final Function? pageContentBuilder = routes[name];
  if (pageContentBuilder != null) {
    if (settings.arguments != null) {
      // final Route route = MaterialPageRoute(
      // 替换MaterialPageRoute
      final Route route = CupertinoPageRoute(
        builder:
            (context) =>
                pageContentBuilder(context, arguments: settings.arguments),
      );
      return route;
    } else {
      // final Route route = MaterialPageRoute(
      final Route route = CupertinoPageRoute(
        builder: (context) => pageContentBuilder(context),
      );
      return route;
    }
  }
  return null;
};

文章作者: bsf
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC 4.0 许可协议。转载请注明来源 bsf !
评 论
  目录