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 参数触发刷新

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

相关推荐
alexhilton32 分钟前
为什么你的App总是忘记所有事情
android·kotlin·android jetpack
小蜜蜂嗡嗡4 小时前
flutter封装vlcplayer的控制器
前端·javascript·flutter
AirDroid_cn4 小时前
OPPO手机怎样被其他手机远程控制?两台OPPO手机如何相互远程控制?
android·windows·ios·智能手机·iphone·远程工作·远程控制
尊治4 小时前
手机电工仿真软件更新了
android
杂雾无尘6 小时前
开发者必看,全面解析应用更新策略,让用户无法拒绝你的应用更新!
ios·xcode·swift
xiangzhihong87 小时前
使用Universal Links与Android App Links实现网页无缝跳转至应用
android·ios
车载应用猿7 小时前
基于Android14的CarService 启动流程分析
android
没有了遇见8 小时前
Android 渐变色实现总结
android
Digitally9 小时前
如何将iPhone备份到Mac/MacBook
macos·ios·iphone
帅次9 小时前
【iOS设计模式】深入理解MVC架构 - 重构你的第一个App
ios·swiftui·objective-c·iphone·swift·safari·cocoapods