У меня есть DataTable
, в котором в некоторых ячейках есть ссылки. В идеале я хотел бы получить предварительный просмотр содержимого ссылки при наведении курсора на ссылку, чего я смог добиться с помощью виджета Stack
. Однако, поскольку сложенный предварительный просмотр находится внутри DataCell
, похоже, я не могу поднять его «z-index
», чтобы он был поверх остальной части таблицы.
Разве это невозможно с Flutter, или есть способ обойти это?
Единственный способ, которым я представляю, что это работает, без чего-либо, чтобы обновить глобальное z-index
, было бы для ячейки, чтобы обновить глобальное состояние, а затем предварительный просмотр эскиза появился на Stack
над уровнем DataTable
. Но я бы хотел, чтобы был менее громоздкий способ сделать это...
3 виджета, которые я пробовал, но безрезультатно — они могут работать, я не знаю —:
Tooltip
Overlay
FloatingActionButton
Все мое приложение — здесь, а точная фиксация — 0303732
. Соответствующий код — это виджет ClickableLink
:
import 'package:flutter/material.dart';
import 'package:flutter/gestures.dart';
import 'package:url_launcher/url_launcher.dart';
import '../schema/links.dart';
@immutable
class ClickableLink extends StatefulWidget {
const ClickableLink({
Key? key,
required this.link,
this.linkText,
this.color = Colors.blue,
}) : super(key: key);
final Link link;
final String? linkText;
final Color color;
@override
State<ClickableLink> createState() => _ClickableLinkState();
}
class _ClickableLinkState extends State<ClickableLink> {
Widget hoverWidget = const SizedBox.shrink();
void _fetchPreview(PointerEvent pointerEvent) {
setState(() {
if (widget.link.host == 'online-go.com' && widget.link.prePath == 'game') {
hoverWidget = Positioned(
top: 25,
child: Image.network('https://online-go.com/api/v1/games/${widget.link.id}/png'),
);
}
});
}
void _onExit(PointerEvent pointerEvent) {
setState(() {
hoverWidget = const SizedBox.shrink();
});
}
@override
Widget build(BuildContext context) {
return MouseRegion(
onHover: _fetchPreview,
onExit: _onExit,
child: Stack(
clipBehavior: Clip.none,
children: [
SelectableText.rich(
TextSpan(
text: widget.linkText ?? widget.link.id,
style: TextStyle(color: widget.color),
recognizer: TapGestureRecognizer()
..onTap = () async => launch(widget.link.completeLink),
),
),
hoverWidget,
],
),
);
}
}
Проблема здесь связана с тем, что ваш виджет Stack
, определенный внутри ClickableLink
, будет находиться в «нижней» точке (внутри дерева виджетов вашего приложения), чем любой другой GameResultCell
.
Так что даже более высокие z-index
все равно будут отставать от других GameResultCell
.
Чтобы исправить это, я бы рекомендовал изменить вашу структуру и определить более высокую точку в вашей структуре, чтобы показать предварительный просмотр.
Другим способом может быть использование библиотеки для вложения предварительного просмотра во всплывающую подсказку. Взгляните, например, на этот:
just_the_tooltip: ^0.0.11+2
. С этим пакетом вы даже можете использовать StatelessWidget
.
Результат здесь больше похож на то, что, я полагаю, вы ожидали.
class ClickableLink extends StatelessWidget {
@override
Widget build(BuildContext context) {
return JustTheTooltip(
content: Image.network(
'https://online-go.com/api/v1/games/${widget.link.id}/png',
),
child: SelectableText.rich(
TextSpan(
text: widget.linkText ?? widget.link.id,
style: TextStyle(
color: widget.color ??
(DogempTheme.currentThemeIsLight(context)
? const Color(0xff1158c7)
: Colors.orange.withOpacity(0.85)),
),
recognizer: TapGestureRecognizer()
..onTap = () async => launch(widget.link.completeLink),
),
),
);
}
}
Наконец, вы можете использовать Dialog
, но результирующее поведение немного отличается.
Взгляните на этот код, если хотите попробовать:
class _ClickableLinkState extends State<ClickableLink> {
Widget hoverWidget = const SizedBox.shrink();
void _fetchPreview(PointerEvent pointerEvent) {
showDialog(
context: context,
builder: (context) {
return Dialog(
backgroundColor: Colors.transparent,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.network(
'https://online-go.com/api/v1/games/${widget.link.id}/png'),
const SizedBox(
height: 16.0,
),
TextButton(
onPressed: () async => launch(widget.link.completeLink),
child: const Text('Go to complete link'))
],
),
);
},
);
}
@override
Widget build(BuildContext context) {
return MouseRegion(
onHover: _fetchPreview,
child: Stack(
clipBehavior: Clip.none,
children: [
SelectableText.rich(
TextSpan(
text: widget.linkText ?? widget.link.id,
style: TextStyle(
color: widget.color ??
(DogempTheme.currentThemeIsLight(context)
? const Color(0xff1158c7)
: Colors.orange.withOpacity(0.85)),
),
recognizer: TapGestureRecognizer()
..onTap = () async => launch(widget.link.completeLink),
),
),
],
),
);
}
}
Черт, этот пакет just_the_tooltip
впечатляет, если посмотреть на его код. И, откровенно говоря, это то, что должно быть одним из родных виджетов Flutter. В любом случае, большое спасибо за ответ.
В решении Dialog
есть ли способ, чтобы оно появлялось только тогда, когда мышь непосредственно наводит на ссылку? Я думаю, что Flutter понимает, что это какой-то другой экран, поэтому он не будет знать, зависает ли что-то на предыдущем экране.
На самом деле в коде, который я написал, диалоговое окно отображается при наведении мыши на ссылку. Дело в том, что диалоговое окно при отображении занимает весь экран. В этот момент вы фактически теряете наведение на ссылку. Вы можете использовать MouseRegion, чтобы лучше справляться с наведением, но было бы очень сложно воспроизвести результат всплывающей подсказки.
Может быть, виджет
Overlay
в этом случае лучше, чемStack
?