状态管理

一. Provider

是对 InheritedWidget组件的上层封装 需要依赖于context上下文 更多详细

image.png

创建model类可以有多个属性,一个app可以有多个model类来存储数据

model类必须要继承ChangeNotifier类,否则无法刷新数据
model管理的状态,内部只有get方法,修改他的值是通过单独的方法进行修改的,在修改后要调用notifyListeners方法

model创建

  1. import 'package:flutter/material.dart';
  2. class Counter with ChangeNotifier {
  3. int _count;
  4. Counter(this._count);
  5. void add() {
  6. _count++;
  7. notifyListeners();
  8. }
  9. get count => _count;
  10. }
  11. 需要混入ChangeNotifier
  12. 写一个增加的方法,然后需要调用notifyListeners();这个方法是通知用到Counter对象的widget刷新用的

导入(注入)model

  1. void main() {
  2. runApp(
  3. ChangeNotifierProvider(
  4. create: (context) => Counter(1),
  5. child: MyApp(),
  6. ));
  7. }

还有以下几个方法

  1. Provider,ListenableProvider,ValueListenableProvider,StreamProvider,

如果是多个Provider

  1. MultiProvider(
  2. providers: [
  3. ChangeNotifierProvider.value(value: UserInfo('','')),
  4. ChangeNotifierProvider.value(value: PageOne('','')),
  5. ],
  6. child: Consumer2<UserInfo,PageOne>(builder: (context, login,user, _) {})
  7. )

获取值或改变Provider
需要导入对应model

  1. 获取
  2. Text("${Provider.of<Counter>(context).count}")
  3. 改变
  4. Provider.of<Counter>(context).add();

Provider.of(context).count取值,Provider.of(context)相当于Provider去查找它管理的Counter(1)

用Provider.of(context).add();调用Counter()中的add()方法改变值

二. GetX

https://pub.dev/packages/get
image.png
优势点:

GetX 因为不需要上下文,突破了InheritedWidget的限制,我们可以在全局和模块间共享状态
AJAX数据请求到更新UI层

主要功能

  1. 状态管理

分别有两个状态管理器(GetBuilder)和响应式状态管理器(GetX)
响应式状态管理器

申明

var num = 0;

监听 num

  1. var num = 0.obs;

UI层显示更新

Obx(() => Text(“${controller.num}”));

  1. 路由管理

不想要上下文的情况下使用

在main.dart文件下把MaterialApp替换变成GetMaterialApp

  1. GetMaterialApp( // Before: MaterialApp(
  2. home: MyHome(),
  3. )

路由跳转

1.导航到新页面

  1. Get.to(NextScreen());

2.用别名导航到新页面

  1. Get.toNamed('/details');

3.要关闭snackbars, dialogs, bottomsheets或任何你通常会用Navigator.pop(context)关闭的东西。

  1. Get.back();

4.进入下一个页面,但没有返回上一个页面的选项(用于闪屏页,登录页面等)。

  1. Get.off(NextScreen());

5.进入下一个页面并取消之前的所有路由(在购物车、投票和测试中很有用)。

  1. Get.offAll(NextScreen());

以上都不需要context上下文

  1. 依赖管理

Get有一个简单而强大的依赖管理器,它允许你只用1行代码就能检索到与你的Bloc或Controller相同的类,无需Provider context,无需inheritedWidget。

  1. Controller controller = Get.put(Controller()); // 而不是 Controller controller = Controller(); 主任依赖

使用获取依赖

  1. Controller controller = Get.find();

更多API移步 https://pub.flutter-io.cn/packages/get

项目示例

首先需要一个控制器

  1. class nController extends GetxController {
  2. int _counter = 0;
  3. int get counter => _counter;
  4. void increment() {
  5. _counter++;
  6. update();
  7. }
  8. }

UI 层显示数字变化以及控制数字

  1. class nPage extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. print('nPage--build');
  5. return GetBuilder<nController>(
  6. init: nController(), //初始化
  7. builder: (controller) {
  8. return Scaffold(
  9. appBar: AppBar(title: Text('数字变化..')),
  10. body: Center(
  11. child: Text(controller.counter.toString()),
  12. ),
  13. floatingActionButton: FloatingActionButton(
  14. onPressed: () {
  15. controller.increment();
  16. },
  17. child: Icon(Icons.add),
  18. ),
  19. );
  20. });
  21. }
  22. }

通过GetBuilder对 Widget 包裹了页面,在 init初始化nController,然后每次点击,都会更新builder对应的 Widget ,GetxController通过update()更新GetBuilder。

注:可以局部更新某个组件
以上代码改造

  1. class nPage extends StatelessWidget {
  2. @override
  3. Widget build(BuildContext context) {
  4. print('nPage--build');
  5. return Scaffold(
  6. appBar: AppBar(title: Text('数字变化..')),
  7. body: Center(
  8. child: GetBuilder<nController>(
  9. init: nController(),
  10. builder: (controller) {
  11. return Text(controller.counter.toString());
  12. }),
  13. ),
  14. floatingActionButton: FloatingActionButton(
  15. onPressed: () {
  16. controller.increment();
  17. },
  18. child: Icon(Icons.add),
  19. ),
  20. );
  21. }
  22. }

以上情况在按钮事件会找不到controller(因为作用域的关于),此时GetX就发会了作用

  1. Get.find<nController>().increment();

GetxController生命周期

  1. @override
  2. void onInit() {
  3. super.onInit();
  4. print('nController--onInit');
  5. }
  6. @override
  7. void onReady() {
  8. super.onReady();
  9. print('nController--onReady');
  10. }
  11. @override
  12. void onClose() {
  13. super.onClose();
  14. print('nController--onClose');
  15. }

局部刷新
只需要在GetBuilder对应的Controller里面添加id

  1. update(['id值']);

响应式刷新

以上是通过Controller以流的方式发送数据,而GetX提供了.obs后缀形式更新某个小部件

  1. var nmu = 0.obs;
  2. 监听
  3. Obx (() => Text (controller.num));
  4. .obs就实现了一个被观察者

.obs就实现了一个被观察者,类型是 RxInt。对应的小部件也不再是GetBuilder了,而是下面两种

  1. final nmb1 = 0.obs;
  2. final nmb2 = 0.obs;
  3. GetX<nController>(
  4. builder: (_) {
  5. print("nmb1 rebuild");
  6. return Text(
  7. '${_.nmb1}',
  8. style: TextStyle(fontWeight: FontWeight.bold),
  9. );
  10. },
  11. ),
  12. Obx(() => Text(
  13. '${Get.find<nController>().nmb2}',
  14. style: TextStyle(fontWeight: FontWeight.bold),
  15. )),

以上是同一个页面兄弟组件跨组件使用

跨路由

在one路由get.put oneController

  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';
  3. import 'package:fluttergetx/controller/OneController.dart';
  4. class OnePage extends StatelessWidget {
  5. @override
  6. Widget build(BuildContext context) {
  7. OneController controller = Get.put(OneController());
  8. return Scaffold(
  9. appBar: AppBar(
  10. title: Text("第一个页面"),
  11. centerTitle: true,
  12. ),
  13. body: Container(
  14. width: double.infinity,
  15. child: Column(
  16. children: [
  17. Text('第一个页面'),
  18. ListTile(
  19. onTap: () {
  20. Get.toNamed('/twoPage');
  21. },
  22. title: Text('第二个页面'),
  23. ),
  24. ],
  25. )
  26. )
  27. );
  28. }
  29. }

在two路由get.find oneController

  1. import 'package:flutter/material.dart';
  2. import 'package:get/get.dart';
  3. import 'package:fluttergetx/controller/OneController.dart';
  4. class TwoPage extends StatelessWidget {
  5. var ages = Get.find<OneController>().show();
  6. @override
  7. Widget build(BuildContext context) {
  8. return Scaffold(
  9. appBar: AppBar(
  10. title: Text("第二个页面"),
  11. centerTitle: true,
  12. ),
  13. body: Container(
  14. width: double.infinity,
  15. child: Column(
  16. children: [
  17. Text('第二个页面'),
  18. Text('第一个页面过来的值:${ages}'),
  19. RaisedButton(
  20. color: Colors.blue,
  21. child: Text('打印OneController的值',style: TextStyle(color: Colors.white),),
  22. // 设置按钮圆角
  23. shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5.0)),
  24. onPressed: () {
  25. print(Get.find<OneController>().show());
  26. }
  27. ),
  28. ],
  29. )
  30. )
  31. );
  32. }
  33. }

GetBuilder vs GetX vs Obx

项目结构

image.png

GetX.gifProvider.gif
demo.gif