0

I have a parent widget called (for this snippet) MainLayout, and a child widget called ChildWidget. I want to add an IconButton in the actions of the AppBar of the parent widget in the child after executed its build method. The actions shown in the AppBar of the parent are stored in its state.

I have a problem with the approach I'm using to do that: The callback triggered after the build method of child widget to set the state of its parent widget doesn't trigger the build method of its parent, but the state (of the parent) has changed. As a result, the search icon is not mounted in the actions of the AppBar of the MainLayout.

Why is working on that way and how to solve it?

The code below is an example of how my child and parent widget looks like:

child_widget.dart:

class ChildWidget extends StatelessWidget {
  // Add a search button in the AppBar of MainLayout widget.
  void _updateAppBar(BuildContext context) async {
    InheritedMainLayout.of(context).appBar.updateActions(<Widget>[
            IconButton(
              icon: Icon(Icons.search),
              onPressed: () => showSearch(
                context: context,
                delegate: SearchPageDelegate(),
              ),
            ),
          ]);
  }

  @override
  Widget build(BuildContext context) {
    _updateAppBar(context);
    return Container(
      width: 100,
      height: 100,
      color: Colors.blue,
    );
  }
}

parent_widget.dart:

class ParentWidget extends StatefulWidget {
  @override
  _ParentWidgetState createState() => new _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  // This is the method that is passed as a parameter to the ChildWidget to update the actions of the AppBar.
  void _updateAppBarActions(List<Widget> actions) =>
    setState(() => _appBarActions = actions);

  @override
  Widget build(BuildContext context) {
    return InheritedMainLayout( // The inherited widget used to use the _updateAppBarActions method in the child widget
      appBar: new AppBarConfig(
        actions: _appBarActions,
      ),
      child: Scaffold(
        appBar: AppBar(
          title: Text("Title of the App"),
          actions: _appBarActions, // Actions showed in the AppBar are from the state, _appBarActions
        ),
        body: Padding(
          padding: EdgeInsets.all(20.0),
          child: _currentChild, // Here is loaded the builded widget called ChildWidget.
        ),
        drawer: AppDrawer(
          items: _drawerItems,
          onTap: _onChangePage,
        ),
      ),
    );
  }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Mikhael
  • 1
  • 1

1 Answers1

0

You may be seeing this issue because you cannot call setState during the build process. You can use the addPostFrameCallback method to call your callback after the build process. See the answer to this question, Calling setState() during build without user interaction, for an example.

Jacob McGowan
  • 592
  • 4
  • 10
  • I have been seen that post before. The problem is that I don't know where to call the addPostFrameCallback to work in a way that indicates there (without creating an infinite loop avoiding using that method in the build method of the child widget). For my case (to avoid the infinite loop) I saw necessary to use a property that I called _isMounted to know if the actions have been rendered in the AppBar. Specifically, I added an if statement evaluating if _isMounted is false to update the actions of the AppBar and in the opposite case do nothing. – Mikhael Jan 29 '20 at 05:53
  • If you could call setState in build, you would still have an infinite loop since setting the state queues another build/render. Using a Boolean flag as you mentioned is a good solution. – Jacob McGowan Jan 30 '20 at 01:32