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

Flutter:ScrollablePositionedList在build中调用scrollTo报错求助

问题解决:ScrollablePositionedList在build阶段调用scrollTo报错

问题描述

使用ScrollablePositionedList实现索引列表时,按钮点击事件中调用jumpTo/scrollTo方法可正常运行,但在build方法return语句前调用该方法,会抛出断言错误:'_scrollableListState != null'不成立。

错误日志

E/flutter ( 3018): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: 'package:scrollable_positioned_list/src/scrollable_positioned_list.dart': Failed assertion: line 236 pos 12: '_scrollableListState != null': is not true.

代码示例

@override
Widget build(BuildContext context) {
  // ...
  ItemScrollController idxCtrl = ItemScrollController();
  listenable.listen(
    (p0) {
      idxCtrl.scrollTo(index: p0, duration: Duration(seconds: 1));//--> 抛出错误
  });
  // ...
  return Container(
    // ...
      IconButton(
        onPressed: () {
          idxCtrl.scrollTo(index: 0, duration: Duration(seconds: 1));//--> 正常运行
        },
        icon: Icon(Icons.tab)),
    // ...
      ScrollablePositionedList.builder(
        itemScrollController: idxCtrl,
    // ...
    // ...
    )
  // ...
}

错误原因

核心问题是时机不匹配:在build方法return前调用scrollTo时,ItemScrollController还未与ScrollablePositionedList完成绑定,此时内部的_scrollableListState为null,触发断言失败。而按钮点击事件是在Widget完全渲染完成后触发,控制器已和列表完成绑定,因此能正常执行。

解决方案

方案1:使用WidgetsBinding.instance.addPostFrameCallback

将滚动操作延迟到当前帧渲染完成后执行,确保控制器已与列表绑定:

@override
Widget build(BuildContext context) {
  ItemScrollController idxCtrl = ItemScrollController();
  
  listenable.listen((p0) {
    // 等待当前Widget渲染完成后执行滚动
    WidgetsBinding.instance.addPostFrameCallback((_) {
      idxCtrl.scrollTo(index: p0, duration: const Duration(seconds: 1));
    });
  });

  return Container(
    // ...
    ScrollablePositionedList.builder(
      itemScrollController: idxCtrl,
      // ...
    ),
    // ...
  );
}

方案2:将控制器提升为State成员变量

不在build方法内创建ItemScrollController,而是在State初始化阶段创建,保证控制器生命周期与Widget一致,同时结合帧回调确保执行时机:

class _YourWidgetState extends State<YourWidget> {
  late final ItemScrollController idxCtrl;
  late final Listenable yourListenable;

  @override
  void initState() {
    super.initState();
    idxCtrl = ItemScrollController();
    yourListenable = ...; // 初始化你的监听对象
    
    yourListenable.listen((p0) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted) {
          idxCtrl.scrollTo(index: p0, duration: const Duration(seconds: 1));
        }
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      // ...
      IconButton(
        onPressed: () {
          idxCtrl.scrollTo(index: 0, duration: const Duration(seconds: 1));
        },
        icon: const Icon(Icons.tab)),
      // ...
      ScrollablePositionedList.builder(
        itemScrollController: idxCtrl,
        // ...
      ),
      // ...
    );
  }
}

方案3:通过ItemPositionsListener确认列表状态

监听列表的位置变化,确认列表已初始化完成后再执行滚动操作:

class _YourWidgetState extends State<YourWidget> {
  late final ItemScrollController idxCtrl;
  late final ItemPositionsListener itemPositionsListener;
  late final Listenable yourListenable;
  bool _isListReady = false;

  @override
  void initState() {
    super.initState();
    idxCtrl = ItemScrollController();
    itemPositionsListener = ItemPositionsListener.create();
    yourListenable = ...;

    // 监听列表位置,标记列表已准备就绪
    itemPositionsListener.itemPositions.addListener(() {
      if (!_isListReady && itemPositionsListener.itemPositions.value.isNotEmpty) {
        setState(() {
          _isListReady = true;
        });
      }
    });

    yourListenable.listen((p0) {
      if (_isListReady && mounted) {
        idxCtrl.scrollTo(index: p0, duration: const Duration(seconds: 1));
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      // ...
      ScrollablePositionedList.builder(
        itemScrollController: idxCtrl,
        itemPositionsListener: itemPositionsListener,
        // ...
      ),
      // ...
    );
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 14:33:38