bihire boris
bihire boris

Reputation: 1662

How to detect and run a loading function when scrolled to the bottom in customScrollView

I am trying to run a load more when the user scrolls to the buttom of the list inside a customScrollView.

I am doing a some sorting with sticky headers so had to use slivers with a flutter_sticky_header package.

this is the page code

class SoldEntryScreen extends StatelessWidget {
  const SoldEntryScreen({
    Key key,
    this.isDataLoading,
    this.isNextPageAvailable,
    this.transactions,
    this.loadNextPage,
    this.noError,
  });

  final bool isDataLoading;
  final bool isNextPageAvailable;
  final transactions;
  final Function loadNextPage;
  final bool noError;

  @override
  Widget build(BuildContext context) {
    var history = {};

    transactions.forEach((element) {
      final formatedDate = Utils.dateFormater(element.createdAt);
      if ((history[formatedDate['dateClass']] == null))
        return (history[formatedDate['dateClass']] = [element]);
      return history[formatedDate['dateClass']].add(element);
    });
    print(history);
    return AppScaffold(
      title: 'List Example',
      slivers: [
        SliverToBoxAdapter(
          child: SoldEntryPage(),
        ),
        _StickyHeaderDateSort(history: history),
      ],
    );
  }
}

class _StickyHeaderList extends StatelessWidget {
  const _StickyHeaderList({
    Key key,
    this.keyTitle,
    this.keyData,
  }) : super(key: key);

  final keyTitle;
  final keyData;

  @override
  Widget build(BuildContext context) {
    print(keyData);
    return SliverStickyHeader(
      header: Header(title: keyTitle),
      sliver: SliverToBoxAdapter(
        child: Container(
          child: ListView.separated(
            shrinkWrap: true,
            physics: ClampingScrollPhysics(),
          itemCount: keyData.length,
          itemBuilder: (context, index) {
            return MoneyTransactionModelListItem(
                            itemIndex: index, transaction: keyData[index]);
          },
          separatorBuilder: (BuildContext context, int index) =>
              Divider(color: Theme.of(context).dividerColor),
        ),
        )
      ),
    );
  }
}

class _StickyHeaderDateSort extends StatelessWidget {
  const _StickyHeaderDateSort({
    Key key,
    this.history,
  }) : super(key: key);

  final history;

  @override
  Widget build(BuildContext context) {
    return SliverStickyHeader(
      header: HeaderDatePicker(),
      sliver: SliverToBoxAdapter(
        child: ShrinkWrappingViewport(
          offset: ViewportOffset.zero(),
          slivers: [
            for (var key in history.keys)
              _StickyHeaderList(keyTitle: key, keyData: history[key])
          ],
        ),
      ),
    );
  }
}

which looks like something like this

enter image description here

this is the AppScaffold widget

class AppScaffold extends StatelessWidget {
  const AppScaffold({
    Key key,
    @required this.title,
    @required this.slivers,
    this.reverse = false,
  }) : super(key: key);

  final String title;
  final List<Widget> slivers;
  final bool reverse;

  @override
  Widget build(BuildContext context) {
    return DefaultStickyHeaderController(
      child: CustomScrollView(
        slivers: slivers,
        reverse: reverse,
      ),
    );
  }
}

Upvotes: 0

Views: 1227

Answers (1)

Lulupointu
Lulupointu

Reputation: 3584

Here is a basic example of a ListView which loads more element when its bottom is hit. Basically the idea that NotificationListener<ScrollNotification> will be notify for each scroll and will give you scrollNotification which you can use to see if you are at the bottom of the ListView.

Here is the code:

import 'package:flutter/material.dart';

main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<Container> containerList = [
    Container(
      color: Colors.red,
      height: 200,
    ),
    Container(
      color: Colors.blue,
      height: 200,
    ),
    Container(
      color: Colors.green,
      height: 200,
    ),
    Container(
      color: Colors.yellow,
      height: 200,
    ),
  ];
  bool isUpdating = false;

  Future<void> addNewWidgets() async {
    isUpdating = true;

    Future.delayed(Duration(seconds: 1), () {
      setState(() {
        containerList.addAll([
          Container(
            color: Colors.red,
            height: 200,
          ),
          Container(
            color: Colors.blue,
            height: 200,
          ),
          Container(
            color: Colors.green,
            height: 200,
          ),
          Container(
            color: Colors.yellow,
            height: 200,
          ),
        ]);
      });

      print('state changed ${containerList.length}');
      isUpdating = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: NotificationListener<ScrollNotification>(
          onNotification: (scrollNotification) {
            if (scrollNotification.metrics.pixels >=
                    scrollNotification.metrics.maxScrollExtent &&
                !isUpdating) {
              addNewWidgets();
            }
            return true;
          },
          child: ListView.builder(
            itemCount: containerList.length,
            itemBuilder: (BuildContext context, int index) {
              return containerList[index];
            },
          ),
        ),
      ),
    );
  }
}

I didn't use sticky_header but I'm sure you will be able to make it work using this method.

Upvotes: 2

Related Questions