您的当前位置:首页正文

深入浅出 GetX:超详细实用指南,全方位掌握 Flutter 开发利器第三篇之路由依赖管理篇

2024-11-17 来源:个人技术集锦

1.前言

2.GetX 依赖管理概述

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

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

3. 基础依赖注入

1.Get.put

2.Get.lazyPut

3.Get.putAsync

4.高级依赖注入

1.使用Get.create

2.依赖生命周期管理

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的时候,还可以配置一些选项:

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

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

图1.Get.put用法

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

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

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

  void increment() {
    count++;
  }
}

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

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

void main() {
  // 注入依赖
  Get.put(HomeController());
  runApp(const MyApp());
}

        3.在页面中使用依赖

        完整代码:

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测试一下。

3.Get.putAsync

        如果你想注册一个异步实例,你可以使用Get.putAsync
        Get.putAsync方法用于异步创建依赖实例,适用于需要进行异步操作的实例化过程。

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方法用于每次请求时创建一个新的依赖实例,适用于需要多次创建的依赖。

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 会被重建,导致性能下降。

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.示例代码

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

        以下面代码为例:

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 方法在不需要时释放控制器实例。

        修改后的代码如下:

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官方文档

显示全文