Slow scroll in infinite scroll flutter

Viewed 539

I am trying to implement infinite scroll for my list which works perfectly fine , the only problem is that the scroll is kind of slow not smooth at all which I don't know why. Is there a way with which I can make my scroll smoother here's my list class:



class QrqcListView extends StatefulWidget {
  @override
  _QrqcListViewState createState() => _QrqcListViewState();
}

late Future<QrqcDetails?> futureQrqc;
late ScrollController _controller;

List<Qrqc>? myList;
String? label;

int _count = 20;
int pageSize = 1;
bool isBottom = false;
bool isLoading = false;
bool _visible = true;

class _QrqcListViewState extends State<QrqcListView> {
  void _showToast(BuildContext context) {
    final scaffold = ScaffoldMessenger.of(context);
    scaffold.showSnackBar(
      SnackBar(
        content: const Text('No more data to load'),
        action: SnackBarAction(
            label: 'UNDO', onPressed: scaffold.hideCurrentSnackBar),
      ),
    );
  }

  List<TypeSettings> listTypes = [];
  void initState() {
    Future.delayed(const Duration(seconds: 8), () {
      //asynchronous delay
      if (this.mounted) {
        setState(() {
          _visible = false;
        });
      }
    });
    _controller = ScrollController();

    Provider.of<MyQrqcListViewModel>(context, listen: false).fetchMyQrqc();
    fetchTypes();

    super.initState();
  }

  fetchTypes() async {
    listTypes = (await SettingsViewModel().fetchTypes());
    setState(() {
      listTypes;
    });
  }

  @override
  Widget build(BuildContext context) {
    var myQrqcListViewModel = Provider.of<MyQrqcListViewModel>(context);

    myList = myQrqcListViewModel.articlesList;
    List<Qrqc>? qrqcList = myList;
    Future<List<Qrqc>>? _fetch(int count) {
      return Future.delayed(
        Duration(seconds: 1),
        () => qrqcList!.take(count).toList(),
      );
    }

    _controller.addListener(() {
      var triggerFetchMoreSize = 0.5 * _controller.position.maxScrollExtent;
      if (_controller.position.pixels ==
          (_controller.position.maxScrollExtent - 50)) {
        setState(() {
          isBottom = true;
        });

        if (qrqcList!.length == _count) {
          setState(() {
            _count += 20;
          });
        }
        print(_count);
      } else {
        setState(() {
          isBottom = false;
        });
      }
      print(isBottom);
    });

    QrqcDetails? result;
    String? type;

    String? setTypeLabel(int j) {
      for (int j = 0; j < qrqcList!.length; j++) {
        for (int i = 0; i < listTypes.length; i++) {
          if (listTypes[i].id == qrqcList![j].typeID) {
            qrqcList[j].typeName = listTypes[i].label;
          }
        }
      }
    }

    return FutureBuilder(
        future: _fetch(_count),
        builder: (BuildContext context, snapshot) {
          if (snapshot.hasData) {
            String? backgroundImage;
            return ScrollListener(
              threshold: 0.8,
              builder: (context, controller) {
                final listView = ListView.builder(
                    controller: _controller,
                    physics: const BouncingScrollPhysics(),
                    shrinkWrap: true,
                    itemCount: qrqcList!.length,
                    itemBuilder: (BuildContext context, int index) {
                      List<String>? typeNames;
                      String? typeName;

                      List<String>? _setTypeName() {
                        for (int i = 0; i < listTypes.length; i++) {
                          if (listTypes[i].id == qrqcList[index].typeID) {
                            typeNames!.add(listTypes[i].label);
                          }
                        }
                        return typeNames;
                        print(listTypes);
                      }

                      String? backgroundImage;

                      String? _setImage() {
                        setTypeLabel(index);
                        if (qrqcList![index].typeName == "Delivery") {
                          backgroundImage = "assets/icons/delivery.png";
                        } else if (qrqcList![index].typeName == "Security") {
                          backgroundImage = "assets/icons/security.png";
                        } else if (qrqcList![index].typeName == "Quality") {
                          backgroundImage = "assets/icons/quality.png";
                        } else if (qrqcList![index].typeName == "Cost") {
                          backgroundImage = "assets/icons/Cost.png";
                        } else if (qrqcList![index].typeName == "People") {
                          backgroundImage = "assets/icons/people.png";
                        } else {
                          backgroundImage = "assets/icons/unknown.png";
                        }

                        print("list types: $type");
                        print("_mTitle: $backgroundImage");
                        return backgroundImage; // here it returns your _backgroundImage value
                      }

                      return Column(
                        children: [
                          ConditionalBuilder(
                              condition: qrqcList[index].status == 'INIT',
                              builder: (context) => QrqcBody(
                                    child: QrqcCard(
                                        child: QrqcCardBody(
                                      color: Colors.orange,
                                      text: qrqcList[index].status,
                                      leading:
                                          QrqcCardLeaing(imgPath: _setImage()),
                                      trailing: QrqcCardtrailing(
                                        text:
                                            qrqcList[index].progress.toString(),
                                        percent:
                                            qrqcList[index].progress.toString(),
                                      ),
                                      title: qrqcList[index].id.toString(),
                                      subtitle: qrqcList[index].title,
                                      chlidren: [
                                        QrqcDetailsCardFirstRow(
                                          product: "No product",
                                          role:
                                              qrqcList[index].role ?? "no role",
                                        ),
                                        const SizedBox(height: 10),
                                        QrqcDetailsCardSecondRow(
                                          perim:
                                              qrqcList[index].perimeterName ??
                                                  "no perim",
                                          date: convertDateTimeDisplay(
                                              qrqcList[index].createdAt!),
                                        ),
                                        const SizedBox(height: 10),
                                      ],
                                    )),
                                  ),
                              fallback: null),
                          ConditionalBuilder(
                              condition: qrqcList[index].status == 'SUBMITTED',
                              builder: (context) => QrqcBody(
                                    child: QrqcCard(
                                        child: QrqcCardBody(
                                      color: Colors.blueAccent,
                                      text: 'SUB',
                                      leading:
                                          QrqcCardLeaing(imgPath: _setImage()),
                                      trailing: QrqcCardtrailing(
                                        text:
                                            qrqcList[index].progress.toString(),
                                        percent:
                                            qrqcList[index].progress.toString(),
                                      ),
                                      title: qrqcList[index].id.toString(),
                                      subtitle: qrqcList[index].title,
                                      chlidren: [
                                        QrqcDetailsCardFirstRow(
                                          product: "No product",
                                          role:
                                              qrqcList[index].role ?? "no role",
                                        ),
                                        const SizedBox(height: 10),
                                        QrqcDetailsCardSecondRow(
                                          perim:
                                              qrqcList[index].perimeterName ??
                                                  "no perim",
                                          date: convertDateTimeDisplay(
                                              qrqcList[index].createdAt!),
                                        ),
                                        const SizedBox(height: 10),
                                      ],
                                    )),
                                  ),
                              fallback: null),
                          ConditionalBuilder(
                              condition: qrqcList[index].status == 'VALIDATED',
                              builder: (context) => QrqcBody(
                                    child: QrqcCard(
                                        child: QrqcCardBody(
                                      color: Colors.green,
                                      text: 'Valid',
                                      leading:
                                          QrqcCardLeaing(imgPath: _setImage()),
                                      trailing: QrqcCardtrailing(
                                        text:
                                            qrqcList[index].progress.toString(),
                                        percent:
                                            qrqcList[index].progress.toString(),
                                      ),
                                      title: qrqcList[index].id.toString(),
                                      subtitle: qrqcList[index].title,
                                      chlidren: [
                                        QrqcDetailsCardFirstRow(
                                          product: "No product",
                                          role:
                                              qrqcList[index].role ?? "no role",
                                        ),
                                        const SizedBox(height: 10),
                                        QrqcDetailsCardSecondRow(
                                          perim:
                                              qrqcList[index].perimeterName ??
                                                  "no perim",
                                          date: convertDateTimeDisplay(
                                              qrqcList[index].createdAt!),
                                        ),
                                        const SizedBox(height: 10),
                                      ],
                                    )),
                                  ),
                              fallback: null),
                          ConditionalBuilder(
                              condition: qrqcList[index].status == 'ESCALATED',
                              builder: (context) => QrqcBody(
                                    child: QrqcCard(
                                        child: QrqcCardBody(
                                      color: Colors.red,
                                      text: 'ESCAL',
                                      leading:
                                          QrqcCardLeaing(imgPath: _setImage()),
                                      trailing: QrqcCardtrailing(
                                        text:
                                            qrqcList[index].progress.toString(),
                                        percent:
                                            qrqcList[index].progress.toString(),
                                      ),
                                      title: qrqcList[index].id.toString(),
                                      subtitle: qrqcList[index].title,
                                      chlidren: [
                                        QrqcDetailsCardFirstRow(
                                          product: "No product",
                                          role:
                                              qrqcList[index].role ?? "no role",
                                        ),
                                        const SizedBox(height: 10),
                                        QrqcDetailsCardSecondRow(
                                          perim:
                                              qrqcList[index].perimeterName ??
                                                  "no perim",
                                          date: convertDateTimeDisplay(
                                              qrqcList[index].createdAt!),
                                        ),
                                        const SizedBox(height: 10),
                                      ],
                                    )),
                                  ),
                              fallback: null)
                        ],
                      );
                    });
                return Stack(
                  children: [
                    listView,
                    ConditionalBuilder(
                        condition: isBottom == true && _visible == true,
                        builder: (context) => Positioned(
                              left: 100,
                              right: 100,
                              bottom: 0,
                              child: Container(
                                width: 80,
                                height: 80,
                                child:
                                    Center(child: CircularProgressIndicator()),
                              ),
                            ),
                        fallback: null),
                  ],
                );
              },
              loadNext: () {
                if (qrqcList!.length == _count) {
                  setState(() {
                    _count += 20;
                  });
                }
                print(_count);
              },
            );
          } else if (snapshot.hasError) {
            return NoDataUI();
          }
          return Center(
            child: CircularProgressIndicator(),
          );
        });
  }
}

String convertDateTimeDisplay(String date) {
  String createdAt = date.substring(1, 10);
  final DateFormat displayFormater = DateFormat('yyyy-MM-dd');
  final DateFormat serverFormater = DateFormat('dd-MM-yyyy');
  final DateTime displayDate = displayFormater.parse(createdAt);
  final String formatted = serverFormater.format(displayDate);
  return formatted;
}

class ScrollListener extends StatefulWidget {
  final Widget Function(BuildContext, ScrollController) builder;
  final VoidCallback loadNext;
  final double threshold;
  ScrollListener({
    required this.threshold,
    required this.builder,
    required this.loadNext,
  });

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

class _ScrollListener extends State<ScrollListener> {
  ScrollController _controller = ScrollController();

  @override
  void initState() {
    super.initState();
    _controller.addListener(() {
      final rate = _controller.offset / _controller.position.maxScrollExtent;
      if (widget.threshold <= rate) {
        widget.loadNext();
      }
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return widget.builder(context, _controller);
  }
}




I was using two disposed methods , In my ListView class and the scroll listener class which gave me the following exception :

a scrollcontroller was used after being disposed

So I removed the disposed method that was in my ListView class , which I assume , resulted in a slow scroll , how can I fix this ? I'd be grateful for your your help

1 Answers

It might be partly due to you running the app in the default debug mode. When active, Flutter compiles code as the app is running, causing it to slow down (for example when scrolling large/infinite lists). Its advantage are the hot reload capabilities while debugging.

Does running the app in profile mode prevent the issue from occuring? You can use flutter run --profile or use the --profile flag as an 'additional run arg' in your run configuration (Android Studio).

As an aside, I would suggest disposing the controller when the widget is destroyed

Related