Flutter中Inkwell双击后背景残留溅墨色的问题求助
解决Flutter InkWell双击与单击冲突导致的溅墨残留问题
问题重现场景
为InkWell组件同时设置onTap和onDoubleTap回调后,执行以下操作会出现异常:
- 先单击组件,间隔约半秒后快速双击
- 溅墨(splash)会缓慢扩散直至填满组件容器,且残留为背景色
- 重复该操作,溅墨颜色会因叠加逐渐变深
问题原因
Flutter框架处理onTap和onDoubleTap时,会默认等待300ms来判断用户操作是单击还是双击。当用户在延迟窗口内完成单击+双击的连续操作时,之前触发的单击溅墨动画未被正确终止,多个动画叠加后就会出现残留、颜色加深的异常效果。
解决方案
方案1:通过GlobalKey手动清除未完成的溅墨
为InkWell绑定GlobalKey,在双击触发时主动清除当前未结束的溅墨动画:
final GlobalKey<InkWellState> _inkWellKey = GlobalKey<InkWellState>(); @override Widget build(BuildContext context) { return Container( height: height, width: width, decoration: decoration, alignment: alignment, margin: margin, child: Material( borderRadius: BorderRadius.circular(borderRadius), clipBehavior: Clip.hardEdge, color: Colors.transparent, shape: shapeBorder, child: InkWell( key: _inkWellKey, highlightColor: Colors.yellow.withOpacity(0.3), splashColor: Colors.red.withOpacity(0.8), focusColor: Colors.green.withOpacity(0.0), hoverColor: Colors.blue.withOpacity(0.8), onTap: onTap, onDoubleTap: () { // 终止当前未完成的溅墨动画 _inkWellKey.currentState?.splashFactory?.dispose(); // 执行原双击逻辑 onDoubleTap?.call(); }, onLongPress: onLongPress, onTapCancel: () { print('tap cancel'); }, canRequestFocus: false, enableFeedback: false, borderRadius: BorderRadius.circular(borderRadius), child: Container( padding: surroundInkEdge ?? EdgeInsets.all(borderRadius), height: height, width: width, alignment: alignment, child: child), ), ), ); }
方案2:用GestureDetector配合InkResponse手动控制溅墨
如果方案1效果不佳,可以改用GestureDetector搭配InkResponse,完全手动控制溅墨的触发与清除,避免框架自带的冲突:
final GlobalKey<InkResponseState> _inkResponseKey = GlobalKey<InkResponseState>(); @override Widget build(BuildContext context) { return Container( height: height, width: width, decoration: decoration, alignment: alignment, margin: margin, child: Material( borderRadius: BorderRadius.circular(borderRadius), clipBehavior: Clip.hardEdge, color: Colors.transparent, shape: shapeBorder, child: InkResponse( key: _inkResponseKey, highlightColor: Colors.yellow.withOpacity(0.3), splashColor: Colors.red.withOpacity(0.8), focusColor: Colors.green.withOpacity(0.0), hoverColor: Colors.blue.withOpacity(0.8), borderRadius: BorderRadius.circular(borderRadius), child: GestureDetector( onTap: () { // 手动触发单击溅墨 _inkResponseKey.currentState?.showInkSplash( Material.of(context), Offset.zero, () => Colors.red.withOpacity(0.8), ); onTap?.call(); }, onDoubleTap: () { // 清除所有溅墨效果 _inkResponseKey.currentState?.dispose(); onDoubleTap?.call(); }, onLongPress: onLongPress, child: Container( padding: surroundInkEdge ?? EdgeInsets.all(borderRadius), height: height, width: width, alignment: alignment, child: child), ), ), ), ); }
方案3:缩短双击识别延迟
调整GestureDetector的doubleTapDelay参数,缩短框架判断单击/双击的等待时间,减少动画叠加的概率:
@override Widget build(BuildContext context) { return Container( height: height, width: width, decoration: decoration, alignment: alignment, margin: margin, child: Material( borderRadius: BorderRadius.circular(borderRadius), clipBehavior: Clip.hardEdge, color: Colors.transparent, shape: shapeBorder, child: GestureDetector( doubleTapDelay: const Duration(milliseconds: 200), onTap: onTap, onDoubleTap: onDoubleTap, child: InkWell( highlightColor: Colors.yellow.withOpacity(0.3), splashColor: Colors.red.withOpacity(0.8), focusColor: Colors.green.withOpacity(0.0), hoverColor: Colors.blue.withOpacity(0.8), onLongPress: onLongPress, onTapCancel: () { print('tap cancel'); }, canRequestFocus: false, enableFeedback: false, borderRadius: BorderRadius.circular(borderRadius), child: Container( padding: surroundInkEdge ?? EdgeInsets.all(borderRadius), height: height, width: width, alignment: alignment, child: child), ), ), ), ); }
内容的提问来源于stack exchange,提问作者GlS
相关产品推荐
相关产品推荐

