Get_it
Get_it是一个服务定位器,更通俗具体一点得说它是一个工具箱。借鉴于.net。
对于想偷懒,直接全局调用的某些对象尤其省事。
配合Provider事半功倍
什么叫做工具箱
我们通过介绍如何使用Get_it
来解释什么叫做工具箱。
比如,如果我是一个铁匠,工作的时候要使用锤子,那么我就从工具箱里面找出来锤子,如果我要用扳手,那么我也从工具箱中把我的扳手找出来。
Get_it在现实中得定位大致就是一个工具箱。要成为一个好用的工具箱,第一步得把各种各样得工具放进去,也就是
步骤一:在工具箱中注册工具。
locator.dart
import 'package:get_it/get_it.dart';
import './core/services/navigation_service.dart';
import './core/services/service_jiandao.dart';
import './core/services/service_chuizi.dart';
import './core/services/service_C.dart';
GetIt locator = GetIt.instance;
Future<void> setupLocator({bool test = false}) async {
/// Services 需要什么就注册什么
// NavigationService 就是一个工具,导航工具
/// 工具一:导航服务,注册(放入工具箱)
locator.registerLazySingleton<NavigationService>(
() => NavigationService(),
);
/// ......
/// 工具二:剪刀,注册(放入工具箱)
locator.registerLazySingleton<ServiceJiandao>(
() => ServiceJiandao(),
);
/// 工具三:锤子,注册(放入工具箱)
locator.registerLazySingleton<ServiceChuizi>(
() => ServiceChuizi(),
);
/// 工具四:工具C,注册(放入工具箱)
locator.registerLazySingleton<ServiceC>(
() => ServiceC(),
);
}
取名随意,不过最好命名为 Service + Name这样的形式。
工具箱里面先在已经装好了工具了,也不能把工具箱丢在荒郊野外,还需要把它放到我们工作室内。
也就是第二步,需要在入口文件 main.dart 启动一下,表示我已经把工具箱带到了工作室.
步骤二:将工具箱放入工作室。
main.dart
void main() {
/// 启动GetIt定位服务
setupLocator();
runApp(MyApp());
}
到了这一步,所有准备工作已经完成,工具箱已经准备好,并且已经被我们放入到工作室中,那么在工作打铁(写页面)得过程中就可以随意的使用了。
如何使用这些工具?
在页面得任何位置,都可以调用任何一个已经注册得工具,调用方式有以下几种,请自行类比:
// 调用工具一:导航服务
// push() 方法在 navigation_service.dart 中定义
FlatButton(
child: Text("Update"),
onPressed: (){
locator<NavigationService>().push(RoutesUtils.homePage);
}
),
// 调用工具二:剪刀 。其中 caijian() 方法在 service_jiandao.dart 中定义
GestureDetector(
child: Text("Update"),
onTap: (){
locator<ServiceJiandao>().caijian();
}
),
// 调用工具三:锤子。 其中 name 属性在 service_chuizi.dart 中定义
FlatButton(
child: Text("Update"),
onPressed: (){
var serverChuizi = locator<ServiceChuizi>();
serverChuizi.name;
}
),
// 调用工具四:工具C。kissMe() 方法在 service_C.dart 中定义
FlatButton(
child: Text("Update"),
onPressed: (){
var serverC = locator<ServiceC>();
serverC.kissMe();
}
),
那么剩下的内容就是如何创造工具,以及如何去写工具中的方法。这就跟普通dart的写法一模一样了。
如何创造工具?工具示例
Get_it | InheritedWidget | Provider 对比
使用
get_it
并不复杂,并且它还跟 InheritedWidget ,Provider 是同样的功能。那为什么要重复造轮子,意义在于:代替 InheritedWidget或Provider成为在widget中访问class最佳的工具 。
在我看来,这一点它做到了.
我们都知道 flutter项目的页面结构都是一个树状结构,上万层树干树杈嵌套也很正常,这三者都是为了解决树干与分支之间数据通信问题, 但是用于同一种功能的工具也有好坏精细之分
get_it
好用多了, 它直接上王八拳:工具一旦注册,那么全局随意调用。
以下内容为拓展阅读
下面列出了使用InheritedWidget 和Provider 得使用方式,自行对比上面Get_it调用,看看哪种更简单灵活。
1. 对比使用:InheritedWidget
创建数据类 ShareDataWidget
,然后在页面中用数据类ShareDataWidget
包裹页面child部分
第一步,创建 ShareDataWidget.dart
文件
class ShareDataWidget extends InheritedWidget {
ShareDataWidget({
@required this.data,
Widget child
}) :super(child: child);
final int data;
static ShareDataWidget of(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<ShareDataWidget>();
}
@override
bool updateShouldNotify(ShareDataWidget old) {
return old.data != data;
}
}
第二步,创建 InheritedWidgetTestRoute.dart
文件
class InheritedWidgetTestRoute extends StatefulWidget {
@override
_InheritedWidgetTestRouteState createState() => new _InheritedWidgetTestRouteState();
}
class _InheritedWidgetTestRouteState extends State<InheritedWidgetTestRoute> {
int count = 0;
@override
Widget build(BuildContext context) {
return Center(
child: ShareDataWidget( // 重点:使用ShareDataWidget包裹
data: count,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 20.0),
child: Text(ShareDataWidget.of(context)
.data.toString())
),
RaisedButton(
child: Text("Increment"),
//每点击一次,将count自增,然后更新页面渲染,ShareDataWidget的data将被更新
onPressed: () => setState(() => ++count),
)
],
),
),
);
}
}
2. 对比使用:Provider
创建数据类
CounterNotifier
,然后在页面中用数据类CounterNotifier
包裹child部分
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flutter/foundation.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 创建 Widget 持有 CounterNotifier 数据
return ChangeNotifierProvider.value(
value: CounterNotifier(),
child: MaterialApp(
title: 'Privoder Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: ProvidePage(title: 'Provider 测试页面'),
),
);
}
}
class ProvidePage extends StatelessWidget {
final String title;
ProvidePage({Key key, this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
// 获取 CounterNotifier 数据 (最简单的方式)
final counter = Provider.of<CounterNotifier>(context);
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'按下按钮,使数字增长:',
),
Text(
'${counter.count}',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
counter.increment();
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
// 核心:继承自ChangeNotifier
// 这种文件本来应该单独放在一个类文件连的
class CounterNotifier with ChangeNotifier {
int _count = 0;
int get count => _count;
increment() {
_count++;
// 核心方法,通知刷新UI,调用build方法
notifyListeners();
}
}
get_it注册工具的几种方式
接下来介绍一下在get_it中注册工具都有哪几种方式
GetIt locator = GetIt.instance;
Future<void> setupLocator({bool test = false}) async {
// 方式一,使用 registerSingleton
// 在main.dart中调用setupLocator()时,就会立即注册ModelA
// 注册单例,保证每一次调用ModelA工具时,都是同一个,不会重新实例化(不会造一个新工具)
locator.registerSingleton<ModelA>(ModelA());
// 常用.方式二,使用 registerLazySingleton
// 调用setupLocator()时,没有生成,直到在其他地方第一次调用ServiceB工具时,才会注册
// registerSingleton注册单例,保证每一次调用ServiceB工具时,都是同一个,不会重新实例化(不会造一个新工具)
locator.registerLazySingleton<ServiceB>(ServiceB());
// 方式三,使用 registerFactory / registerFactoryParam / registerFactoryParamAsync
// 调用setupLocator()时,没有生成,直到在其他地方第一次调用ViewModelC工具时,才会注册
// 参数必须时工厂函数,工厂函数不需要参数用registerFactory
// 参数必须时工厂函数,工厂函数需要参数用registerFactoryParam
// 参数必须时工厂函数,工厂函数是异步且需要参数用registerFactoryParamAsync
locator.registerFactory<ViewModelC>( () => ViewModelCImpl()) );
locator.registerFactoryParam<ViewModelD,String,int>((str,num)
=> ViewModelDImpl(param1:str, param2: num));
locator.registerFactoryParamAsync<ViewModelE,String,int>((str,num)
=> ViewModelEImpl(param1:str, param2: num));
}
get_it提供的其他方法
- allReady():所有在 GetIt 中注册的对象都已发出可以使用的信号时,该函数便会完成。
Future allReady(Timeout timeout)
用法示例:getIt内的工具还未准备好,那么会显示一个正在加载的 CircularProgressIndicator,如果都已经准备好了,那么会显示一个Text:
return FutureBuilder(
future: getIt.allReady(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return Scaffold(
body: Center(
child: Text('page is ok'),
),
);
} else {
return CircularProgressIndicator();
}
});
前端中的服务定位是什么样子
在前端中,require 其实就是服务定位。与上文中的 registerLazySingleton 方法类似,将一个又一个得工具给装入工具箱 (demo.js文件就是工具箱)
demo.js
var fs = require("fs");
var path = require("path");
var moduleA = require("./modules/moduleA");
var moduleB = require("./modules/moduleB");
参考资料
get_it 文档GuruMeditation博客Splat借鉴
包含get_it标准使用方法的MVVM架构模板