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

Flutter中使用scrollable_positioned_list时如何重启滚动?

scrollable_positioned_list自动滚动二次触发失效问题解决

问题现象

使用scrollable_positioned_list包实现自动滚动功能时,首次从索引0滚动到最后索引一切正常,但再次从索引0触发滚动时完全失效;手动滑动列表一次后,第二次滚动又能恢复正常。

相关代码

初始化代码

// Initialization
final ItemScrollController itemScrollController = ItemScrollController();
final ItemPositionsListener itemPositionsListener =
    ItemPositionsListener.create();

列表组件实现

// Positioned List Widget Usage
Widget _detailedReportList() {
  return Expanded(
    child: ScrollablePositionedList.separated(
      itemScrollController: itemScrollController,
      itemPositionsListener: itemPositionsListener,
      // shrinkWrap: true,
      padding: const EdgeInsets.fromLTRB(16, 0, 16, 60),
      itemBuilder: (context, index) {
        return _detailedReportListItem(
          context,
          index,
          _userErrorFramesUri(index),
          _trainerErrorFramesUri(index),
          _errorFramesText(index),
          _errorTimeInSeconds(index),
          _errorFrameIdx(index),
        );
      },
      separatorBuilder: (context, index) {
        return SizedBox(
          height: 2,
        );
      },
      itemCount: _detailedReportListCount(),
    ),
  );
}

自动滚动逻辑

// Autoscrolling listview code
if (itemScrollController.isAttached) {
   itemScrollController.scrollTo(
   index: errortimeList.indexOf(searchValue),
   duration: Duration(milliseconds: 300),
   curve: Curves.easeInOutCubic);
   if (!mounted) {
     return;
   }
   if (mounted) {
     // For changing the list item that is currently scrolled to
     setState(() {
       _selectedListIndex = errortimeList.indexOf(searchValue);
     });
   }
}

解决方案

这个问题本质是列表回到索引0后,ItemScrollController的内部状态未正确同步,导致后续滚动请求被忽略。以下是几种可行的解决办法:

1. 给滚动请求加微小延迟

当从索引0触发滚动时,延迟100ms左右执行,确保列表状态已经稳定:

if (itemScrollController.isAttached) {
  final targetIndex = errortimeList.indexOf(searchValue);
  Future.delayed(const Duration(milliseconds: 100), () {
    if (mounted && itemScrollController.isAttached) {
      itemScrollController.scrollTo(
        index: targetIndex,
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeInOutCubic,
      );
      setState(() {
        _selectedListIndex = targetIndex;
      });
    }
  });
}

2. 触发列表状态刷新后再滚动

在调用scrollTo前先执行一次空的setState,强制更新列表状态:

if (itemScrollController.isAttached) {
  final targetIndex = errortimeList.indexOf(searchValue);
  setState(() {}); // 刷新列表状态
  itemScrollController.scrollTo(
    index: targetIndex,
    duration: const Duration(milliseconds: 300),
    curve: Curves.easeInOutCubic,
  );
  setState(() {
    _selectedListIndex = targetIndex;
  });
}

3. 先跳转再平滑滚动

如果scrollTo直接失效,先用jumpTo定位到目标索引,再执行平滑滚动:

if (itemScrollController.isAttached) {
  final targetIndex = errortimeList.indexOf(searchValue);
  itemScrollController.jumpTo(index: targetIndex);
  Future.delayed(const Duration(milliseconds: 50), () {
    if (mounted && itemScrollController.isAttached) {
      itemScrollController.scrollTo(
        index: targetIndex,
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeInOutCubic,
      );
    }
  });
  setState(() {
    _selectedListIndex = targetIndex;
  });
}

4. 监听列表位置状态再触发滚动

通过itemPositionsListener监听当前列表的位置,确保滚动请求发出时列表不在过渡状态:

// 初始化时添加监听
@override
void initState() {
  super.initState();
  itemPositionsListener.itemPositions.addListener(_onListPositionChanged);
}

void _onListPositionChanged() {
  // 可以在这里记录当前列表的位置状态
}

// 触发滚动时
if (itemScrollController.isAttached) {
  final positions = itemPositionsListener.itemPositions.value;
  if (positions.isNotEmpty) {
    final currentIndex = positions.first.index;
    final targetIndex = errortimeList.indexOf(searchValue);
    if (currentIndex == 0) {
      Future.delayed(const Duration(milliseconds: 100), () {
        itemScrollController.scrollTo(
          index: targetIndex,
          duration: const Duration(milliseconds: 300),
          curve: Curves.easeInOutCubic,
        );
        setState(() {
          _selectedListIndex = targetIndex;
        });
      });
    } else {
      itemScrollController.scrollTo(
        index: targetIndex,
        duration: const Duration(milliseconds: 300),
        curve: Curves.easeInOutCubic,
      );
      setState(() {
        _selectedListIndex = targetIndex;
      });
    }
  }
}

// 记得在dispose时移除监听
@override
void dispose() {
  itemPositionsListener.itemPositions.removeListener(_onListPositionChanged);
  super.dispose();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 16:10:29