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

Flutter Stack叠加线性渐变容器时保持PageView可滚动

问题根因
  • Stack上层覆盖的渐变Container默认会拦截命中区域的所有手势,即使区域是完全透明的,触摸事件也不会透传给下层的PageView,直接导致滚动失效。
  • Expanded仅可在Column/Row/Flex这类弹性布局组件内使用,直接放入Stack的子节点列表属于语法错误,会触发布局异常。
  • 现有代码重复实现了两次完全一致的线性渐变,属于冗余逻辑。
修复方案

核心逻辑是将纯展示用的渐变遮罩设置为不参与手势命中,让触摸事件直接穿透到下层的PageView,同时将需要响应点击的顶部按钮单独放在最上层保证交互可用:

  1. 移除Stack内所有误用的Expanded组件,Stack子节点按层级从下到上排列:最底层放PageView视频流,中间层放渐变遮罩,最顶层放可交互的顶部按钮。
  2. 渐变遮罩层用IgnorePointer包裹,该组件不会拦截任何手势事件,所有触摸操作都会直接透传给下层的PageView,完全不影响纵向滚动。
  3. 删除PageView外层冗余的渐变Container,渐变效果仅需在遮罩层实现一次即可。

修正后的核心页面代码如下:

class FeedScreen extends StatefulWidget {
  static const String id = 'FeedScreen';

  const FeedScreen({Key? key}) : super(key: key);

  @override
  State<FeedScreen> createState() => _FeedScreenState();
}

class _FeedScreenState extends State<FeedScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      endDrawer: CategoriesDrawer(),
      extendBodyBehindAppBar: true,
      bottomNavigationBar: BottomBar(selectedIndex: 0),
      appBar: AppBar(
        actions: [
          Builder(
            builder: (context) => IconButton(
              icon: const Icon(Icons.grid_view_outlined,
                  color: Colors.black, size: 24),
              onPressed: () => Scaffold.of(context).openEndDrawer(),
              tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
            ),
          ),
        ],
        leading: const Image(
          image: AssetImage(
            'assets/images/logo.png',
          ),
          height: 28,
        ),
        toolbarHeight: 36,
        elevation: 0,
        backgroundColor: Colors.white,
      ),
      body: SafeArea(
        child: Stack(
          alignment: Alignment.topCenter,
          children: [
            // 最底层:纵向滚动视频流
            PageView(
              scrollDirection: Axis.vertical,
              children: const [
                Feed(
                    video_url:
                        'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4'),
                Feed(
                    video_url:
                        'https://citjos3.s3.ap-south-1.amazonaws.com/videos/1605781113_1369820376withSound.mp4'),
                Feed(
                    video_url:
                        'https://citjos3.s3.ap-south-1.amazonaws.com/videos/1606913499_1670916107withSound.mp4'),
              ],
            ),
            // 中间层:渐变遮罩,不响应手势,不阻塞滚动
            IgnorePointer(
              child: Container(
                width: double.infinity,
                height: double.infinity,
                decoration: const BoxDecoration(
                  gradient: LinearGradient(
                    begin: Alignment.topCenter,
                    end: Alignment.bottomCenter,
                    colors: [
                      Color(0xFF000000),
                      Colors.transparent,
                      Colors.transparent,
                      Color(0xFF000000),
                    ],
                    // 可自行调整stops值控制上下黑色渐变的高度占比
                    stops: [0.0, 0.15, 0.85, 1.0],
                  ),
                ),
              ),
            ),
            // 最上层:顶部可交互按钮栏
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 24),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  TextButton(
                    onPressed: () {},
                    child: const Text(
                      'Subscribed',
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 18,
                      ),
                    ),
                  ),
                  TextButton(
                    onPressed: () {},
                    child: const Text(
                      'Global',
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 18,
                      ),
                    ),
                  ),
                  TextButton(
                    onPressed: () {},
                    child: const Text(
                      'Local',
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 18,
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

补充说明:渐变配置里的stops参数可以根据UI需求自行调整,示例中[0.0, 0.15, 0.85, 1.0]代表顶部0%-15%高度从纯黑过渡到全透明,底部85%-100%高度从全透明过渡到纯黑,数值越大上下黑色遮罩的覆盖范围越高。

运行效果截图

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 21:30:42