This is the 10th day of my participation in the August More text Challenge. For details, see: August More Text Challenge
preface
In the last few articles we have written about the mechanism of state management and the plug-in for state management. In the next few articles we will use the official recommended Provider to modify the old code. You will see a big difference before and after the modification. As an example of Provider, I translated the shopping cart example article entitled “Introduction to Flutter and Practice (40)”.
The Provider profile
The latest version of Provider is 5.0.0. The component tree can share status data in the following ways:
Provider (
create: (_) => Model(),
child: someWidget(),
);
Copy the code
The Provider class itself does not automatically update its children when the state changes, so it is more common to use subclasses:
ListenableProvider
: Listener implementedListenable
And expose it to the child components. When an event is triggered, the child component is notified that the dependency has changed and the rebuild is implemented.ChangeNotifierProvider
One of the most commonly used ways isListenableProvider
The subclass. Listening is implementedChangeNotifier
Interface to an object when the object is callednotifyListeners
, all listening components are notified to update the component.ValueListenableProvider
: Listener implementedValueListenable
Object of the interface. When the object changes, its child components are updated.StreamProvider
: Listens on a Stream object and exposes its contents to a child component. Typically, a large amount of content, such as battery level monitoring, Firebase queries, and so on, is streamed to a component.
If an object is shared by multiple components, the following methods can be used:
// An object shared by multiple components
MyChangeNotifier variable;
ChangeNotifierProvider.value(
value: variable,
child: ...
)
Copy the code
There are three ways to use state data in widgets:
- use
context.read<T>()
Method: This method returns a status data object of type T, but does not listen for changes to the object. - use
context.watch<T>()
Method: This method returns a t-type state data object and listens for changes to it, for situations that need to be updated based on status. - use
context.select<T,R>(R cb(T value))
Method: Returns an object of type R in a T objectWidget
Listen only for part of the data on the status object.
For more details, please refer to the official documentation of Provider, which will be covered in the following chapters.
The code analysis
In the previous chapter, we introduced the management of a dynamic module, including the entire CRUD process. Details can be found in columns XII through XXVII, Introduction to Flutter and Combat. First, let’s change the code for the list. If we go back to the previous code, we will see why developers who use setState directly to update the interface are rated as “** **”.
Before the code is very messy, the first is in the list including the add, edit, delete the callback code, is to want to if the business is a bit more complex, is not the callback to full screen fly! The second is business code and UI code mixed, one is the code smelly and long – commonly known as 💩 code, the other is business code reuse reduced. For example, in other places, we may also use the dynamic business of adding, changing, deleting and checking, can not copy, paste again?
Code transformation
Now let’s use a Provider to separate the business from the UI. The business-related code is put into state management, and only the interface-related code is handled on the UI side. Dart = dynamic_model.dart = dynamic_model.dart = dynamic_model.dart
- Tabular data: Use one
List<DynamicEntity>
Object stores list data and defaults to an empty array. - Pagination data: Current page number
_currentPage
, fixed the page size to 20. - Refresh method:
refresh
, set the current page number to 1, and request the first page data again. - Loading method:
load
, add the current page number by 1, and request the data on page N. - Get paging data: Requests dynamic data based on the current page and page size, and updates the list data.
- The reserved
delete
,add
和update
Method for subsequent deletes, additions, and updates.
The code for the entire DynamicModel class is as follows. The key point here is to mix DynamicModel with ChangeNotifer’s features using the with ChangeNotifier. So that ChangeNotifierProvider can add listeners to it and notify state-dependent children of updates when notiferListeners are called.
class DynamicModel with ChangeNotifier {
List<DynamicEntity> _dynamics = [];
int _currentPage = 1;
final int _pageSize = 20;
List<DynamicEntity> get dynamics => _dynamics;
void refresh() {
_currentPage = 1;
_requestNewItems();
}
void load() {
_currentPage += 1;
_requestNewItems();
}
void _requestNewItems() async {
var response = await DynamicService.list(_currentPage, _pageSize);
if(response ! =null && response.statusCode == 200) {
List<dynamic> _jsonItems = response.data;
List<DynamicEntity> _newItems =
_jsonItems.map((json) => DynamicEntity.fromJson(json)).toList();
if (_currentPage == 1) {
_dynamics = _newItems;
} else {
_dynamics += _newItems;
}
}
notifyListeners();
}
void removeWithId(String id) {}
void add(DynamicEntity newDynamic) {}
void update() {}
}
Copy the code
The next step is to use providers to provide state management for dynamic modules. As described in the previous chapters, providers need to be superior to components in order to provide state sharing for sub-components, so there are two ways to do this.
- In the build
DynamicPage
List pageapp.dart
Lt.DynamicPage
As aProvider
At a lower level. As shown below, the disadvantage of this approach is that this is the home page, and if the code of each module is directed to each other here, app.dart will become bloated and highly coupled.
@override
void initState() {
super.initState();
_homeWidgets = [
ChangeNotifierProvider<DynamicModel>(
create: (context) => DynamicModel(),
child: DynamicPage(),
),
MessagePage(),
CategoryPage(),
MineSliverPage(),
];
}
Copy the code
- The use of a
Widget
The parcelDynamicPage
As well asProvider
To reduce the coupling of the code and avoidapp.dart
The code in
class DynamicWrapper extends StatelessWidget {
const DynamicWrapper({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
returnChangeNotifierProvider( create: (context) => DynamicModel(), child: DynamicPage(), ); }}Copy the code
After that, DynamicPage was transformed. First, DynamicPage was changed from StatefulWidget to StatelessWidget, and then relevant business code was removed. Finally, get the data required by the interface from the Provider in the build method, or call the corresponding method. The modified DynamicPage is very clean, as shown below:
class DynamicPage extends StatelessWidget {
DynamicPage({Key key}) : super(key: key);
final EasyRefreshController _refreshController = EasyRefreshController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('dynamic', style: Theme.of(context).textTheme.headline4),
actions: [
IconButton(
icon: Icon(Icons.add),
onPressed: () {
RouterManager.router
.navigateTo(context, RouterManager.dynamicAddPath);
}),
],
brightness: Brightness.dark,
),
body: EasyRefresh(
controller: _refreshController,
firstRefresh: true,
onRefresh: () async {
context.read<DynamicModel>().refresh();
},
onLoad: () async {
context.read<DynamicModel>().load();
},
child: ListView.builder(
itemCount: context.watch<DynamicModel>().dynamics.length,
itemBuilder: (context, index) {
return DynamicItem(context.watch<DynamicModel>().dynamics[index],
(Stringid) { context.read<DynamicModel>().removeWithId(id); }); },),),); }}Copy the code
In listView. builder we use the contxt.watch
method to get the latest dynamic list, so that we can refresh the interface when the list data changes. For calling methods, we use the context.read
method because we don’t need to listen for state changes. Run it and find the same effect as before, the transformation is complete.
Comparison before and after renovation
Let’s compare the DynamicPage code before and after modification, as shown in the figure below (the old code is on the left). As you can see, most of the code has been removed. In fact, the original code was 120 lines, but now the code is only 40 lines, a 2/3 reduction!
Of course, the code is reduced because the business code is taken away, but the business code itself is reusable. In the next article, we’ll take a look at how the Provider can further improve code reuse and simplify the page code after the deleters, additions, and edits are complete.
conclusion
The biggest benefit of Provider state management is the separation of UI layer and business layer code, which simplifies UI layer code and improves the reusability of business code. The Provider’s local refresh feature can also improve the performance of interface rendering.
I’m an island user with the same name on wechat. This is a column on introduction and practice of Flutter.
👍🏻 : feel a harvest please point to encourage!
🌟 : Collect articles, convenient to read back!
💬 : Comment exchange, mutual progress!