I. Introduction of Overlay
1. What is Overlay
The introduction of overlays on the official website is:
A brief translation:
Overlay Widget is a stack-based management widget that can be used independently.
Overlay is implemented by inserting a separate widget into the stack of the Overlay to make the widget appear on top of other widgets. Overlays manage the display layer of widgets through OverlayEntry (you cannot use widgets directly).
You can create an Overlay object directly to use, but more generally, you create the Overlay using the WidgetsApp or Navigator in the MaterialApp, The Navigator uses this overlay to manage the route display (we can use this overlay to implement a global popover).
2. Overlay use steps
- Create OverlayEntry
Overlay entry=new OverlayEntry(builder:(){// own widget});Copy the code
- Insert the OverlayEntry into the Overlay
Overlay.of(context).insert(overlayEntry);
Copy the code
- Remove the OverlayEntry
entry.remove();
Copy the code
2. Implement Toast based on Overlay
// Overlay Toast class Toast {static void show({@required BuildContext context, @required String message}) {// create overlayEntry overlayEntry overlayEntry = new overlayEntry (Builder: (context) {returnOf (context).size. Height * 0.8, child: new Material(child: new Container(width: MediaQuery.of(context).size.width, alignment: Alignment.center, child: new Center( child: new Card( child: New Padding(Padding: edgeinset.all (8), child: new Text(message),), color: color.grey.withopacity (0.6),),)); }); OverlayEntry Overlay. Of (context).insert(OverlayEntry); OverlayEntry new future.delayed (Duration(seconds: 2)). Then ((value) {OverlayEntry. Remove (); }); }Copy the code
Use:
RaisedButton(
child: Text('SUBMIT'),
onPressed: () {
Toast.show(context: context, message: "Information has been submitted!"); },)Copy the code
Iii. Implement automatic prompt for text input box based on Overlay (complete)
Suppose we have an information input interface as follows:
We hope that when the user enters in the input box, he can display a list below according to the user’s input. When the input box gets focus, he can display a list to assist the user to enter (this can be adjusted according to his own needs) as follows:
Of course, in order to achieve this function, you can rewrite the interface by using Stack + Position, but this may require code reconstruction, increase interface complexity, and poor scalability. You can implement this function by using Overlay.
1. Display the hover window with Overlay
What we need to implement is that when TextFormField gets focus, it pops up, and when it loses focus, it removes the popover. Define the popover control as follows:
class CountriesField extends StatefulWidget {
@override
_CountriesFieldState createState() => _CountriesFieldState();
}
class _CountriesFieldState extends State<CountriesField> {
final FocusNode _focusNode = FocusNode();
OverlayEntry _overlayEntry;
@override
void initState() {
_focusNode.addListener(() {
if (_focusNode.hasFocus) {
this._overlayEntry = this._createOverlayEntry();
Overlay.of(context).insert(this._overlayEntry);
} else{ this._overlayEntry.remove(); }}); } OverlayEntry_createOverlayEntry() {
RenderBox renderBox = context.findRenderObject();
var size = renderBox.size;
var offset = renderBox.localToGlobal(Offset.zero);
returnOverlayEntry(Builder: (context) => toy (left: offset. Dx, top: offset. Dy + size. Width, child: Material(elevation: 4.0, child: ListView(padding: EdgeInsets. Zero, shrinkWrap:true,
children: <Widget>[
ListTile(
title: Text('Syria'),
),
ListTile(
title: Text('Lebanon'), a)],),),)); } @override Widget build(BuildContext context) {return TextFormField(
focusNode: this._focusNode,
decoration: InputDecoration(
labelText: 'Country')); }}Copy the code
- TextFormField gets or loses focus events are listened for by FocusNode, which is initialized in initState.
- When TextFormField gets focus, we insert the OverlayEntry created by _createOverlayEntry at the top of the Overlay using the overlay.of (context).insert method, So that shows the suspension window.
- When TextFormField loses focus, we remove the popover with the _overlayentry. remove method.
- Because when we display the popover, we need to know the location of the popover, which must be relevant to the current input field. We can use context.findRenderObject to find the current RenderBox. Using the RenderBox, we can know the position, size, or other rendering information of the widget that is currently displayed. Based on this information, we can adjust the position and size of the popover.
- Renderbox.localtoglobal (offset.zero) retrieves the position of the current widget on the screen.
- Inside the pop-up, we position the pop-up through the toy window, and show the contents through the ListView.
One problem with this custom control, however, is that when we scroll through the ListView long enough, the popover position is fixed.
2, pop-ups follow scrolling
We need to do is, our global popup window can follow TextField rolling, Flutter provides two components: CompositedTransformFollower and CompositedTransformTarget.
We link a follower to a target (specifying the same LayerLink for both widgets) so that when target scrolls, the followers follow.
In our example, the popover is the Follower and the TextField is the Target. We use CompositedTransformFollower and CompositedTransformTarget parcel popup window and TextField respectively, the code is as follows:
class CountriesField extends StatefulWidget {
@override
_CountriesFieldState createState() => _CountriesFieldState();
}
class _CountriesFieldState extends State<CountriesField> {
final FocusNode _focusNode = FocusNode();
OverlayEntry _overlayEntry;
final LayerLink _layerLink = LayerLink();
@override
void initState() {
_focusNode.addListener(() {
if (_focusNode.hasFocus) {
this._overlayEntry = this._createOverlayEntry();
Overlay.of(context).insert(this._overlayEntry);
} else{ this._overlayEntry.remove(); }}); } OverlayEntry_createOverlayEntry() {
RenderBox renderBox = context.findRenderObject();
var size = renderBox.size;
return OverlayEntry(
builder: (context) => Positioned(
width: size.width,
child: CompositedTransformFollower(
link: this._layerLink,
showWhenUnlinked: false,
offset: Offset(0.0, size.height + 5.0),
child: Material(
elevation: 4.0,
child: ListView(
padding: EdgeInsets.zero,
shrinkWrap: true,
children: <Widget>[
ListTile(
title: Text('Syria'),
onTap: () {
print('Syria Tapped');
},
),
ListTile(
title: Text('Lebanon'),
onTap: () {
print('Lebanon Tapped'); }, ()], (), (), (); } @override Widget build(BuildContext context) {return CompositedTransformTarget(
link: this._layerLink,
child: TextFormField(
focusNode: this._focusNode,
decoration: InputDecoration(
labelText: 'Country'),),); }}Copy the code
- In the above code, we passed the CompositedTransformFollower parcel OverlayEntry, through the TextFormTield CompositedTransformTarget package.
- We provide the same instance of LayerLink for the followers and Target so that pop-ups follow scrolling.
- Because the pop-up rolls after Target, there is no longer a need for the top and left properties in tourist space.
- Set showWhenUnlinked to false so that pop-ups hide when target is not on screen (roll off screen).
Effect:
Using Overlay to display floating widgets
Code: making