Getx插件的使用
Getx插件的使用
官网Getx使用
Get_storage本地存储
内存中一个快速、超轻且同步的键值对,在每次操作时将数据备份到磁盘。它完全用 Dart 编写,很容易与 Flutter 的 Get 框架集成。
支持 Android、iOS、Web、Mac、Linux 以及 fuchsia 和 Windows**。可以存储String、int、double、Map和List
dependencies:
get_storage: ^2.0.3
import 'package:get_storage/get_storage.dart';
需要在main函数中init一下
main() async {
await GetStorage.init();
runApp(App());
}
然后就可以使用了,使用时创建一个实例对象:
final GetStorage storageBox = GetStorage();
存
storageBox.write('quote', 'GetX is the best');
取
storageBox.read('quote')
删
storageBox.remove('quote');
清楚所有容器内数据
storageBox.erase();
也可以监听,所有键值对的改动,当然在你不需要了的时候务必将监听移除
//添加监听
storageBox.listen((){
print('box changed');
});
//移除监听
storageBox.removeListen(listen);
当然如果你不想监听所有只想监听某一个的时候也可以
storageBox.listenKey('key', (value){
print('new key is $value');
});
GetX插件
dependencies:
get: ^4.6.5
import 'package:get/get.dart';
初始化
只需要将MaterialApp改成GetMaterialApp即可
void main() {
runApp(GetMaterialApp(home: MyApp()))
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Center(),
);
}
}
注意:这并没有修改Flutter的MaterialApp,GetMaterialApp不是修改后的MaterialApp,它只是一个预先配置好的Widget,它有默认的MaterialApp作为child。您可以手动配置它,但绝对没有必要。GetMaterialApp 将创建路线、注入路线、注入翻译、注入路线导航所需的一切。如果只使用 Get 进行状态管理或依赖管理,则无需使用 GetMaterialApp。
路由管理
路由也是 Flutter 项目重要的一环,在 Flutter 中进行页面跳转就是通过路由实现,GetX 提供了 普通路由 和 别名路由 。
普通路由导航
to:进入下一个界面
Get.to(CounterPage());
使用 arguments 进行参数传递
Get.to(CounterPage(), arguments: count);
使用 arguments 方式可以传递任意类型的参数。
在下个页面获取参数:
dynamic args = Get.arguments;
off:打开新页面,并且用新页面替换旧页面(删除旧页面)
Get.off(CounterPage());
offAll: 打开新页面并删除之前的所有路由
Get.offAll(CounterPage());
back:返回
Get.back();
返回传参:
Get.back(result: 'success');
获取返回参数:
var data = await Get.to(CounterPage());
别名路由导航
声明别名:
abstract class Routes {
static const Initial = '/';
static const NextScreen = '/NextScreen';
}
注册路由表:
abstract class RouteGet {
static final pages = [
GetPage(
name: Routes.Initial,
page: () => HomePage(),
),
GetPage(
name: Routes.NextScreen,
page: () => NextScreen(),
),
];
}
替换MaterialApp为GetMaterialApp:
void main() {
runApp(GetMaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: '/',
theme: appThemeData,
defaultTransition: Transition.fade,
getPages: RouteGet.pages,
home: HomePage(),
));
}
使用
导航到下一个页面
Get.toNamed(Routes.NextScreen);
导航到下一个页面并删除前一个页面
Get.offNamed(Routes.NextScreen);
导航到下一个页面并删除以前所有的页面
Get.offAllNamed(Routes.NextScreen);
发送数据到别名路由:
Get在这里接受任何东西,无论是一个字符串,一个Map,一个List,甚至一个类的实例。
Get.toNamed(Routes.NextScreen, arguments: 'dsdf的地方');
获取参数:
String name=Get.arguments;
动态网页链接:
像web一样携带参数,适合前端开发的风格。
Get.offAllNamed("/NextScreen?device=phone&id=354&name=Enzo");
获取参数:
int id = Get.parameters['id'];
// out: 354
String name=Get.parameters['name'];
还可以这样定义路由别名:
GetPage(
name: '/profile/:user',
page: () => UserProfile(),
),
导航:
Get.toNamed("/profile/34954");
在第二个页面上,通过参数获取数据
print(Get.parameters['user']);
// out: 34954
中间件(比如判断是否登录)
在跳转前做些事情,比如判断是否登录,可以使用routingCallback来实现:
GetMaterialApp(
routingCallback: (routing) {
if(routing.current == '/second'){
// 如果登录。。。
}
}
)
响应式变量
GetX 提供了两种响应式状态管理的方法:响应式变量方式和状态管理器方式。
var count = 0.obs;
定义响应式变量
响应式变量可以用在任何类型上:
第一种是使用 Rx{Type}。
// 建议使用初始值,但不是强制性的
final name = RxString('');
final isLogged = RxBool(false);
final count = RxInt(0);
final balance = RxDouble(0.0);
final items = RxList<String>([]);
final myMap = RxMap<String, int>({});
第二种是使用 Rx,规定泛型
final name = Rx<String>('');
final isLogged = Rx<Bool>(false);
final count = Rx<Int>(0);
final balance = Rx<Double>(0.0);
final number = Rx<Num>(0)
final items = Rx<List<String>>([]);
final myMap = Rx<Map<String, int>>({});
// 自定义类 - 可以是任何类
final user = Rx<User>();
第三种更实用、更简单、更可取的方法,只需添加 .obs 作为value的属性。
final name = ''.obs;
final isLogged = false.obs;
final count = 0.obs;
final balance = 0.0.obs;
final number = 0.obs;
final items = <String>[].obs;
final myMap = <String, int>{}.obs;
// 自定义类 - 可以是任何类
final user = User().obs;
获取响应式变量的值
使用的时候调用 value 即可拿到变量的值。对于 List 、Map 则不需要加 .value。
获取响应式变量
使用的时候调用 value 即可拿到变量的值。对于 List 、Map 则不需要加 .value。
String nameValue = name.value
bool isLoggedValue = isLogged.value
int countValue = count.value
double numberValue = number.value
String item = items[0] //不需要.value
int value = myMap['key'] //不需要.value
String name = user.value.name
更新响应式变量
name.value = "123"
isLogged.value = true
count.value = 1
number.value = 12.0
对于其他数据类型需要调用 update 或者变量方法更新,如下:
user.update((value) {
value?.name = "123";
});
或者使用变量名方法重新赋值一个对象,比如变量名为 user 则可使用 user() 方法进行更新:
user(User(name: "abcd", age: 25));
在界面上使用响应式变量只需在使用变量的控件上包裹 Obx 即可实现响应式更新,即变量的值发生变化时自动刷新界面:
Obx(() => Text("${count.value}"))
登录操作判断
var isLogged = false.obs;
然后,你检查用户是否 "登录",以触发ever的事件。
@override
onInit(){
ever(isLogged, fireRoute);
isLogged.value = await Preferences.hasToken();
}
fireRoute(logged) {
if (logged) {
Get.off(Home());
} else {
Get.off(Login());
}
}
如果 "hasToken "是 "false","isLogged "就不会有任何变化,所以 "ever() "永远不会被调用。 为了避免这种问题,第一次变化将总是触发一个事件,即使它包含相同的.value。
如果你想删除这种行为,你可以使用: isLogged.firstRebuild = false;。
可以使用.obs的地方
你可以在 obs 上转换任何东西,这里有两种方法:
- 可以将你的类值转换为 obs
class RxUser {
final name = "Camila".obs;
final age = 18.obs;
}
- 或者可以将整个类转换为一个可观察的类。
class User {
User({String name, int age});
var name;
var age;
}
//实例化时。
final user = User(name: "Camila", age: 18).obs;
关于List的说明
List和它里面的对象一样,是完全可以观察的。这样一来,如果你在List中添加一个值,它会自动重建使用它的widget。
你也不需要在List中使用".value",神奇的dart api允许我们删除它。 不幸的是,像String和int这样的原始类型不能被扩展,使得.value的使用是强制性的,但是如果你使用get和setter来处理这些类型,这将不是一个问题。
// controller
final String title = 'User Info:'.obs
final list = List<User>().obs;
// view
Text(controller.title.value), // 字符串后面需要有.value。
ListView.builder (
itemCount: controller.list.length // List不需要它
)
当你在使自己的类可观察时,有另外一种方式来更新它们:
// model
// 我们将使整个类成为可观察的,而不是每个属性。
class User{
User({this.name = '', this.age = 0});
String name;
int age;
}
// controller
final user = User().obs;
//当你需要更新user变量时。
user.update( (user) { // 这个参数是你要更新的类本身。
user.name = 'Jonny';
user.age = 18;
});
// 更新user变量的另一种方式。
user(User(name: 'João', age: 35));
// view
Obx(()=> Text("Name ${user.value.name}: Age: ${user.value.age}"));
// 你也可以不使用.value来访问模型值。
user().name; // 注意是user变量,而不是类变量(首字母是小写的)。
你可以使用 "assign "和" assignAll "。 "assign "会清除你的List,并添加一个单个对象。 "assignAll "将清除现有的List,并添加任何可迭代对象。
数据变化监听Workers
Workers将协助你在事件发生时触发特定的回调。
除了使用 Obx 实现界面数据自动刷新外,GetX 提供了多种手动方式对响应式变量进行数据变化监听,当数据发生变化时执行自定义的逻辑,比如数据变更后重新请求接口等。
- ever 当数据发生改变时触发
- everAll 和 "ever "很像,只是监听的是多个响应式变量的变化,当其中一个发生变化就会触发回调
- once 只在变量第一次被改变时被调用
- debounce 防抖,即延迟一定时间调用,且在规定时间内只有最后一次改变会触发回调。如设置时间为 1 秒,发生了3次数据变化,每次间隔500毫秒,则只有最后一次变化会触发回调。
- interval 时间间隔内只有最后一次变化会触发回调。如设置时间间隔为1秒,则在1秒内无论点击多少次都只有最后一次会触发回调,然后进入下一次的时间间隔。
使用方法
///每次`count`变化时调用。
ever(count, (newValue) => print("$newValue has been changed"));
///只有在变量count在第一次被改变时才会被调用。
once(count, (newValue) => print("$newValue was changed once"));
///防DDos - 每当用户停止输入1秒时调用,例如。
debounce(count, (newValue) => print("debouce$newValue"), time: Duration(seconds: 1));
///忽略1秒内的所有变化,只有最后一次会触发回调。
interval(count, (newValue) => print("interval $newValue"), time: Duration(seconds: 1));
实例
使用响应式变量实现计数器功能:
class MyHonePage extends StatelessWidget {
var count = 0.obs;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Counter"),
),
body: Center(
child: Obx(() => Text("${count.value}", style: const TextStyle(fontSize: 50))),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () => count ++,
),
);
}
}
上述代码就实现了简单的计数器功能,仔细查看发现并没有使用 StatefulWidget 也能实现计数的自动更新。这就是响应式变量的强大之处。
依赖管理
状态管理器其实已经使用到了 GetX 的依赖管理,在 GetBuilder 里初始化 Controller 后,在其他地方就可以使用 Get.find() 找到对应的 Controller ,这就是 GetX 的依赖管理。GetX 依赖管理可以注入任意类型的实例,并提供了多种依赖插入/注册方式。
Get.put注册依赖
使用 put 将需要依赖的对象插入到 GetX 中:
Get.put<CounterController>(CounterController());
Get.put<CounterController>(CounterController(), permanent: true);
Get.put<CounterController>(CounterController, tag: "counter");
插入依赖时除了依赖类的实例以外还可以设置额外参数:
- permanent:是否永久,默认 false 当实例不再使用时会进行销毁,true 则会一直保留
- tag:标签,用于区分同一个类不同实例。
Get.lazyPut延迟初始化
延迟初始化,在需要用到的时候才会初始化实例对象,即第一次 find 某一个类的时候才会进行初始化。
///只有当第一次使用Get.find<CounterController>时,CounterController才会被调用。
Get.lazyPut<CounterController>(() => CounterController());
Get.lazyPut<CounterController>(
() {
// ... some logic if needed
return CounterController();
},
tag: Math.random().toString(),
fenix: true
)
lazyPut 同样有额外参数,跟 put 基本相同。
- fenix:类似'永久',不同的是,当不使用时,实例会被丢弃,但当再次需要使用时,Get会重新创建实例
- tag:标签,用于区分同一个类不同实例。
Get.putAsync异步注册
putAsync 可以异步注册一个实例。用于某些实例需要异步初始化时使用,比如 SharedPreferences:
Get.putAsync<SharedPreferences>(() async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('counter', 12345);
return prefs;
});
跟 put 一样,同样拥有 permanent 和 tag 参数,且作用一样。
Get.create同步注册
create 与 put 使用方式上基本类似,不同的是它的 permanent 默认为 true。
Get.create<CounterController>(() => CounterController());
使用
通过 find() 方法获取依赖的实例:
final controller = Get.find<CounterController>();
// 或者
CounterController controller = Get.find();
///通过 tag 获取
final controller = Get.find<CounterController>("counter");
也可以通过 delete() 方法来手动移除注入的依赖实例,大部分情况下不需要手动调用该方法,GetX 内部会自动处理,当不需要时自动移除
Get.delete<CounterController>();
状态管理
GetX 还提供了使用 Controller 来管理状态,实现一个自定义 Controller 类继承自 GetxController ,Controller 中进行业务逻辑的处理,当需要改变状态数据时调用 update() 来通知数据改变。
目录结构

第 1 步:在您的 MaterialApp 之前添加“Get”,将其转换为 GetMaterialApp
void main() {
runApp(GetMaterialApp(home: const MyApp()));
}
注意:这并没有修改Flutter的MaterialApp,GetMaterialApp不是修改后的MaterialApp,它只是一个预先配置好的Widget,它有默认的MaterialApp作为child。您可以手动配置它,但绝对没有必要。GetMaterialApp 将创建路线、注入路线、注入翻译、注入路线导航所需的一切。如果只使用 Get 进行状态管理或依赖管理,则无需使用 GetMaterialApp。
第 2 步:创建业务逻辑类并将所有变量、方法和控制器放入其中。您可以使用简单的“.obs”使任何变量可观察
先创建一个全局管理状态contaerUtils.dart
import 'package:get/get.dart';
class ContaerUtils extends GetxController{
var count = 0.obs;
void increase() {
count++;
}
}
第 3 步:创建您的视图,使用 StatelessWidget 并节省一些 RAM,使用 Get,您可能不再需要使用 StatefulWidget
在main.dart页面,注入依赖,后面就不用注入了,直接使用即可
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:get/get.dart';
import 'package:startkey/tabbar/home.dart';
import 'package:startkey/utils/contaerUtils.dart';
void main() {
//添加Get
runApp(GetMaterialApp(home: const MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home:MyHome()
);
}
}
class MyHome extends StatelessWidget {
//注入依赖
final ContaerUtils logic = Get.put(ContaerUtils());
@override
Widget build(BuildContext context) {
//定义一个定时器
Duration timeout = const Duration(seconds: 2);
return Scaffold(
appBar: AppBar(
title: Text('Counter'), //标题部分
),
body: ListView(
children: [
FlatButton(
onPressed: () => {
Get.to(Home())
},
child: Container(
width: 100,
height: 20,
color: Colors.blue,
child: Text('打开页面按钮'),
)
),
Center(
//使用Obx监听全局变量
child: Obx(() => Text('${logic.count.value}',style: const TextStyle(fontSize: 50),)),
)
],
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: ()=>{
//通过定时器来实时监听
Timer.periodic(timeout,(timer){
//定时器,调用全局方法
logic.increase();
})
},
),
);
}
}
第4步:创建home.dart文件,然后通过定时器来监听是否实现来全局状态管理的功能
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:startkey/utils/contaerUtils.dart';
class Home extends StatefulWidget {
Home({Key? key}) : super(key: key);
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
//获取状态管理中的数据
ContaerUtils controller = Get.find();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('设备信息获取'),
),
body: ListView(
children: [
Center(
//使用Obx监听全局变量
child: Obx(() => Text('${controller.count.value}',style: TextStyle(fontSize: 50),)),
),
FlatButton(onPressed: ()=>{
//点击按钮,调用全局方法
controller.increase()
}, child: Text('查看'))
],
),
);
}
}


小部件
snackbar 弹出框
GetX 提供了方便快捷使用 snackbar 的方法, 使用如下:
Get.snackbar("title", "message");
默认是在上方弹出的,可以使用 snackPosition 修改弹出的位置,效果如图:

除了位置以外,还可以设置很多属性,比如文字颜色、背景颜色等
Dialog对话框
Get.defaultDialog(
radius: 10.0,
contentPadding: const EdgeInsets.all(20.0),
title: 'title',
middleText: 'content',
textConfirm: 'Okay',
confirm: OutlinedButton.icon(
onPressed: () => Get.back(),
icon: const Icon(
Icons.check,
color: Colors.blue,
),
label: const Text(
'Okay',
style: TextStyle(color: Colors.blue),
),
),
cancel: OutlinedButton.icon(
onPressed: () {},
icon: Icon(Icons.check),
label: Text('12'),
),
);

除了 title 、middleText 外,还可以设置确定、取消按钮以及对应的回调、圆角、背景颜色等等参数
bottomSheet(底部弹框)
Get.bottomSheet(Container(
height: 200,
color: Colors.white,
child: const Center(
child: Text("bottomSheet"),
),
));

仔细查看发现无论是 snackbar 、dialog 还是 bottomSheet 都不需要用到 context, 这意味着可以在项目的任何地方进行调用。
GetUtils(工具)
GetX 还提供了很多工具方法,可以使用 GetUtils 调用, 比如判断是否是邮箱,判断文件格式类型等
除此之外 GetX 还提供了一些扩展方法:
//检查应用程序在哪个平台上运行。
GetPlatform.isAndroid
GetPlatform.isIOS
GetPlatform.isMacOS
GetPlatform.isWindows
GetPlatform.isLinux
GetPlatform.isFuchsia
//检查设备类型
GetPlatform.isMobile
GetPlatform.isDesktop
//所有平台都是独立支持web的!
//你可以知道你是否在浏览器内运行。
//在Windows、iOS、OSX、Android等系统上。
GetPlatform.isWeb
// 相当于.MediaQuery.of(context).size.height,
//但不可改变。
Get.height
Get.width
// 提供当前上下文。
Get.context
// 在你的代码中的任何地方,在前台提供 snackbar/dialog/bottomsheet 的上下文。
Get.contextOverlay
// 注意:以下方法是对上下文的扩展。
// 因为在你的UI的任何地方都可以访问上下文,你可以在UI代码的任何地方使用它。
// 如果你需要一个可改变的高度/宽度(如桌面或浏览器窗口可以缩放),你将需要使用上下文。
context.width
context.height
// 让您可以定义一半的页面、三分之一的页面等。
// 对响应式应用很有用。
// 参数: dividedBy (double) 可选 - 默认值:1
// 参数: reducedBy (double) 可选 - 默认值:0。
context.heightTransformer()
context.widthTransformer()
/// 类似于 MediaQuery.of(context).size。
context.mediaQuerySize()
/// 类似于 MediaQuery.of(context).padding。
context.mediaQueryPadding()
/// 类似于 MediaQuery.of(context).viewPadding。
context.mediaQueryViewPadding()
/// 类似于 MediaQuery.of(context).viewInsets。
context.mediaQueryViewInsets()
/// 类似于 MediaQuery.of(context).orientation;
context.orientation()
///检查设备是否处于横向模式
context.isLandscape()
///检查设备是否处于纵向模式。
context.isPortrait()
///类似于MediaQuery.of(context).devicePixelRatio。
context.devicePixelRatio()
///类似于MediaQuery.of(context).textScaleFactor。
context.textScaleFactor()
///查询设备最短边。
context.mediaQueryShortestSide()
///如果宽度大于800,则为真。
context.showNavbar()
///如果最短边小于600p,则为真。
context.isPhone()
///如果最短边大于600p,则为真。
context.isSmallTablet()
///如果最短边大于720p,则为真。
context.isLargeTablet()
///如果当前设备是平板电脑,则为真
context.isTablet()
///根据页面大小返回一个值<T>。
///可以给值为:
///watch:如果最短边小于300
///mobile:如果最短边小于600
///tablet:如果最短边(shortestSide)小于1200
///desktop:如果宽度大于1200
context.responsiveValue<T>()