2

I am using the pull_to_refresh package.

I am having a Stack() with two elements. One of them is the Refresher(). When I pull down on my screen, activating the refreshing animation, the build method is called constantly. The problem is that my second Widget in my Stack is quite complex to build and takes some time. I want to prevent having it build all the time when triggering the Refresher-Animation. Is this possible?

My simplified code would look like this:

@override
  Widget build(BuildContext context) {
    return Scaffold(
        key: _scaffoldKey,
        body: Stack(children: <Widget>[
          SafeArea(
              child: Column(children: [
            Expanded(
              child: Container(
                  margin: EdgeInsets.all(0),
                  width: 100.w,
                  constraints: const BoxConstraints.expand(),
                  child: SizedBox(
                              width: 100.w,
                              child: Refresher( refresher stuff )
                              )
                          )
                      )
                  )
              ),
              SecondItem()
          )
      )
 }

Somehow the build method of SecondItem is called all the time. Not the build method of the whole scaffold.

progNewbie
  • 4,362
  • 9
  • 48
  • 107
  • The Refresher Widget shouldn't rebuild the entire screen unless `setState()` is called. Are you calling `setState()` when a refresh is done? If you'd like to update the children widgets displayed inside the Refresher, you may want consider using Streams. Then use `StreamBuilder` to only rebuild the widgets inside it when the Stream has changed. With this approach, you don't need to call `setState()` and the entire screen won't need to be rebuilt. – Omatt Feb 28 '22 at 08:48
  • @Omatt the `build` is called during the pulldown of the animation which will trigger the refresh in the end. I am not able to call `setState()` while this happens. – progNewbie Feb 28 '22 at 09:21

2 Answers2

0

If Your second Item dont want to get refresh, then add it as a separeate class like,

 Expanded(
              child: Container(
                  margin: EdgeInsets.all(0),
                  width: 100.w,
                  constraints: const BoxConstraints.expand(),
                  child: SizedBox(
                              width: 100.w,
                              child: Refresher( refresher stuff )
                              )
                          )
                      )
                  )
              ),
              SecondItem()
          )


class SecondItem extends StatefulWidget {
  @override
  _SecondItemState createState() => _SecondItemState();
}

class _SecondItemState extends State<SecondItem> {
  int counter = 0;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Text("Your Second Widget"),
    );
  }
}

Now your SecondItem() will not get refresh when you refresh your FirstItem()

Anand
  • 4,355
  • 2
  • 35
  • 45
0

Since I wasn't really able to replicate the problem, I build a working structure that implements refreshing.

First the main widget, in my case MyHomePage. This widget implements the Scaffold and Stack with FirstWidget and SecondWidget as children.

class MyHomePage extends StatefulWidget {
  const MyHomePage({
    Key? key,
  }) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: const <Widget>[
          FirstWidget(),
          SecondWidget(),
        ],
      ),
    );
  }
}

FirstWidget is a statefull widget with a counter in the state. It implements the refresher with a specific controller. Once the refresh is triggered, it calls set state and updates the counter within his state. That should trigger only his build again and not any other. I implemented a Text to show the counter value increasing at each refresh, and a Print to expose the build.

class FirstWidget extends StatefulWidget {
  const FirstWidget({
    Key? key,
  }) : super(key: key);

  @override
  State<FirstWidget> createState() => _FirstWidgetState();
}

class _FirstWidgetState extends State<FirstWidget> {
  late int _counter;
  late RefreshController _refreshController;

  @override
  void initState() {
    _counter = 1;
    _refreshController = RefreshController(initialRefresh: false);
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    print('First widget built');
    return SafeArea(
      child: Column(
        children: [
          Container(
            margin: const EdgeInsets.all(0),
            width: double.infinity,
            height: 500,
            color: Colors.red,
            child: SmartRefresher(
              controller: _refreshController,
              onRefresh: () async {
                setState(() {
                  _counter++;
                });
                await Future.delayed(const Duration(milliseconds: 1000));
                _refreshController.refreshCompleted();
              },
            ),
          ),
          Text("Counter: $_counter"),
        ],
      ),
    );
  }
}

Last we got the SecondWidget which is a another simple widget with a print statement. In case of build it writes on the console. When the FirstWidget refresh, the second doesn't build becouse his state has not changed.

class SecondWidget extends StatefulWidget {
  const SecondWidget({
    Key? key,
  }) : super(key: key);

  @override
  State<SecondWidget> createState() => _SecondWidgetState();
}

class _SecondWidgetState extends State<SecondWidget> {
  @override
  Widget build(BuildContext context) {
    print('Second widget built');
    return const Center(child: Text('Second here!'));
  }
}

Possible cause of your problem.

It could be that when refreshing, you actually are updating the state of a parent widget that, on cascade, causes the re build of your second widget. If state is handled correctly, and your second widget doesn't depends on your first widget state, the refresh should not rebuild the second.

L. Gangemi
  • 3,110
  • 1
  • 22
  • 47
  • 1
    Thank you for your effort! I don't understand why it is not working for me. I am not updating the state of a parent widget while the refreshing anmiation is done. I mean not even `onRefresh` is loaded while this happens, so how could I reload the parent widget. – progNewbie Mar 07 '22 at 09:17
  • There must be some relation that causes the build to be triggered inside the second widget. Could you try replacing the second widget with the one I've provided? In that way we can exclude that the problem is caused by your second widget – L. Gangemi Mar 07 '22 at 10:11
  • Another problem is the onRefresh which is not getting called, that means that probably even the same refreshing widget gets rebuilt, which is not ok. The 'refresh' widget should only rebuild his child and not itself – L. Gangemi Mar 07 '22 at 10:15
  • Have you got any updates? – L. Gangemi Mar 09 '22 at 12:02