zoukankan      html  css  js  c++  java
  • Flutter状态管理Provider,简单上手

    在之前的文章中介绍了 Google 官方仓库下的一个状态管理 Provide。乍一看这俩玩意可能很容易就被认为是同一个东西,仔细一看,这不就差了一个字吗,有什么区别呢。

    首先,你要知道的最大的一个区别就是,Provide 被 Provider 干掉了...假如你就是用了 Provide 的,你的内心应该已经开始骂了,这不是坑爹吗 。不过幸运的是,你要从 Provide 迁移到 Provider 并不是太难。

    Provider 从名字上就很容易理解,它就是用于提供数据,无论是在单个页面还是在整个 app 都有它自己的解决方案,我们可以很方便的管理状态。可以说,Provider 的目标就是完全替代StatefulWidget。

    Provider非全局状态管理:

    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    
    class HomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('搜索结果'),
          ),
          body: Provider<String>.value(  //provider
            value: 'This is from MyHomePage',
            child: MyHomePage(),
          ),
        ); 
      } 
    }
    
    //Provider.of
    class MyHomePage extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return Center(
             child: Text('${Provider.of<String>(context)}'),
          );
        }
    }
    //Consumer同样实现
    class MyHomePage extends StatelessWidget {
        @override
        Widget build(BuildContext context) {
          return Center(
            //child: Text('${Provider.of<String>(context)}'),
            child:Consumer<String>(builder:(context, data, child){
              return Text(data);
            }),
          );
        }
      }

    官方示例代码:

    import 'package:flutter/foundation.dart';
    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    
    
    class Counters with ChangeNotifier {
      int _count = 0;
      int get count => _count;
    
      void increment() {
        _count++;
        notifyListeners();
      }
    }
    
    class ProviderPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MultiProvider(
          providers: [
            ChangeNotifierProvider(builder: (_) => Counters()),
          ],
          child: Consumer<Counters>(
            builder: (context, counter, _) {
              return MaterialApp(
                supportedLocales: const [Locale('en')],
                localizationsDelegates: [
                  DefaultMaterialLocalizations.delegate,
                  DefaultWidgetsLocalizations.delegate,
                  _ExampleLocalizationsDelegate(counter.count),
                ],
                home: const MyHomePage(),
              );
            },
          ),
        );
      }
    }
    
    class ExampleLocalizations {
      static ExampleLocalizations of(BuildContext context) =>
          Localizations.of<ExampleLocalizations>(context, ExampleLocalizations);
    
      const ExampleLocalizations(this._count);
    
      final int _count;
    
      String get title => 'Tapped $_count times';
    }
    
    class _ExampleLocalizationsDelegate
        extends LocalizationsDelegate<ExampleLocalizations> {
      const _ExampleLocalizationsDelegate(this.count);
    
      final int count;
    
      @override
      bool isSupported(Locale locale) => locale.languageCode == 'en';
    
      @override
      Future<ExampleLocalizations> load(Locale locale) =>
          SynchronousFuture(ExampleLocalizations(count));
    
      @override
      bool shouldReload(_ExampleLocalizationsDelegate old) => old.count != count;
    }
    
    class MyHomePage extends StatelessWidget {
      const MyHomePage({Key key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: const Title()),
          body: const Center(child: CounterLabel()),
          floatingActionButton: const IncrementCounterButton(),
        );
      }
    }
    
    class IncrementCounterButton extends StatelessWidget {
      const IncrementCounterButton({Key key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return FloatingActionButton(
          onPressed: () {
            // `listen: false` is specified here because otherwise that would make
            // `IncrementCounterButton` rebuild when the counter updates.
            Provider.of<Counters>(context, listen: false).increment();
          },
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        );
      }
    }
    
    class CounterLabel extends StatelessWidget {
      const CounterLabel({Key key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        final counter = Provider.of<Counters>(context);
        return Column(
          mainAxisSize: MainAxisSize.min,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '${counter.count}',
              style: Theme.of(context).textTheme.display1,
            ),
            RaisedButton(
              onPressed: (){
                Navigator.push(context, MaterialPageRoute(builder: (context)=>ProviderSecond()));
              },
              child: Text('下一页'),
            )
          ],
        );
      }
    }
    
    class Title extends StatelessWidget {
      const Title({Key key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Text(ExampleLocalizations.of(context).title);
      }
    }
    
    class ProviderSecond extends StatelessWidget { const ProviderSecond({Key key}) : super(key: key); @override Widget build(BuildContext context) { final counter = Provider.of<Counters>(context); return Scaffold( appBar: AppBar( leading: IconButton( //返回按钮 onPressed: (){ Navigator.pop(context); //返回上级页面 }, icon: Icon(Icons.arrow_back), ), title: Text('第二页'), ), body: Text( '${counter.count}', style: Theme.of(context).textTheme.display1, ), ); } }

    效果图:

    点击+到7,然后点击下一页,下一页的内容同样是7。

    Provider2.0到3.0的改变:

    2.0:
    ChangeNotifierProvider.value(notifier: myNotifier),
    
    StreamProvider(builder: (_) => StreamController<int>()),
    
    3.0:
    ChangeNotifierProvider.value(value: myNotifier),
    
    StreamProvider.controller(builder: (_) => StreamController<int>()),
     
    下面基于Provider v-3.0 写个简单的示例: 

    第一步,添加Provider依赖

    provider: ^3.1.0+1

    pub地址:https://pub.dev/packages/provider

    第二步,创建Model

    class Counter with ChangeNotifier {
      int _count = 0;
      int get count => _count;
    
      void increment() {
        _count++;
        notifyListeners();
      }
    }

    简单的一个Counters对象,里面只有一个字段_count

    1. 这里需要混入ChangeNotifier
    2. 写一个增加的方法,然后需要调用notifyListeners();这个方法是通知用到Counters对象的widget刷新用的。
    3. get方法

    第三步,使用ChangeNotifierProvider

    我们要监听改变要在MyApp()外面套一层,这个是全局的,于是代码如下 :
    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    import './provider/counter.dart';
    
    void main() { 
      //runApp(new MyApp());
      runApp(
         ChangeNotifierProvider<Counter>.value(//ChangeNotifierProvider调用value()方法,里面传出value和child
          value: Counter(),//value设置了默认的Counter()
          child: MyApp(),
        )
      );
    }    
    当然Provider不止提供了ChangeNotifierProvider,还有Provider、ListenableProvider、ValueListenableProviderStreamProvider,
    具体可以看wiki.
    如果想管理多个对象可以用MultiProvider,如下:
    void main() { 
      //runApp(new MyApp());
      runApp(
        // ChangeNotifierProvider<Counter>.value(//ChangeNotifierProvider调用value()方法,里面传出value和child
        //   value: Counter(),//value设置了默认的Counter()
        //   child: MyApp(),
        // )
        MultiProvider(
          providers: [
            ChangeNotifierProvider.value(value: Counter()),
            //ChangeNotifierProvider(builder: (_) => Counter()),
          ],
          child: MyApp(),
        ),
      );
    }

    第四步,使用Provider获取Counter的值

    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    import '../provider/counter.dart';
    
    class HomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Home"),
            actions: <Widget>[
              FlatButton(
                child: Text("下一页"),
                onPressed: () =>
                    Navigator.push(context, MaterialPageRoute(builder: (context) {
                      return SecondPage();
                    })),
              ),
            ],
          ),
          body: Center(
            child: Text("${Provider.of<Counter>(context).count}"),//用Provider.of<Counter>(context).count获取_count的值,Provider.of<T>(context)相当于Provider去查找它管理的Counter()
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              Provider.of<Counter>(context).increment();//用Provider.of<Counter>(context).increment();调用Counter()中的increment()方法
            },
            child: Icon(Icons.add),
          ),
        );
      }
    }
    

     同样第二个页面也这样写,如下

    class SecondPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        var counter = Provider.of<Counter>(context).count;
        return Scaffold(
          appBar: AppBar(
            title: Text("SecondPage"),
          ),
          body: Center(
            child: Text("${counter}"),
            //child: Text("${Provider.of<Counter>(context).count}"),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              Provider.of<Counter>(context).increment();
            },
            child: Icon(Icons.add),
          ),
        );
      }
    }

    这样,当每个页面都点击+号按钮时,_count便会+1,同时通知并更新到使用它的地方。

    完整代码写到一个页面,copy后可直接运行:

    import 'package:flutter/material.dart';
    import 'package:provider/provider.dart';
    
    main() {
      runApp(ChangeNotifierProvider<Counter>.value(
        value: Counter(),
        child: MyApp(),
      ));
    }
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Provider",
          home: HomePage(),
        );
      }
    }
    
    class Counter with ChangeNotifier {//混入ChangeNotifier
      int _count = 0;
      get count => _count;
    
      void increment() {
        _count++;
        notifyListeners();//通知
      }
    }
    
    class HomePage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Home"),
            actions: <Widget>[
              FlatButton(
                child: Text("下一页"),
                onPressed: () =>
                    Navigator.push(context, MaterialPageRoute(builder: (context) {
                      return SecondPage();
                    })),
              ),
            ],
          ),
          body: Center(
            child: Text("${Provider.of<Counter>(context).count}"),//用Provider.of<Counter>(context).count获取_count的值,Provider.of<T>(context)相当于Provider去查找它管理的Counter()
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              Provider.of<Counter>(context).increment();//用Provider.of<Counter>(context).increment();调用Counter()中的increment()方法
            },
            child: Icon(Icons.add),
          ),
        );
      }
    }
    
    
    class SecondPage extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        var counter = Provider.of<Counter>(context).count;
        return Scaffold(
          appBar: AppBar(
            title: Text("SecondPage"),
          ),
          body: Center(
            child: Text("${counter}"),
            //child: Text("${Provider.of<Counter>(context).count}"),//1 
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              Provider.of<Counter>(context).increment();//2
            },
            child: Icon(Icons.add),
          ),
        );
      }
    }

    效果图:

  • 相关阅读:
    EasyExcel无法用转换器或者注解将java字段写入为excel的数值格式
    IE浏览器报400错误:Invalid character found in the request target. The valid characters are defined in RFC 7230 and RFC 3986
    list集合根据字段分组统计转换成map
    博客调网易云歌单JS
    如何一次性add library to classpath
    有趣的统计数据表格显示
    span标签的巧用
    "错误: 找不到或无法加载主类"解决办法
    通过改变注入方式以消除警告
    day17--作业
  • 原文地址:https://www.cnblogs.com/joe235/p/11883350.html
Copyright © 2011-2022 走看看