Flutter:从单个父Widget控制多个子Widget的状态

4
我有一个Menu Stateful Widget,它是多个MenuIcon子widget的父widget,每个MenuIcon返回一个容器。用户可以单独点击MenuIcon widget,当active bool为true时突出显示,否则不突出显示。目前,这一切都在MenuIcon类中控制,但从here阅读来看,这并不是最好的方法,因为每个图标的状态在应用程序中将起重要作用,所以我希望能够在父widget中管理它。但我不确定如何做到这一点,之前链接的教程只有一个父widget->一个子widget,因此很容易从父widget中管理状态。但在这种情况下,由于父widget包含多个具有自己的活动/非活动状态的子widget实例,我想不出简单的方法来做到这一点。

目前我的层次结构如下: 我希望MenuIcons仅管理它们自己的动画,Menu类来管理每个MenuIcon的状态,而HomePage则保存当前活动的每个MenuIcon的列表。

MenuMenuIcon类的代码如下:

//----------------- 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个回答


1
另一个可行的选择是在pub.dev上提供的Async_Redux包。
它具有更少的样板代码,并允许非常容易的状态实现。您可以配置ReduxActions来从操作中操纵状态。整个应用程序会对状态操纵做出反应。然后使用ReduxSelectors显示每个屏幕上的特定更改。
请探索:

https://pub.dev/packages/async_redux

https://pub.dev/packages/provider_for_redux

有详细的文档可供参考。

https://www.fireship.io 还提供了一些有关状态管理的有趣内容。


0
你可以使用 bloc 包来处理子树的状态(在这种情况下,以菜单为根)。
这里是一个例子:
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);
  }
}

如果您需要在单击MenuIcon时在子树的任何位置执行某些操作,则可以使用BlocListener,该监听器每次状态更改时触发。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接