1

I have a ListView with children FutureBuilder and ListView.Builder. And in FutureBuilder I have ListView. Now the problem is when I scrolling ListView parent why FutureBuilder is rebuild again?

You can see how I reproduce this problem in here https://youtu.be/OKjiMOSJmYA

import 'package:flutter/material.dart';

class DebugScreen extends StatefulWidget {
  @override
  _DebugScreenState createState() => _DebugScreenState();
}

class _DebugScreenState extends State<DebugScreen> {
  List<int> numbers = [1, 2, 3, 4, 5, 6, 7];
  var futureData;
  var futureBuilderHelloWorld;

  Future<List<int>> getFutureData() async {
    print("getFutureData");
    var numbers = [
      1,
      2,
      3,
      4,
      5,
      6,
      7,
      8,
      9,
      10,
      11,
      12,
      13,
      14,
      15,
      16,
      17,
      18,
      19,
      20,
      21,
      22,
      23,
      24,
      25,
      26,
      27,
      28,
      29,
      30,
    ];
    await Future.delayed(Duration(seconds: 3));
    return await numbers;
  }

  @override
  void initState() {
    futureData = getFutureData();
    futureBuilderHelloWorld = FutureBuilder(
      future: futureData,
      builder: (context, AsyncSnapshot<List<int>> snapshot) {
        print("future builder"); // why this is called when scrolling
        switch (snapshot.connectionState) {
          case ConnectionState.none:
          case ConnectionState.waiting:
            return Center(
              child: CircularProgressIndicator(),
            );
          default:
            if (snapshot.hasError) {
              return Text("Error: " + snapshot.error.toString());
            } else {
              var numbers = snapshot.data;
              return Container(
                height: 190.0,
                child: ListView.builder(
                  shrinkWrap: true,
                  itemCount: numbers.length,
                  itemBuilder: (context, index) {
                    return Padding(
                      padding: const EdgeInsets.all(16.0),
                      child: Text("Hello world ${numbers[index]}"),
                    );
                  },
                ),
              );
            }
        }
      },
    );
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Debug Mode"),
      ),
      body: ListView(
        children: <Widget>[
          futureBuilderHelloWorld,
          ListView(
            shrinkWrap: true,
            physics: ClampingScrollPhysics(),
            children: List.generate(100, (index) {
              return Text("Flutter $index");
            }).toList(),
          )
        ],
      ),
    );
  }
}

Now in the console just like this

I/flutter (18634): future builder
I/flutter (18634): future builder
I/flutter (18634): future builder
I/flutter (18634): future builder
Yudi Setiawan
  • 13
  • 2
  • 4

1 Answers1

0

In documentations it is stated that if your future changes, build method will always be called twice:

If the old future has already completed successfully with data as above, changing configuration to a new future results in snapshot pairs of the form:

new AsyncSnapshot<String>.withData(ConnectionState.none, 'data of first future')
new AsyncSnapshot<String>.withData(ConnectionState.waiting, 'data of second future')
In general, the latter will be produced only when the new future is non-null, and the former only when the old future is non-null.

Check out the documentation: https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html

Your code should be adapted to this by something like keeping call counter and updating list in second call only.

Akmal Rakhimov
  • 459
  • 6
  • 5