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 logic
  • ItemRefresherTo refresh a single list Item, see Selector in Provider
  • MultiProviderThe: Provider library makes it easy for widgets to use multiple providers or, if not, to nest multiple layers of providers
  • EventBus: event_bus:^1.1.1 An implementation of the event bus on pub
  • SmartRefresher: 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.

  • WidgetThe UI parts
  • ModelMVVM 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.