繁体   English   中英

Flutter:从单个父窗口小部件控制多个子窗口小部件的状态

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

我有一个Menu状态小部件,它是返回容器的MenuIcon子小部件的多个实例的父小部件。 用户可以单独点击MenuIcon小部件,以便在active布尔为真时突出显示,而不是在不为真时突出显示。 现在这一切都在MenuIcon class中进行控制,考虑到每个图标的 state 将在应用程序中发挥重要作用,因此我希望能够管理它。在父小部件中。 但我不确定如何执行此操作,之前链接的教程只有一个父小部件 -> 一个子小部件,因此从父小部件管理 state 非常容易。 但在这种情况下,由于父小部件拥有多个子小部件实例,每个实例都有自己的活动/非活动 state 我想不出一个简单的方法来做到这一点。

目前我的层次结构如下所示: 我打算让 MenuIcon 简单地管理自己的 animation,让Menu class 管理每个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,
    );
  }
}

另一个可行的选项是 pub.dev 上提供的 Async_Redux package

它的样板代码更少,并且可以非常轻松地实现 state。 您可以配置 ReduxActions 以从您的操作中操作 state。 整个应用程序对 state 操作做出反应。 然后使用 ReduxSelector 显示每个屏幕上的特定更改。

探索:

https://pub.dev/packages/async_redux

https://pub.dev/packages/provider_for_redux

有大量文档可用。

https://www.fireship.io也有一些关于 state 管理的有趣内容。

您可以使用bloc package 来处理子树的 state(在这种情况下,菜单作为根)

这里有一个例子:

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 每次 state 更改时触发

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM