关于flutter:Flutter-Provider状态管理框架

42次阅读

共计 8104 个字符,预计需要花费 21 分钟才能阅读完成。

一、简介

在 Flutter 开发中,或多或少的都会设计到页面的多状态治理,如果大家对 Flutter 技术比拟相熟的话,那么应该晓得上面的一些状态治理框架,像 Bloc,Getx 我都用过,整体来说再状态治理不是很简单的状况下还是能够的。


接下来,咱们来看一下 Flutter 官网举荐的状态治理框架 Provider 是如何应用的。Flutter 针对不同类型对象提供了多种不同的 Provider;Provider 也是借助了 InheritWidget,将共享状态放到顶层 MaterialApp 之上;

  • setState 能刷新 widget 子树,刷新范畴太大,并且须要把数据对象传递到子类。
  • InheritedWidget 不必传递数据对象,通过 context.dependOnInheritedWidgetOfExactType<ShareDataWidget>(); 获取父类的数据。然而刷新范畴大,只能由上而下传递。
  • Provider 能够实现部分刷新。只有数据对象扭转,UI 能主动变动,实现响应式编程。屏蔽刷新逻辑, 实现响应式数据与 UI 的绑定。无论是子类或父类扭转数据都能刷新绑定的 UI。

二、根本应用

应用之前,须要先在 pubspec.yaml 增加 provider

dependencies:
  flutter:
    sdk: flutter

  provider: ^6.0.2

2.1 Provider 根本应用

Provider 是一款基于数据流的观察者模式,应用的第一步就是新建一个继承自 ChangeNotifier 的数据管理类。上面,咱们来看一下官网的例子应用 Provider 形式如何实现。

import 'package:flutter/cupertino.dart';

class CountProviderModel extends ChangeNotifier {
  
  int _count = 0;
  int get count => _count;

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

在 CountProviderModel 类中,咱们定义了有一个数据减少的办法,最初还调用 notifyListeners 发送告诉。接着,咱们新建一个测试页面,该页面的根部应用 ChangeNotifierProvider 组件进行包裹,须要刷新的中央应用 Consumer 组件进行包裹,用于生产

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

import 'count_provider.dart';

class ProviderPage extends StatelessWidget {const ProviderPage({Key? key}) : super(key: key);

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

最初,咱们批改一下 Flutter 我的项目的入口 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(),);
  }
}

从新运行我的项目,点击右下角的按钮时数字就会主动加。

2.2 跨页面状态共享

作为一个全局的状态治理框架,跨页面的状态共享是必须的,为了不便阐明,咱们再新建两个页面 ProviderPageTwo 和 ProviderPageThree,对应的代码如下:
ProviderPageTwo.dart

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

import 'count_provider.dart';

class ProviderPageTwo extends StatelessWidget {const ProviderPageTwo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("ProviderPageTwo"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[const Text('You have pushed the button this many times:'),
            Consumer<CountProviderModel>(builder: (context, notifier, child) {return Text("${notifier.count}",style: const TextStyle(fontSize: 24,));
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(onPressed: () {context.read<CountProviderModel>().increment();
          // 2 秒后跳转至新的页面
          Future.delayed(const Duration(seconds: 2), () {Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {return ProviderPageThree();
            }));
          });
        },
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

ProviderPageThree.dart

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

import 'count_provider.dart';

class ProviderPageThree extends StatelessWidget {const ProviderPageThree({Key? key}) : super(key: key);

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

接着,咱们在 App 顶层进行全局监听,因而,其余页面无需 ChangeNotifierProvider 也能够获取 Model。

void main() {
  runApp(ChangeNotifierProvider(create: (_) => CountProviderModel(),
    child: MyApp(),),);
}

class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue,),
      home:  ProviderPageTwo(),);
  }
}

2.3 多 Model 全局共享

除了后面的两种应用场景,Provider 还反对多 Model 的状态共享。首先,咱们再新建一个 ProviderModel 类。

import 'package:flutter/material.dart';

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

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

而后,咱们批改 ProviderPageTwo 和 ProviderPageThree 两个页面,对应的代码如下:
ProviderPageTwo.dart

import 'dart:math';

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

import 'count_provider.dart';
import 'list_provider.dart';

class ProviderPageTwo extends StatelessWidget {const ProviderPageTwo({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("ProviderPageTwo"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[const 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("List-${Random().nextInt(10)}");
          Future.delayed(const Duration(seconds: 2), () {Navigator.of(context).push(MaterialPageRoute(builder: (BuildContext context) {return const ProviderPageThree();
            }));
          });
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

ProviderPageThree.dart

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

import 'count_provider.dart';
import 'list_provider.dart';

class ProviderPageThree extends StatelessWidget {const ProviderPageThree({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("ProviderPageThree"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[const 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: const Icon(Icons.add),
      ),
    );
  }
}

最初,咱们批改 main 入口文件的代码,应用 MultiProvider 包裹多个 Model,如下。

void main() {
  runApp(
    MultiProvider(
      providers: [ChangeNotifierProvider(create: (_) => CountProviderModel()),
        ChangeNotifierProvider(create: (_) => ListProviderModel()),
      ],
      child: const MyApp(),),
  );
}

class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(primarySwatch: Colors.blue,),
      home:  ProviderPageTwo(),);
  }
}

三、总结

通过后面的例子能够看到,应用 Provider 时咱们须要先新建一个 Model 对象,它继承自 ChangeNotifier,是一个被察看的对象,当 Model 对象扭转时须要调用 notifyListeners 告诉观察者刷新。上面是 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) {......}
      }
    }
  }
}

ChangeNotifier 把办法增加到数组中,而后调用 notifyListeners 时会告诉观察者实现,整个工作流程示意图如下。

正文完
 0