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

Flutter:为ListView.builder添加ScrollController后如何保持滚动流畅?

解决NestedScrollView中ListView使用自定义ScrollController后的滚动流畅问题

当给ListView.builder添加自定义ScrollController后,它会脱离NestedScrollView的滚动协调机制——原本NestedScrollView会统一管理头部Sliver和body滚动组件的滚动状态,自定义控制器打破了这种联动,导致滚动出现断层、不流畅的情况。

下面是两种可行的解决方法:

方案1:使用NestedScrollView内置控制器(推荐)

不需要给ListView单独设置控制器,而是通过NestedScrollController来统一管理整个嵌套滚动,同时可以通过其innerController属性控制body部分的滚动:

import 'package:flutter/material.dart';

class Profile extends StatefulWidget {
  const Profile({ Key? key, }) : super(key: key);
  @override
  ProfileState createState() => ProfileState();
}

class ProfileState extends State<Profile>{
  final NestedScrollController _nestedScrollController = NestedScrollController();

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

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: NestedScrollView(
        controller: _nestedScrollController,
        headerSliverBuilder: (context,value){
          return[
            const SliverAppBar(
              expandedHeight: 400,
            )
          ];
        },
        body: ListView.builder(
          itemCount: 200,
          itemBuilder: (BuildContext context, int index) {
            return Center(child: Text(index.toString()));
          },
        )
      ),
    );
  }

  // 示例:控制body滚动到顶部
  void scrollBodyToTop() {
    _nestedScrollController.innerController.jumpTo(0);
  }
}

这种方式完全复用了NestedScrollView的原生滚动协调逻辑,能完美保持原本的流畅滚动效果。

方案2:关联自定义控制器与NestedScrollView的inner控制器

如果业务场景必须使用自定义ScrollController,可以通过监听同步的方式,将自定义控制器与NestedScrollView的inner控制器关联起来:

import 'package:flutter/material.dart';

class Profile extends StatefulWidget {
  const Profile({ Key? key, }) : super(key: key);
  @override
  ProfileState createState() => ProfileState();
}

class ProfileState extends State<Profile>{
  final ScrollController _listController = ScrollController();
  NestedScrollController? _nestedController;

  @override
  void initState() {
    super.initState();
    // 同步自定义控制器的滚动偏移到NestedScrollView的inner控制器
    _listController.addListener(() {
      if (_nestedController?.innerController.hasClients ?? false) {
        _nestedController!.innerController.jumpTo(_listController.offset);
      }
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: NestedScrollView(
        onControllerCreated: (controller) {
          _nestedController = controller;
          // 初始化时同步偏移量
          _nestedController!.innerController.jumpTo(_listController.offset);
        },
        headerSliverBuilder: (context,value){
          return[
            const SliverAppBar(
              expandedHeight: 400,
            )
          ];
        },
        body: ListView.builder(
          controller: _listController,
          primary: false, // 必须设为false,避免与PrimaryScrollController冲突
          itemCount: 200,
          itemBuilder: (BuildContext context, int index) {
            return Center(child: Text(index.toString()));
          },
        )
      ),
    );
  }
}

重要提示

  • 给ListView设置自定义控制器时,必须将primary设为false,否则会与Flutter的PrimaryScrollController机制冲突,引发滚动异常。
  • 优先选择方案1,它的实现更简洁,且能最大程度保证滚动的流畅性。

内容的提问来源于stack exchange,提问作者Mohammed Hamdan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 19:31:21