116 lines
2.6 KiB
Dart
116 lines
2.6 KiB
Dart
|
import 'package:flutter/material.dart';
|
||
|
|
||
|
class AnchoredOverlay extends StatelessWidget {
|
||
|
final bool showOverlay;
|
||
|
final Widget Function(BuildContext, Offset anchor) overlayBuilder;
|
||
|
final Widget child;
|
||
|
|
||
|
const AnchoredOverlay({
|
||
|
super.key,
|
||
|
required this.showOverlay,
|
||
|
required this.overlayBuilder,
|
||
|
required this.child,
|
||
|
});
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) {
|
||
|
return OverlayBuilder(
|
||
|
showOverlay: showOverlay,
|
||
|
overlayBuilder: (BuildContext overlayContext) {
|
||
|
RenderBox box = context.findRenderObject() as RenderBox;
|
||
|
final center = box.size.center(box.localToGlobal(const Offset(0.0, 0.0)));
|
||
|
|
||
|
return overlayBuilder(overlayContext, center);
|
||
|
},
|
||
|
child: child,
|
||
|
);
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class OverlayBuilder extends StatefulWidget {
|
||
|
final bool showOverlay;
|
||
|
final Widget Function(BuildContext) overlayBuilder;
|
||
|
final Widget child;
|
||
|
|
||
|
const OverlayBuilder({
|
||
|
super.key,
|
||
|
this.showOverlay = false,
|
||
|
required this.overlayBuilder,
|
||
|
required this.child,
|
||
|
});
|
||
|
|
||
|
@override
|
||
|
State<OverlayBuilder> createState() => _OverlayBuilderState();
|
||
|
}
|
||
|
|
||
|
class _OverlayBuilderState extends State<OverlayBuilder> {
|
||
|
OverlayEntry? overlayEntry;
|
||
|
|
||
|
@override
|
||
|
void initState() {
|
||
|
super.initState();
|
||
|
|
||
|
if (widget.showOverlay) {
|
||
|
WidgetsBinding.instance.addPostFrameCallback((_) => showOverlay());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
void didUpdateWidget(OverlayBuilder oldWidget) {
|
||
|
super.didUpdateWidget(oldWidget);
|
||
|
WidgetsBinding.instance.addPostFrameCallback((_) => syncWidgetAndOverlay());
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
void reassemble() {
|
||
|
super.reassemble();
|
||
|
WidgetsBinding.instance.addPostFrameCallback((_) => syncWidgetAndOverlay());
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
void dispose() {
|
||
|
if (isShowingOverlay()) {
|
||
|
hideOverlay();
|
||
|
}
|
||
|
|
||
|
super.dispose();
|
||
|
}
|
||
|
|
||
|
bool isShowingOverlay() => overlayEntry != null;
|
||
|
|
||
|
void showOverlay() {
|
||
|
overlayEntry = OverlayEntry(
|
||
|
builder: widget.overlayBuilder,
|
||
|
);
|
||
|
addToOverlay(overlayEntry!);
|
||
|
}
|
||
|
|
||
|
void addToOverlay(OverlayEntry entry) async {
|
||
|
print('addToOverlay');
|
||
|
Overlay.of(context).insert(entry);
|
||
|
}
|
||
|
|
||
|
void hideOverlay() {
|
||
|
print('hideOverlay');
|
||
|
if (overlayEntry != null) {
|
||
|
overlayEntry!.remove();
|
||
|
overlayEntry = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void syncWidgetAndOverlay() {
|
||
|
if (isShowingOverlay() && !widget.showOverlay) {
|
||
|
hideOverlay();
|
||
|
} else if (!isShowingOverlay() && widget.showOverlay) {
|
||
|
showOverlay();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@override
|
||
|
Widget build(BuildContext context) {
|
||
|
return widget.child;
|
||
|
}
|
||
|
}
|