Demir
Demir

Reputation: 859

While setting state from parent class setState() or markNeedsBuild() called during build

I need to call the funciton via constructor of stateless widget. Since, I cannot set a state inside a stateless widget. I need to set it from parent of the LightButton class which is Classroom. I tried to create a funciton(func) and pass it into the LightButton class' constructor. And set the state via using the onButtonPressed. The logic of this code is when clicking on LigthButtonthe the text should switch from OFF to ON from LightBulb class and the title() should switch from Turn light on to Turn light off from LigtButton class. I get two errors like the followings.

The following assertion was thrown building LightButton(dirty):
setState() or markNeedsBuild() called during build.

This Classroom 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.
    state: _ClassroomState#4c34c
The widget which was currently being built when the offending call was made was: LightButton
    dirty
The relevant error-causing widget was
LightButton
package:hw_20170602012/widgets/widgets.dart:27
When the exception was thrown, this was the stack
#0      Element.markNeedsBuild.<anonymous closure>
package:flutter/…/widgets/framework.dart:4292
#1      Element.markNeedsBuild
package:flutter/…/widgets/framework.dart:4307
#2      State.setState
package:flutter/…/widgets/framework.dart:1264
#3      _ClassroomState.onButtonPressed
package:hw_20170602012/widgets/widgets.dart:11
#4      LightButton.title
package:hw_20170602012/widgets/widgets.dart:59
.

The second one is

══════ Exception caught by rendering library ═════════════════════════════════
A RenderFlex overflowed by 99300 pixels on the bottom.
The relevant error-causing widget was
Column
lib\main.dart:26
════════════════════════════════════════════════════════════════════════════════

Here is the code

import 'package:flutter/material.dart';

class LightBulb extends StatelessWidget {
  bool isLit;
  LightBulb(bool isLit) {
    this.isLit = isLit;
  }

  Widget build(BuildContext context) {
    return Container(
      color: isLit ? Colors.green : Colors.red,
      padding: EdgeInsets.all(5),
      child: isLit ? Text('ON') : Text('OFF'),
    );
  }
}


class LightButton extends StatelessWidget {

  Function func;  // getting the value from constructor and send it below onPressed function.
  LightButton(Function func) {           //Constructor for getting the state from Classroom
    this.func = func;
  }
  //title change
  String title() {
    if (func()) return "Turn light off"; //Funciton
    return "Turn light on";
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(vertical: 4, horizontal: 12),
      color: Colors.red,
      child: Container(
        color: Colors.blue,
        child: MaterialButton(
          textColor: Colors.white,
          onPressed: ()=>func(),                    //Function call happens here.
          child: Text(
            title(),
            style: TextStyle(color: Colors.white),
          ),
        ),
      ),
    );
  }
}

class Classroom extends StatefulWidget {
  @override
  _ClassroomState createState() => _ClassroomState();
}

class _ClassroomState extends State<Classroom> {
   bool isLightOn= false;  //by default isLightOn is false,
 //This method switchs the boolean condition and will be passed into the constructor of the LightButton function
  onButtonPressed() {          
    setState(() {
      return isLightOn = !isLightOn;  //returns the oppositon of the current contion(true->false,false->true)
    });
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        color: Colors.blue,
        padding: EdgeInsets.all(5),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            LightBulb(onButtonPressed), //If the function return true,text switchs to 'ON' else 'OFF'
            LightButton(onButtonPressed), // If function
          ],
        ),
      ),
    );
  }
}


Upvotes: 0

Views: 108

Answers (2)

chunhunghan
chunhunghan

Reputation: 54367

You can copy paste run full code below
Reason: title() means execute function title, in LightButton call Text(title(),style: TextStyle(color: Colors.white),), cause widget tree try to rebuild again
Step 1: Remove return keyword in onButtonPressed()

onButtonPressed() {
    setState(() {
      isLightOn =
          !isLightOn; //returns the oppositon of the current contion(true->false,false->true)
    });
  }

Step 2: Pass isLightOn to LightButton

class LightButton extends StatelessWidget {
  Function
      func; // getting the value from constructor and send it below onPressed function.
  bool isLightOn;
  LightButton(Function func, bool isLightOn) {
    //Constructor for getting the state from Classroom
    this.func = func;
    this.isLightOn = isLightOn;
  }
  //title change
  String title() {
    if (isLightOn) return "Turn light off"; //Funciton
    return "Turn light on";
  }

...
LightButton(onButtonPressed, isLightOn), 

working demo

enter image description here

full code

import 'package:flutter/material.dart';

class LightBulb extends StatelessWidget {
  bool isLit;
  LightBulb(bool isLit) {
    this.isLit = isLit;
  }

  Widget build(BuildContext context) {
    return Container(
      color: isLit ? Colors.green : Colors.red,
      padding: EdgeInsets.all(5),
      child: isLit ? Text('ON') : Text('OFF'),
    );
  }
}

class LightButton extends StatelessWidget {
  Function
      func; // getting the value from constructor and send it below onPressed function.
  bool isLightOn;
  LightButton(Function func, bool isLightOn) {
    //Constructor for getting the state from Classroom
    this.func = func;
    this.isLightOn = isLightOn;
  }
  //title change
  String title() {
    if (isLightOn) return "Turn light off"; //Funciton
    return "Turn light on";
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(vertical: 4, horizontal: 12),
      color: Colors.red,
      child: Container(
        color: Colors.blue,
        child: MaterialButton(
          textColor: Colors.white,
          onPressed: () => func(), //Function call happens here.
          child: Text(
            title(),
            style: TextStyle(color: Colors.white),
          ),
        ),
      ),
    );
  }
}

class Classroom extends StatefulWidget {
  @override
  _ClassroomState createState() => _ClassroomState();
}

class _ClassroomState extends State<Classroom> {
  bool isLightOn = false; //by default isLightOn is false,
  //This method switchs the boolean condition and will be passed into the constructor of the LightButton function
  onButtonPressed() {
    setState(() {
      isLightOn =
          !isLightOn; //returns the oppositon of the current contion(true->false,false->true)
    });
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        color: Colors.blue,
        padding: EdgeInsets.all(5),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            //LightBulb(onButtonPressed), //If the function return true,text switchs to 'ON' else 'OFF'
            LightButton(onButtonPressed, isLightOn), // If function
          ],
        ),
      ),
    );
  }
}

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

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

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Classroom(),
    );
  }
}

Upvotes: 2

Mohammad Kurjieh
Mohammad Kurjieh

Reputation: 1143

Your problem is in LightBulb(onButtonPressed). You are passing a function to it whereas it is expecting a bool. Remember that you are not calling the onButtonPressed here, so you can't assume that it is a bool it is a reference to the function. Replace LightBulb(onButtonPressed) with LightBulb(isLightOn) to fix this issue.

Upvotes: 0

Related Questions