首页
Preview

Flutter - Provider的几类用法

什么是Provider

Provider是 Flutter 中最流行、最成熟的状态管理方法之一。

  • Provider 的内部 DelegateWidget 是一个 StatefulWidget ,所以可以更新且具有生命周期。
  • Provider 是InheritedWidget的包装器。

Provider原理

创建Provider最常用到的类有:
  • ChangeNotifierProvider
  • Provider
  • MultiProvider
  • ProxyProvider
  • ChangeNotifierProxyProvider

通常,ProviderChangeNotifer一起使用。设置到 ChangeNotifierProviderChangeNotifer 会被执行 addListener 添加监听 listener

listener 内会调用 StateDelegateStateSetter 方法,从而调用到 StatefulWidgetsetState

当我们执行 ChangeNotifernotifyListeners 时,就会最终触发 setState 更新。

访问Providerstate常见方法:
  • 使用Provider.of()方法
  • 使用Consumer() 方法

Provider常见类ChangeNotifierProvider的使用

  • 安装依赖
dependencies: 
  provider: ^6.0.4
  • 定义需要共享的数据 我们这里创建了一个类CountNotifier继承了ChangeNotifier,我们这里的案例是以计数器为案例,所有我们定义一个变量count,以及一个改变数值的increment方法,当我们调用increment后会对count进行+1,最后调用notifyListeners()来更新数据,代码如下:
class CountNotifier extends ChangeNotifier{
  int count = 0;
  void increment(){
    count++;
    notifyListeners();
  }
}
  • 在应用程序入口初始化,在MaterialApp之前对定义的共享数据进行了初始化
import 'package:flutter/material.dart';
import 'package:flutter_base/ProviderPage.dart';
import 'package:provider/provider.dart';

void main() => runApp( MyApp());
class MyApp extends StatelessWidget{
  const MyApp({super.key});

  @override
  Widget build(BuildContext context){
    return ChangeNotifierProvider(
      create: (_)=>CountNotifier(),
      child: const MaterialApp(
        home: ProviderPage(),
      ),
    );
  }
}
  • 使用共享数据
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

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

  @override
  State<ProviderPage> createState() => _ProviderPageState();
}
class _ProviderPageState extends State<ProviderPage> {
  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<CountNotifier>(context);
    return Scaffold(
      appBar: AppBar(title: const Text('Provider'),),
      floatingActionButton: FloatingActionButton(
        onPressed: (){
          counter.increment();
        },
        child: const Icon(Icons.add),
      ),
      body: Center(
        child: Text(counter.count.toString()),
      ),
    );
  }
}

class CountNotifier extends ChangeNotifier{
  int count = 0;
  void increment(){
    count++;
    notifyListeners();
  }
}

Provider常见类Provider的使用

Provider是最基本的Provider组件,可以使用它为组件树中的任何位置提供值,但是当该值更改的时候,它并不会更新UI,下面我们给出一个示例:

  • 第一步:创建模型
class UserModel{
  String name = "张三";
  void changeName(){
    name = "李四";
  }
}
  • 第二步:应用程序入口设置
import 'package:flutter/material.dart';
import 'package:flutter_base/ProviderPage2.dart';
import 'package:provider/provider.dart';

void main() => runApp( MyApp());
class MyApp extends StatelessWidget{
  const MyApp({super.key});

  @override
  Widget build(BuildContext context){
    return Provider(
      create: (_) => UserModel(),
      child: const MaterialApp(
        home: ProviderPage2(),
      ),
    );
  }
}
  • 第三步:使用共享数据 这里只需要知道有两个消费者,第一个用于展示模型的数据,第二个用于改变模型的数据, 第一个Comsumer是用于读取模型的数据name,第二个Consumer用于改变模型的数据name。
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class UserModel{
  String name = "张三";
  void changeName(){
    print('22222');
    name = "李四";
  }
}

class ProviderPage2 extends StatefulWidget {
  const ProviderPage2({Key? key}) : super(key: key);

  @override
  State<ProviderPage2> createState() => _ProviderPage2State();
}

class _ProviderPage2State extends State<ProviderPage2> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Provider'),),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Consumer<UserModel>(
              builder: (_,userModel,child){
                //取值
                return Text(userModel.name,style: TextStyle(color: Colors.red,fontSize: 30),);
              }
            ),
            Consumer<UserModel>(
              builder: (_,userModel,child){
                return Padding(
                  padding: const EdgeInsets.all(20),
                  child: ElevatedButton(
                    //调用方法
                    onPressed: (){userModel.changeName();},
                    child: const Text('改变值'),
                  ),
                );
              }
            )
          ],
        ),
      ),
    );
  }
}

我们点击按钮的会导致模型数据改变,但是模型数据改变之后UI并没有变化也没有重建,那是因为Provider提供者组件不会监听它提供的值的变化。 provider.gif ChangeNotifierProviderProvider组件不同,ChangeNotifierProvider会监听模型对象的变化,而且当数据改变时,它也会重建Consumer(消费者),我们可以在创建模型时混入ChangeNotifier,并且调用notifyListeners()方法:

class UserModel with ChangeNotifier{
  String name = "张三";
  void changeName(){
    
    print('22222');
    name = "李四";
    notifyListeners();
  }
}

第二步修改成

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

void main() => runApp( MyApp());
class MyApp extends StatelessWidget{
  const MyApp({super.key});

  @override
  Widget build(BuildContext context){
    return ChangeNotifierProvider<UserModel>(
      create: (_) => UserModel(),
      child: const MaterialApp(
        home: ProviderPage2(),
      ),
    );
  }
}

修改之后运行的结果为: provider2.gif

Provider常见类MultiProvider的使用

Provider放置的位置一般是在相应的widget的外层,也就是数据状态的共享都是在该层widget内部进行。需要使用多个Provider的话,可以选择MultiProvider来完成放置。

  • 第一步:创建两个模型
class UserModelOne with ChangeNotifier{
  String name = '张三';
  void changeName (){
    name = '李四';
    notifyListeners();
  }
}

class UserModelTwo with ChangeNotifier{
  String name = '张三';
  int age = 18;
  void changeName(){
    name = '李四';
    age = 20;
    notifyListeners();
  }
}
  • 程序入口设置

方式一:嵌套设置

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

void main() => runApp( MyApp());
class MyApp extends StatelessWidget{
  const MyApp({super.key});

  @override
  Widget build(BuildContext context){
    return ChangeNotifierProvider<UserModelOne>(
      create: (_) => UserModelOne(),
      child: ChangeNotifierProvider<UserModelTwo>(
        create: (_) => UserModelTwo(),
        child: const MaterialApp(
          home: ProviderPage3(),
        ),
      )
    );
  }
}

方式二:MultiProvider

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

void main() => runApp( MyApp());
class MyApp extends StatelessWidget{
  const MyApp({super.key});

  @override
  Widget build(BuildContext context){
    return MultiProvider(
        providers: [
          ChangeNotifierProvider<UserModelOne>(create: (_) => UserModelOne()),
          ChangeNotifierProvider<UserModelTwo>(create: (_) => UserModelTwo())
        ],
        child: const MaterialApp(
          home: ProviderPage3(),
        ),
    );
  }
}

provider3.gif

Provider常见类ProxyProvider的使用

当我们有多个模型的时候,会有模型依赖另一个模型的情况,在这种情况下,我们可以使用ProxyProvider从另一个提供者获取值,然后将其注入到另一个提供者中。

  • 第一步:创建两个模型

创建两个模型UserModelThreeWalletModel,而WalletModel依赖与UserModelThree,调用WalletModelchangeName方法时会改变UserModelThree里面的name

class UserModelThree with ChangeNotifier{
  String name = '张三';
  void changeName({required String newName}){
    name = newName;
    notifyListeners();
  }
}

class WalletModel{
  UserModelThree? userModelThree;
  WalletModel({this.userModelThree});
  void changeName(){
    userModelThree?.changeName(newName: '李四');
  }
}
  • 第二步:程序入口设置
class MyApp extends StatelessWidget{
  const MyApp({super.key});

  @override
  Widget build(BuildContext context){
    return MultiProvider(
        providers: [
          ChangeNotifierProvider<UserModelThree>(create: (_) => UserModelThree()),
          ProxyProvider<UserModelThree,WalletModel>(
              update: (_,userModelThree,walletModel) => WalletModel(userModelThree: userModelThree)
          )
        ],
        child: const MaterialApp(
          home: ProxyProviderPage(),
        ),
    );
  }
}
  • 第三步:使用共享数据
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class UserModelThree with ChangeNotifier{
  String name = '张三';
  void changeName({required String newName}){
    name = newName;
    notifyListeners();
  }
}

class WalletModel{
  UserModelThree? userModelThree;
  WalletModel({this.userModelThree});
  void changeName(){
    userModelThree?.changeName(newName: '王五');
  }
}

class ProxyProviderPage extends StatefulWidget {
  const ProxyProviderPage({Key? key}) : super(key: key);

  @override
  State<ProxyProviderPage> createState() => _ProxyProviderPageState();
}

class _ProxyProviderPageState extends State<ProxyProviderPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title:const Text('ProxyProvider'),),
      body: Center(
        child: Column(
          children: [
            Consumer<UserModelThree>(
              builder: (_,userModel,child){
                return Text(userModel.name,style: const TextStyle(
                  color: Colors.red,
                  fontSize: 30
                ),);
              }
            ),
            Consumer<UserModelThree>(
                builder: (_,userModel,child){
                 return Padding(
                   padding: const EdgeInsets.all(20),
                   child: ElevatedButton(
                     onPressed: (){
                       userModel.changeName(newName: '李四');
                     },
                     child: const Text('改变值'),
                   ),
                 );
                }
            ),
            Consumer<WalletModel>(
                builder: (_,walletModel,child){
                  return Padding(
                    padding: const EdgeInsets.all(20),
                    child: ElevatedButton(
                      onPressed: (){
                        walletModel.changeName();
                      },
                      child: const Text('通过代理改变值'),
                    ),
                  );
                }
            ),
          ],
        ),
      ),
    );
  }
}

provider4.gif

Provider常见类ChangeNotifierProxyProvider的使用

ProxyProvider原理一样,唯一的区别在于它构建和同步ChangeNotifierChangeNotifierProvider,当提供者数据变化时,将会重构UI。 下面我们给出一个例子:

  1. 获取书籍列表
  2. 获取收藏书籍列表
  3. 点击书籍可加入或者取消收藏
  4. 通过代理实时重构UI
  • 第一步:创建两个模型

1、BookModel BookModel用户存储模型数据,将书籍转换成模型。

class BookModel{
  static final _books = [
    Book(1,'插翅难逃'),
    Book(2,'西城往事'),
    Book(3,'偷偷藏不住'),
    Book(4,'黑白'),
    Book(5,'我终究是爱你的'),
    Book(6,'凉风有信'),
  ];
  //获取书籍长度
  int get length => _books.length;
  //根据ID获取书籍
  Book getById(int id) => _books[id - 1];
  // 根据索引获取数据
  Book getByPosition(int position) => _books[position];

}

class Book{
  final int bookId;
  final String bookName;
  Book(this.bookId,this.bookName);
}

2、BookManagerModel

BookManagerModel主要用于管理书籍、收藏书籍、取消收藏等操作

import 'package:flutter/cupertino.dart';
import 'package:flutter_base/ChangeNotifierProxyProviderPage.dart';

class BookManagerModel with ChangeNotifier{
  //依赖bookModel
  final BookModel _bookModel;
  //获取数据说有ID
  List<int>? _bookIds;
  //构造函数
  BookManagerModel(this._bookModel,{BookManagerModel?bookManagerModel})
      : _bookIds = bookManagerModel?._bookIds??[];

  //获取所有的书
  List<Book>? get books => _bookIds?.map((id) => _bookModel.getById(id)).toList();
  //根据索引获取数据
  Book getByPosition(int position) => books![position];
  //获取书籍长度
  int get length => _bookIds?.length ?? 0;
  //添加书籍
  void addBook(Book book){
    _bookIds?.add(book.bookId);
    notifyListeners();
  }
  //删除书籍
  void removeBook(Book book){
    _bookIds!.remove(book.bookId);
    notifyListeners();
  }
}
  • 第二步:应用程序入口设置
class MyApp extends StatelessWidget{
  const MyApp({super.key});

  @override
  Widget build(BuildContext context){
    return MultiProvider(
      providers: [
        Provider(create: (_) => BookModel()),
        ChangeNotifierProxyProvider<BookModel,BookManagerModel>(
          create: (_) => BookManagerModel(BookModel()),
          update: (_,bookModel,bookManagerModel)=> BookManagerModel(bookModel)
        )
      ],
      child: const MaterialApp(
        home: ChangeNotifierProxyProviderPage(),
      ),
    );
  }
}
  • 第三步:设置BottomNavigationBar
import 'package:flutter/material.dart';
import 'package:flutter_base/pages/PageA.dart';
import 'package:flutter_base/pages/PageB.dart';
class ChangeNotifierProxyProviderPage extends StatefulWidget {
  const ChangeNotifierProxyProviderPage({Key? key}) : super(key: key);

  @override
  State<ChangeNotifierProxyProviderPage> createState() => _ChangeNotifierProxyProviderPageState();
}
class _ChangeNotifierProxyProviderPageState extends State<ChangeNotifierProxyProviderPage> {
 var _selectedIndex = 0;
 var _pages = [PageA(),PageB()];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ChangeNotifierProxyProvider'),),
      body: _pages[_selectedIndex],
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _selectedIndex,
        onTap: (index){
          setState(() {
            _selectedIndex = index;
          });
        },
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.book),
            label: '书籍列表'
          ),
          BottomNavigationBarItem(
              icon: Icon(Icons.favorite),
              label: '收藏'
          ),
        ],
      ),
    );
  }
}
  • PageA书籍列表
import 'package:flutter/material.dart';
import 'package:flutter_base/model/BookManagerModel.dart';
import 'package:flutter_base/model/BookModel.dart';
import 'package:provider/provider.dart';

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

  @override
  Widget build(BuildContext context) {
    //获取书籍列表数据
    var bookLists = Provider.of<BookModel>(context);
    return Scaffold(
      appBar: AppBar(title: const Text('书籍列表'),),
      body: ListView.builder(
        itemCount: bookLists.length,
        itemBuilder: (context,index) => BookItem(id:index+1)
      ),
    );
  }
}

class BookItem extends StatelessWidget {
  final int id;
  const BookItem({Key? key, required this.id}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    var bookLists = Provider.of<BookModel>(context);
    var book = bookLists.getById(id);
    return ListTile(
      leading: CircleAvatar(
        child: Text("${book.bookId}"),
      ),
      title: Text(book.bookName,style: const TextStyle(
        color: Colors.black87
      ),),
      trailing: BookButton(book:book),
    );
  }
}

class BookButton extends StatelessWidget {
  final Book book;
  const BookButton({Key? key, required this.book}) : super(key: key);

  @override
  Widget build(BuildContext context) {
   final bookManagerModel = Provider.of<BookManagerModel>(context);
    return GestureDetector(
      onTap: (bookManagerModel.books!).contains(book)
          ? ()=>bookManagerModel.removeBook(book)
          : ()=>bookManagerModel.addBook(book),
      child: SizedBox(
        width: 100,
        height: 60,
        child: (bookManagerModel.books!).contains(book)
            ? const Icon(Icons.star,color: Colors.red,)
            : const Icon(Icons.star_border),
      ),
    );
  }
}



  • PageB 收藏列表
import 'package:flutter/material.dart';
import 'package:flutter_base/pages/PageA.dart';
import 'package:provider/provider.dart';

import '../model/BookManagerModel.dart';

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

  @override
  Widget build(BuildContext context) {
    var bookManagerModel = Provider.of<BookManagerModel>(context);
    var bookCount = bookManagerModel.length;
    return Scaffold(
      appBar: AppBar(title: const Text('收藏列表'),),
      body: ListView.builder(
        itemCount: bookCount,
        itemBuilder: (context,index) => BookItem(id: bookManagerModel.getByPosition(index).bookId),
      ),
    );
    
  }
}

provider5.gif

参考原文:https://www.liujunmin.com/flutter/provider/eight_provider.html

版权声明:本文内容由TeHub注册用户自发贡献,版权归原作者所有,TeHub社区不拥有其著作权,亦不承担相应法律责任。 如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

点赞(0)
收藏(0)
励志猿
励志每天写一篇文章,有价值的文章,提升自我!

评论(0)

添加评论