TransWikia.com

Flutter: Controlling the states of multiple child widgets from a single parent widget

Stack Overflow Asked by Tom Ryan on January 1, 2022

I have a Menu Stateful Widget which is a Parent Widget to multiple instances of a MenuIcon child widget which returns a container. The user is able to tap the MenuIcon widgets individually so they highlight when an active bool is true and don’t when it is not. Right now this is all controlled within the MenuIcon class, which from reading here is not the best approach considering that the state of each of the Icons are going to play an important role in the app, and so I want to be able to manage it within the parent widgets. But I’m not exactly sure how to do this, the tutorial linked earlier has simply one parent widget -> one child widget and so it is very easy to manage the state from the parent widget. But in this case since the parent widget holds multiple instances of the child widgets each with their own active/inactive state I can’t think of an easy way to do this.

Currently my hierarchy looks like this:

I intend for the MenuIcons to simply manage their own animation, the Menu class to manage the states of each MenuIcon, and the HomePage to hold a list of every MenuIcon that is currently active.

The code for the Menu and MenuIcon classes are as follows:

//----------------- Menu ------------------------------------------
//These classes control the scrollable menu that appears when the
//dropdown is pressed

class Menu extends StatefulWidget {
  final String _category;

  Menu(this._category);

  @override
  _MenuState createState() => _MenuState(category: this._category);

}

class _MenuState extends State<Menu> {
  List<Offer> _offersList = createOffers();
  String category;

  _MenuState({@required this.category});

  //build method
  Widget build(BuildContext context) {
    final _menuItems = List<Container>();
    //builds an item widget if the category is correct.
    for (int i = 0; i < _offersList.length; i++) {
      if (this.category == _offersList[i].category) {
        // adds a container containing the MenuIcon object, with the offer
        // in question passed through _offersList[i]
        _menuItems.add(Container(
            child: MenuIcon(
                offer: _offersList[i],
        )));
      }
    }

    //This particular widget tree allows to have a horizontal scrolling
    //menu within a fixed widget
    if (_menuItems.length > 0) {
      return SizedBox(
        child: Row(
          children: [
            Expanded(
                child: ListView(
                  children: _menuItems,
                  scrollDirection: Axis.horizontal,
            )),
          ],
        ),
        height: 154,
     );
   } else {
      return Row(
        children: [
          Container(
            child: Text(
              'Sorry! There are no offers available for this category',
              textAlign: TextAlign.center,
              style: TextStyle(
                fontSize: 14.0,
              ),
            ),
            padding: EdgeInsets.only(left: 12),
          ),
        ],
      );
    }
  }
}

//------------------- MenuIcon class -----------------------------

class MenuIcon extends StatefulWidget {
  Offer offer;

  MenuIcon({@required this.offer});

  @override
  _MenuIconState createState() => _MenuIconState(this.offer);
}

class _MenuIconState extends State<MenuIcon> {
  Offer _offer;
  bool active;

  _MenuIconState(this._offer) {
    this.active = false;
  }

  void _handleTap() {
    setState(() {
      active = !active;
    });
  }

  Widget build(BuildContext context) {
    //print('icon rebuilt with active = $active');
    var label = _offer.discount.toString();
    return Container(
      child: GestureDetector(
        child: Column(
          children: [
            _offer.image,
            Text(
               '$label% off',
               style: TextStyle(
                  color: Colors.red,
                  fontSize: 14.0,
               ),
            ),
          ],
          mainAxisAlignment: MainAxisAlignment.center,
        ),
        onTap: _handleTap,
      ),
      //changes the colour if the icon is selected or not
      color: active ? Colors.yellow : Colors.white,
    );
  }
}

3 Answers

Another viable option is the Async_Redux package available on pub.dev

It has way less boilerplate code and allows very easy state implementation. You can configure ReduxActions to manipulate state from your actions. The whole app reacts to the state manipulations. ReduxSelectors are then used to show the specific changes on every screen.

Do explore:

https://pub.dev/packages/async_redux

https://pub.dev/packages/provider_for_redux

Extensive documentation is available.

https://www.fireship.io has some interesting content as well regarding state management.

Answered by Hamza A.Malik on January 1, 2022

You can use bloc package to handle the state of a subtree (in this case with Menu as root)

Here an example:

class MenuBloc extends Bloc<int, int> {
  MenuBloc() : super(0);

  @override
  Stream<int> mapEventToState(int event) async* {
    yield event;
}


class Menu extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider<MenuBloc>(
      create: (context) => MenuBloc(),
      child: Column(
        children: [
          MenuIcon(menuIndex 0),
          MenuIcon(menuIndex: 1),
          MenuIcon(menuIndex: 2),
        ],
      ),
    );
  }
}

class MenuIcon extends StatelessWidget {
  final int menuIndex;

  const MenuIcon({Key key, this.menuIndex}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => _updateState(context),
      child:
          BlocBuilder<MenuBloc, int>(builder: (context, state) {
            final isActive = menuIndex == state;
        return Container();
      }),
    );
  }

  void _updateState(BuildContext context) {
    BlocProvider.of<MenuBloc>(context).add(menuIndex);
  }
}

If you need to do something anywhere in the subtree when you click on a MenuIcon, you can use a BlocListener that triggers every time the state changes

Answered by Luca Oropallo on January 1, 2022

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP