目前已知的 Flutter 限制会导致小部件溢出而Stack
无法接收手势事件。
然而,有一种似乎可以通过扩展课程来实现的技巧Stack
。
以下是我对这个黑客攻击的更新实现。
class StackTappableOutside extends Stack {
const StackTappableOutside({
super.key,
super.alignment,
super.textDirection,
super.fit,
super.clipBehavior,
super.children,
});
@override
RenderStack createRenderObject(BuildContext context) {
return RenderStackTappableOutside(
alignment: alignment,
textDirection: textDirection ?? Directionality.of(context),
fit: fit,
clipBehavior: clipBehavior,
);
}
}
class RenderStackTappableOutside extends RenderStack {
RenderStackTappableOutside({
super.alignment,
super.textDirection,
super.fit,
super.clipBehavior,
super.children,
});
@override
bool hitTest(BoxHitTestResult result, {required Offset position}) {
if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
result.add(BoxHitTestEntry(this, position));
return true;
}
return false;
}
}
我测试过了,它运行Column
良好Row
(可复制,只需复制并粘贴)
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class StackTappableOutside extends Stack {
const StackTappableOutside({
super.key,
super.alignment,
super.textDirection,
super.fit,
super.clipBehavior,
super.children,
});
@override
RenderStack createRenderObject(BuildContext context) {
return RenderStackTappableOutside(
alignment: alignment,
textDirection: textDirection ?? Directionality.of(context),
fit: fit,
clipBehavior: clipBehavior,
);
}
}
class RenderStackTappableOutside extends RenderStack {
RenderStackTappableOutside({
super.alignment,
super.textDirection,
super.fit,
super.clipBehavior,
super.children,
});
@override
bool hitTest(BoxHitTestResult result, {required Offset position}) {
if (hitTestChildren(result, position: position) || hitTestSelf(position)) {
result.add(BoxHitTestEntry(this, position));
return true;
}
return false;
}
}
class TapOutsideStackDemo extends StatefulWidget {
const TapOutsideStackDemo({super.key});
@override
State<TapOutsideStackDemo> createState() => _TapOutsideStackDemoState();
}
class _TapOutsideStackDemoState extends State<TapOutsideStackDemo> {
List<int> items = [0, 1, 2, 3, 4];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Tap Outside Stack Demo'),
),
body: _body(),
);
}
Widget _itemBuilder(int index, int element) {
final showAddButton = index > 0;
return StackTappableOutside(
clipBehavior: Clip.none,
children: [
Container(
child: ListTile(
title: Text('Todo List Item $element'),
subtitle: Text('Add a new item after'),
),
decoration: BoxDecoration(
color: Colors.deepOrange,
border: Border.all(color: Colors.yellow),
),
),
if (showAddButton)
Positioned(
top: -24,
right: 8,
child: Container(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.green,
),
child: IconButton(
icon: Icon(Icons.add),
onPressed: () {
print('add after');
},
),
),
),
],
);
}
Widget _body() {
return Column(
children: items.mapIndexed(_itemBuilder).toList(),
);
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final element = items[index];
return _itemBuilder(index, element);
},
);
}
}
void main() {
runApp(MaterialApp(
home: TapOutsideStackDemo(),
));
}
使用上述代码,当您从任何地方点击绿色按钮时,它会打印:
add after
add after
add after
但是,我有一个要求,要将其改为 ,ListView
而不是Column
。所以我从
Widget _body() {
return Column(
children: items.mapIndexed(_itemBuilder).toList(),
);
}
到:
Widget _body() {
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final element = items[index];
return _itemBuilder(index, element);
},
);
}
突然,绿色按钮只有一半能用。只有底部能用。
我还注意到,如果我们用 包装列表项GestureDetector
,那么黑客也会停止工作,即使Column
使用之前有效的简单方法。
有人能解释一下为什么它在 ListView 上不起作用吗?
有什么方法可以强制它在
ListView
和上工作GestureDetector
吗?我需要使用ListView
命令式,因为我需要ReorderableListView.builder
在之后实现。
似乎没有简单的现成方法来实现这一点,这似乎很奇怪。但我找到了一个不错的 pub 包,它似乎可以帮我完成这项工作:https://pub.dev/packages/defer_pointer
对于您的示例,它的工作原理是将整体包裹
ListView.builder
在里面DeferredPointerHandler
,然后将IconButton
内部包裹起来DeferPointer
。这是您修改后的代码示例:
另请注意,这不再需要
StackTappableOutside
解决方法。