关于flutter:Flutter-Provider状态管理八种提供者使用分析

31次阅读

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

文章系列

Flutter Provider 状态治理 — 介绍、类图剖析、根本应用

Flutter Provider 状态治理 — 八种提供者应用剖析

视频系列

Flutter Provider 状态治理 — 介绍、类图剖析、根本应用

Flutter Provider 状态治理 — 八种提供者应用剖析

源码仓库地址

github 仓库地址

前言

在咱们上一篇文章中对 Provider 进行了介绍以及类构造的阐明,最初还写了一个简略的示例,通过上一章节咱们对 Provider 有了一个根本的理解,这一章节咱们来说说 Provider 的 8 种提供者以及他们的应用区别。

Provider

Provider是最根本的 Provider 组件,能够应用它为组件树中的任何地位提供值,然而当该值更改的时候,它并不会更新 UI,上面咱们给出一个示例

第一步:创立模型

class UserModel {

  String name = "Jimi";

  void changeName() {name = "hello";}
}

第二步:应用程序入口设置

return Provider<UserModel>(create: (_) => UserModel(),
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: ProviderExample(),),
);

第三步:应用共享数据

对于 Consumer 前面将消费者在提及,咱们这里只须要晓得有两个消费者,第一个用于展现模型的数据,第二个用于扭转模型的数据。

  • 第一个 Comsumer 是用于读取模型的数据name
  • 第二个 Consumer 用于扭转模型的数据name
import 'package:flutter/material.dart';
import 'package:flutter_provider_example/provider_example/user_model.dart';
import 'package:provider/provider.dart';

class ProviderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("ProviderExample"),
      ),
      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: EdgeInsets.all(20),
                  child: ElevatedButton(onPressed: (){userModel.changeName();
                    },
                    child: Text("扭转值"),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

运行后果

咱们点击按钮的会导致模型数据扭转,然而模型数据扭转之后 UI 并没有变动也没有重建,那是因为 Provider 提供者组件不会监听它提供的值的变动。

ChangeNotifierProvider

它跟 Provider 组件不同,ChangeNotifierProvider会监听模型对象的变动,而且当数据扭转时,它也会重建Consumer(消费者),上面咱们给出一个示例

第一步:创立模型

仔细点咱们能够发现这里定义的模型有两处变动,如下:

  • 混入了ChangeNotifier
  • 调用了notifyListeners()

因为模型类应用了 ChangeNotifier,那么咱们就能够拜访notifyListeners() 并且在调用它的任何时候,ChangeNotifierProvider都会收到告诉并且消费者将重建 UI。

import 'package:flutter/material.dart';

class UserModel1 with ChangeNotifier {

  String name = "Jimi";

  void changeName() {
    name = "hello";
    notifyListeners();}
}

第二步:应用程序入口设置

return ChangeNotifierProvider<UserModel1>(create: (_) => UserModel1(),
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: ChangeNotifierProviderExample(),),
);

第三步:应用共享数据

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/change_notifier_provider_example/user_model1.dart';
import 'package:provider/provider.dart';

class ChangeNotifierProviderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("ChangeNotifierProvider"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Consumer<UserModel1>(builder: (_, userModel, child) {
                return Text(userModel.name,
                    style: TextStyle(
                        color: Colors.red,
                        fontSize: 30
                    )
                );
              },
            ),
            Consumer<UserModel1>(builder: (_, userModel, child) {
                return Padding(padding: EdgeInsets.all(20),
                  child: ElevatedButton(onPressed: (){userModel.changeName();
                    },
                    child: Text("扭转值"),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

运行后果

FutureProvider

简略来说,FutureProvider用于提供在组件树中筹备好应用其值时可能尚未筹备好的值,次要是确保空值不会传递给任何子组件,而且 FutureProvider 有一个初始值,子组件能够应用该 Future 值并通知子组件应用新的值来进行重建。

留神:

  • FutureProvider只会重建一次
  • 默认显示初始值
  • 而后显示 Future
  • 最初不会再次重建

第一步:创立模型

这里和 Provider 不同的是减少了构造函数,以及 changeName 变成了Future,咱们模仿网络申请提早两秒后扭转其值。

class UserModel2{UserModel2({this.name});

  String? name = "Jimi";

  Future<void> changeName() async {await Future.delayed(Duration(milliseconds: 2000));
    name = "hello";
  }
}

第二步:提供 Future

咱们有一个办法,就是异步获取 userModel2,模仿网络申请提早两秒执行,最初批改了name 并返回UserModel2


import 'package:flutter_provider_example/future_provider_example/user_model2.dart';

class UserFuture {Future<UserModel2> asyncGetUserModel2() async {await Future.delayed(Duration(milliseconds: 2000));
    return UserModel2(name: "获取新的数据");
  }

}

第三步:应用程序入口设置

initialData是默认值,create参数咱们传了一个Future<UserModel2>,因为它接管的模型Create<Future<T>?>

return FutureProvider<UserModel2>(initialData: UserModel2(name: "hello"),
  create: (_) => UserFuture().asyncGetUserModel2(),
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: FutureProviderExample(),),
);

第四步:应用共享数据

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/future_provider_example/user_model2.dart';
import 'package:provider/provider.dart';

class FutureProviderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("FutureProviderExample"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Consumer<UserModel2>(builder: (_, userModel, child) {
                return Text(userModel.name ?? "",
                    style: TextStyle(
                        color: Colors.red,
                        fontSize: 30
                    )
                );
              },
            ),
            Consumer<UserModel2>(builder: (_, userModel, child) {
                return Padding(padding: EdgeInsets.all(20),
                  child: ElevatedButton(onPressed: (){userModel.changeName();
                    },
                    child: Text("扭转值"),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

运行后果

咱们能够看到先展现默认值 hello,最初获取到后果的时候展现了 获取新的数据,咱们尝试扭转其值,尽管值扭转然而并没有刷新 UI。

StreamProvider

StreamProvider提供流值,是围绕 StreamBuilder,所提供的值会在传入的时候替换掉新值。和FutureProvider 一样,次要的区别在于值会依据屡次触发从新构建 UI。

如果你对 StreamBuilder 不太理解的话,那么你就很难了解StreamProvider,StreamProvider 文档地址

第一步:创立模型

class UserModel3{UserModel3({this.name});

  String? name = "Jimi";

  void changeName() {name = "hello";}
}

第二步:提供 Stream

上面这段代码相似计时器,每隔一秒钟生成一个数字

import 'package:flutter_provider_example/stream_provider_example/user_model3.dart';

class UserStream {Stream<UserModel3> getStreamUserModel() {return Stream<UserModel3>.periodic(Duration(milliseconds: 1000),
        (value) => UserModel3(name: "$value")
    ).take(10);
  }
}

第三步:应用程序入口设置

这里也有 initialData 初始值,和 FutureProvider 相似,只是 create 属性是获取一个 Stream 流。

return StreamProvider<UserModel3>(initialData: UserModel3(name: "hello"),
  create: (_) => UserStream().getStreamUserModel(),
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: StreamProviderExample(),),
);

第四步:应用共享数据

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/stream_provider_example/user_model3.dart';
import 'package:provider/provider.dart';

class StreamProviderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("StreamProviderExample"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Consumer<UserModel3>(builder: (_, userModel, child) {
                return Text(userModel.name ?? "",
                    style: TextStyle(
                        color: Colors.red,
                        fontSize: 30
                    )
                );
              },
            ),
            Consumer<UserModel3>(builder: (_, userModel, child) {
                return Padding(padding: EdgeInsets.all(20),
                  child: ElevatedButton(onPressed: (){userModel.changeName();
                    },
                    child: Text("扭转值"),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

运行后果

MultiProvider

在下面的例子中咱们都只是返回了一个提供者,在理论开发过程中必定会有多个提供者,咱们尽管能够采纳嵌套的形式来解决,然而这样无疑是凌乱的,可读性级差。这个时候弱小的 MultiProvder 就产生了,咱们来看下示例:

第一步:创立两个模型

import 'package:flutter/material.dart';

class UserModel1 with ChangeNotifier {

  String name = "Jimi";

  void changeName() {
    name = "hello";
    notifyListeners();}
}

class UserModel4 with ChangeNotifier {

  String name = "Jimi";
  int age = 18;

  void changeName() {
    name = "hello";
    age = 20;
    notifyListeners();}
}

第二步:应用程序入口设置

绝对于形式一这种嵌套形式设置,形式二就显得尤为简略。

形式一:嵌套设置

return ChangeNotifierProvider<UserModel1>(create: (_) => UserModel1(),
  child: ChangeNotifierProvider<UserModel4>(create: (_) => UserModel4(),
    child: MaterialApp(
      debugShowCheckedModeBanner: false,
      home: MultiProviderExample(),),
  ),
);

形式二:应用 MultiProvider

return MultiProvider(
  providers: [ChangeNotifierProvider<UserModel1>(create: (_) => UserModel1()),
    ChangeNotifierProvider<UserModel4>(create: (_) => UserModel4()),
    /// 增加更多
  ],
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: MultiProviderExample(),),
);

第三步:应用共享数据

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/change_notifier_provider_example/user_model1.dart';
import 'package:flutter_provider_example/multi_provider_example/user_model4.dart';
import 'package:provider/provider.dart';

class MultiProviderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("MultiProviderExample"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Consumer<UserModel1>(builder: (_, userModel, child) {
                return Text(userModel.name,
                    style: TextStyle(
                        color: Colors.red,
                        fontSize: 30
                    )
                );
              },
            ),
            Consumer<UserModel4>(builder: (_, userModel, child) {return Text(userModel.age.toString(),
                    style: TextStyle(
                        color: Colors.green,
                        fontSize: 30
                    )
                );
              },
            ),
            Consumer2<UserModel1, UserModel4>(builder: (_, userModel1, userModel4, child) {
                return Padding(padding: EdgeInsets.all(20),
                  child: ElevatedButton(onPressed: (){userModel1.changeName();
                      userModel4.changeName();},
                    child: Text("扭转值"),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

运行后果

ProxyProvider

当咱们有多个模型的时候,会有模型依赖另一个模型的状况,在这种状况下,咱们能够应用 ProxyProvider 从另一个提供者获取值,而后将其注入到另一个提供者中。咱们来看下代码演示

第一步:创立两个模型

上面咱们创立了两个模型 UserModel5WalletModel,而 WalletModel 依赖与 UserModel5,当调用WalletModelchangeName办法时会扭转 UserModel5 外面的 name,当然咱们在理论开发的过程中并不是这么简略,这里只是演示模型依赖时如果应用ProxyProvider

import 'package:flutter/material.dart';

class UserModel5 with ChangeNotifier {

  String name = "Jimi";

  void changeName({required String newName}) {
    name = newName;
    notifyListeners();}
}


class WalletModel {

  UserModel5? userModel5;

  WalletModel({this.userModel5});

  void changeName() {userModel5?.changeName(newName: "JIMI");
  }
}

第二步:应用程序入口设置

return MultiProvider(
  providers: [ChangeNotifierProvider<UserModel5>(create: (_) => UserModel5()),
    ProxyProvider<UserModel5, WalletModel>(update: (_, userModel5, walletModel) => WalletModel(userModel5: userModel5),
    )
  ],
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: ProxyProviderExample(),),
);

第三步:应用共享数据

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/proxy_provider_example/user_model5.dart';
import 'package:flutter_provider_example/proxy_provider_example/wallet_model.dart';
import 'package:provider/provider.dart';

class ProxyProviderExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("ProxyProviderExample"),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Consumer<UserModel5>(builder: (_, userModel, child) {
                return Text(userModel.name,
                    style: TextStyle(
                        color: Colors.red,
                        fontSize: 30
                    )
                );
              },
            ),
            Consumer<UserModel5>(builder: (_, userModel, child) {
                return Padding(padding: EdgeInsets.all(20),
                  child: ElevatedButton(onPressed: (){userModel.changeName(newName: "hello");
                    },
                    child: Text("扭转值"),
                  ),
                );
              },
            ),
            Consumer<WalletModel>(builder: (_, walletModel, child) {
                return Padding(padding: EdgeInsets.all(20),
                  child: ElevatedButton(onPressed: (){walletModel.changeName();
                    },
                    child: Text("通过代理扭转值"),
                  ),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

运行后果

ChangeNotifierProxyProvider

ProxyProvider 原理一样,惟一的区别在于它构建和同步 ChangeNotifierChangeNotifierProvider,当提供者数据变动时,将会重构 UI。

上面咱们给出一个例子:

  • 获取书籍列表
  • 获取珍藏书籍列表
  • 点击书籍可退出或者勾销珍藏
  • 通过代理实时重构 UI

第一步:创立两个模型

1、BookModel

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

class BookModel {
  
  static var _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/material.dart';
import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.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 addFaves(Book book) {_bookIds!.add(book.bookId);
    notifyListeners();}

  // 删除书籍
  void removeFaves(Book book) {_bookIds!.remove(book.bookId);
    notifyListeners();}
}

第二步:应用程序入口设置

return MultiProvider(
  providers: [Provider(create: (_) => BookModel()),
    ChangeNotifierProxyProvider<BookModel, BookManagerModel>(create: (_) => BookManagerModel(BookModel()),
      update: (_, bookModel, bookManagerModel) => BookManagerModel(bookModel),
    )
  ],
  child: MaterialApp(
    debugShowCheckedModeBanner: false,
    home: ChangeNotifierProxyProviderExample(),),
);

第三步:设置 BottomNavigationBar

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/change_notifier_proxy_provider_example/pages/page_a.dart';
import 'package:flutter_provider_example/change_notifier_proxy_provider_example/pages/page_b.dart';

class ChangeNotifierProxyProviderExample extends StatefulWidget {
  @override
  _ChangeNotifierProxyProviderExampleState createState() => _ChangeNotifierProxyProviderExampleState();
}

class _ChangeNotifierProxyProviderExampleState extends State<ChangeNotifierProxyProviderExample> {


  var _selectedIndex = 0;
  var _pages = [PageA(), PageB()];

  @override
  Widget build(BuildContext context) {
    return Scaffold(body: _pages[_selectedIndex],
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _selectedIndex,
        onTap: (index) {setState(() {_selectedIndex = index;});
        },
        items: [
          BottomNavigationBarItem(icon: Icon(Icons.book),
            label: "书籍列表"
          ),
          BottomNavigationBarItem(icon: Icon(Icons.favorite),
            label: "珍藏"
          )
        ],
      ),
    );
  }
}

第四步:书籍列表 UI 构建

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart';
import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart';
import 'package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_item.dart';
import 'package:provider/provider.dart';

class PageA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {var bookModel = Provider.of<BookModel>(context);
    
    return Scaffold(
      appBar: AppBar(title: Text("书籍列表"),
      ),
      body: ListView.builder(
        itemCount: bookModel.length,
        itemBuilder: (_, index) => BookItem(id: index + 1),
      ),
    );
  }
}

第五步:珍藏列表 UI 构建

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart';
import 'package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_item.dart';
import 'package:provider/provider.dart';

class PageB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {var bookManagerModel = Provider.of<BookManagerModel>(context);
    var bookCount = bookManagerModel.length;

    return Scaffold(
      appBar: AppBar(title: Text("珍藏列表"),
      ),
      body: ListView.builder(
        itemCount: bookCount,
        itemBuilder: (_, index) => BookItem(id: bookManagerModel.getByPosition(index).bookId),
      ),
    );
  }
}

其余辅助封装类

import 'package:flutter/material.dart';
import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_manager_model.dart';
import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart';
import 'package:provider/provider.dart';

class BookButton extends StatelessWidget {
  
  final Book book;
  
  BookButton({
    Key? key,
    required this.book
  }) : super(key: key);
  
  @override
  Widget build(BuildContext context) {var bookManagerModel = Provider.of<BookManagerModel>(context);
    
    return GestureDetector(onTap: bookManagerModel.books.contains(this.book)
          ?  () => bookManagerModel.removeFaves(this.book)
          :  () => bookManagerModel.addFaves(this.book),
      child: SizedBox(
        width: 100,
        height: 60,
        child: bookManagerModel.books.contains(this.book)
            ?  Icon(Icons.star, color: Colors.red,)
            :  Icon(Icons.star_border),
      ),
    );
  }
}
import 'package:flutter/material.dart';
import 'package:flutter_provider_example/change_notifier_proxy_provider_example/models/book_model.dart';
import 'package:flutter_provider_example/change_notifier_proxy_provider_example/widgets/book_button.dart';
import 'package:provider/provider.dart';

class BookItem extends StatelessWidget {

  final int id;

  BookItem({
    Key? key,
    required this.id
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {var bookModel = Provider.of<BookModel>(context);
    var book = bookModel.getById(id);

    return ListTile(
      leading: CircleAvatar(child: Text("${book.bookId}"),
      ),
      title: Text("${book.bookName}",
        style: TextStyle(color: Colors.black87),
      ),
      trailing: BookButton(book: book),
    );
  }
}

运行后果

ListenableProxyProvider

ListenableProxyProviderListenableProvider 的一个变体,然而在应用上和 ChangeNotifierProvider 成果惊人的统一,如果大家对 ListenableProxyProvider 有更深的了解,请分割我补充。

总结

Provider为咱们提供了十分多的提供者,总共有八种。但咱们比拟罕用的是ChangeNotifierProviderMultiProviderChangeNotifierProxyProvider,对于其余的提供者可依据本人的理论利用场景来。

正文完
 0