This is the 23rd day of my participation in the August More Text Challenge

preface

In the previous two articles, we had a basic understanding of the basic use of Redux and middleware. In this article, we will explore how to implement multi-component sharing of state data. Similar to providers, data can only be shared if the components are subordinate to the same parent.

Page structure

Our page structure is divided into three parts, corresponding to three components:

  • Shopping list: support to select or cancel the selection of a single item, indicating whether the item has been purchased;
  • New shopping item pop-up: Click ok to add new items to the list;
  • Bottom shopping list statistics: indicates the progress of the current shopping list.

The interface is shown in the figure below. The three components in the interface are independent of each other, but they all need the same state data, that is, the shopping list.

The interface building code is not posted here, you can download the source code here: Redux state management source code.

State is shared between components

The three components above are not really related, so state needs to be defined on the common parent component of the three components. There is a special component here which is the add shopping item dialog popup. The call method is:

void _openAddItemDialog(BuildContext context) {
  showDialog(context: context, builder: (context) => AddItemDialog());
}
Copy the code

The showDialog method here actually pops up a new page, which results in a dialog box that is not a child of any component other than the MaterialApp. Therefore, the definition of the state needs to be referred to the top level, which is placed above the MaterialApp, as shown below. This allows all components of the application to share state management.

class MainApp extends StatelessWidget {
  MainApp({Key? key}) : super(key: key);
  final store = Store<ShoppingListState>(
    shoppingListReducer,
    initialState: ShoppingListState.initial(),
  );
  @override
  Widget build(BuildContext context) {
    return StoreProvider(
      store: store,
      child: MaterialApp(
        title: 'Redux Counter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: ShoppingListHome(), builder: EasyLoading.init(), ), ); }}Copy the code

The core business

The actual business component is ShoppingListHome:

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Shopping list'),
      ),
      body: StoreConnector<ShoppingListState, List<Widget>>( converter: (store) => _ShoppListViewModel.build(store), builder: (context, items) => ListView.builder( itemBuilder: (context, index) => items[index], itemCount: items.length, ), ), bottomSheet: _BottomStatisticBar(), floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: () => _openAddItemDialog(context), ), ); }}Copy the code

Core business is divided into four parts:

  • The listing shows: it is used hereStoreConnectorPass the list of states directlyconverterConvert to a list of components to display, like thisListView.builderIt can be used directly.
  • Bottom statistics: Bottom statistics read the number of selected entries in the status list, also converted by Converter.
class _BottomStatisticBar extends StatelessWidget {
  const _BottomStatisticBar({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return StoreConnector<ShoppingListState, int>(
      builder: (context, selectedCount) => Container(
        height: 60,
        width: double.infinity,
        color: Colors.grey,
        padding: EdgeInsets.all(10),
        child: Text('has been completed$selectedCountItem '), ), converter: (store) => store.state.shoppingItems.where((item) => item.selected).length, ); }}Copy the code
  • Check box Select or deselect: You only need to passstoreInitiating a state changeToggleItemStateAction.
// ...Checkbox( value: item.selected, onChanged: (value) { store.dispatch(ToggleItemStateAction(item: item)); },),/ /...
Copy the code
  • Add new shopping item: here we passconverterConverts the state to a callback function that can be used to initiate a callback after the dialog box clicks the Add button.
class AddItemDialog extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return StoreConnector<ShoppingListState, OnItemAddedCallback>(
      converter: (store) {
        return (itemName) => store.dispatch(
              AddItemAction(
                  item: ShoppingItem(
                name: itemName,
                selected: false,))); }, builder: (context, callback) {return_AddItemDialogWidget(callback); }); }}Copy the code

As you can see from these businesses, Converter has a very flexible way of converting states into viewmodels based on what elements we need in the interface, thus simplifying our viewmodel construction.

The results

The result is shown in the figure below, where we find that if two shopping items have the same name, the checkbox selection will be problematic, when in fact, we can increase the number of items adding the same option. In the next article, we will make further improvements to this application, such as quantity display issues and offline storage, to achieve a relatively complete application example of Redux.

conclusion

This article shows how to build a Store on a top-level component that enables multiple components to share the state of Redux. Components in the same component tree can share state in this way. At the same time, the Converter parameters of StoreConnector can be flexibly used in actual development, which can simplify the construction of the interface.


This is a column about the entry and practice of Flutter. The corresponding source code is here: Entry and Practice of Flutter.

👍🏻 : feel the harvest please point a praise to encourage!

🌟 : Collect articles, easy to look back!

💬 : Comment exchange, mutual progress!