Created
April 28, 2020 14:11
-
-
Save micimize/61505c392f877bd0b2df9103d5a2bb97 to your computer and use it in GitHub Desktop.
example usage for https://gist.github.com/micimize/7579a66861cf56ba241b224a64566d51
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter/material.dart'; | |
void main() => runApp(MyApp()); | |
class MyApp extends StatelessWidget { | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Flutter Demo', | |
debugShowCheckedModeBanner: false, | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
), | |
home: MyHomePage(title: 'Flutter Demo Home Page'), | |
); | |
} | |
} | |
class MyHomePage extends StatefulWidget { | |
MyHomePage({Key key, this.title}) : super(key: key); | |
final String title; | |
@override | |
_MyHomePageState createState() => _MyHomePageState(); | |
} | |
class _MyHomePageState extends State<MyHomePage> { | |
bool showOverlay = true; | |
@override | |
Widget build(BuildContext context) { | |
final blankArea = Container( | |
height: 300, | |
color: Colors.black, | |
); | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('overlay demo'), | |
), | |
body: ListView( | |
children: <Widget>[ | |
blankArea, | |
blankArea, | |
OverlayContainer( | |
offset: Offset(300, 50), | |
show: showOverlay, | |
overlay: Container( | |
height: 40, width: 100, color: Colors.red, child: Text("wow")), | |
child: ListTile( | |
title: Text("wowee I've got an overlay"), | |
leading: IconButton( | |
onPressed: () => setState( | |
() => showOverlay = !showOverlay, | |
), | |
tooltip: 'toggle overlay', | |
icon: Icon( | |
showOverlay ? Icons.expand_less : Icons.expand_more, | |
)), | |
), | |
), | |
blankArea, | |
blankArea, | |
blankArea, | |
blankArea | |
], | |
), | |
floatingActionButton: FloatingActionButton( | |
onPressed: () => setState( | |
() => showOverlay = !showOverlay, | |
), | |
tooltip: 'toggle overlay', | |
child: Icon( | |
showOverlay ? Icons.expand_less : Icons.expand_more, | |
), | |
), | |
); | |
} | |
} | |
class OverlayContainer extends StatefulWidget { | |
/// The child to render in the regular document flow (defaults to Container()) | |
final Widget child; | |
/// The widget to render inside the [OverlayEntry]. | |
final Widget overlay; | |
/// Offset to apply to the [CompositedTransformFollower] | |
final Offset offset; | |
/// Controlling whether the overlay is current showing or not. | |
/// | |
/// Note: if you have an animated component you'll probably need to leave this `true` | |
/// and wrap your floating widget in a `Visible`: | |
/// ```dart | |
/// Visibility( | |
/// visible: isVisible, | |
/// maintainSize: true, | |
/// maintainState: true, | |
/// maintainAnimation: true, | |
/// // ... | |
/// ``` | |
final bool show; | |
OverlayContainer({ | |
Key key, | |
@required this.overlay, | |
this.child, | |
this.offset, | |
this.show = true, | |
}) : super(key: key); | |
@override | |
_OverlayContainerState createState() => _OverlayContainerState(); | |
} | |
class _OverlayContainerState extends State<OverlayContainer> | |
with WidgetsBindingObserver { | |
OverlayEntry _overlayEntry; | |
final LayerLink _layerLink = LayerLink(); | |
@override | |
void initState() { | |
super.initState(); | |
if (widget.show) { | |
_show(); | |
} | |
WidgetsBinding.instance.addObserver(this); | |
} | |
@override | |
void didChangeMetrics() { | |
// We would want to re render the overlay if any metrics | |
// ever change. | |
if (widget.show) { | |
_show(); | |
} else { | |
_hide(); | |
} | |
} | |
@override | |
void didChangeDependencies() { | |
super.didChangeDependencies(); | |
// We would want to re render the overlay if any of the dependencies | |
// ever change. | |
if (widget.show) { | |
_show(); | |
} else { | |
_hide(); | |
} | |
} | |
@override | |
void didUpdateWidget(OverlayContainer oldWidget) { | |
super.didUpdateWidget(oldWidget); | |
if (widget.show) { | |
_show(); | |
} else { | |
_hide(); | |
} | |
} | |
@override | |
void dispose() { | |
if (widget.show) { | |
_hide(); | |
} | |
WidgetsBinding.instance.removeObserver(this); | |
super.dispose(); | |
} | |
void _show() { | |
WidgetsBinding.instance.addPostFrameCallback((_) async { | |
if (_overlayEntry == null) { | |
_overlayEntry = _buildOverlayEntry(); | |
Overlay.of(context).insert(_overlayEntry); | |
} else { | |
_overlayEntry.markNeedsBuild(); | |
} | |
}); | |
} | |
void _hide() { | |
if (_overlayEntry != null) { | |
WidgetsBinding.instance.addPostFrameCallback((_) { | |
_overlayEntry.remove(); | |
_overlayEntry = null; | |
}); | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
// Listen to changes in media query such as when a device orientation changes | |
// or when the keyboard is toggled. | |
// MediaQuery.of(context); | |
return CompositedTransformTarget( | |
link: this._layerLink, | |
child: widget.child ?? Container(), | |
); | |
} | |
OverlayEntry _buildOverlayEntry() { | |
return OverlayEntry( | |
opaque: false, | |
builder: (context) { | |
return FittedBox( | |
fit: BoxFit.cover, | |
child: CompositedTransformFollower( | |
offset: widget.offset, | |
link: this._layerLink, | |
//showWhenUnlinked: false, | |
child: FittedBox( | |
fit: BoxFit.none, | |
child: widget.overlay, | |
), | |
), | |
); | |
}, | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@hermitdave let's continue discussion from MustansirZia/overlay_container#6 here –
If you mean the exact area around the overlay, that's just a matter of normal gesture handling. Adding an
IgnorePointer
solved it for me: