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),
      ),
    );
  }
}

效果图:

 

posted on 2019-11-20 15:07  JoeYoung  阅读(5779)  评论(0编辑  收藏  举报