Issue: Duplicate Items in Infinite Scrolling Firebase Leaderboard in Flutter

Code Explanation
The provided code implements an infinite scrolling feature in a Flutter app that retrieves leaderboard data from Firebase Realtime Database. Here’s a breakdown of how the code works:

Initialization and Scroll Controller Setup:
The initState method is overridden to initialize the leaderboard data retrieval and set up a scroll listener.
_scrollController is created to monitor the scroll position.
Variable Declarations:

lastKey: Tracks the last retrieved key for pagination.
loadingMore: Flags whether more items are currently being loaded.
_scrollController: Controls the scrolling behavior.
Disposing the Scroll Controller:

The dispose method is overridden to properly dispose of the scroll controller.
Scroll Listener Method:

_scrollListener detects when the user reaches the end of the list and triggers _getLeaderboards to load more items if loadingMore is false.
Leaderboard Data Retrieval:

_getLeaderboards is an asynchronous method that fetches leaderboard data from Firebase.
It first checks for network connectivity using NetworkHelper.checkNetwork.
It then queries the Firebase database for the leaderboard items, using pagination to fetch items in batches of 10.
If lastKey is not null, it appends the new items to the existing list, updates the lastKey, and sorts the leaderboard.
Error Handling:

If an error occurs during data retrieval, it prints the error and resets loadingMore.
If there is no network, it shows a dialog prompting the user to retry.
Issue of Getting Repeated List
The issue of getting a repeated list likely arises due to the way pagination is handled. Specifically, the following points may contribute to this issue:

Incorrect Use of endAt for Pagination:

The query uses .endAt(lastKey), which includes the item with lastKey in the results. This can cause the last item of the previous batch to be included again in the new batch, leading to duplicates.
Appending Items Without Proper Filtering:

The retrieved items are appended directly to the leaderList without checking if they are already present. This can result in duplicate entries if the pagination query overlaps.
Resetting loadingMore Flag:

The loadingMore flag is reset after sorting and processing the leaderboard, but duplicates might already be added by that time.

`

@override
  void initState() {
    _getLeaderboards();
    super.initState();
    _scrollController.addListener(_scrollListener);
  }

  // Define a variable to track the last retrieved key for pagination
  String? lastKey;

// Define a variable to track whether more items are being loaded
  bool loadingMore = false;

// Set up a ScrollController
  final ScrollController _scrollController = ScrollController();

// Dispose the ScrollController in dispose
  @override
  void dispose() {
    _scrollController.dispose();
    super.dispose();
  }

// Scroll listener method to detect when the user reaches the end of the list
  void _scrollListener() {
    if (!_scrollController.position.outOfRange &&
        _scrollController.position.pixels ==
            _scrollController.position.maxScrollExtent) {
      // User has scrolled to the end of the list, load more items
      if (!loadingMore) {
        _getLeaderboards(); // Load more items
      }
    }
  }

  // Modify _getLeaderboards to load more items
  _getLeaderboards() async {
    setState(() {
      loadingMore = true; // Set loading flag
    });

    bool network = await NetworkHelper.checkNetwork();
    if (network) {
      final dbRef = FirebaseDatabase.instance.ref().child('LeaderBoard');

      Query query = dbRef
          .orderByChild('stars')
          .limitToLast(10); // Fetching 10 items initially
      if (lastKey != null) {
        query = query.endAt(lastKey); // Exclude the last retrieved item
      }

      query.once().then((DatabaseEvent event) {
        Map<dynamic, dynamic>? map = event.snapshot.value as Map?;
        if (map != null) {
          map.forEach((key, value) {
            Leaderboard leaderboard = Leaderboard.fromSnapshot(value);
            setState(() {
              leaderList.add(leaderboard);
              lastKey = key;
            });
            // Update lastKey for next pagination
          });
          setState(() {
            // Sort the list and handle pagination
            leaderList.sort((a, b) => a.stars!.compareTo(b.stars!));
            leaderList = leaderList.reversed.toList();
            if (leaderList.isNotEmpty) {
              gold = leaderList.elementAt(0);
              leaderList.removeAt(0);
            }
            if (leaderList.isNotEmpty) {
              silver = leaderList.elementAt(0);
              leaderList.removeAt(0);
            }
            if (leaderList.isNotEmpty) {
              bronze = leaderList.elementAt(0);
              leaderList.removeAt(0);
            }

            loadingMore = false; // Reset loading flag
          });
        }
      }).catchError((error) {
        print("Error fetching data: $error");
        // Handle error here
        loadingMore = false; // Reset loading flag
      });
    } else {
      // Show dialog if there's no network
      if (context.mounted) {
        showDialog(
          context: context,
          builder: (_) => CustomAlert(
            onCancel: () {},
            onConfirm: () {
              Navigator.pop(context);
              _getLeaderboards();
            },
            desc: 'No NetworknPlease connect and retry',
            image: 'images/icons/no_wifi.png',
            confirmText: "Retry",
            isSingleButton: true,
          ),
        );
      }
      loadingMore = false; // Reset loading flag
    }
  }

Tried Solution
To resolve the issue of repeated items, you should modify the pagination logic to exclude the last retrieved item and ensure no duplicates are appended. Here’s a suggested fix:

Modify Pagination Query:

Use .startAfter(lastKey) instead of .endAt(lastKey) to exclude the last retrieved item from the new batch.
Check for Duplicates Before Appending:

Ensure that the new items are not already present in the leaderList before appending them.
Here’s the updated part of the code:

// Modify _getLeaderboards to load more items
_getLeaderboards() async {
  setState(() {
    loadingMore = true; // Set loading flag
  });

  bool network = await NetworkHelper.checkNetwork();
  if (network) {
    final dbRef = FirebaseDatabase.instance.ref().child('LeaderBoard');

    Query query = dbRef
        .orderByChild('stars')
        .limitToLast(10); // Fetching 10 items initially
    if (lastKey != null) {
      query = query.startAfter(lastKey); // Exclude the last retrieved item
    }

    query.once().then((DatabaseEvent event) {
      Map<dynamic, dynamic>? map = event.snapshot.value as Map?;
      if (map != null) {
        map.forEach((key, value) {
          Leaderboard leaderboard = Leaderboard.fromSnapshot(value);
          if (!leaderList.any((item) => item.key == leaderboard.key)) {
            setState(() {
              leaderList.add(leaderboard);
              lastKey = key;
            });
          }
        });
        setState(() {
          // Sort the list and handle pagination
          leaderList.sort((a, b) => a.stars!.compareTo(b.stars!));
          leaderList = leaderList.reversed.toList();
          if (leaderList.isNotEmpty) {
            gold = leaderList.elementAt(0);
            leaderList.removeAt(0);
          }
          if (leaderList.isNotEmpty) {
            silver = leaderList.elementAt(0);
            leaderList.removeAt(0);
          }
          if (leaderList.isNotEmpty) {
            bronze = leaderList.elementAt(0);
            leaderList.removeAt(0);
          }

          loadingMore = false; // Reset loading flag
        });
      }
    }).catchError((error) {
      print("Error fetching data: $error");
      // Handle error here
      setState(() {
        loadingMore = false; // Reset loading flag
      });
    });
  } else {
    // Show dialog if there's no network
    if (context.mounted) {
      showDialog(
        context: context,
        builder: (_) => CustomAlert(
          onCancel: () {},
          onConfirm: () {
            Navigator.pop(context);
            _getLeaderboards();
          },
          desc: 'No NetworknPlease connect and retry',
          image: 'images/icons/no_wifi.png',
          confirmText: "Retry",
          isSingleButton: true,
        ),
      );
    }
    setState(() {
      loadingMore = false; // Reset loading flag
    });
  }
}

New contributor

Sudhanshu Saurabh Gupta is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật