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

Flutter:Stack内上层ScrollView阻挡下层视频手势的解决方法

解决方案:视差视频播放器手势冲突处理

核心问题是SingleChildScrollView位于Stack上层,同时拦截了滚动和点击手势。我们可以通过拆分ScrollView的子组件,让顶部空白区域的手势穿透到下层视频,同时保留滚动功能:

  • 给ScrollView内顶部的空白SizedBox添加GestureDetector,设置behavior: HitTestBehavior.translucent,使该区域的手势向下传递给视频组件
  • 下方的内容区域正常保留手势拦截,确保滚动和内容交互不受影响

修改后的完整代码

import "package:black_coffer/theme/colors.dart";
import "package:flutter/material.dart";
import "package:video_player/video_player.dart";
import '../../models/post.dart';
import "package:chewie/chewie.dart";

class PostScreen extends StatefulWidget {
  const PostScreen({super.key, required this.post});
  final Post post;
  @override
  State<PostScreen> createState() => _PostScreenState();
}

class _PostScreenState extends State<PostScreen> {
  late VideoPlayerController _controller;
  late ChewieController _chewieController;
  double videoOffset = 0;
  @override
  void initState() {
    super.initState();
    _controller =
        VideoPlayerController.networkUrl(Uri.parse(widget.post.videoUrl))
          ..initialize().then((_) {
            setState(() {
              _chewieController = ChewieController(
                  videoPlayerController: _controller,
                  showOptions: false,
                  allowFullScreen: false,
                  aspectRatio: _controller.value.aspectRatio);
            });
          });
  }

  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
    _chewieController.dispose();
  }

  @override
  Widget build(BuildContext context) {
    var height = MediaQuery.of(context).size.height;
    return Scaffold(
      body: NotificationListener(
        onNotification: (notif) {
          if (notif is ScrollUpdateNotification) {
            if (notif.scrollDelta == null) return true;
            setState(() {
              videoOffset -= notif.scrollDelta! / 1.9;
            });
          }
          return true;
        },
        child: Stack(children: [
          Positioned(
            top: videoOffset,
            left: 0,
            right: 0,
            child: Hero(
                tag: widget.post.videoUrl,
                child: SizedBox(
                  height: height * 0.7,
                  child: AspectRatio(
                    aspectRatio: _controller.value.aspectRatio,
                    child: _controller.value.isInitialized
                        ? Chewie(
                            controller: _chewieController,
                          )
                        : Container(
                            alignment: Alignment.center,
                            child: const CircularProgressIndicator.adaptive(),
                          ),
                  ),
                )),
          ),
          SingleChildScrollView(
            physics: const ClampingScrollPhysics(),
            child: Column(
              children: [
                // 顶部空白区域:手势穿透到视频,同时保留滚动识别
                SizedBox(
                  height: height * 0.7,
                  child: GestureDetector(
                    behavior: HitTestBehavior.translucent,
                    // 可选:添加点击控制视频播放/暂停的逻辑
                    onTap: () {
                      setState(() {
                        _controller.value.isPlaying
                            ? _controller.pause()
                            : _controller.play();
                      });
                    },
                  ),
                ),
                Container(
                  height: height,
                  color: getColors(context).background,
                  child: Column(
                    children: [
                      Text(widget.post.videoTitle),
                      Text(widget.post.videoDescription),
                      Text(widget.post.videoCategory),
                      Text(widget.post.videoLocation),
                    ],
                  ),
                ),
              ],
            ),
          )
        ]),
      ),
    );
  }
}

原理说明

  • HitTestBehavior.translucent会让GestureDetector既不拦截手势,也不会阻止手势向下传递,视频组件就能正常接收点击事件
  • SingleChildScrollView依然能捕获滚动事件,因为滚动是基于整个视图的触摸滑动,穿透的手势不影响滚动的识别
  • 如果需要保留Chewie自带的手势(比如双击暂停、进度拖动),不需要额外处理,因为Chewie的手势组件会接收到穿透过来的触摸事件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 10:37:02