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

Flutter中Overlay/Dialog内水平滚动失效问题求助

在Overlay/Dialog中实现可选择水平滚动列表项的问题解决

问题说明

尝试在Overlay或Dialog内实现可选择的水平滚动列表项,已通过ListView在Overlay中展示内容,但水平滚动功能无效,同时出现如下报错:

The following assertion was thrown during a scheduler callback:
A ScrollController is required when Scrollbar.thumbVisibility is true. The Scrollbar was not provided a ScrollController, and attempted to use the PrimaryScrollController, but none was found.
'package:flutter/src/widgets/scrollbar.dart':
package:flutter/…/widgets/scrollbar.dart:1
Failed assertion: line 1468 pos 7: 'scrollController != null'

相关代码

插入Overlay的代码

void _insertOverlay(BuildContext context) {
    late OverlayEntry _overlay;
    _overlay = OverlayEntry(builder: (ctx) {
      final size = MediaQuery.of(context).size;
      print(size.width);
      return Material(
        color: Colors.transparent,
        child: Stack(
          alignment: Alignment.center,
          children: [
            GestureDetector(
              onTap: () => _overlay.remove(),
              child: Container(
                color: Colors.grey.withAlpha(150),
              ),
            ),
            Positioned.fill(
              child: Container(
                width: 500,
                height: 500,
                color: Colors.blue,
                child: Scrollbar(
                  interactive: true,
                  thickness: 20,
                  // thumbVisibility: true,
                  trackVisibility: true,
                  child: ListView.builder(
                      scrollDirection: Axis.horizontal,
                      padding: EdgeInsets.all(2),
                      itemCount: 10,
                      itemBuilder: ((context, index) {
                        return getpriceItem();
                      })),
                ),
              ),
            ),
          ],
        ),
      );
    });
    return Overlay.of(context)?.insert(_overlay);
  }

调用代码

Container(
              decoration: const BoxDecoration(
                  // shape: BoxShape.circle,
                  color: Colors.red),
              child: InkWell(
                  onTap: () => _insertOverlay(context),
                  child: const Center(child: Text('1'))),
              //height: 75.0,
              //width: 75.0,
            )

解决步骤

1. 修复Scrollbar报错

报错原因是开启trackVisibility时,Scrollbar必须绑定ScrollController,而Overlay环境下没有默认的PrimaryScrollController。

修改代码,创建并绑定ScrollController:

void _insertOverlay(BuildContext context) {
    late OverlayEntry _overlay;
    final scrollController = ScrollController();
    _overlay = OverlayEntry(builder: (ctx) {
      final size = MediaQuery.of(context).size;
      print(size.width);
      return Material(
        color: Colors.transparent,
        child: Stack(
          alignment: Alignment.center,
          children: [
            GestureDetector(
              onTap: () {
                scrollController.dispose();
                _overlay.remove();
              },
              child: Container(
                color: Colors.grey.withAlpha(150),
              ),
            ),
            Positioned.fill(
              child: Container(
                width: 500,
                height: 500,
                color: Colors.blue,
                child: Scrollbar(
                  controller: scrollController,
                  interactive: true,
                  thickness: 20,
                  trackVisibility: true,
                  child: ListView.builder(
                      controller: scrollController,
                      scrollDirection: Axis.horizontal,
                      padding: EdgeInsets.all(2),
                      itemCount: 10,
                      itemBuilder: ((context, index) {
                        return getpriceItem();
                      })),
                ),
              ),
            ),
          ],
        ),
      );
    });
    return Overlay.of(context)?.insert(_overlay);
  }

2. 解决水平滚动无效问题

水平滚动无效通常是因为ListView子项没有固定宽度,导致无法计算滚动范围。需要给getpriceItem()返回的Widget设置明确宽度:

Widget getpriceItem() {
  return Container(
    width: 120,
    height: 480,
    margin: EdgeInsets.symmetric(horizontal: 4),
    color: Colors.white,
    child: Center(child: Text('选项')),
  );
}

另外,Positioned.fill会让容器占满屏幕,和手动设置的width:500可能产生约束冲突,建议改用Center包裹容器:

// 替换原Positioned.fill部分
Center(
  child: Container(
    width: 500,
    height: 500,
    color: Colors.blue,
    child: Scrollbar(...),
  ),
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 11:57:17