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

Flutter中使用CustomScrollView实现无限滚动的Provider调用问题

Flutter CustomScrollView 无限滚动实现方案

核心问题解决

1. onScroll 中调用 Provider 的方法

State 类本身自带全局可用的 context 成员,只要组件处于挂载(mounted == true)状态就可以直接使用,不需要额外传参。你之前注释的代码只需要补充挂载判断和加载锁即可正常运行。

2. 初始化加载的最佳实践

你当前用的 Timer.run 是为了等待组件初始化完成再获取上下文,更规范的实现是用 Flutter 官方提供的首帧回调API WidgetsBinding.instance.addPostFrameCallback,不需要依赖定时器。

优化后完整实现代码

class _FeedScreenState extends State<FeedScreen> {
  final _scrollController = ScrollController();
  double _prevScrollPos = 0.0;
  // 加载锁,避免重复触发请求
  bool _isLoadingMore = false;

  _onScroll() async {
    if (_isLoadingMore) return; // 加载中直接跳过
    
    double maxScroll = _scrollController.position.maxScrollExtent;
    double currentScrollPos = _scrollController.position.pixels;
    double delta = 200.0;

    if (maxScroll - currentScrollPos <= delta && _prevScrollPos - currentScrollPos < 0 && mounted) {
      _isLoadingMore = true;
      // 直接使用State自带的context调用provider
      await Provider.of<GlobalFeeds>(context, listen: false).addPost();
      _isLoadingMore = false;
    }

    _prevScrollPos = currentScrollPos;
  }

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_onScroll);
    // 官方推荐的首帧完成后执行初始化加载
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (mounted) {
        Provider.of<GlobalFeeds>(context, listen: false).addPost();
      }
    });
  }

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

  @override
  Widget build(BuildContext context) {
    final globalFeeds = Provider.of<GlobalFeeds>(context);
    final posts = globalFeeds.posts;

    return Scaffold(
      body: CustomScrollView(
        controller: _scrollController,
        slivers: [
          SliverAppBar(
            // 原有实现保留
          ),
          SliverList(
            delegate: SliverChildBuilderDelegate((context, index) {
              // 可选:底部加加载中指示器
              if (index == posts.length) {
                return _isLoadingMore 
                  ? const Padding(padding: EdgeInsets.all(16), child: CircularProgressIndicator())
                  : const SizedBox.shrink();
              }
              final Post post = posts[index];
              return PostContainer(post: post);
            },
            childCount: posts.length + 1, // 多一个位置放加载指示器
            ),
          ),
        ],
      )
    );
  }
}

额外优化建议

  • 可以把加载状态 isLoadingMore 放到 GlobalFeeds provider 中管理,跨组件共享加载状态更方便
  • 增加是否还有更多数据的判断(比如后端返回空列表时标记hasMore = false),没有更多数据时直接停掉滚动监听,避免无意义的判断
  • 请求失败时重置 _isLoadingMore 标记,避免后续无法触发加载

内容的提问来源于stack exchange,提问作者Jérémie Chazelle

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.17 05:45:04