Shafiul Islam Sajib
Shafiul Islam Sajib

Reputation: 45

setstate() or markneedsbuild() called during build on passing a widget as an argument

I have a Stateful Widget that has some buttons. On each button press, I want to show different types of Dialog. I made a function for my Button and for each button I simply call that function.

Since I want to show different Dialog on a different button press, I tried to pass the Dialog Widget as a parameter of the button function and call that widget inside onPressed. When I try to build the app it shows these errors.


════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following assertion was thrown building DialogPage(dirty, dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#e6799]], state: _DialogPageState#a5060):
setState() or markNeedsBuild() called during build.

This Overlay widget cannot be marked as needing to build because the framework is already in the process of building widgets.  A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: Overlay-[LabeledGlobalKey<OverlayState>#8e4e3]
  state: OverlayState#665ea(entries: [OverlayEntry#1cc53(opaque: true; maintainState: false), OverlayEntry#020ad(opaque: false; maintainState: true), OverlayEntry#1d32e(opaque: false; maintainState: false), OverlayEntry#9a4ae(opaque: false; maintainState: true), OverlayEntry#2728f(opaque: false; maintainState: false), OverlayEntry#33d56(opaque: false; maintainState: true)])
The widget which was currently being built when the offending call was made was: DialogPage
  dirty
  dependencies: [_InheritedTheme, _LocalizationsScope-[GlobalKey#e6799]]
  state: _DialogPageState#a5060
The relevant error-causing widget was: 
  DialogPage file:///C:/Users/imsajib02/AndroidStudioProjects/Flutter/widgets/lib/main.dart:43:32
When the exception was thrown, this was the stack: 
#0      Element.markNeedsBuild.<anonymous closure> (package:flutter/src/widgets/framework.dart:3896:11)
#1      Element.markNeedsBuild (package:flutter/src/widgets/framework.dart:3911:6)
#2      State.setState (package:flutter/src/widgets/framework.dart:1168:14)
#3      OverlayState.insertAll (package:flutter/src/widgets/overlay.dart:344:5)
#4      OverlayRoute.install (package:flutter/src/widgets/routes.dart:44:24)
...
════════════════════════════════════════════════════════════════════════════════════════════════════

════════ (2) Exception caught by widgets library ═══════════════════════════════════════════════════
The method 'drive' was called on null.
Receiver: null
Tried calling: drive<Color>(Instance of '_ChainedEvaluation<Color>')
The relevant error-causing widget was: 
  MaterialApp file:///C:/Users/imsajib02/AndroidStudioProjects/Flutter/widgets/lib/main.dart:11:12
════════════════════════════════════════════════════════════════════════════════════════════════════

════════ (3) Exception caught by widgets library ═══════════════════════════════════════════════════
'package:flutter/src/animation/animations.dart': Failed assertion: line 376 pos 15: 'parent != null': is not true.
The relevant error-causing widget was: 
  MaterialApp file:///C:/Users/imsajib02/AndroidStudioProjects/Flutter/widgets/lib/main.dart:11:12
════════════════════════════════════════════════════════════════════════════════════════════════════
D/FlutterView( 3883): Detaching from a FlutterEngine: io.flutter.embedding.engine.FlutterEngine@e78ea39



Class where the button widget is placed:

class DialogPage extends StatefulWidget {

  @override
  _DialogPageState createState() => _DialogPageState();
}

class _DialogPageState extends State<DialogPage> {

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(
        title: Text("Dialogs"),
      ),
      body: Center(
        child: SingleChildScrollView(
          padding: EdgeInsets.only(top: 35),

          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            verticalDirection: VerticalDirection.down,
            children: <Widget>[

              Button("DEFAULT DIALOG", DialogCollection().defaultDialog(context)),
              Button("DIALOG WITH ICON - 1", DialogCollection().customDialog_1(context)),
              Button("Widget", DialogCollection().defaultDialog(context)),
              Button("Widget", DialogCollection().defaultDialog(context)),
              Button("Widget", DialogCollection().defaultDialog(context)),
              Button("Widget", DialogCollection().defaultDialog(context)),
            ],
          ),
        ),
      ),
    );
  }


  Button(String text, Widget widget) {

    return Column(
      children: <Widget>[

        Container(
          width: 230,
          child: RaisedButton(
            onPressed: () {


            },
            shape: RoundedRectangleBorder(
              borderRadius: new BorderRadius.circular(25.0),
              //side: BorderSide(color: Colors.red)
            ),
            color: Colors.blueGrey,
            padding: EdgeInsets.only(top: 12.0, bottom: 12.0, left: 40.0, right: 40.0),
            textColor: Colors.white,
            child: Text(text, textAlign: TextAlign.center ,style: TextStyle(
              fontWeight: FontWeight.bold,
              fontSize: 18,
            ),
            ),
          ),
        ),

        Container(
          height: 35,
        )
      ],
    );
  }
}



class from where I call the dialog widgets:

class DialogCollection {

  defaultDialog(BuildContext context) {

    return showDialog(
        context: context,
        //barrierDismissible: false, //on touch outside does not dismiss the dialog
        builder: (BuildContext context) {

          return WillPopScope( //WillPopScope stops dismissing dialog on outside touch and back press
            onWillPop: () {
              Navigator.of(context).pop();
              return null;
            },
            child: AlertDialog(

              title: Text("Dialog Title", textAlign: TextAlign.center,),
              titlePadding: const EdgeInsets.all(15.0),
              titleTextStyle: TextStyle(
                color: Colors.redAccent,
                fontSize: 20,
                fontWeight: FontWeight.bold,
              ),
              content: Text("This is the description of the dialog."),
              actions: <Widget>[

                FlatButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                  color: Colors.lightBlueAccent,
                  textColor: Colors.white,
                  child: Text("Yes", style: TextStyle(

                    fontSize: 18,
                  ),
                  ),
                ),

                FlatButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                  color: Colors.redAccent,
                  textColor: Colors.white,
                  child: Text("No", style: TextStyle(

                    fontSize: 18,
                  ),
                  ),
                ),
              ],
            ),
          );
        }
    );
  }
}

Upvotes: 0

Views: 1445

Answers (1)

chunhunghan
chunhunghan

Reputation: 54407

You can copy paste run full code below
DialogCollection().defaultDialog(context) means execute this function
You can call Button with DialogCollection().defaultDialog to pass function address and pass context

code snippet

Button("DEFAULT DIALOG", DialogCollection().defaultDialog, context)

Button(String text, Function callback, BuildContext context) {
    return Column(
      children: <Widget>[
        Container(
          width: 230,
          child: RaisedButton(
            onPressed: () {
              callback(context);
            },

working demo

enter image description here

full code

import 'package:flutter/material.dart';

class DialogCollection {
  defaultDialog(BuildContext context) {
    return showDialog(
        context: context,
        //barrierDismissible: false, //on touch outside does not dismiss the dialog
        builder: (BuildContext context) {
          return WillPopScope(
            //WillPopScope stops dismissing dialog on outside touch and back press
            onWillPop: () {
              Navigator.of(context).pop();
              return null;
            },
            child: AlertDialog(
              title: Text(
                "Dialog Title",
                textAlign: TextAlign.center,
              ),
              titlePadding: const EdgeInsets.all(15.0),
              titleTextStyle: TextStyle(
                color: Colors.redAccent,
                fontSize: 20,
                fontWeight: FontWeight.bold,
              ),
              content: Text("This is the description of the dialog."),
              actions: <Widget>[
                FlatButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                  color: Colors.lightBlueAccent,
                  textColor: Colors.white,
                  child: Text(
                    "Yes",
                    style: TextStyle(
                      fontSize: 18,
                    ),
                  ),
                ),
                FlatButton(
                  onPressed: () {
                    Navigator.of(context).pop();
                  },
                  color: Colors.redAccent,
                  textColor: Colors.white,
                  child: Text(
                    "No",
                    style: TextStyle(
                      fontSize: 18,
                    ),
                  ),
                ),
              ],
            ),
          );
        });
  }
}

class DialogPage extends StatefulWidget {
  @override
  _DialogPageState createState() => _DialogPageState();
}

class _DialogPageState extends State<DialogPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Dialogs"),
      ),
      body: Center(
        child: SingleChildScrollView(
          padding: EdgeInsets.only(top: 35),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            verticalDirection: VerticalDirection.down,
            children: <Widget>[
              Button(
                  "DEFAULT DIALOG", DialogCollection().defaultDialog, context),
              //Button("DIALOG WITH ICON - 1", DialogCollection().customDialog_1(context)),
              /* Button("Widget", DialogCollection().defaultDialog(context)),
              Button("Widget", DialogCollection().defaultDialog(context)),
              Button("Widget", DialogCollection().defaultDialog(context)),
              Button("Widget", DialogCollection().defaultDialog(context)),*/
            ],
          ),
        ),
      ),
    );
  }

  Button(String text, Function callback, BuildContext context) {
    return Column(
      children: <Widget>[
        Container(
          width: 230,
          child: RaisedButton(
            onPressed: () {
              callback(context);
            },
            shape: RoundedRectangleBorder(
              borderRadius: new BorderRadius.circular(25.0),
              //side: BorderSide(color: Colors.red)
            ),
            color: Colors.blueGrey,
            padding: EdgeInsets.only(
                top: 12.0, bottom: 12.0, left: 40.0, right: 40.0),
            textColor: Colors.white,
            child: Text(
              text,
              textAlign: TextAlign.center,
              style: TextStyle(
                fontWeight: FontWeight.bold,
                fontSize: 18,
              ),
            ),
          ),
        ),
        Container(
          height: 35,
        )
      ],
    );
  }
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: DialogPage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

Upvotes: 1

Related Questions