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

目录

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中的计时器的点击次数。

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

///只有当第一次使用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方法用于异步创建依赖实例,适用于需要进行异步操作的实例化过程。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/843549.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

视频翻译保留原音色pyvideotrans+clone-voice

剪映的视频翻译时长限制5分钟以内&#xff0c;需要积分2700首次有减免大概21.6元&#xff08;1秒9积分/1元100积分&#xff09; • 视频翻译配音工具pyvideotrans 将视频从一种语言翻译为另一种语言&#xff0c;并添加配音 打包链接&#xff1a;夸克网盘分享 升级补丁&#…

lse:一款专为渗透测试和CTF设计的Linux枚举工具

关于linux-smart-enumeration linux-smart-enumeration是一款专为渗透测试和CTF设计的Linux枚举工具&#xff0c;该工具可以帮助广大研究人员收集与本地Linux系统安全相关的信息。 工具特性 该工具从2.0版本开始符合POSIX标准&#xff0c;并且经过了shellcheck和posh测试。它…

太速科技-基于XCVU9P+ C6678的8T8R的无线MIMO平台

基于XCVU9P C6678的8T8R的无线MIMO平台 一、板卡概述 板卡基于TI TMS320C6678 DSP和XCVU9P高性能FPGA&#xff0c;FPGA接入4片AD9361 无线射频&#xff0c;构建8输入8输出的无线MIMO平台&#xff0c;丰富的FPGA资源和8核DSP为算法验证和信号处理提供强大能力。 二…

解决显存不足问题:深度学习中的 Batch Size 调整【模型训练】

解决显存不足问题&#xff1a;深度学习中的 Batch Size 调整 在深度学习训练中&#xff0c;显存不足是一个常见的问题&#xff0c;特别是在笔记本等显存有限的设备上。本文将解释什么是 Batch Size&#xff0c;为什么调整 Batch Size 可以缓解显存不足的问题&#xff0c;以及调…

【开发踩坑】 MySQL不支持特殊字符(表情)插入问题

背景 线上功能报错&#xff1a; Cause:java.sql.SQLException:Incorrect string value:xFO\x9F\x9FxBO for column commentat row 1 uncategorized SQLException; SQL state [HY000]:error code [1366]排查 初步觉得是编码问题&#xff08;utf8 — utf8mb4&#xff09; 参考上…

昇思25天学习打卡营第17天|LLM-基于MindSpore的GPT2文本摘要

打卡 目录 打卡 环境准备 准备阶段 数据加载与预处理 BertTokenizer 部分输出 模型构建 gpt2模型结构输出 训练流程 部分输出 部分输出2&#xff08;减少训练数据&#xff09; 推理流程 环境准备 pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspo…

Web前端Promise

Promise介绍与使用 Promise是什么&#xff1f; 1.抽象表达&#xff1a; Promise是一门新的技术&#xff08;ES6规范&#xff09;Promise是JS中进行异步编程的新解决方案备注&#xff1a;旧方案是单纯使用回调函数 2.具体表达&#xff1a; 从语法上来说&#xff1a;Promise…

多层全连接神经网络(六)---各种优化算法的变式

梯度下降算法原理此处不做过多说明 梯度下降法的变式 1. SGD 随机梯度下降法是梯度下降法的一个小变形&#xff0c;就是每次使用一批(batch)数据进行梯度的计算&#xff0c;而不是计算全部数据的梯度&#xff0c;因为现在深度学习的数据量都特别大&#xff0c;所以每次都计算…

Lua基础知识入门

1 基础知识 标识符&#xff1a;标识符的定义和 C语言相同&#xff1a;字母和下划线_ 开头&#xff0c; 下划线_ 大写字母一般是lua保留字&#xff0c; 如_VERSION 全局变量&#xff1a;默认情况下&#xff0c;变量总是认为是全局的&#xff0c;不需要申明&#xff0c;给一个变…

docker默认存储地址 var/lib/docker 满了,换个存储地址操作流程

1. 查看docker 存储地址 docker info如下 var/lib/docker2、查看内存大小 按需执行 df -h 找超过100M的大文件 find / -type f -size 100M -exec ls -lh {} \; df -Th /var/lib/docker 查找这个文件的容量 df -h 查找所有挂载点 du -hs /home/syy_temp/*1、df -h 2、sud…

Linux网络——套接字与UdpServer

目录 一、socket 编程接口 1.1 sockaddr 结构 1.2 socket 常见API 二、封装 InetAddr 三、网络字节序 四、封装通用 UdpServer 服务端 4.1 整体框架 4.2 类的初始化 4.2.1 socket 4.2.2 bind 4.2.3 创建流式套接字 4.2.4 填充结构体 4.3 服务器的运行 4.3.1 rec…

全国区块链职业技能大赛国赛考题区块链产品需求分析与方案设计

任务1-1:区块链产品需求分析与方案设计 本任务需要依据项目背景完成需求分析与方案设计,具体要求如下: 依据给定区块链食品溯源系统的业务架构图,对考题进行业务分析,尽可能多的去考虑一个业务系统所需要的模块,使用Visio或思维导图工具展现本系统的基本设计概念和处理流…

基于ffmepg的视频剪辑

1.ffmpeg命令实现视频剪辑 FFmpeg是一个非常强大的视频处理工具&#xff0c;可以用来剪辑视频。以下是一个基本的FFmpeg命令行示例&#xff0c;用于剪辑视频&#xff1a; $ ffmpeg -i ./最后一滴水.mp4 -ss 0:0:20 -t 50 -c copy output.mp4-i ./最后一滴水.mp4 输入文件  …

图像生成(Text-to-Image)发展脉络

这篇博客对 图像生成&#xff08;image generation&#xff09; 领域的经典工作发展进行了梳理&#xff0c;包括重要的一些改进&#xff0c;目的是帮助读者对此领域有一个整体的发展方向把握&#xff0c;并非是对每个工作的详细介绍。 脉络发展&#xff08;时间顺序&#xff0…

探究大语言模型(LLM)漏洞和安全优秀实践

你可能已听说过LLM强势亮相&#xff0c;至少ChatGPT就是代表。 大语言模型(LLM)指语言处理模型。这类模型经过训练&#xff0c;可以执行各种各样的语言任务&#xff1a;翻译、文本生成和问题回答等。 有几个LLM家族和架构&#xff0c;最著名的是GPT(生成式预训练Transformer)…

Grafana :利用Explore方式实现多条件查询

背景 日志统一推送到Grafana上管理。所以&#xff0c;有了在Grafana上进行日志搜索的需求&#xff0c;而进行日志搜索通常需要多条件组合。 解决方案 通过Grafana的Explore的方式实现多条件查询。 直接看操作步骤&#xff1a; 在主页搜索框中输入“Explore” 进入这个界面…

python—日期相差多少天(PythonTip)

[题目描述] 编写一个程序&#xff0c;计算两个日期之间的天数。 导入datetime模块。定义函数calculate_days_between()数&#xff0c;其中有两个参数&#xff1a;(date1, date2)&#xff0c;类型为字符串&#xff0c;格式为YYYY-MM-DD。在函数内&#xff0c;将字符串转换为date…

全面战争模拟器免费下载地址,纯分享

全面战争模拟器以其独特的物理引擎和搞笑的战斗场面吸引了大量玩家&#xff0c;并在游戏社区中赢得了极高的评价。它不仅提供了丰富的策略性玩法&#xff0c;还通过滑稽的视觉效果和搞笑的战斗带来了极大的娱乐性。游戏的沙盒模式和自定义功能更是让玩家能够充分发挥创意&#…

磁盘的作业

1、新添加一块硬盘&#xff0c;大小为5g&#xff0c;给这块硬盘分一个mbr格式的主分区(大小为3g)&#xff0c;给此主分区创建ext2的文件系统&#xff0c;挂载到/guazai1目录&#xff0c;并写入文件内容为"this is fist disk"文件名为1.txt的文件。 [rootwyk ~]# fdis…

02线性表 - 链表

这里是只讲干货不讲废话的炽念&#xff0c;这个系列的文章是为了我自己以后复习数据结构而写&#xff0c;所以可能会用一种我自己能够听懂的方式来描述&#xff0c;不会像书本上那么枯燥和无聊&#xff0c;且全系列的代码均是可运行的代码&#xff0c;关键地方会给出注释^_^ 全…