當你瞭解了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等等,咱們後續會分析分析。