Flutter中GetX的用法(超详细使用指南之路由依赖管理篇)

目录

1.前言

[2.GetX 依赖管理概述](#2.GetX 依赖管理概述)

[1.GetX 依赖管理的基本概念](#1.GetX 依赖管理的基本概念)

2.与其他依赖管理工具的比较

[3. 基础依赖注入](#3. 基础依赖注入)

1.Get.put

2.Get.lazyPut

3.Get.putAsync

4.高级依赖注入

1.使用Get.create

2.依赖生命周期管理

[5. 参考资料](#5. 参考资料)


1.前言

今天这篇博客主要介绍Getx的三大功能之一的依赖管理。依赖管理是软件开发中的一个关键部分,尤其是在复杂应用中。它帮助开发者管理应用中的各种依赖,确保依赖的实例化和生命周期管理变得更加简单和高效。

2.GetX 依赖管理概述

1.GetX 依赖管理的基本概念

GetX 提供了一种简单且高效的依赖注入方式,通过少量代码即可实现依赖的注入、管理和访问。它主要通过Get.put、Get.lazyPut、Get.putAsync和Get.create等方法来实现依赖管理。

2.与其他依赖管理工具的比较

Provider:Provider 是 Flutter 官方推荐的依赖注入和状态管理工具。它需要较多的样板代码,使用起来相对复杂。

Riverpod:Riverpod 是 Provider 的增强版,提供了更多的功能和更好的性能,但学习曲线较陡。

GetX:GetX 简单易用,提供了更多的功能,如路由管理和状态管理,适合各种规模的项目。

3. 基础依赖注入

1.Get.put

Get.put 是 GetX 提供的一个方法,用于将一个实例注入到依赖管理系统中。它的主要作用是创建一个新的实例,并将其注册为依赖,供整个应用程序的其他部分使用。使用 Get.put 可以方便地管理和访问依赖实例,避免手动管理实例的生命周期。

它的使用场景如下:

1.全局单例:需要在整个应用程序中共享的依赖。

2.初始化即创建:需要在应用启动时立即创建的依赖。

我们使用Get.put的时候,还可以配置一些选项:

  1. dependency:要注入的依赖实例
  2. tag:依赖实例的可选标签,用于区分相同类型的不同实例。
  3. permanent:是否将依赖设置为永久存在,即使不再使用也不会被销毁。

我们可以通过一个例子说明Get.put的用法。

在这个例子中,我们在首页操作计时器,然后在收藏页面调用HomeController中的计时器的点击次数。

图1.Get.put用法

我们来看看如何实现这个功能。

1.首先我们创建一个控制器,它包含计数逻辑。

Dart 复制代码
class HomeController extends GetxController {
  var count = 0.obs;

  void increment() {
    count++;
  }
}

2.在应用启动时注入依赖

在 main 方法中使用 Get.put 将 CounterController 注入到依赖管理系统中。

Dart 复制代码
void main() {
  // 注入依赖
  Get.put(HomeController());
  runApp(const MyApp());
}

3.在页面中使用依赖

完整代码:

Dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

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

  @override
  State<DependencyManagerMain> createState() => _DependencyManagerMainState();
}

class _DependencyManagerMainState extends State<DependencyManagerMain>
    with SingleTickerProviderStateMixin {
  late TabController _tabController;

  @override
  void initState() {
    super.initState();
    // 初始化 TabController
    _tabController = TabController(length: 3, vsync: this);
  }

  @override
  void dispose() {
    // 销毁 TabController
    _tabController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: TabBarView(
        controller: _tabController,
        children: [
          HomePage(),
          FavoritesTab(),
          const SettingsTab(),
        ],
      ),
      bottomNavigationBar: TabBar(
        controller: _tabController,
        tabs: const [
          Tab(icon: Icon(Icons.home), text: "首页"),
          Tab(icon: Icon(Icons.star), text: "收藏"),
          Tab(icon: Icon(Icons.settings), text: "设置"),
        ],
      ),
    );
  }
}


class HomeController extends GetxController {
  var count = 0.obs;

  void increment() {
    count++;
  }
}
class HomePage extends StatelessWidget {
  HomePage({super.key});

  final HomeController controller = Get.find<HomeController>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('首页'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 使用 Obx 来监听 count 的变化
            Obx(() => Text(
              'Count: ${controller.count}',
              style: const TextStyle(fontSize: 20),
            )),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: controller.increment,
              child: const Text('Increment'),
            ),
          ],
        ),
      ),
    );
  }
}

class FavoritesTab extends StatelessWidget {
  FavoritesTab({super.key});

  final HomeController controller = Get.find<HomeController>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('收藏'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 使用 Obx 来监听 count 的变化
            Obx(() => Text(
              '点击次数: ${controller.count}',
              style: const TextStyle(fontSize: 20),
            )),
            const SizedBox(height: 20),
          ],
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return const Center(
      child: Text('Settings Tab Content'),
    );
  }
}

2.Get.lazyPut

可以懒加载一个依赖,这样它只有在使用时才会被实例化。这对于计算代价高的类来说非常有用,或者如果你想在一个地方实例化几个类(比如在Bindings类中),而且你知道你不会在那个时候使用这个类。

Get.lazyPut方法用于延迟创建依赖实例,只有在第一次使用时才会创建。

这里就不写demo了,感兴趣的话,可以参考上一个例子中的demo写Demo测试一下。

///只有当第一次使用Get.find<ApiMock>时,ApiMock才会被调用。

Get.lazyPut<ApiMock>(() => ApiMock());

Get.lazyPut<FirebaseAuth>(

() {

// ... some logic if needed

return FirebaseAuth();

},

tag: Math.random().toString(),

fenix: true

)

Get.lazyPut<Controller>( () => Controller() )

3.Get.putAsync

如果你想注册一个异步实例,你可以使用Get.putAsync

Get.putAsync方法用于异步创建依赖实例,适用于需要进行异步操作的实例化过程。

Dart 复制代码
Get.putAsync<SharedPreferences>(() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setInt('counter', 12345);
  return prefs;
});

Get.putAsync<YourAsyncClass>( () async => await YourAsyncClass() )

我们还可以设置如下参数:

Get.putAsync<S>(

// 必备:一个将被执行的异步方法,用于实例化你的类。

AsyncInstanceBuilderCallback<S> builder,

// 可选:和Get.put()一样,当你想让同一个类有多个不同的实例时,就会用到它。

// 必须是唯一的

String tag,

// 可选:与Get.put()相同,当你需要在整个应用程序中保持该实例的生命时使用。

// 默认值为false

bool permanent = false

)

4.GetView、GetxView 和GetxController

4.高级依赖注入

1.使用Get.create

Get.create方法用于每次请求时创建一个新的依赖实例,适用于需要多次创建的依赖。

Dart 复制代码
void main() {
  Get.create<MyController>(() => MyController());
  runApp(MyApp());
}

2.依赖生命周期管理

GetX 提供了三种依赖生命周期管理模式:

Permanent:永久存在,直到应用关闭。

Singleton:默认模式,实例在第一次创建后存在于整个应用生命周期内。

Transient:每次请求时创建新的实例。

模块化依赖注入:

在大型项目中,可以将依赖注入模块化,便于管理和维护。

class AppBinding extends Bindings {

@override

void dependencies() {

Get.lazyPut<MyController>(() => MyController());

}

}

void main() {

runApp(GetMaterialApp(

initialBinding: AppBinding(),

home: MyApp(),

));

}

5.依赖管理中的常见问题及解决方案

1.依赖未能正确注入或找到

例如在下面的例子中,我在首页调用Get.find的方法,但是想相应的实例方法却没有被创建。

CounterController counterController = Get.find();

图2.依赖未能正确注入或找到

控制台日志输出如下:

图3.控制台输入日志

解决的方法也很简单,就是在调用之前确保相应的GetxController被实例化。

例如我们可以在app启动之前,实例化相应的控制器或者初始化的时候就绑定相应的GetxController。

图3.全局绑定GetxController

2.实例化过程中的性能问题

在使用 GetX 时,如果不小心,可能会引入性能问题。例如,不恰当地更新整个 UI 而不是局部更新,或者频繁地创建和销毁控制器。下面是一个示例,展示了如何使用 GetX 并说明可能的性能问题,以及如何优化这些问题。

在下面的代码中,PerformancelssueExample类展示了可能的性能问题。每次计数器值变化时,整个 Column 会被重建,导致性能下降。

Dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: HomePage(),
    );
  }
}

class HomeController extends GetxController {
  var counter = 0.obs;
  void increment() => counter.value++;
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final HomeController controller = Get.put(HomeController());

    return Scaffold(
      appBar: AppBar(
        title: Text('GetX Performance Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            GetX<HomeController>(
              builder: (controller) {
                return Text(
                  '${controller.counter.value}',
                  style: Theme.of(context).textTheme.headline4,
                );
              },
            ),
            PerformanceIssueExample(),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.increment,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

class PerformanceIssueExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final HomeController controller = Get.find();

    return Column(
      children: [
        Obx(() {
          print("Rebuilding entire Column");
          return Column(
            children: [
              for (int i = 0; i < 100; i++)
                Text('Item $i'),
              Text('Counter: ${controller.counter.value}'),
            ],
          );
        }),
        ElevatedButton(
          onPressed: controller.increment,
          child: Text('Increment Counter'),
        ),
      ],
    );
  }
}

优化建议:

  1. 避免在可观察值变化时重建整个组件树。

  2. 将变化范围限制在最小的部件中,以减少重建次数

  3. 使用 GetX 或 Obx 只在必要时更新 UI

3.依赖生命周期管理不当导致的内存泄漏

在使用 GetX 时,内存管理是一个重要的考虑因素。如果不正确地管理控制器和依赖项,可能会导致内存泄漏。下面是一个示例,说明 GetX 内存管理中的常见问题及其解决方案。

1.示例代码

假设我们有一个简单的计数器应用,每次进入一个新页面都会创建一个新的控制器实例,但未正确释放旧的控制器实例,这会导致内存泄漏。

以下面代码为例:

Dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

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

class CounterController extends GetxController {
  var count = 0.obs;

  void increment() => count++;
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Page'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Get.to(CounterPage());
          },
          child: Text('Go to Counter Page'),
        ),
      ),
    );
  }
}

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 不推荐的做法:每次进入页面时创建新的控制器实例
    final CounterController controller = Get.put(CounterController());

    return Scaffold(
      appBar: AppBar(
        title: Text('Counter Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            GetX<CounterController>(
              builder: (_) {
                return Text('Count: ${controller.count.value}');
              },
            ),
            ElevatedButton(
              onPressed: controller.increment,
              child: Text('Increment'),
            ),
          ],
        ),
      ),
    );
  }
}

在这个示例中,每次导航到 CounterPage 时,都会创建一个新的 CounterController 实例,但未正确释放旧的实例,这会导致内存泄漏。

2.解决方案

为了避免内存泄漏,可以使用 Get.put 或 Get.lazyPut 在合适的地方创建和释放控制器实例,并使用 Get.delete 方法在不需要时释放控制器实例。

修改后的代码如下:

Dart 复制代码
import 'package:flutter/material.dart';
import 'package:get/get.dart';

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

class CounterController extends GetxController {
  var count = 0.obs;

  void increment() => count++;
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      home: HomePage(),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Page'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            Get.to(CounterPage());
          },
          child: Text('Go to Counter Page'),
        ),
      ),
    );
  }
}

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // 推荐的做法:使用 Get.put 只创建一个控制器实例,直到不再需要
    final CounterController controller = Get.put(CounterController());

    return Scaffold(
      appBar: AppBar(
        title: Text('Counter Page'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            GetX<CounterController>(
              builder: (_) {
                return Text('Count: ${controller.count.value}');
              },
            ),
            ElevatedButton(
              onPressed: controller.increment,
              child: Text('Increment'),
            ),
          ],
        ),
      ),
    );
  }

  @override
  void dispose() {
    // 当页面销毁时,释放控制器实例
    Get.delete<CounterController>();
    super.dispose();
  }
}

修改之后的实例中:

1.正确使用Get.put:在 CounterPage 页面创建时,只创建一个 CounterController 实例。

2.使用Get.put和Get.lazyPut:根据需要创建和管理控制器实例。

3.释放控制器实例:在页面销毁时,通过 Get.delete<CounterController>() 方法释放控制器实例,防止内存泄漏。

6. 参考资料

1.Getx官方文档

相关推荐
孤鸿玉16 小时前
Fluter InteractiveViewer 与ScrollView滑动冲突问题解决
flutter
叽哥1 天前
Flutter Riverpod上手指南
android·flutter·ios
BG2 天前
Flutter 简仿Excel表格组件介绍
flutter
zhangmeng2 天前
FlutterBoost在iOS26真机运行崩溃问题
flutter·app·swift
恋猫de小郭2 天前
对于普通程序员来说 AI 是什么?AI 究竟用的是什么?
前端·flutter·ai编程
卡尔特斯2 天前
Flutter A GlobalKey was used multipletimes inside one widget'schild list.The ...
flutter
w_y_fan2 天前
Flutter 滚动组件总结
前端·flutter
醉过才知酒浓2 天前
Flutter Getx 的页面传参
flutter
火柴就是我3 天前
flutter 之真手势冲突处理
android·flutter
Speed1233 天前
`mockito` 的核心“打桩”规则
flutter·dart