跳转到正文
YGJoon 技术博客

Flutter 基础导航详解

发布于:
·1.0k 字 · 约 3 分钟·作者:
Navigator.push/pop 入门、await 等页面返回值、PageRouteBuilder 手写滑动淡入转场:导航最常用的三板斧
Flutter 基础导航详解文章封面
目录

简介

导航是应用程序中不可或缺的功能,Flutter 提供了简单而强大的导航 API。本文介绍 Flutter 中最基本的导航方法。

基本导航

页面跳转

// 跳转到新页面
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => SecondPage(),
  ),
);

// 返回上一页
Navigator.pop(context);

带返回值的导航

// 跳转并等待返回值
final result = await Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => SelectionPage(),
  ),
);

// 返回并传值
Navigator.pop(context, 'selected value');

路由动画

自定义转场动画

Navigator.push(
  context,
  PageRouteBuilder(
    pageBuilder: (context, animation, secondaryAnimation) => SecondPage(),
    transitionsBuilder: (context, animation, secondaryAnimation, child) {
      return SlideTransition(
        position: Tween<Offset>(
          begin: const Offset(1.0, 0.0),
          end: Offset.zero,
        ).animate(animation),
        child: child,
      );
    },
  ),
);

常用转场效果

// 淡入淡出
FadeTransition(
  opacity: animation,
  child: child,
);

// 缩放
ScaleTransition(
  scale: animation,
  child: child,
);

// 旋转
RotationTransition(
  turns: animation,
  child: child,
);

完整示例

class NavigationDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Navigation Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () => _navigateToSecondPage(context),
              child: Text('Go to Second Page'),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => _navigateAndWaitForResult(context),
              child: Text('Select an Option'),
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _navigateToSecondPage(BuildContext context) async {
    await Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => SecondPage(),
      ),
    );
  }

  Future<void> _navigateAndWaitForResult(BuildContext context) async {
    final result = await Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => SelectionPage(),
      ),
    );

    if (result != null) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Selected: $result')),
      );
    }
  }
}

class SecondPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Second Page'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () => Navigator.pop(context),
          child: Text('Go Back'),
        ),
      ),
    );
  }
}

class SelectionPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Make a Selection'),
      ),
      body: ListView(
        children: [
          ListTile(
            title: Text('Option 1'),
            onTap: () => Navigator.pop(context, 'Option 1'),
          ),
          ListTile(
            title: Text('Option 2'),
            onTap: () => Navigator.pop(context, 'Option 2'),
          ),
          ListTile(
            title: Text('Option 3'),
            onTap: () => Navigator.pop(context, 'Option 3'),
          ),
        ],
      ),
    );
  }
}

最佳实践

  1. 登录成功用 Get.offNamed:Get.offNamed(RouteNames.main) 把登录页从栈里清掉——用 toNamed 的话,用户按返回键会回到登录页,这是登录流程的经典 bug
  2. 退出登录用 Get.offAllNamed:清掉整个路由栈,并配合清掉登录态 controller,防止切账号后看到旧数据
  3. 需要返回值的页面用 await:final result = await Get.to(() => EditPage()),编辑页 Get.back(result: data)——表单回填、选择器回传都靠这个,别用全局变量传
  4. controller 里直接跳:GetX 跳转不依赖 context,网络回调/定时器里 Get.toNamed 直接调——这是作者从 Navigator 切 GetX 的主要爽点

注意事项

  1. 路由栈别超过 5 层:详情→评论→用户主页→关注列表……无限 push,内存涨、返回键要按十几次;深层级用 offNamed 替换而不是 push
  2. 根页面调 Get.back() 会直接退出 App:退出前用 PopScope/WillPopScope 做”再按一次退出”确认,别让用户误触就闪退
  3. 转场动画别覆盖 iOS 边缘右滑返回手势:自定义转场和原生返回手势冲突,测试机上偶现卡死(自定义动画篇的坑)——业务 App 默认转场就够了
  4. 跳转前判空:if (id == null) return; 再跳详情页——空 id 的详情页除了展示”加载失败”没有任何意义

实战案例

我的货运项目 某物流冷链项目 里,页面跳转全走 GetX,不用原生 Navigator。最常用的就两行:Get.toNamed(RouteNames.idCardVerify) 跳身份证认证页,Get.to(() => const CustomServicePage()) 跳客服页。前者是命名路由,后者是直接传 widget——我混着用:需要深链接/统一管理的走命名,需要临时弹个页面的直接传 widget。GetX 的好处是跳转不用传 context,在 controller 里(比如网络请求回调里)直接 Get.toNamed 就行,这点比 Navigator 爽很多。但代价是路由表要集中维护,我单独搞了个 RouteNames 类全放 static const,getPages: RoutePages.list 统一注册,不然字符串满天飞,改名就是灾难。

总结

基础导航是 Flutter 应用开发中的重要组成部分。通过合理使用 Navigator 的 push 和 pop 方法,可以实现流畅的页面跳转效果。掌握基础导航对于开发高质量的 Flutter 应用至关重要。


分享这篇文章:


相关文章

上一篇
Flutter 基础动画详解
下一篇
Flutter 命名路由详解