This is the 17th day of my participation in the August More text Challenge. For details, see: August More Text Challenge

preface

In some scenarios, our components are only concerned with part of the data for state management. Take, for example, the dynamic details interface below.There are two buttons at the bottom:

  • Like button: After clicking the button, the number of likes will be increased, and the number of likes will be cancelled and reduced.
  • Favorites button: Click the button to increase favorites, click again to cancel favorites and reduce favorites.

In this case, if we use the Provider’s watch method to monitor the changes of state objects, every time we click any button, the whole interface will be refreshed, which will inevitably lead to performance loss. So is there a way that the interface can only listen for data that’s related to it? In this article, we show how to implement local changes in the listening state.

Provider state local monitoring

The Provider provides the method R select

(R Function(T value) Selector) to implement local changes in state data. This method takes a selector Function argument of type T and returns a value of type R. That is, listen only for R parts of the status data, not all of the data. Components that depend on the data are notified to refresh only when the value of type R changes, and other components that do not depend on the value are not refreshed.
,>

Details page modification

Let’s create a new DynamicDetailModel class for the detail page state management, with three state data:

  • _currentDynamic: Current dynamic detail data, providing the GET property for the detail component to access;
  • _praiseCount: Number of likes, providing the GET property for the like button component to access;
  • _favorCount: Favorcount. Provides the GET property for the favorite-button component to access.

UpdatePraiseCount updateFavorCount updateFavorCount updateFavorCount updateFavorCount updatePraiseCount updatePraiseCount updateFavorCount updateFavorCount

  • UpdatePraiseCount: +1 if no likes, -1 if yes.
  • UpdateFavorCount: +1 if no favorites, -1 if favorites.

The detail page is divided into three large modules wrapped in a DynamicDetailWrapper so that the three modules give themselves a self-managing state. Two of the buttons are Positioned at the bottom of the page using a Stack + c-module.

class DynamicDetailWrapper extends StatelessWidget {
  final String id;
  const DynamicDetailWrapper({Key? key, required this.id}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<DynamicDetailModel>(
        create: (context) => DynamicDetailModel(),
        child: Stack(
          children: [
            _DynamicDetailPage(id),
            Positioned(
                bottom: 0,
                height: 60, width: MediaQuery.of(context).size.width, child: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _PraiseButton(), _FavorButton(), ], )) ], )); }}Copy the code

Next, each of the three components listens for local data on the state using the SELECT method.

// _DynamicDetailPage
Widget build(BuildContext context) {
    print('_DynamicDetailPage'); DynamicEntity? currentDynamic = context.select<DynamicDetailModel, DynamicEntity? >( (dynamicDetail) => dynamicDetail.currentDynamic, );return Scaffold(
      appBar: AppBar(
        title: Text('Dynamic details'),
        brightness: Brightness.dark,
      ),
      body: currentDynamic == null
          ? Center(
              child: Text('Just a moment, please... '),
            )
          : _getDetailWidget(currentDynamic),
    );
  }

  / /... Omit other code
}

// _PraiseButton
class _PraiseButton extends StatelessWidget {
  const _PraiseButton({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    print('PraiseButton');
    return Container(
      alignment: Alignment.center,
      color: Colors.blue,
      child: TextButton(
        onPressed: () {
          context.read<DynamicDetailModel>().updatePraiseCount();
        },
        child: Text(
          context.select<DynamicDetailModel, String>(
            (DynamicDetailModel dyanmicDetail) {
              return 'thumb up' + dyanmicDetail.praiseCount.toString();
            },
          ),
          style: TextStyle(color: Colors.white),
        ),
        style: ButtonStyle(
            minimumSize: MaterialStateProperty.resolveWith((states) =>
                Size((MediaQuery.of(context).size.width / 2 - 1), 60))))); }}// The _PraiseButton is similar to the _PraiseButton
Copy the code

All three components use the select method instead of the Watch method on the dependent objects, and each component prints its component name in the build method to verify that it only refreshes when the state data for that part of its dependency changes.

The “Like” button and “Save” button are both set to mediaquery.of (context).size.width / 2. As a result, when the “Save” button is clicked at the same time, the “Like” button is clicked at the same time. This is supposed to be a floating-point precision problem, resulting in two button areas being covered, which triggers both button click events.

The results

Now looking at the results of the run, against the emulator interface action and the console log, you can see that when any button is clicked, only the button itself changes, and the unrelated components do not call the build method.

From the console print, clicking the button only calls the build method of the button, and the other components are not rebuilt, which does achieve a partial refresh effect.

conclusion

In this paper, the dynamic detail page is separated into three components that depend on different state data, and the select method provided by the Provider is used to realize the state local monitoring. Through this example, it actually gives us the recommended practice of state management, that is, as far as possible to use the select method to do local monitoring, so that in the future state management after the extension of the state management can also do the state management of different parts of the interface update separation, to achieve data only drive the associated interface refresh, so as to achieve better performance.


I am an island code farmer, wechat public account with the same name, this is the entry and combat column Flutter, the corresponding source code please see here: Entry and combat column source code.

👍🏻 : feel a harvest please point to encourage!

🌟 : Collect articles, convenient to read back!

💬 : Comment exchange, mutual progress!