Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

flutter

dart

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,
    );
  }
}
like image 616
Tom Ryan Avatar asked Sep 06 '25 03:09

Tom Ryan


2 Answers

You can check the package provider, using this it is easy to manage UI in every page,

Adding some articles

  1. https://www.raywenderlich.com/6373413-state-management-with-provider
  2. https://medium.com/flutter-community/making-sense-all-of-those-flutter-providers-e842e18f45dd
  3. https://medium.com/flutterpub/provider-state-management-in-flutter-d453e73537c5
like image 173
Afinas EM Avatar answered Sep 07 '25 21:09

Afinas EM


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.

like image 41
Hamza A.Malik Avatar answered Sep 07 '25 21:09

Hamza A.Malik