Stateful Child Widget not updating when parent widget updates

Viewed 384

Is it true that a stateful widget has to be updated/rebuild by itself and if the parent widget updates, it should also update right ?

What are the solutions for that.

I want to implement a quiz app. it has playquiz widget shows viewquiz or resultpage depending on question index which are all fetched using provider.

viewquiz has question text and answeroptions(stateful widget). When I click answer it notifies all consumers. I can use setstate to change color but when next question comes the color stays the same if i change color to blue then it stays blue. for now the solution is to use timer for that and reset back again using setstate.

Code

Here is my solution. If someone can help me imporve it. i feel like this is not the best way to do it.

quizmodel.dart

class QuizModel extends ChangeNotifier {
  var questions;

  QuizModel(this.questions);

  var qindex = 0;
  var score = 0;
  void check(ans) {
    if (ans) {
      score = score + 1;
    }

    qindex = qindex + 1;
    notifyListeners();
  }
}

playquiz.dart

 return ChangeNotifierProvider<QuizModel>(
              create: (context) => QuizModel(snapshot.data),
              child: Scaffold(
                //appBar: AppBar(title: Text("nice app")),
                body: Consumer<QuizModel>(builder: (context, QuizModel, child) {
                  if (QuizModel.questions.length == 0) {
                    return Center(
                      child: Text("Loading..."),
                    );
                  } else {
                    print(snapshot.data);
                    // return Text("Ok");
                    return QuizModel.qindex < QuizModel.questions.length
                        ? ViewQuiz(QuizModel.questions, QuizModel.qindex)
                        : ViewResult(QuizModel.score);
                  }
                }),
              ),
            );

answer.dart


class Answer extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final qzm = Provider.of<QuizModel>(context, listen: false);

    //var ptions = widget.options;
    return Column(
      children: (qzm.questions[qzm.qindex]["qoptions"] as List<dynamic>)
          .map((opt) => AnswerOption(qzm, opt))
          .toList(),
    );
  }
}

class AnswerOption extends StatefulWidget {
  var qzm;
  var opt;
  AnswerOption(this.qzm, this.opt);
  
  @override
  _AnswerOptionState createState() => _AnswerOptionState();
}

var colordata = Colors.white;

class _AnswerOptionState extends State<AnswerOption> {
  _checker(ans) {
    // print(idx);
    if (widget.qzm.questions[widget.qzm.qindex]["answer"] == ans) {
      print("nice");
      setState(() {
        colordata = Colors.green;
      });

      Timer(
          Duration(milliseconds: 500),
          () => {
                setState(() {
                  colordata = Colors.white;
                }),
                widget.qzm.check(true),
              });
    } else {
      setState(() {
        colordata = Colors.red;
      });
      Timer(
          Duration(milliseconds: 1000),
          () => {
                setState(() {
                  colordata = Colors.white;
                }),
                widget.qzm.check(false),
              });
    }
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: 250,
      child: RaisedButton(
          onPressed: () => _checker(widget.opt),
          child: Text(
            widget.opt,
            style: TextStyle(fontSize: 15, color: Colors.black),
          ),
          color: colordata
          // duration: Duration(milliseconds: 300),
          ),
    );
  }
}

full code :

https://github.com/ishworgiri1999/quizel/tree/main/lib

2 Answers

Stateful widget cannot update itself until programmer specify where and when it must be updated calling the seState method.

Parent widget rebuilds child widget only if child widget is in subtree of parent widget which is updated.

Widgets can be updated completely using setState or partially, using Provider/Consumer or ValueListenableBuilder.

If you are familiar with state management, I would use the provider package. Otherwise if it's a smaller app in which you don't have global state (state shared between multiple widgets) you can call setState if the UI should be rebuilt. The UI will only update if the state used by the child widget is changed.

Please provide a short code snippet to reproduce your situation.

Related