Flutter开发,GetX框架路由相关详细示例

需求背景: 使用getx进行路由管理,可以设置根路由,以及返回根路由,返回指定页面路由,并且中间页面可以自动销毁,多次重复跳转同一个页面,可以实现页面数据刷新的功能.

1. 配置路由管理器

main.dart 中配置 GetMaterialApp 和路由表: 可以单独抽取两个类实现

dart

less 复制代码
void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      initialRoute: '/',
      getPages: [
        GetPage(name: '/', page: () => HomePage()),
        GetPage(name: '/detail', page: () => DetailPage()),
        GetPage(name: '/settings', page: () => SettingsPage()),
      ],
    );
  }
}

GetPage属性介绍

  • 1.name (String): 页面的名称,用于在路由表或导航时引用此页面。
  • 2.page (WidgetBuilder): 用于构建页面的WidgetBuilder函数。这个函数应该返回一个Widget实例,通常是GetWidget的实例。
  • 3.transition (Transition): 页面转换动画。例如,Transition.fadeIn会使页面以渐入的方式出现。
  • 4.transitionDuration (Duration): 页面转换动画的持续时间。
  • 5.binding (Bindings): 页面绑定的类,用于初始化页面所需的控制器或服务。
  • 6.middlewares (List): 中间件列表,用于在页面跳转前后执行一些逻辑,例如身份验证检查。
  • 7.middlewaresBuilder (BindingsBuilder?): 与middlewares类似,但以函数形式提供,允许更灵活的中间件管理。
  • 8 unknownRoute (bool): 如果设置为true,当尝试导航到不存在的路由时,将使用此页面作为回退。
  • 9 preventDuplicate (bool): 如果设置为true,当尝试重复导航到同一个页面时,将不会再次创建页面实例。
  • 10 fullscreenDialog (bool): 如果设置为true,此页面将作为全屏对话框显示。
  • 11 customTransition (CustomTransition?): 自定义页面转换动画,允许更复杂的动画效果。
  • 12 curve (Curve): 页面转换动画的曲线。
  • 13 popGesture (bool): 如果设置为true,启用从边缘滑动返回的返回手势。
  • 14 popGesturePathMatchPattern (String?): 用于匹配哪些路由可以使用返回手势。
  • 15 showCupertinoParallax 属性用于控制Cupertino导航栏的透视效果

2. 路由跳转与返回逻辑

实现核心路由操作:

dart

less 复制代码
//1、导航到新的页面
Get.to(NextScreen());
Get.toNamed("/NextScreen");
//2、关闭SnackBars、Dialogs、BottomSheets或任何你通常会用Navigator.pop(context)关闭的东西
Get.back();
//3、进入下一个页面,但没有返回上一个页面的选项(用于SplashScreens,登录页面等)
Get.off(NextScreen());
Get.offNamed("/NextScreen");
//4、进入下一个界面并取消之前的所有路由(在购物车、投票和测试中很有用)
Get.offAll(NextScreen());
Get.offAllNamed("/NextScreen");
//5、发送数据到其它页面只要发送你想要的参数即可。Get在这里接受任何东西,无论是一个字符串,一个Map,一个List,甚至一个类的实例。
Get.to(NextScreen(), arguments: 'Get is the best');
Get.toNamed("/NextScreen", arguments: 'Get is the best');
在你的类或控制器上:
print(Get.arguments);
//print out: Get is the best
//6. 要导航到下一条路由,并在返回后立即接收或更新数据
var data = await Get.to(Payment());
var data = await Get.toNamed("/payment");
// 在另一个页面上,发送前一个路由的数据
Get.back(result: 'success');
// 并使用它,例:
if(data == 'success') madeAnything();
//7. 跳转到详情页(支持重复跳转刷新)
Get.toNamed(
  '/detail',
  arguments: {'id': 123}, // 传递参数
  preventDuplicates: false, // 允许重复页面
);

//8. 返回到栈中第一个路由,即首页。
Get.until((route) => route.isFirst);

//9. 返回根路由并销毁所有中间页面
Get.offAllNamed('/');

//10. 返回到指定页面(如设置页)并销毁中间页面
Get.offNamedUntil(
  '/settings',
  (route) => route.settings.name == '/', // 保留根路由
);

3. 页面刷新机制

在目标页面控制器中实现数据刷新:

dart

ini 复制代码
class DetailController extends GetxController {
  var itemId = 0.obs;
  var data = ''.obs;

  @override
  void onInit() {
    super.onInit();
    loadData();
  }

  // 从参数获取ID并刷新数据
  void loadData() {
    final args = Get.arguments;
    itemId.value = args['id'] ?? 0;
    
    // 模拟数据加载
    data.value = '正在加载...';
    Future.delayed(Duration(seconds: 1), () {
      data.value = '数据内容 ${itemId.value}';
    });
  }

  // 手动刷新按钮
  void refreshData() {
    loadData();
  }
}

4. 页面自动销毁配置

在路由跳转时使用 Get.offXXX 系列方法自动销毁页面:

dart

less 复制代码
// 跳转后销毁当前页
Get.offNamed('/detail');

// 跳转后销毁所有历史页
Get.offAllNamed('/detail');

// 跳转到新页面并销毁直到指定路由
Get.offNamedUntil('/detail', ModalRoute.withName('/'));

5. 完整页面示例

详情页 (detail_page.dart)

dart

less 复制代码
class DetailPage extends StatelessWidget {
  final DetailController controller = Get.put(DetailController());

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('详情页')),
      body: Center(
        child: Obx(() => Column(
          children: [
            Text(controller.data.value),
            ElevatedButton(
              onPressed: controller.refreshData,
              child: Text('刷新数据'),
            ),
            ElevatedButton(
              onPressed: () => Get.offAllNamed('/'),
              child: Text('返回根路由'),
            ),
          ],
        )),
      ),
    );
  }
}

根路由页 (home_page.dart)

dart

less 复制代码
class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('首页')),
      body: Center(
        child: Column(
          children: [
            ElevatedButton(
              onPressed: () => Get.toNamed('/detail', arguments: {'id': DateTime.now().millisecond}),
              child: Text('跳转详情页(带随机参数)'),
            ),
            ElevatedButton(
              onPressed: () => Get.offNamedUntil('/settings', (route) => route.isFirst),
              child: Text('跳设置页并销毁中间页'),
            )
          ],
        ),
      ),
    );
  }
}

关键特性说明

  1. 路由管理

    • Get.toNamed():普通跳转
    • Get.offNamed():替换当前路由
    • Get.offAllNamed():清空历史栈
    • Get.offNamedUntil():定向跳转并销毁中间页
  2. 页面刷新

    • preventDuplicates: false 允许相同路由重复打开
    • 通过每次传递新参数触发控制器更新
    • 手动刷新按钮调用控制器方法
  3. 自动销毁

    • 使用 off 系列方法自动释放不再使用的页面
    • 结合 until 条件精确控制保留的页面栈
  4. 控制器生命周期

    • 使用 Get.put() 绑定控制器
    • 页面销毁时自动回收资源
    • onInit() 处理初始化逻辑

注意事项

  1. GetPage 配置中不要使用 binding 如果不需要强制绑定控制器
  2. 使用 Get.arguments 获取路由参数而非传统构造方法
  3. 对于需要保持状态的页面可使用 Get.lazyPut 配合 fenix: true
  4. 复杂场景可使用 Get.parameters 处理 URL 式参数

这种实现方式提供了灵活的路由控制,同时确保内存高效管理,特别适合需要频繁导航和状态更新的应用场景。

对在 GetPage 配置中不要使用 binding 如果不需要强制绑定控制器的详细解释。

为什么在不需要强制绑定控制器时不建议使用 binding?

  1. 资源浪费问题

    • 当使用 binding 时,每次路由跳转都会创建新的控制器实例
    • 对于不需要独立状态的页面,这会创建不必要的对象
    • 增加内存开销和垃圾回收负担
  2. 生命周期冲突

    dart

    scss 复制代码
    GetPage(
      name: '/profile',
      page: () => ProfilePage(),
      binding: BindingsBuilder(() {
        Get.lazyPut<ProfileController>(() => ProfileController());
      }),
    )

    即使页面还在栈中,返回时控制器也会被销毁(GetX 默认行为)

  3. 状态管理冲突

    • 绑定会强制创建新控制器,覆盖可能已存在的实例
    • 破坏可能存在的跨页面状态共享

如果强制绑定了会怎样?

  1. 重复创建问题

    dart

    arduino 复制代码
    // 多次跳转相同路由
    Get.toNamed('/detail');  // 创建 ControllerA
    Get.toNamed('/detail');  // 创建 ControllerB 覆盖 ControllerA

    前一个控制器实例会被丢弃,可能导致内存泄漏

  2. 状态丢失问题

    dart

    scss 复制代码
    // 页面A
    Get.toNamed('/detail'); // 跳转时携带状态
    
    // 绑定中的控制器
    Get.lazyPut<DetailController>(() => DetailController()); // 忽略传入状态
  3. 性能影响

    • 每次跳转都执行初始化逻辑
    • 对于复杂控制器,会降低路由切换速度

不绑定时如何访问控制器数据?

方法1:手动获取控制器(推荐)

dart

scala 复制代码
// 在页面中
class DetailPage extends StatelessWidget {
  final DetailController controller = Get.find<DetailController>();

  @override
  Widget build(BuildContext context) {
    return Obx(() => Text(controller.data.value));
  }
}

方法2:按需初始化

dart

scss 复制代码
// 在跳转前初始化
void navigateToDetail() {
  // 检查是否已存在控制器
  if (!Get.isRegistered<DetailController>()) {
    Get.put(DetailController());
  }
  Get.toNamed('/detail');
}

方法3:使用参数传递数据

dart

php 复制代码
// 跳转时
Get.toNamed('/detail', arguments: {'item': item});

// 页面中
final item = Get.arguments['item'];

何时应该使用 binding?

  1. 需要严格隔离状态的场景:

    dart

    less 复制代码
    GetPage(
      name: '/checkout',
      page: () => CheckoutPage(),
      binding: BindingsBuilder(() {
        Get.put(CheckoutController()); // 每次都是全新状态
      }),
    )
  2. 需要自动依赖注入的复杂页面:

    dart

    vbnet 复制代码
    binding: BindingsBuilder(() {
      Get.put<ApiService>(ApiServiceImpl());
      Get.put(CheckoutController(Get.find<ApiService>()));
    })
  3. 需要强制刷新的页面:

    dart

    arduino 复制代码
    Get.toNamed('/product?refresh=true'); 
    // 每次打开都强制刷新产品数据

最佳实践建议

  1. 状态保持策略

    dart

    csharp 复制代码
    // 在main.dart中初始化全局控制器
    void main() {
      Get.put<AuthController>(AuthController(), permanent: true);
      runApp(MyApp());
    }
  2. 混合使用模式

    dart

    scss 复制代码
    GetPage(
      name: '/detail',
      page: () => DetailPage(),
      // 可选绑定 - 仅当不存在控制器时创建
      binding: BindingsBuilder(() {
        if (!Get.isRegistered<DetailController>()) {
          Get.lazyPut(() => DetailController());
        }
      }),
    )
  3. 智能刷新方案

    dart

    javascript 复制代码
    // 在控制器中添加刷新方法
    void refreshData({bool force = false}) {
      if (force || data.isEmpty) {
        // 获取数据
      }
    }
    
    // 跳转时
    Get.toNamed('/detail?refresh=${DateTime.now().milliseconds}');
    
    // 页面初始化
    onInit() {
      refreshData(force: Get.parameters['refresh'] != null);
    }
  4. 路由观察模式

    dart

    scss 复制代码
    // 监听路由变化
    ever(Get.routing, (route) {
      if (route.current == '/detail') {
        // 执行特定逻辑
      }
    });

总结对比表

场景 使用 Binding 不使用 Binding
需要独立页面状态 ✅ 推荐 ⚠️ 可能状态污染
需要共享状态 ❌ 不推荐 ✅ 推荐
简单数据展示页 ❌ 过度设计 ✅ 简洁高效
需要自动依赖注入 ✅ 必要 ❌ 需手动处理
频繁打开的页面 ⚠️ 有性能损耗 ✅ 更高效
需要严格生命周期控制 ✅ 自动管理 ❌ 需手动管理

根据你的需求描述,推荐:

  1. 根路由使用永久控制器 (permanent: true)
  2. 详情页不使用 binding,采用参数驱动刷新
  3. 使用 Get.offAllNamed('/') 返回根路由时自动销毁中间控制器
  4. 对需要强制刷新的页面使用 URL 参数触发刷新

这样既能满足路由管理需求,又能优化内存使用和性能表现。

相关推荐
AD钙奶-lalala2 小时前
android:foregroundServiceType详解
android
大胃粥5 小时前
Android V app 冷启动(13) 焦点窗口更新
android
Swift社区5 小时前
Swift 解法详解:如何在二叉树中寻找最长连续序列
开发语言·ios·swift
小小鱼儿小小林7 小时前
苹果WWDC25重磅发布:Apple Intelligence与Liquid Glass引领未来
ios·wwdc·苹果
I烟雨云渊T7 小时前
2025年的WWDC所更新的内容
macos·ios·wwdc
Fatbobman(东坡肘子)7 小时前
WWDC 2025 开发者特辑 | 肘子的 Swift 周报 #088
开发语言·macos·ios·swiftui·ai编程·swift·wwdc
vastgrassland7 小时前
从WWDC看苹果产品发展的规律
macos·ios·wwdc
Digitally9 小时前
如何将文件从 iPhone 传输到闪存驱动器
ios·iphone
fatiaozhang95279 小时前
中兴B860AV1.1_晨星MSO9280芯片_4G和8G闪存_TTL-BIN包刷机固件包
android·linux·adb·电视盒子·av1·魔百盒刷机
fatiaozhang952711 小时前
中兴B860AV1.1_MSO9280_降级后开ADB-免刷机破解教程(非刷机)
android·adb·电视盒子·av1·魔百盒刷机·移动魔百盒·魔百盒固件