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

2024-07-21 1060阅读

目录

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

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

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

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

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

图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 createState() => _DependencyManagerMainState();
}
class _DependencyManagerMainState extends State
    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();
  @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();
  @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才会被调用。

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

Get.lazyPut(

  () {

    // ... some logic if needed

    return FirebaseAuth();

  },

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

  fenix: true

)

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

3.Get.putAsync

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

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

Get.putAsync(() async {
  final prefs = await SharedPreferences.getInstance();
  await prefs.setInt('counter', 12345);
  return prefs;
});
Get.putAsync( () async => await YourAsyncClass() )

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

Get.putAsync(

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

  AsyncInstanceBuilderCallback 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());
  runApp(MyApp());
}

2.依赖生命周期管理

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

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

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

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

        模块化依赖注入:

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

class AppBinding extends Bindings {

  @override

  void dependencies() {

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

  }

}

void main() {

  runApp(GetMaterialApp(

    initialBinding: AppBinding(),

    home: MyApp(),

  ));

}

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

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

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

CounterController counterController = Get.find();

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

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

        控制台日志输出如下:

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

        图3.控制台输入日志

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

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

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

图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: [
            Text('You have pushed the button this many times:'),
            GetX(
              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  

         优化建议:

  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: [
            GetX(
              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: [
            GetX(
              builder: (_) {
                return Text('Count: ${controller.count.value}');
              },
            ),
            ElevatedButton(
              onPressed: controller.increment,
              child: Text('Increment'),
            ),
          ],
        ),
      ),
    );
  }
  @override
  void dispose() {
    // 当页面销毁时,释放控制器实例
    Get.delete();
    super.dispose();
  }
}

        修改之后的实例中:

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

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

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

6. 参考资料

        1.Getx官方文档

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]