Flutter

Flutter状态管理:Provider

2021-01-20  本文已影响0人  青叶小小

一、前言

此处系列章节目录,待更新

二、引入Provider第三方库

// pubspec.yaml
dependencies:
  flutter:
    sdk: flutter

  provider: ^4.3.2

三、新增Model(CountProviderModel)

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

class CountProviderModel extends ChangeNotifier {
  int _count = 0;

  int get count => _count;

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

四、局部刷新(单组件/单页面内部状态)

4.1、新增页面(ProviderPage)

// ProviderPage.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';

class ProviderPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<CountProviderModel>(
      create: (_) => CountProviderModel(),
      builder: (context, child) {
        return Scaffold(
          appBar: AppBar(
            title: Text("ProviderPage"),
          ),
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text('You have pushed the button this many times:'),
                Consumer<CountProviderModel>(
                  builder: (context, notifier, child) {
                    return Text("${notifier.count}");
                  },
                ),
              ],
            ),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () => context.read<CountProviderModel>().increment(),
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        );
      },
    );
  }
}

4.2、修改main文件

// 改写 main.dart
import 'package:flutter/material.dart';
import 'package:stateresearch/pages/ProviderPage.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter状态管理',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: ProviderPage(),
    );
  }
}

五、全局刷新(页面/组件状态共享)

5.1、新增两个页面(ProviderPageTwo和ProviderPageThree)

// ProviderPageTwo.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';
import 'package:stateresearch/pages/ProviderPageThree.dart';

class ProviderPageTwo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ProviderPageTwo"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Consumer<CountProviderModel>(
              builder: (context, notifier, child) {
                return Text("${notifier.count}");
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          // 扩展 BuildContext,添加 read 方法,具体看附录
          // context.read<T> 实际调用的是 Provider.of<T>
          context.read<CountProviderModel>().increment();
          
          // 2秒后跳转至新的页面
          Future.delayed(Duration(seconds: 2), () {
            Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
              return ProviderPageThree();
            }));
          });
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
// ProviderPageThree.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';

class ProviderPageThree extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ProviderPageThree"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Consumer<CountProviderModel>(
              builder: (context, notifier, child) {
                return Text("${notifier.count}");
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => Provider.of<CountProviderModel>(context, listen: false).increment(),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

5.2、修改main文件

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';
import 'package:stateresearch/pages/ProviderPageTwo.dart';

void main() {
  runApp(
     // APP顶层进行全局监听
     // route 会进行向下传递该 Model
    // 因此其它页面无需 ChangeNotifierProvider
    // 只需要通过 Provider.of 或者 Consumer 获取 Model 即可
    ChangeNotifierProvider(
      create: (_) => CountProviderModel(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter状态管理',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: ProviderPageTwo(),
    );
  }
}

六、多Model全局共享

6.1、新增Model(ListProviderModel)

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

class ListProviderModel extends ChangeNotifier {
  List<String> _list = [];

  List<String> get list => _list;

  void push(String value) {
    _list.add(value);
    notifyListeners();
  }
}

6.2、修改两个页面(ProviderPageTwo和ProviderPageThree)

// ProviderPageTwo.dart
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';
import 'package:stateresearch/model/ListProviderModel.dart';
import 'package:stateresearch/pages/ProviderPageThree.dart';

class ProviderPageTwo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ProviderPageTwo"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Consumer<CountProviderModel>(
              builder: (context, notifier, child) {
                return Text("${notifier.count}");
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          context.read<CountProviderModel>().increment();
          context.read<ListProviderModel>().push("chris-${Random().nextInt(10)}");

          Future.delayed(Duration(seconds: 2), () {
            Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {
              return ProviderPageThree();
            }));
          });
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
// ProviderPageThree.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';
import 'package:stateresearch/model/ListProviderModel.dart';

class ProviderPageThree extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("ProviderPageThree"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Consumer<CountProviderModel>(
              builder: (context, notifier, child) {
                return Text("${notifier.count}");
              },
            ),
            Consumer<ListProviderModel>(
              builder: (context, notifier, child) {
                return Text("${notifier.list}");
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => Provider.of<CountProviderModel>(context, listen: false).increment(),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

6.3、修改main文件

// main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:stateresearch/model/CountProviderModel.dart';
import 'package:stateresearch/model/ListProviderModel.dart';
import 'package:stateresearch/pages/ProviderPageTwo.dart';

void main() {
  runApp(
    // 多个 Model 需要全局共享时,需要用 MultiProvider 包一层
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => CountProviderModel()),
        ChangeNotifierProvider(create: (_) => ListProviderModel()),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter状态管理',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: ProviderPageTwo(),
    );
  }
}

七、总结

Provider是2019年Google I/O大会上主推的状态管理工具。
Provider的Model与ScopedModel的Model其实是一样的,都是继承于Listenable。
Provider还有其它继承于ChangeNotifier的子类,大家可以自行去尝试了解。
Provider可以全局+局部使用(即使用了全局Provider,也不影响某个Widget使用自己的Provider);

对比ScopedModel与Provider,我们发现,两者其实在应用上,区别不大:

附录、源码之ChangeNotifier

class ChangeNotifier implements Listenable {
  ObserverList<VoidCallback>? _listeners = ObserverList<VoidCallback>();

  @protected
  bool get hasListeners {
    return _listeners!.isNotEmpty;
  }

  @override
  void addListener(VoidCallback listener) {
    _listeners!.add(listener);
  }

  @override
  void removeListener(VoidCallback listener) {
    _listeners!.remove(listener);
  }

  @mustCallSuper
  void dispose() {
    _listeners = null;
  }

  @protected
  @visibleForTesting
  void notifyListeners() {
    if (_listeners != null) {
      final List<VoidCallback> localListeners = List<VoidCallback>.from(_listeners!);
      for (final VoidCallback listener in localListeners) {
        try {
          if (_listeners!.contains(listener))
            listener();
        } catch (exception, stack) {
          ......
        }
      }
    }
  }
}
上一篇 下一篇

猜你喜欢

热点阅读