Flutter插件Get(4):依赖管理篇.md
一、GetX 依赖管理概述
GetX 提供了一种简单且高效的依赖注入方式,通过少量代码即可实现依赖的注入、管理和访问。它主要通过Get.put
、Get.lazyPut
、Get.putAsync
和Get.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 中的计时器的点击次数。效果图如下所示:
(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中我们可以用同样的方式组织项目。
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中GetX的用法(超详细使用指南之路由依赖管理篇)-CSDN博客
使用Getx框架简化Flutter开发_flutter get教程-CSDN博客
Flutter开发之——getX-依赖管理(07)_flutter get.find-CSDN博客