You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中Firebase无限滚动排行榜重复条目问题排查与修复

Flutter Firebase Realtime Database 无限滚动排行榜重复条目问题修复

代码功能说明

这段Flutter代码实现了从Firebase Realtime Database获取排行榜数据的无限滚动功能,核心逻辑如下:

  • 初始化与滚动控制:在initState中启动数据初始化并绑定滚动监听器,通过_scrollController监控滚动位置;用lastKey追踪分页最后一条数据的键,loadingMore标记是否正在加载更多数据。
  • 滚动监听:_scrollListener检测用户滚动到列表底部时,触发_getLeaderboards加载更多数据。
  • 数据获取:_getLeaderboards先检查网络连接,再分页从Firebase拉取10条数据,追加到现有列表后排序。
  • 错误处理:网络异常时弹出重试弹窗,数据获取失败时重置加载状态。

重复条目问题分析

出现重复条目问题,根源在于分页逻辑处理不当:

  1. 分页查询错误使用endAt:endAt(lastKey)会包含lastKey对应的条目,导致上一批的最后一条被重复加入新批次。
  2. 未过滤重复条目:新获取的条目直接追加到leaderList,没有检查是否已存在,分页重叠时就会产生重复。
  3. 加载状态重置时机滞后:loadingMore在排序和处理排行榜后才重置,此时重复条目可能已经被添加到列表中。

原问题代码

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

// 追踪分页最后一条数据的键
String? lastKey;

// 标记是否正在加载更多数据
bool loadingMore = false;

// 滚动控制器
final ScrollController _scrollController = ScrollController();

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

// 滚动监听:到达列表底部时加载更多
void _scrollListener() {
  if (!_scrollController.position.outOfRange &&
      _scrollController.position.pixels ==
          _scrollController.position.maxScrollExtent) {
    if (!loadingMore) {
      _getLeaderboards();
    }
  }
}

// 获取排行榜数据
_getLeaderboards() async {
  setState(() {
    loadingMore = true;
  });

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

    Query query = dbRef
        .orderByChild('stars')
        .limitToLast(10);
    if (lastKey != null) {
      query = query.endAt(lastKey);
    }

    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;
          });
        });
        setState(() {
          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;
        });
      }
    }).catchError((error) {
      print("Error fetching data: $error");
      loadingMore = false;
    });
  } else {
    if (context.mounted) {
      showDialog(
        context: context,
        builder: (_) => CustomAlert(
          onCancel: () {},
          onConfirm: () {
            Navigator.pop(context);
            _getLeaderboards();
          },
          desc: 'No Network\nPlease connect and retry',
          image: 'images/icons/no_wifi.png',
          confirmText: "Retry",
          isSingleButton: true,
        ),
      );
    }
    loadingMore = false;
  }
}

修复方案

针对问题点,调整分页逻辑并添加重复过滤:

  1. 替换分页查询方法:用startAfter(lastKey)替代endAt(lastKey),确保排除已获取的最后一条数据。
  2. 添加重复检查:将新条目加入leaderList前,通过any方法检查是否已存在相同key的条目。

修复后代码

// 获取排行榜数据(修复版)
_getLeaderboards() async {
  setState(() {
    loadingMore = true;
  });

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

    Query query = dbRef
        .orderByChild('stars')
        .limitToLast(10);
    if (lastKey != null) {
      query = query.startAfter(lastKey);
    }

    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(() {
          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;
        });
      }
    }).catchError((error) {
      print("Error fetching data: $error");
      setState(() {
        loadingMore = false;
      });
    });
  } else {
    if (context.mounted) {
      showDialog(
        context: context,
        builder: (_) => CustomAlert(
          onCancel: () {},
          onConfirm: () {
            Navigator.pop(context);
            _getLeaderboards();
          },
          desc: 'No Network\nPlease connect and retry',
          image: 'images/icons/no_wifi.png',
          confirmText: "Retry",
          isSingleButton: true,
        ),
      );
    }
    setState(() {
      loadingMore = false;
    });
  }
}

内容的提问来源于stack exchange,提问作者Sudhanshu Saurabh Gupta

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 06:57:06