简体   繁体   中英

How to call into a flutter widget's state from the widget

I'm new to dart/flutter and having a little trouble getting my head around communication patterns.

One reoccurring problem is that I keep looking to expose a public method on a widget so it can be called by other widgets.

The problem is with stateful widgets. In these cases, I need to call down to the widgets state to do the actual work.

The problem is that the widget doesn't have a copy of the state. I have been saving a copy of the state in the widget but of course this throws a warning as it makes the widget mutable.

Let me give a specific example:

I have a specialised menu which can have a set of menu items. Each are stateful.

When the menu is closing it needs to iterate over the list of menu items that it owns and tell each one to hide (the menu items are not visually contained within the menu so hiding the menu doesn't work).

So the menu has the following code:

class Menu{
  closeMenu() {
    for (var menuItem in menuItems) {
      menuItem.close();
    }
  }

So that works fine, but of course in the MenuItem class I need to:

class MenuItem {
  MenuItemState state;

  close()
  {
    state.close();
  }

But of course having the state object stored In the MenuItem is a problem given that MenuItem is meant to be immutable. (It is only a warning so the code works, but its clearly not the intended design pattern).

I could do with seeing more of your code to get a better idea of how to solve your specific issue but it appears that the Flutter documentation will help you in some regard, specifically the section on Lifting state up :

In Flutter, it makes sense to keep the state above the widgets that use it.

Why? In declarative frameworks like Flutter, if you want to change the UI, you have to rebuild it.

…it's hard to imperatively change a widget from outside, by calling a method on it. And even if you could make this work, you would be fighting the framework instead of letting it help you.

It appears you're trying to fight the framework in your example and that you were correct to be apprehensive about adding public methods to your Widget s. What you need to do is something closer to what's detailed in the documentation (which details all of the new classes etc you'll see below). I've put a quick example together based on this and the use of Provider which makes this approach to state management easy. Here's a Google I/O talk from this year encouraging its use .

void main() {
  runApp(
    ChangeNotifierProvider(
      builder: (context) => MenuModel(),
      child: MyApp(),
    ),
  );
}


class MyApp extends StatelessWidget {

    …

    // call this when the menu is closed
    void onMyMenuClosed(BuildContext context) {
        var menuModel = getMyMenuModel(context);
        menuModel.hideMenuItems();
    }
}



class MenuModel extends ChangeNotifier {
  
  bool _displayItems = false;
  
  void hideMenuItems() {
    _displayItems = false;
    notifyListeners();
  }
  
  void showMenuItems() {
    _displayItems = true;
    notifyListeners();
  }
}

Calling hideMenuItems() makes a call to notifyListeners() that'll do just that; notify any listeners of a change which in turn prompts a rebuild of the Widget /s you wrap in a Consumer<MenuModel> Now, when the Widget that displays the menu is rebuilt, it just grabs the appropriate detail from the MenuModel class - the one source of truth for the state. This reduces the number of code paths you'd otherwise have to deal with to one and makes it far easier to see what's happening when you make further changes.

@override
  Widget build(BuildContext context) {
    return Consumer<MenuModel>(
      builder: (context, menuModel, child) {
        return menuModel._displayItems() ? MenuItemsWidget() : Container();
      },
    );
  }

I recommend you read the entire page on state management .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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