preface
Once you know about providers and plan to use them in your projects, this article will help you get started quickly.
Demo Repository address entry: main_provider. Dart — Code Application (simple community)
Why have a code framework
The previous notes introduced the easy use Demo and source code for the Provider.
From Demo to project landing, there is a process.
The interaction between the data and the UI itself is both simple and complex. Different people write it, it must be different. There are many ways to manage the optical state.
- The data processing
- Dispatching events
- State management – within/across components/across pages
We need to have a code framework that reduces learning costs, reduces maintenance costs. Next is based on Provider, combined with the experience of the project, to write a code framework.
The business scenario
Let’s say we have a community application. We have two pages
- Post list page, showing part of the post body/like or not. Click the post to enter the details page of the post, but can not like/unlike.
- On the detail page of the post, the main content of the post/whether the post is liked or not is displayed. You can upvote/unupvote, and when you return to the list page, you need to display the latest upvote status.
rendering
Demand analysis
- 1. Go to the list page, request the server list, and display.
- 2. Click an item in the list to transfer the ID to the details page.
- 3. Go to the details page, request server details based on the ID, and display.
- 4. Click “Like” on the details page, ask server, update the “like” status on the details page, and notify the change of the “like” status globally.
- 5. The list page receives notification to change the status of likes of corresponding posts.
Framework idea
Data processing is separated from UI. These are the framework rules that mobile development follows, like MVP, MVVM, etc.
- The ChangeNotifierProvider implements the InteritedElement, Element section.
- MyWidget is a page component that needs to be drawn ourselves
- MyModel is where the data is processed.
- When Notify, Model(Subject) is not passed, but Inherited is Inherited.
The main technical points
ChangNotifier
: Implement Listenable interface, observer mode implementation.ChangNotifierProvider
: ChangNotifier as its argument, ChangNotifier notifyListeners trigger its refresh logicItemRefresher
To refresh a single list Item, see Selector in ProviderMultiProvider
The: Provider library makes it easy for widgets to use multiple providers or, if not, to nest multiple layers of providersEventBus
: event_bus:^1.1.1 An implementation of the event bus on pubSmartRefresher
: Pull-up refresh load components
Framework implementations
Post entity Class
First we need to have an entity class that defines the post.
class PostBean { int id; // Unique identifier String content; // text bool isLike; // Thumbs up or notCopy the code
Mock Server
We need a Server from a client perspective. Here we mock. The Client and Server interact with each other using JSON. The Server should have the following interfaces: get lists, get details, request likes.
Class PostServer{/// get list // return JSON list, Future<List<Map<String, dynamic>>> loadPosts() async Future<Map<String, dynamic>> getPostDetail(int ID) async // request a like // return whether the operation succeeded {"success": true}
Future<Map<String, dynamic>> like(int id, bool toLike) async
}
Copy the code
Event bus
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);
}
Copy the code
Build the page
We have two pages, the list page and the details page. The page is split into two components: Widget and Model.
Widget
The UI partsModel
MVVM ViewModel or MVP Presenter. Responsible for data acquisition and processing.
List of pp.
PostListModel
class PostListModel with ChangeNotifier { var posts = new List<PostBean>(); //smartRefresher RefreshController RefreshController = RefreshController(); // Remove event listener VoidCallback _eventDispose; // A single refresh of ChangeNotifier PostListItemListenable itemListenable;PostListModel() { itemListenable = new PostListItemListenable(); } /// subscribe PostLikeEvent voidsubscribePostLike() {StreamSubscription = eventbus. on<PostLikeEvent>(). Update the isLike status posts corresponding to the current page? .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)); } /// pull down refresh and notify smartRefresher void when data is retrievedrefresh() { loadData(callback: () => refreshController.refreshCompleted()); } ///ChangeNotifier unlistener method. @override voiddispose() {
super.dispose();
_eventDispose?.call();
}
}
Copy the code
PostListItemListenable
class PostListItemListenable with ChangeNotifier {
int id;
}
Copy the code
PostListWidget
class PostListWidget extends StatefulWidget { ..... } class _PostListWidgetState extends State<PostListWidget> {PostListModel _listModel; @override voidinitState() { super.initState(); _listModel = PostListModel()... loadData(); } @override Widget build(BuildContext context) {returnScaffold( ..... / / / the Provider to use, set up ahead of the body: MultiProvider (will: [ChangeNotifierProvider. Value (the 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]); },...). ; /// Set SmartRefresher: refreshController, onRefresh. / / / onRefresh callback. Instead of handling data-related callbacks, the widget hands them over to the Modelreturn SmartRefresher(
controller: model.refreshController,
enablePullDown: true.enablePullUp: true, onRefresh: () => model.refresh(), child: child, ); },),),); } Widget _buildListItem(BuildContext Context, PostBean POST) {///ItemRefresher is a custom list item refresh toolreturnItemRefresher<PostListItemListenable, PostBean>( value: post, shouldRebuild: (itemListenable, value) => (itemListenable.id ! = null && itemListenable.id == value.id), builder: (context, value, child) {returnPostItemWidget( post: value, click: _skipPostDetail, ); }); } _skipPostDetail(BuildContext context, PostBean post) { Navigator.of(context).push(MaterialPageRoute( builder: (context) => PostDetailWidget(id: post.id), )); }Copy the code
PostItemWidget
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) {returnGestureDetector( 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, ), ), ], ), )); }}Copy the code
Details page and likes
The detail page structure is similar to the list page. Let’s separate out the “like” processing.
PostDetailModel
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; PostLikeEvent(post.id, toLike).fire(); } /// Notify the PostDetailWidget to refresh the notifyListeners(); }).catchError((e) =>print(e)); }}Copy the code
This article covers EventBus,MultiProvider, Selector, and so on, and we’ll look at that later.