Search code examples
flutterdartpopupwindowsetstate

How to call setState (rebuild page), after pop of PopUpWindow?


I'm trying to update/rebuild a route beneath a popup window when the popup window is popped, using Navigator.pop(context). On the popup window the user can add something to a list, and the route beneath (the page), the added item shows in a ListView. So more specifically I need my ListView to be up to date with the actual list. But nothing I have tried yet, have worked.

As mentioned, I have already tried quite a few things, including: didPopNext() (using RouteAware), changedExternalState (which I couldn't quite get the grasp of), trying to pass a function only containing a setState(() {}), and other things.

Hope somebody can help with how I can rebuild the ListView when the popup window is popped.

Thanks in advance.

This example should summarize my problem decently close (couldn't show you the actual code, because it's gotten way over 2.000 lines, but the core elements of the problem should be here):

import 'package:flutter/material.dart';

void main() => runApp(new MaterialApp(
  home: MyPage()));

class PopupLayout extends ModalRoute<void> {
  @override
  Duration get transitionDuration => Duration(milliseconds: 300);
  @override
  bool get opaque => false;
  @override
  bool get barrierDismissible => false;
  @override
  bool get semanticsDismissible => true;
  @override
  Color get barrierColor =>
      bgColor == null ? Colors.black.withOpacity(0.5) : bgColor;
  @override
  String get barrierLabel => null;
  @override
  bool get maintainState => false;


  double top;
  double bottom;
  double left;
  double right;
  Color bgColor;
  final Widget child;

  PopupLayout(
      {Key key,
      this.bgColor,
      @required this.child,
      this.top,
      this.bottom,
      this.left,
      this.right});

  @override
  Widget buildPage(
    BuildContext context,
    Animation<double> animation,
    Animation<double> secondaryAnimation,
  ) {
    if (top == null) this.top = 10;
    if (bottom == null) this.bottom = 20;
    if (left == null) this.left = 20;
    if (right == null) this.right = 20;

    return GestureDetector(
      onTap: () {},
      child: Material(
        type: MaterialType.transparency,
        child: _buildOverlayContent(context),
      ),
    );
  }
  Widget _buildOverlayContent(BuildContext context) {
    return Container(
      margin: EdgeInsets.only(
          bottom: this.bottom,
          left: this.left,
          right: this.right,
          top: this.top),
      child: child,
    );
  }

  @override
  Widget buildTransitions(BuildContext context, Animation<double> animation,
    Animation<double> secondaryAnimation, Widget child) {
    return FadeTransition(
      opacity: animation,
      child: ScaleTransition(
        scale: animation,
        child: child,
      ),
    );
  }
}

class PopupContent extends StatefulWidget {
  final Widget content;
  PopupContent({
    Key key,
    this.content,
  }) : super(key: key);
  _PopupContentState createState() => _PopupContentState();
}

class _PopupContentState extends State<PopupContent> {
  @override
  void initState() {
    super.initState();
  }
  @override
  Widget build(BuildContext context) {
    return Container(
      child: widget.content,
    );
  }
}

popupContent(BuildContext context, Widget widget,
      {BuildContext popupContext}) {
  Navigator.push(
    context,
    PopupLayout(
      top: 120,
      left: 20,
      right: 20,
      bottom: 120,
      child: PopupContent(
        content: Scaffold(
          backgroundColor: Colors.white,
          resizeToAvoidBottomInset: false,
          body: widget,
        ),
      ),
    ),
  );
}

Widget popupWidget(BuildContext context) {
  return new Container(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text("This is a popupPage"),
          FlatButton(
          onPressed: () {
            list.add("Irrelevant");
            Navigator.pop(context); //The rebuild of the page underneath, needs to be called when this pops
          },
      child: Text("Press me to change text on home page"),
        ),
      ]
    ),
  );
}

List list = [];

class MyPage extends StatefulWidget {
  @override
  createState() => MyPageState();
}

class MyPageState extends State<MyPage> {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Center(
            child: Container(
              width: 300,
              height: 400,
              child: ListView.builder(
                itemCount: list.length,
                itemBuilder: (BuildContext context, int index) {
                  return Center(
                    child: Text(index.toString()),
                  );
                },
              ),
            ),
          ),
          Center(
            child: FlatButton(
              onPressed: () {
                popupContent(context, popupWidget(context));
              },
              child: Text("Press me for popup window"),
            ),
          ),
          Center(
            child: FlatButton(
              onPressed: () {
                setState(() {});
              },
              child: Text("Press me to rebuild page (setState)"),
            ),
          ),
        ]
      ),
    );
  }
}

Solution

  • You can add a function in MyPage and pass it to your popUpWidget. Code:

    import 'package:flutter/material.dart';
    
    void main() => runApp(new MaterialApp(home: MyPage()));
    
    class PopupLayout extends ModalRoute<void> {
      @override
      Duration get transitionDuration => Duration(milliseconds: 300);
      @override
      bool get opaque => false;
      @override
      bool get barrierDismissible => false;
      @override
      bool get semanticsDismissible => true;
      @override
      Color get barrierColor =>
          bgColor == null ? Colors.black.withOpacity(0.5) : bgColor;
      @override
      String get barrierLabel => null;
      @override
      bool get maintainState => false;
    
      double top;
      double bottom;
      double left;
      double right;
      Color bgColor;
      final Widget child;
    
      PopupLayout(
          {Key key,
          this.bgColor,
          @required this.child,
          this.top,
          this.bottom,
          this.left,
          this.right});
    
      @override
      Widget buildPage(
        BuildContext context,
        Animation<double> animation,
        Animation<double> secondaryAnimation,
      ) {
        if (top == null) this.top = 10;
        if (bottom == null) this.bottom = 20;
        if (left == null) this.left = 20;
        if (right == null) this.right = 20;
    
        return GestureDetector(
          onTap: () {},
          child: Material(
            type: MaterialType.transparency,
            child: _buildOverlayContent(context),
          ),
        );
      }
    
      Widget _buildOverlayContent(BuildContext context) {
        return Container(
          margin: EdgeInsets.only(
              bottom: this.bottom,
              left: this.left,
              right: this.right,
              top: this.top),
          child: child,
        );
      }
    
      @override
      Widget buildTransitions(BuildContext context, Animation<double> animation,
          Animation<double> secondaryAnimation, Widget child) {
        return FadeTransition(
          opacity: animation,
          child: ScaleTransition(
            scale: animation,
            child: child,
          ),
        );
      }
    }
    
    class PopupContent extends StatefulWidget {
      final Widget content;
      PopupContent({
        Key key,
        this.content,
      }) : super(key: key);
      _PopupContentState createState() => _PopupContentState();
    }
    
    class _PopupContentState extends State<PopupContent> {
      @override
      void initState() {
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return Container(
          child: widget.content,
        );
      }
    }
    
    popupContent(BuildContext context, Widget widget, {BuildContext popupContext}) {
      Navigator.push(
        context,
        PopupLayout(
          top: 120,
          left: 20,
          right: 20,
          bottom: 120,
          child: PopupContent(
            content: Scaffold(
              backgroundColor: Colors.white,
              resizeToAvoidBottomInset: false,
              body: widget,
            ),
          ),
        ),
      );
    }
    
    Widget popupWidget(BuildContext context, Function callback) {
      //Pass The Function Here //////////////////////
      return new Container(
        child:
            Column(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
          Text("This is a popupPage"),
          FlatButton(
            onPressed: () {
              list.add("Irrelevant");
              callback(); // Call It Here /////////////////////////
              Navigator.pop(
                  context); //The rebuild of the page underneath, needs to be called when this pops
            },
            child: Text("Press me to change text on home page"),
          ),
        ]),
      );
    }
    
    List list = [];
    
    class MyPage extends StatefulWidget {
      @override
      createState() => MyPageState();
    }
    
    class MyPageState extends State<MyPage> {
      //The Calback Function //////////////////////////
      void callback() {
        setState(() {});
      }
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          body: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Center(
                  child: Container(
                    width: 300,
                    height: 400,
                    child: ListView.builder(
                      itemCount: list.length,
                      itemBuilder: (BuildContext context, int index) {
                        return Center(
                          child: Text(index.toString()),
                        );
                      },
                    ),
                  ),
                ),
                Center(
                  child: FlatButton(
                    onPressed: () {
                      popupContent(context, popupWidget(context, this.callback)); //Passing the Function here ////////////
                    },
                    child: Text("Press me for popup window"),
                  ),
                ),
                Center(
                  child: FlatButton(
                    onPressed: () {
                      setState(() {});
                    },
                    child: Text("Press me to rebuild page (setState)"),
                  ),
                ),
              ]),
        );
      }
    }