资料

Flutter状态管理:Provider4 SelectorFlutter使用Provider之Cosumer和SelectorFlutter Provider状态管理 - Selector

最简单的使用

上代码

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.orange,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(),
    );
  }
}

class Counter extends ChangeNotifier {
  // 这里也可以使用with来进行实现
  int _count = 0; //数值计算
  int get count => _count;

  addCount() {
    _count++;
    notifyListeners();
  }
}

Counter _counter = Counter();

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
    _counter.addListener(() {
      // 数值改变的监听
      print('YM--->新数值:${_counter.count}');
    });
  }

  @override
  void dispose() {
    super.dispose();
    _counter.dispose(); //移除监听
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Hello')),
      body: Center(
        child: Container(
          child: RaisedButton(onPressed: () {
            _counter.addCount();
          }, child: Text('计数'),),
        ),
      ),
    );
  }
}

创建一个继承ChangeNotifier的类

class Counter extends ChangeNotifier {
  // 这里也可以使用with来进行实现
  int _count = 0; //数值计算
  int get count => _count;

  addCount() {
    _count++;
    notifyListeners();
  }
}

在外边创建一个持久化的对象

Counter _counter = Counter();

在initState的时候注册Listener

@override
  void initState() {
    super.initState();
    _counter.addListener(() {
      // 数值改变的监听
      print('YM--->新数值:${_counter.count}');
    });
  }

dispose的时候dispose掉,然后就不能用了

@override
  void dispose() {
    super.dispose();
    _counter.dispose(); //移除监听,再调用的话会崩溃
  }

调用_counter.addCount();

_counter.addCount(); // 里面调用了notifyListeners()方法

第二个例子 ChangeNotifierProvider

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';

class TestProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
  int _number = 0;

  int get number => _number;

  set number(int value) {
    _number = value;
    notifyListeners();
  }

  void addNumber() {
    _number++;
    notifyListeners();
  }
}

class UserModel {
  String name;
  String userID;
  bool isAuthor;
  bool isVIP;

  UserModel(this.name, this.userID, this.isAuthor, this.isVIP);
}

class UserProviderModel with ChangeNotifier, DiagnosticableTreeMixin {
  UserModel _user;

  UserProviderModel(this._user);

  set user(UserModel value) {
    _user = value;
    notifyListeners();
  }
}

void main() {
  List<SingleChildWidget> providerList = [
    ChangeNotifierProvider(create: (_) => TestProviderModel()),
    ChangeNotifierProvider(
      create: (_) => UserProviderModel(UserModel("cy", "1", true, true)),
    )
  ];

  runApp(
    MultiProvider(
      providers: providerList,
      child: TestProvider(),
    ),
  );
}

class TestProvider extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _TestProviderState();
}

class _TestProviderState extends State<TestProvider> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('测试provider')),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ChildOne(),
              ChildTwo(),
            ],
          ),
        ),
        floatingActionButton: FloatingActionButton(
          child: Icon(Icons.add),
          onPressed: () {
            context.read<TestProviderModel>().addNumber();
          },
        ),
      ),
    );
  }
}

class ChildOne extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _ChildOneState();
}

class ChildTwo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _ChildTwoState();
}

class _ChildOneState extends State<ChildOne> {
  @override
  Widget build(BuildContext context) {
    print('1build');
    return Container(
      child: Text('1number = ${context.watch<TestProviderModel>()._number}'),
    );
  }
}

class _ChildTwoState extends State<ChildTwo> {
  @override
  Widget build(BuildContext context) {
    print('2build');
    return Container(
      child: Consumer2<TestProviderModel, UserProviderModel>(
        builder: (context, testProvider, userProvider, child) {
          return Text(
              'testProviderNumber:${testProvider.number}\n用户:${userProvider._user.name}');
        },
      ),
    );
  }
}

例子 CartModel

import 'dart:collection';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';

class Item {
  Item(this.price, this.count);

  double price;
  int count;
}

class CartModel extends ChangeNotifier {
  // 用于保存购物车中商品列表
  final List<Item> _items = [];
  // 禁止改变购物车里的商品信息
  UnmodifiableListView<Item> get items => UnmodifiableListView(_items);
  // 购物车中商品的总价
  double get totalPrice =>
      _items.fold(0, (value, item) => value + item.count + item.price);
  // 将[item]添加到购物车。这里唯一一种可能从外部改变购物车的方法
  void add(Item item) {
    _items.add(item);
    //通知监听器(订阅者),重新构建InheritedProvider,更新状态。
    notifyListeners();
  }
}
void main() {
  runApp(MyApp());
}
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Welcome to Flutter",
      home: Scaffold(
        appBar: AppBar(title: Text('Hello')),
        body: ProviderRoute(),
      ),
    );
  }
}
class ProviderRoute extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _PreviderRouteState();
}

// 这是一个便捷类,会获得当前context和指定数据类型的Provider
class _Consumer<T> extends StatelessWidget {

  final Widget Function(BuildContext context, T? value) builder;

  const _Consumer({Key? key,required this.builder}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return builder(
      context, context.watch<T>()
    );
  }
}

class _PreviderRouteState extends State<ProviderRoute> {
  @override
  Widget build(BuildContext context) => Center(
        child: ChangeNotifierProvider<CartModel>(
          create: (_) => CartModel(),
          child: Builder(
            builder: (context) {
              return Column(
                children: [
                  Text('Hi~'),
                  // 下面可以做如下优化
                  // 1. 需要显示调用ChangeNotifierProvider.of,
                  //    当APP内部依赖CartModel很多时,这样的代码将会很冗余
                  // 2. 语义不明确:由于ChangeNotifierProvider是订阅者,那么依赖CartModel
                  //    的Widget自然就是订阅者,其实也就是状态的消费者,如果我们用Builder
                  //    来构建,语义就不是很明确;如果我们能使用一个具有明确意义的Widget,比如就叫Consumer
                  //    ,这样最终的代码语义将会很明确,只要看到使用Consumer,我们就知道它是依赖某个跨组件或全局的状态。
                  // Builder(builder: (context) {
                  //   var cart = context.watch<CartModel>();
                  //   return Text("Hello cart => ${cart.totalPrice}");
                  // }),
                  _Consumer<CartModel>(
                    builder: (context, cart) => Text("总价:${cart?.totalPrice}"),
                  ),
                  Builder(builder: (context) {
                    print('ElevatedButton build');
                    return ElevatedButton(
                      onPressed: () {
                        // 给购物车中添加商品,添加后总价会更新
                        Provider.of<CartModel>(context, listen: false)
                            .add(Item(20.0, 1));
                      },
                      child: Text("添加商品"),
                    );
                  }),
                ],
              );
            },
          ),
        ),
      );
}

1. 首先定义一个ChangeNotifier的子类

class CartModel extends ChangeNotifier {
  // 用于保存购物车中商品列表
  final List<Item> _items = [];
  // 禁止改变购物车里的商品信息
  UnmodifiableListView<Item> get items => UnmodifiableListView(_items);
  // 购物车中商品的总价
  double get totalPrice =>
      _items.fold(0, (value, item) => value + item.count + item.price);
  // 将[item]添加到购物车。这里唯一一种可能从外部改变购物车的方法
  void add(Item item) {
    _items.add(item);
    //通知监听器(订阅者),重新构建InheritedProvider,更新状态。
    notifyListeners();
  }
}

2. 监听(弄一个工具类)

// 这是一个便捷类,会获得当前context和指定数据类型的Provider
class _Consumer<T> extends StatelessWidget {

  final Widget Function(BuildContext context, T? value) builder;

  const _Consumer({Key? key,required this.builder}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return builder(
      context, context.watch<T>()
    );
  }
}

3. 发送修改事件

Provider.of<CartModel>(context, listen: false).add(Item(20.0, 1));

Selector

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

// 得到的结果, 改成了Selector的形式
// I/flutter (15906): Button 1 被点击了......
// I/flutter (15906): Selector Text 1 重绘了。。。。。。
// I/flutter (15906): Button 2 被点击了.....
// I/flutter (15906): Selector Text 2 重绘了。。。。。。


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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.orange,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyPage(),
    );
  }
}

class CounterProvider with ChangeNotifier {
  int _count = 0;
  int _count1 = 100;

  int get value => _count;

  int get value1 => _count1;

  void increment() {
    _count++;
    notifyListeners();
  }

  void increment1() {
    _count1++;
    notifyListeners();
  }
}

class MyPage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return MyPageState();
  }
}

class MyPageState extends State<MyPage> {
  // 初始化CounterProvider
  CounterProvider _counterProvider = new CounterProvider();

  @override
  Widget build(BuildContext context) {
    print('页面重绘了.........');
    // 整个页面使用ChangeNotifier来包裹
    return ChangeNotifierProvider(
      create: (context) => _counterProvider,
      // child里面的内容不会因为数据的改变而重绘
      child: Scaffold(
        appBar: AppBar(title: Text('my page')),
        body: Center(
          // 使用Consumer来获取provider
          child: Column(
            children: [
              // 使用Selector
              Selector(
                builder: (BuildContext context, int data, Widget? child) {
                  print('Selector Text 1 重绘了。。。。。。');
                  return Text(
                    'Text1 : ${data.toString()}',
                    style: TextStyle(fontSize: 20),
                  );
                },
                selector: (
                  BuildContext context,
                  CounterProvider counterProvider,
                ) {
                  return counterProvider.value;
                },
              ),
              Selector(
                builder: (BuildContext context, int data, Widget? child) {
                  print('Selector Text 2 重绘了。。。。。。');
                  return Text(
                    'Text2 : ${data.toString()}',
                    style: TextStyle(fontSize: 20),
                  );
                },
                selector: (
                  BuildContext context,
                  CounterProvider counterProvider,
                ) {
                  return counterProvider.value1;
                },
              ),
              RaisedButton(
                onPressed: () {
                  print('Button 1 被点击了......');
                  _counterProvider.increment();
                },
                child: Text('Button1'),
              ),
              RaisedButton(
                onPressed: () {
                  print('Button 2 被点击了.....');
                  _counterProvider.increment1();
                },
                child: Text('Button2'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}