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
相关产品推荐
相关产品推荐

