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

iOS无Home键设备PiP模式Overlay Widget触摸区域异常问题

问题:iOS无Home键设备上PiP模式Overlay Widget点击区域仅下半部分有效

我开发的音乐播放器实现了PiP模式的Overlay Widget,显示位置正常,但点击按钮时功能异常——只有Widget下半部分(红色方框区域)能触发点击。安卓设备及带Home键的iPhone无此问题,仅iPhone X及后续无Home键机型出现该异常。

相关代码

AudioTitleOverlayWidget 代码

class AudioTitleOverlayWidget extends StatefulWidget {
  final Function onClear;
  final Widget widget;

  const AudioTitleOverlayWidget(
      {Key? key, required this.onClear, required this.widget})
      : super(key: key);

  @override
  _AudioTitleOverlayWidgetState createState() =>
      _AudioTitleOverlayWidgetState();
}

class _AudioTitleOverlayWidgetState extends State<AudioTitleOverlayWidget> {
  late double width;
  late double oldWidth;
  late double oldHeight;
  late double height;

  bool isInPipMode = false;

  Offset offset = const Offset(0, 500);

  _onExitPipMode() {
    Future.microtask(() {
      setState(() {
        isInPipMode = false;
        width = oldWidth;
        height = oldHeight;
        offset = const Offset(0, 0);
      });
    });
    Future.delayed(const Duration(milliseconds: 250), () {
      Get.find<OverlayHandler>().disablePip();
    });
  }

  _onPipMode() {
    Future.delayed(const Duration(milliseconds: 100), () {
      setState(() {
        isInPipMode = true;
        width = oldWidth;
        height = Constants.VIDEO_TITLE_HEIGHT_PIP - 6.0;
        // height = 20;
        // offset = Offset(0, oldHeight - height - Constants.BOTTOM_PADDING_PIP);
        offset = Offset(0, height * 7.55);
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    final bottomPadding = MediaQuery.of(context).padding.bottom;
    oldWidth = width = Get.width;
    oldHeight = height = Get.height;
    final overlayController = Get.put(OverlayHandler());
    final hasBottomTab = overlayController.hasBottomTab;
    return GetBuilder<OverlayHandler>(builder: (context) {
      if (Get.find<OverlayHandler>().inPipMode != isInPipMode) {
        isInPipMode = Get.find<OverlayHandler>().inPipMode;
        if (isInPipMode) {
          _onPipMode();
        } else {
          _onExitPipMode();
        }
      }

      if(Get.currentRoute == '/home'){
        overlayController.hasBottomTab.value = true;
      } else{
        overlayController.hasBottomTab.value = false;
      }

      return Obx(()=>Positioned(
        // duration: const Duration(milliseconds: 200),
        // curve: Curves.fastOutSlowIn,
        left:
        Get.isBottomSheetOpen == true
            ? 10000 //final bottomPadding = MediaQuery.of(context).padding.bottom;
            : offset.dx,
        top: (isInPipMode == true && hasBottomTab.value == false)
            ? Get.height - 80 - bottomPadding
            : (isInPipMode == true && hasBottomTab.value == true)
            ? Get.height - 160 - bottomPadding
            : (isInPipMode == false && hasBottomTab.value == true)
            ? bottomPadding
            : bottomPadding,
        bottom: (isInPipMode == true && hasBottomTab.value == false)
            ? bottomPadding
            : (isInPipMode == true && hasBottomTab.value == true)
            ? 80 + bottomPadding
            : (isInPipMode == false && hasBottomTab.value == true)
            ? bottomPadding
            : bottomPadding,
        child: SizedBox(
          height: height,
          width: width,
          child: widget.widget, // the code of the widget is below:
        ),
      ));
    });
  }
}

PiP模式下Overlay Widget代码片段

@override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      backgroundColor: const Color(0xff1c1c1e),
      body: GetBuilder<OverlayHandler>(
        builder: (getContext) {
          if (!Get.find<OverlayHandler>().inPipMode != true) {
              return Stack(
                     children: [
                       Positioned(
                           child: GestureDetector(
                               behavior: HitTestBehavior.translucent,
                               onTap: () {Get.find<OverlayHandler>().disablePip();},
                               child: Column(
                                 children: [
                                   Container(
                                       width: Get.width,
                                       height: 80,
                                   
                                    .......
                                   )
                                 ]
                               )
                            )
                        )
                     .....
         

解决方案

1. 移除Positioned的bottom属性,统一用top控制位置

同时设置top和bottom会强制约束SizedBox的高度,结合iOS底部安全区域(Home Indicator)的padding,导致实际可点击区域与视觉区域不符。修改后代码:

return Obx(()=>Positioned(
  left: Get.isBottomSheetOpen == true ? 10000 : offset.dx,
  top: (isInPipMode == true && hasBottomTab.value == false)
      ? Get.height - 80 - bottomPadding
      : (isInPipMode == true && hasBottomTab.value == true)
      ? Get.height - 160 - bottomPadding
      : bottomPadding,
  // 移除bottom属性,避免高度被强制约束
  child: SizedBox(
    height: height,
    width: width,
    child: widget.widget,
  ),
));

2. 调整PiP模式下的height与offset计算,适配安全区域

在_onPipMode中加入底部安全区域的计算,确保Widget不会被安全区域覆盖:

_onPipMode() {
  final bottomPadding = MediaQuery.of(context).padding.bottom;
  Future.delayed(const Duration(milliseconds: 100), () {
    setState(() {
      isInPipMode = true;
      width = oldWidth;
      height = Constants.VIDEO_TITLE_HEIGHT_PIP - 6.0 + bottomPadding;
      offset = Offset(0, oldHeight - height);
    });
  });
}

3. 修改GestureDetector的点击行为

将HitTestBehavior.translucent改为HitTestBehavior.opaque,确保GestureDetector能接收整个Widget区域的点击:

GestureDetector(
    behavior: HitTestBehavior.opaque,
    onTap: () {Get.find<OverlayHandler>().disablePip();},
    child: Column(
      children: [
        Container(
            width: Get.width,
            height: 80,
            // ...其他代码
        )
      ]
    )
)

4. 避免在build方法中重复初始化控制器

将Get.put(OverlayHandler())改为Get.find<OverlayHandler>(),防止每次build重新创建控制器:

@override
Widget build(BuildContext context) {
  final bottomPadding = MediaQuery.of(context).padding.bottom;
  oldWidth = width = Get.width;
  oldHeight = height = Get.height;
  final overlayController = Get.find<OverlayHandler>();
  final hasBottomTab = overlayController.hasBottomTab;
  // ...其他代码
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 01:40:45