Flutter插件Get(4):依赖管理篇.md

一、GetX 依赖管理概述

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

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

  • Provider:Provider 是 Flutter 官方推荐的依赖注入和状态管理工具。它需要较多的样板代码,使用起来相对复杂。
  • Riverpod:Riverpod 是 Provider 的增强版,提供了更多的功能和更好的性能,但学习曲线较陡。
  • GetX:GetX 简单易用,提供了更多的功能,如路由管理和状态管理,适合各种规模的项目。

二、创建 Controller实例的方法介绍(并实现手动注入依赖的示例)

GetX 提供的创建 Controller实例的方法有以下几种,可根据不同的业务来进行选择:

  • Get.put(): 不使用控制器实例也会被创建
  • Get.lazyPut(): 懒加载方式创建实例,只有在使用时才创建
  • Get.putAsync(): Get.put() 的异步版版本
  • Get.create(): 每次使用都会创建一个新的实例

2.1 Get.put

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

它的使用场景如下:

  • 全局单例:需要在整个应用程序中共享的依赖。
  • 初始化即创建:需要在应用启动时立即创建的依赖。

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

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

我们可以通过一个例子说明Get.put的用法。在这个例子中,我们在首页操作计时器,然后在收藏页面调用 HomeController 中的计时器的点击次数。效果图如下所示:

Flutter_get_B.gif


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

// (1)首先我们创建一个控制器,它包含计数逻辑
class HomeController extends GetxController {
  var count = 0.obs; // 计数值

  // 增加函数
  void increment() {
    count++;
  }
}

(2)在应用启动时手动注入依赖。在 main 方法中使用 Get.put 将 CounterController 注入到依赖管理系统中:

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

(3)在页面中使用依赖:

// (3)在页面中使用依赖
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();
  }

  // TabBarView页面实现
  @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: "设置"),
        ],
      ),
    );
  }
}

(4)最后分别实现这3个页面:

// 首页
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.2 Get.lazyPut

懒加载一个依赖,只有在使用时才会被实例化。适用于不确定是否会被使用的依赖或者计算高昂的依赖。类似 Kotlin 的 Lazy 代理。

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

LazyController 在这时候并不会被创建,而是等到你使用 LazyController 的时候才会被 initialized,也就是执行下面代码的时候才 initialized:

Get.find<LazyController>();

在使用后,使用时的 Wdiget 的生命周期结束,也就是这个 Widgetdispose,这个实例就会被销毁。


2.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
)

2.4 Get.create

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

void main() {
  Get.create<MyController>(() => MyController());
  runApp(MyApp());
}

三、使用Getx Bindings依赖注入

3.1 Binding的介绍

上面我们知道在页面中使用 Controller 需要先进行Get.put操作。但是每一个界面都这样操作,这种手动注入的方式麻烦不说还很难进行统一管理。为了更好的是组件中使用 Controller,我们可以用到 Getx 提供的 Binding 功能可以实现自动注入功能。

Binding 类是一个将依赖注入进行分离,Binding 模块在路由跳转时,统一对界面通过懒注入的方式将 binding 模块的 GetXController 注入到界面中,简单说,就是把 UI 中的控制器实例化部分抽离出来了,抽离时需要实现 Bindings 类。

可以统一管理复杂模块的多个 GetXController,可以将路由、状态管理器和依赖管理器完全集成。

不管是写web前端,还是Android或者ios,面对复杂的页面我们都希望页面(UI)和实现(逻辑)进行分离,那么常用的模式有MVC,MVVM等进行抽离,那么在Flutter中我们可以用同样的方式组织项目。

Flutter_Get_F.png


3.2 如何使用Binding

(1) 创建一个控制器

import 'package:flutter/material.dart';
import 'package:get/get.dart';

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

// 控制器
class HomeController extends GetxController {
  var count = 0.obs;
  increment() => count++;
}

(2) 第二步: 创建一个类并实现 Binding,如果有多个控制器,就在这实例化多个

// 实现 Binding
class HomeBinding implements Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => HomeController());
  }
}

(3) 绑定到路由中

我们要使用该 Binding 来建立路由管理器、依赖关系和状态之间的连接。 这里有两种方式,如果使用的是命名路由表:

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
          primarySwatch: Colors.blue,
          appBarTheme: const AppBarTheme(
            centerTitle: true,
          )),
      // 命名路由表
      getPages: [
        GetPage(name: '/', page: () => HomeView(), binding: HomeBinding()), // 绑定到路由中
      ],
    );
  }
}

如果是直接跳转:

Get.to(HomeBinding(), binding: HomeBinding());

(4) Widget 中使用

使用Get.find()可以处理一个 UI 中有多个控制器的情况,如果你只有一个控制器,可以选择使用 GeView:

// 视图界面实现
class HomeView extends StatelessWidget {
  HomeView({super.key});

  final c = Get.find<HomeController>();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: const Text("Flutter Binding demo")),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Obx(() => Text('${c.count}')),
          ],
        ),
        floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add), onPressed: c.increment));
  }
}

3.3 如何使用GetView

上面使用注入依赖解耦了,但是获取还是略显不方便,GetX 也为我们考虑到了。使用 GetView 代替 StatelessWidget,可以直接使用controller.变量调用,前提条件就是只能用于只有一个 controller 控制器的项目。

在前面示例的后面编写这个类,可以代替前面的 HomeView。

// 视图界面实现
class SimpleView extends GetView<HomeController> { // 这里extends的是GetView 
  const SimpleView({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Obx(() => Text('${controller.count}')),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          controller.increment();
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

首先,你需要将你的 class 继承自 GetxView(T 为你的Controller)。然后 GetxView 会自动帮你把前面定义的 Controller 注入到 view 中,你可以简单理解为它自动帮你执行了以下步骤:

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

GetView 是一个 Stateless Widget,仅是为了方便使用 controller。如果我们只有一个 controller 作为依赖,可以使用 GetView 取代 StatelessWidget 并且不用再写Get.find这样可以不需要 StatelessWidget 和 StatefulWidget。这也是开发最常用的模式,推荐大家使用。


这里贴一下所有的代码实现:

import 'package:flutter/material.dart';
import 'package:get/get.dart';

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

// 控制器
class HomeController extends GetxController {
  var count = 0.obs;
  increment() => count++;
}

// 实现 Binding
class HomeBinding implements Bindings {
  @override
  void dependencies() {
    Get.lazyPut(() => HomeController());
  }
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      theme: ThemeData(
          primarySwatch: Colors.blue,
          appBarTheme: const AppBarTheme(
            centerTitle: true,
          )),
      getPages: [
        GetPage(name: '/', page: () => HomeView(), binding: HomeBinding()),
        //GetPage(name: '/', page: () => const SimplePage(), binding: HomeBinding()),
      ],
    );
  }
}

// 视图界面实现
class HomeView extends StatelessWidget {
  HomeView({super.key});

  final c = Get.find<HomeController>();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(title: const Text("Flutter Binding demo")),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Obx(() => Text('${c.count}')),
          ],
        ),
        floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add), onPressed: c.increment));
  }
}

// 视图界面实现
class SimplePage extends GetView<HomeController> {
  const SimplePage({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Obx(() => Text('${controller.count}')),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          controller.increment();
        },
        child: const Icon(Icons.add),
      ),
    );
  }
}

效果图如下所示:

Flutter_Get_F.png


参考:

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

使用Getx框架简化Flutter开发_flutter get教程-CSDN博客

Flutter开发之——getX-依赖管理(07)_flutter get.find-CSDN博客


posted @ 2024-10-18 10:10  fengMisaka  阅读(51)  评论(0编辑  收藏  举报