当你了解了Provider,并打算用到你的项目中,这篇文章能够帮你快速进入实战开发。git
Demo仓库地址 入口:main_provider.dart -- 代码应用(简易社区)github
前面的笔记介绍了Provider的简易使用Demo和源码。json
从Demo到项目落地,有个过程。bash
数据与UI的交互自己,说简单也简单,说复杂也复杂。不一样的人写出来,必然是不尽相同的。光状态管理就多种方式。markdown
咱们得有个代码框架,减小学习成本,减小后期维护成本。接下来就是基于Provider,同时结合项目体会,写了一个代码框架。框架
假定咱们有一个社区的应用。咱们有两个页面less
数据处理与UI分离。 这是移动开发遵照的框架规则,好比MVP,MVVM等等。 async
ChangNotifier
:实现Listenable接口,观察者模式实现。ChangNotifierProvider
:ChangNotifier做为其参数,ChangNotifier.notifyListeners触发其刷新逻辑ItemRefresher
:列表Item单个刷新功能,参考 Provider中的SelectorMultiProvider
:Provider库中,方便Widget使用多个Provider,不用的话,就嵌套多层ProviderEventBus
:event_bus:^1.1.1 pub上事件总线的某个实现SmartRefresher
:上拉下拉刷新加载组件首先咱们要有个实体类定义帖子。ide
class PostBean {
int id;//惟一标识
String content; // 正文
bool isLike;// 点赞与否
复制代码
咱们从客户端的视角来看,须要一个Server。这里咱们mock下。Client与Server用JSON交互。Server应该具有如下接口:获取列表
,获取详情
,请求点赞
。oop
class PostServer{ ///获取列表 //返回JSON列表,可转换为PostBean列表 Future<List<Map<String, dynamic>>> loadPosts() async ///获取详情 //返回JSON,可转换为PostBean对象 Future<Map<String, dynamic>> getPostDetail(int id) async ///请求点赞 //返回是否操做成功 {"success": true} Future<Map<String, dynamic>> like(int id, bool toLike) async } 复制代码
EventBus eventBus = EventBus(); class BaseEvent { void fire() { eventBus.fire(this); } } class PostLikeEvent extends BaseEvent with ChangeNotifier{ int id; bool isLike; PostLikeEvent(this.id, this.isLike); } 复制代码
咱们有两个页面,列表页和详情页。 页面分红两个组件:Widget和Model。
Widget
UI部分Model
能够理解成MVVM的ViewModel或者MVP的Presenter。负责数据的获取和处理。class PostListModel with ChangeNotifier { var posts = new List<PostBean>(); ///smartRefresher的刷新控制器 RefreshController refreshController = RefreshController(); ///解除事件监听方法 VoidCallback _eventDispose; /// 单个刷新的ChangeNotifier PostListItemListenable itemListenable; PostListModel() { itemListenable = new PostListItemListenable(); } ///订阅PostLikeEvent void subscribePostLike() { StreamSubscription subscription = eventBus.on<PostLikeEvent>().listen((event) { ///拿到event,更新下当前页面对应post的isLike状态 posts?.firstWhere((post) => post.id == event.id, orElse: () => null) ?.isLike = event.isLike; }); _eventDispose = () => subscription.cancel(); } ///加载数据 void loadData({VoidCallback callback}) { PostServer.instance().loadPosts().then((jsonList) { posts = jsonList.map((json) => PostBean.map(json)).toList(); notifyListeners(); callback.call(); }).catchError((e) => print(e)); } ///下拉刷新,数据获取到后,通知smartRefresher void refresh() { loadData(callback: () => refreshController.refreshCompleted()); } ///ChangeNotifier的 解除监听方法。 @override void dispose() { super.dispose(); _eventDispose?.call(); } } 复制代码
class PostListItemListenable with ChangeNotifier {
int id;
}
复制代码
class PostListWidget extends StatefulWidget { .....省略部分代码 } class _PostListWidgetState extends State<PostListWidget> { PostListModel _listModel; @override void initState() { super.initState(); ///初始化构建Model,同时加载数据 _listModel = PostListModel()..loadData(); } @override Widget build(BuildContext context) { return Scaffold( ..... ///多Provider使用,提早设置好 body: MultiProvider( providers: [ ChangeNotifierProvider.value(value: _listModel), ChangeNotifierProvider.value(value: _listModel.itemListenable), ], child: Consumer<PostListModel>( builder: (context, model, child) { Widget child = ListView.separated( itemBuilder: (itemContext, index) { return _buildListItem(itemContext, model.posts[index]); },....); ///设置 SmartRefresher: refreshController,onRefresh。 ///onRefresh回调。widget不处理数据相关的回调,而是交给model处理 return SmartRefresher( controller: model.refreshController, enablePullDown: true, enablePullUp: true, onRefresh: () => model.refresh(), child: child, ); }, ), ), ); } Widget _buildListItem(BuildContext context, PostBean post) { ///ItemRefresher 自定义的列表item刷新利器 return ItemRefresher<PostListItemListenable, PostBean>( value: post, shouldRebuild: (itemListenable, value) => (itemListenable.id != null && itemListenable.id == value.id), builder: (context, value, child) { return PostItemWidget( post: value, click: _skipPostDetail, ); }, ); } _skipPostDetail(BuildContext context, PostBean post) { Navigator.of(context).push(MaterialPageRoute( builder: (context) => PostDetailWidget(id: post.id), )); } 复制代码
class PostItemWidget extends StatelessWidget { final PostBean post; ///点击回调处理 final void Function(BuildContext context, PostBean post) click; const PostItemWidget({Key key, this.post, this.click}) : super(key: key); @override Widget build(BuildContext context) { return GestureDetector( onTap: () => click?.call(context, post), child: Container( height: 80, child: Row( children: <Widget>[ Expanded( child: Text( "${post?.content}", maxLines: 2, overflow: TextOverflow.ellipsis, ), ), Container( width: 50, child: Icon( Icons.favorite, color: (post?.isLike ?? false) ? Colors.red : Colors.grey, ), ), ], ), )); } } 复制代码
详情页结构与列表页类似。咱们单独点出来 点赞的处理部分。
class PostDetailModel with ChangeNotifier { PostBean post; initPost(int id) { PostServer.instance().getPostDetail(id).then((json) { post = PostBean.map(json); notifyListeners(); }).catchError((e) => print(e)); } likePost(bool toLike) { PostServer.instance().like(post.id, toLike).then((result) { if (result["success"]) { post.isLike = toLike; ///EventBus 全局事件通知 PostLikeEvent(post.id, toLike).fire(); } ///通知PostDetailWidget刷新 notifyListeners(); }).catchError((e) => print(e)); } } 复制代码
这篇文章中涉及到了EventBus,MultiProvider,Selector等等,咱们后续会分析分析。