Flutter – SetState not working on extracted widgets

Issue

Hello so basically I want to know why setState() works on widgets extracted as methods but does not work on widgets extracted as widgets….

and also when to use extracted widgets/ extracted widgets as methods or are they both the same? but I guess they cant be the same since one works and other doesnt?

If you want the code i tried before changing it to extracted methods:

from main class:

void refresh() => setState(() {});

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        resizeToAvoidBottomInset: false,
        backgroundColor: Colors.blue,
        body: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: const [
            SizedBox(height: 50),
            PageHeader(),
            PageBody(),
          ],
        ),
        floatingActionButton: AddTaskButton(
          transitionAnimationController: transitionAnimationController,
          refresh: refresh,
        ),
      ),
    );
  }

Extracted widgets:

class PageHeader extends StatelessWidget {
  const PageHeader({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Expanded(
      flex: 1,
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 15.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const CircleAvatar(
              radius: 30,
              backgroundColor: Colors.white,
              child: Icon(
                Icons.list,
                color: Colors.blue,
                size: 30,
              ),
            ),
            const SizedBox(
              height: 15,
            ),
            const Text(
              'Todoey',
              style: TextStyle(color: Colors.white, fontSize: 35),
            ),
            Text(
              ' ${TaskData.tasks.length} Tasks',
              style: const TextStyle(color: Colors.white, fontSize: 15),
            ),
          ],
        ),
      ),
    );
  }
}

class PageBody extends StatelessWidget {
  const PageBody({
    Key? key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Expanded(
      flex: 3,
      child: Container(
        decoration: const BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.only(
            topLeft: Radius.circular(20),
            topRight: Radius.circular(20),
          ),
        ),
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 15.0),
          child: TaskList(taskData: TaskData.tasks),
        ),
      ),
    );
  }
}

Extracted Methods:

Expanded pageHeader() {
    return Expanded(
      flex: 1,
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 15.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const CircleAvatar(
              radius: 30,
              backgroundColor: Colors.white,
              child: Icon(
                Icons.list,
                color: Colors.blue,
                size: 30,
              ),
            ),
            const SizedBox(
              height: 15,
            ),
            const Text(
              'Todoey',
              style: TextStyle(color: Colors.white, fontSize: 35),
            ),
            Text(
              ' ${TaskData.tasks.length} Tasks',
              style: const TextStyle(color: Colors.white, fontSize: 15),
            ),
          ],
        ),
      ),
    );
  }
}

Expanded pageBody() {
  return Expanded(
    flex: 3,
    child: Container(
      decoration: const BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.only(
          topLeft: Radius.circular(20),
          topRight: Radius.circular(20),
        ),
      ),
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 15.0),
        child: TaskList(taskData: TaskData.tasks),
      ),
    ),
  );
}

Solution

setState() actually doesn’t care if it is extracted widget och method. It has no "check" for that.

It tells the UI that the state of the widget has changed, and that it might be needed to redraw. It schedules a new build. But it does not necessarily redraw everything if it doesn’t have to.

So depending on what types of widget are below the parent widget (calling setState), and what they contain, they might be redrawn.

However, a method ("extracted widget by method") is always called when redrawn, making it very un-efficient. That is why you might observe a difference.

Look at this video for more information about the differences:

https://www.youtube.com/watch?v=IOyq-eTRhvo

As a rule of thumb, "always" extract widgets, not creating widgets by methods.

Why your child widgets isn’t rebuilding can be one of several things. Either way, I don’t think it has anything to do with the parents setState()-call in this case.

It is the child widgets that doesn’t have any reason to rebuild. You seem to be adding stuff through some global variable(?) TaskData.tasks… ? The child widgets doesn’t "observe" that variable for changes. So they have no reason to rebuild.

If you from the parent either inject the data, use some state management like Bloc, or use Provider or ChangeNotifier or similar, for the data that PageHeader(), PageBody() and their children base their builds on, then everything will update accordingly.

Answered By – Robert Sandberg

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published