我有一个 ListView.builder,其中包含包含在 MouseRegion 中的 ListTiles。当使用鼠标进入 MouseRegion 时,附加到此 MouseRegion 的 Overlay 应显示在此 MouseRegion 的右上角。只能同时显示一个 Overlay,并且始终是附加到悬停的 MouseRegion 的那个。由于 Overlay 包含可点击的 IconButtons,因此当通过 Overlay 退出 MouseRegion 时,Overlay 仍应显示。
如果每个 ListEntry 使用两个计时器都有自己的 Overlay,我找到了解决方案。
但后来我遇到了如何在移动设备上处理它的问题。在此处点击 ListTile,将显示此 ListTile 的叠加层,并且另一个 ListTile 的叠加层可能会消失。我用我的方法没有找到最后一个任务的解决方案。
然后我只用一个(全局)覆盖进行了尝试,这对我来说似乎很合乎逻辑,因为同时只显示一个覆盖,那么移动设备的解决方案就很容易了。但是:适用于 web 的代码并非 100% 有效。当您使用鼠标非常快时,覆盖层不会消失。
正如您在以下最小版本中看到的,我正在使用回调更新 CompositedTransformFollower 的 layerLink。我也尝试使用提供者来执行此操作,但后来我总是遇到错误:无法在此 ListWidget 小部件上方找到正确的提供者。
感谢您的帮助:-)
import 'dart:async';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
LayerLink? layerLinkHovering;
LayerLink? layerLinkShowing;
OverlayEntry? entry;
Timer? showTimer;
Timer? hideTimer;
bool overlayIsActive = false;
OverlayState? overlay;
OverlayEntry buildOverlayEntry() {
print('buildOverlayEntry');
return OverlayEntry(
builder: (context) => Positioned(
right: 1,
child: CompositedTransformFollower(
link: layerLinkShowing!,
showWhenUnlinked: false,
offset: const Offset(0, -20),
followerAnchor: Alignment.topRight,
targetAnchor: Alignment.topRight,
child: buildOverlay(),
),
),
);
}
void showOverlay() {
print('showOverlay');
overlay = Overlay.of(context);
entry = buildOverlayEntry();
overlay!.insert(entry!);
overlayIsActive = true;
layerLinkShowing = layerLinkHovering;
}
void hideOverlay() {
print('hideOverlay');
entry?.remove();
entry = null;
overlayIsActive = false;
}
void enterListTile(LayerLink l) {
print('enterListTile');
layerLinkHovering = l;
if (!overlayIsActive) {
showTimer = Timer(const Duration(milliseconds: 100), showOverlay);
} else if (layerLinkHovering == layerLinkShowing) {
hideTimer!.cancel();
} else {
showTimer = Timer(const Duration(milliseconds: 100), showOverlay);
}
}
void exitListTile() {
print('exitListTile');
hideTimer = Timer(const Duration(milliseconds: 100), hideOverlay);
}
Widget? buildOverlay() {
print('build Overlay');
return MouseRegion(
onEnter: (e) {
print('onEnterOverlay');
hideTimer?.cancel();
},
onExit: (e) {
print('onExitOverlay');
hideTimer = Timer(const Duration(milliseconds: 100), hideOverlay);
},
child: Material(
child: Row(
children: [
IconButton(
icon: const Icon(Icons.delete),
onPressed: () {},
tooltip: 'delete message',
),
IconButton(
icon: const Icon(Icons.edit),
onPressed: () {},
tooltip: 'edit message (in progress)',
),
IconButton(
icon: const Icon(Icons.chat_bubble_rounded),
onPressed: () {},
tooltip: 'start thread (in progress)',
),
IconButton(icon: const Icon(Icons.emoji_emotions), onPressed: () {}, tooltip: 'in progress'),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('MinimalListTile'),
),
body: ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: 20,
itemBuilder: (BuildContext context, int index) {
LayerLink layerLink = LayerLink();
return CompositedTransformTarget(
link: layerLink,
child: MouseRegion(
onEnter: (e) {
print('onEnter');
enterListTile(layerLink);
},
onExit: (e) {
print('onExit');
exitListTile();
},
child: ListTile(
leading: const Icon(Icons.list),
title: Text("List item $index"),
onTap: () {
// TODO: implement for Mobile-Platform (easy)
},
),
),
);
},
),
);
}
}