Overlays 通过将它们插入到叠加层的 Stack 中,让独立的子小部件将视觉元素浮动在其他小部件的顶部。
本文讨论 Overlays 在Flutter的实现。为了实现覆盖在Flutter,我们需要知道大约两Flutter内置类OverlayEntry类的Nd OverlayState 类。
覆盖条目:
模糊地说 OverlayEntry 是 Overlay 中的一个地方,可以包含一个小部件。
OverlayEntry 类的构造函数:
OverlayEntry(
{
required WidgetBuilder builder,
bool opaque = false,
bool maintainState = false
}
)
OverlayEntry 类的属性:
- builder:需要一个小部件构建器。
- 不透明: 采用一个布尔值来决定此条目是否遮挡整个叠加层。如果一个条目声称是不透明的,那么为了效率,覆盖将跳过在该条目下方构建条目,除非它们设置了维护状态。
- 维护状态:采用 bool 值,如果设置为 true,它会强制在不透明条目下方构建被遮挡的条目。
OverlayEntry 类的方法:
- remove:从叠加层中删除此条目。
覆盖状态:
Overlay 的当前状态用于将 OverlayEntries 插入到覆盖层中。
OverlayState 类的方法:
- debugIsVisible:检查给定的OverlayEntry是否可见并返回一个布尔值。
- insert:将给定的OverlayEntry插入到 Overlay 中。
- insertAll:获取一个OverlayEntries 列表并将所有条目插入到 Overlay 中。您还可以指定上面和下面的属性来说明要插入的订单条目。
- 重新排列:删除给定的 OverlayEntries 列表中列出的所有条目,然后按照给定的顺序将它们重新插入到叠加层中。
我知道你对阅读理论不太感兴趣,所以让我们继续看一些例子。
示例 1:
Dart
import 'package:flutter/material.dart';
class Example1 extends StatefulWidget {
const Example1({Key key}) : super(key: key);
@override
_Example1State createState() => _Example1State();
}
class _Example1State extends State {
void _showOverlay(BuildContext context) async {
// Declaring and Initializing OverlayState
// and OverlayEntry objects
OverlayState overlayState = Overlay.of(context);
OverlayEntry overlayEntry;
overlayEntry = OverlayEntry(builder: (context) {
// You can return any widget you like here
// to be displayed on the Overlay
return Positioned(
left: MediaQuery.of(context).size.width * 0.2,
top: MediaQuery.of(context).size.height * 0.3,
child: Container(
width: MediaQuery.of(context).size.width * 0.8,
child: Stack(
children: [
Image.asset(
'images/commentCloud.png',
colorBlendMode: BlendMode.multiply,
),
Positioned(
top: MediaQuery.of(context).size.height * 0.13,
left: MediaQuery.of(context).size.width * 0.13,
child: Row(
children: [
Material(
color: Colors.transparent,
child: Text(
'This is a button!',
style: TextStyle(
fontSize: MediaQuery.of(context).size.height * 0.03,
color: Colors.green),
),
),
SizedBox(
width: MediaQuery.of(context).size.width * 0.18,
),
GestureDetector(
onTap: () {
// When the icon is pressed the OverlayEntry
// is removed from Overlay
overlayEntry.remove();
},
child: Icon(Icons.close,
color: Colors.green,
size: MediaQuery.of(context).size.height * 0.025),
)
],
),
),
],
),
),
);
});
// Inserting the OverlayEntry into the Overlay
overlayState.insert(overlayEntry);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'GeeksForGeeks Example 2',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
body: SafeArea(
child: Center(
child: MaterialButton(
color: Colors.green,
minWidth: MediaQuery.of(context).size.width * 0.4,
height: MediaQuery.of(context).size.height * 0.06,
child: Text(
'show Overlay',
style: TextStyle(color: Colors.white),
),
onPressed: () {
// calling the _showOverlay method
// when Button is pressed
_showOverlay(context);
},
))),
);
}
}
Dart
import 'package:flutter/material.dart';
class Example2 extends StatefulWidget {
const Example2({Key key}) : super(key: key);
@override
_Example2State createState() => _Example2State();
}
class _Example2State extends State {
void _showOverlay(BuildContext context) async {
// Declaring and Initializing OverlayState
// and OverlayEntry objects
OverlayState overlayState = Overlay.of(context);
OverlayEntry overlayEntry;
overlayEntry = OverlayEntry(builder: (context) {
// You can return any widget you like
// here to be displayed on the Overlay
return Positioned(
left: MediaQuery.of(context).size.width * 0.2,
top: MediaQuery.of(context).size.height * 0.3,
child: Container(
width: MediaQuery.of(context).size.width * 0.8,
child: Stack(
children: [
Image.asset(
'images/commentCloud.png',
colorBlendMode: BlendMode.multiply,
),
Positioned(
top: MediaQuery.of(context).size.height * 0.13,
left: MediaQuery.of(context).size.width * 0.13,
child: Material(
color: Colors.transparent,
child: Text(
'I will disappear in 3 seconds.',
style: TextStyle(
fontSize: MediaQuery.of(context).size.height * 0.025,
color: Colors.green),
),
),
),
],
),
),
);
});
// Inserting the OverlayEntry into the Overlay
overlayState.insert(overlayEntry);
// Awaiting for 3 seconds
await Future.delayed(Duration(seconds: 3));
// Removing the OverlayEntry from the Overlay
overlayEntry.remove();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'GeeksForGeeks Example 2',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
body: SafeArea(
child: Center(
child: MaterialButton(
color: Colors.green,
minWidth: MediaQuery.of(context).size.width * 0.4,
height: MediaQuery.of(context).size.height * 0.06,
child: Text(
'show Overlay',
style: TextStyle(color: Colors.white),
),
onPressed: () {
// calling the _showOverlay method
// when Button is pressed
_showOverlay(context);
},
))),
);
}
}
Dart
import 'package:flutter/material.dart';
class Example3 extends StatefulWidget {
const Example3({Key key}) : super(key: key);
@override
_Example3State createState() => _Example3State();
}
class _Example3State extends State {
void _showOverlay(BuildContext context) async {
// Declaring and Initializing OverlayState and
// OverlayEntry objects
OverlayState overlayState = Overlay.of(context);
OverlayEntry overlayEntry1;
OverlayEntry overlayEntry2;
OverlayEntry overlayEntry3;
overlayEntry1 = OverlayEntry(builder: (context) {
// You can return any widget you like here
// to be displayed on the Overlay
return Positioned(
left: MediaQuery.of(context).size.width * 0.1,
top: MediaQuery.of(context).size.height * 0.3,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Container(
padding: EdgeInsets.all(MediaQuery.of(context).size.height * 0.02),
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.1,
color: Colors.pink.withOpacity(0.3),
child: Material(
color: Colors.transparent,
child: Text('I will disappear in 3 seconds',
style: TextStyle(
fontSize: MediaQuery.of(context).size.height * 0.03,
fontWeight: FontWeight.bold,
color: Colors.white)),
),
),
),
);
});
overlayEntry2 = OverlayEntry(builder: (context) {
// You can return any widget you like here
// to be displayed on the Overlay
return Positioned(
left: MediaQuery.of(context).size.width * 0.1,
top: MediaQuery.of(context).size.height * 0.5,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Container(
padding: EdgeInsets.all(MediaQuery.of(context).size.height * 0.02),
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.1,
color: Colors.blue.withOpacity(0.3),
child: Material(
color: Colors.transparent,
child: Text('I will disappear in 5 seconds',
style: TextStyle(
fontSize: MediaQuery.of(context).size.height * 0.03,
fontWeight: FontWeight.bold,
color: Colors.white)),
),
),
),
);
});
overlayEntry3 = OverlayEntry(builder: (context) {
// You can return any widget you like
// here to be displayed on the Overlay
return Positioned(
left: MediaQuery.of(context).size.width * 0.1,
top: MediaQuery.of(context).size.height * 0.7,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Container(
padding: EdgeInsets.all(MediaQuery.of(context).size.height * 0.02),
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.1,
color: Colors.green.withOpacity(0.3),
child: Material(
color: Colors.transparent,
child: Text('I will disappear in 7 seconds',
style: TextStyle(
fontSize: MediaQuery.of(context).size.height * 0.03,
fontWeight: FontWeight.bold,
color: Colors.white)),
),
),
),
);
});
// Inserting the OverlayEntry into the Overlay
overlayState.insertAll([overlayEntry1, overlayEntry2, overlayEntry3]);
// Awaiting for 3 seconds
await Future.delayed(Duration(seconds: 3));
// Removing the first OverlayEntry from the Overlay
overlayEntry1.remove();
// Awaiting for 2 seconds more
await Future.delayed(Duration(seconds: 2));
// Removing the second OverlayEntry from the Overlay
overlayEntry2.remove();
// Awaiting for 2 seconds more
await Future.delayed(Duration(seconds: 2));
// Removing the third OverlayEntry from the Overlay
overlayEntry3.remove();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'GeeksForGeeks Example 3',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
body: SafeArea(
child: Center(
child: MaterialButton(
color: Colors.green,
minWidth: MediaQuery.of(context).size.width * 0.4,
height: MediaQuery.of(context).size.height * 0.06,
child: Text(
'show Overlay',
style: TextStyle(color: Colors.white),
),
onPressed: () {
//calling the _showOverlay method
// when Button is pressed
_showOverlay(context);
},
))),
);
}
}
输出:
解释:
在这个flutter应用程序中,我在MaterialButton的onPressed回调中调用了一个函数_showOverlay 。 在_showOverlay函数,我已经声明并初始化了OverlayState和OverlayEntry对象。在OverlayEntry 中,我传递了Comment Cloud的小部件,它有一个Text和一个Icon ,我用GestureDetector包裹了Icon ,在它的onTap回调中,我为OverlayEntry调用了remove函数,它从覆盖。您还可以让OverlayEntry在一段时间后自动移除,下一个示例解决了这个问题。在OverlayEntry初始化之后,我调用了OverlayState 的insert方法并传入了当前的OverlayEntry ,这会将Entry添加到Overlay 中。
示例 2:
Dart
import 'package:flutter/material.dart';
class Example2 extends StatefulWidget {
const Example2({Key key}) : super(key: key);
@override
_Example2State createState() => _Example2State();
}
class _Example2State extends State {
void _showOverlay(BuildContext context) async {
// Declaring and Initializing OverlayState
// and OverlayEntry objects
OverlayState overlayState = Overlay.of(context);
OverlayEntry overlayEntry;
overlayEntry = OverlayEntry(builder: (context) {
// You can return any widget you like
// here to be displayed on the Overlay
return Positioned(
left: MediaQuery.of(context).size.width * 0.2,
top: MediaQuery.of(context).size.height * 0.3,
child: Container(
width: MediaQuery.of(context).size.width * 0.8,
child: Stack(
children: [
Image.asset(
'images/commentCloud.png',
colorBlendMode: BlendMode.multiply,
),
Positioned(
top: MediaQuery.of(context).size.height * 0.13,
left: MediaQuery.of(context).size.width * 0.13,
child: Material(
color: Colors.transparent,
child: Text(
'I will disappear in 3 seconds.',
style: TextStyle(
fontSize: MediaQuery.of(context).size.height * 0.025,
color: Colors.green),
),
),
),
],
),
),
);
});
// Inserting the OverlayEntry into the Overlay
overlayState.insert(overlayEntry);
// Awaiting for 3 seconds
await Future.delayed(Duration(seconds: 3));
// Removing the OverlayEntry from the Overlay
overlayEntry.remove();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'GeeksForGeeks Example 2',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
body: SafeArea(
child: Center(
child: MaterialButton(
color: Colors.green,
minWidth: MediaQuery.of(context).size.width * 0.4,
height: MediaQuery.of(context).size.height * 0.06,
child: Text(
'show Overlay',
style: TextStyle(color: Colors.white),
),
onPressed: () {
// calling the _showOverlay method
// when Button is pressed
_showOverlay(context);
},
))),
);
}
}
输出:
解释:
在这个例子中,我在MaterialButton的onPressed回调中调用了一个函数_showOverlay 。在_showOverlay函数,我已经声明并初始化了OverlayState和OverlayEntry对象。在OverlayEntry 中,我传递了 Comment Cloud 的小部件,它显示了一个Text 。在OverlayEntry初始化之后,我调用了OverlayState 的insert方法并传入了当前的OverlayEntry ,这会将Entry添加到Overlay 中。之后,我在Future.delayed上等待https://github.com/curiousyuvi/overlay_implementation延迟 3 秒,然后调用remove方法从Overlay 中删除当前的OverlayEntry 。这会使条目出现 3 秒钟,然后消失。
示例 3:
Dart
import 'package:flutter/material.dart';
class Example3 extends StatefulWidget {
const Example3({Key key}) : super(key: key);
@override
_Example3State createState() => _Example3State();
}
class _Example3State extends State {
void _showOverlay(BuildContext context) async {
// Declaring and Initializing OverlayState and
// OverlayEntry objects
OverlayState overlayState = Overlay.of(context);
OverlayEntry overlayEntry1;
OverlayEntry overlayEntry2;
OverlayEntry overlayEntry3;
overlayEntry1 = OverlayEntry(builder: (context) {
// You can return any widget you like here
// to be displayed on the Overlay
return Positioned(
left: MediaQuery.of(context).size.width * 0.1,
top: MediaQuery.of(context).size.height * 0.3,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Container(
padding: EdgeInsets.all(MediaQuery.of(context).size.height * 0.02),
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.1,
color: Colors.pink.withOpacity(0.3),
child: Material(
color: Colors.transparent,
child: Text('I will disappear in 3 seconds',
style: TextStyle(
fontSize: MediaQuery.of(context).size.height * 0.03,
fontWeight: FontWeight.bold,
color: Colors.white)),
),
),
),
);
});
overlayEntry2 = OverlayEntry(builder: (context) {
// You can return any widget you like here
// to be displayed on the Overlay
return Positioned(
left: MediaQuery.of(context).size.width * 0.1,
top: MediaQuery.of(context).size.height * 0.5,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Container(
padding: EdgeInsets.all(MediaQuery.of(context).size.height * 0.02),
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.1,
color: Colors.blue.withOpacity(0.3),
child: Material(
color: Colors.transparent,
child: Text('I will disappear in 5 seconds',
style: TextStyle(
fontSize: MediaQuery.of(context).size.height * 0.03,
fontWeight: FontWeight.bold,
color: Colors.white)),
),
),
),
);
});
overlayEntry3 = OverlayEntry(builder: (context) {
// You can return any widget you like
// here to be displayed on the Overlay
return Positioned(
left: MediaQuery.of(context).size.width * 0.1,
top: MediaQuery.of(context).size.height * 0.7,
child: ClipRRect(
borderRadius: BorderRadius.circular(20),
child: Container(
padding: EdgeInsets.all(MediaQuery.of(context).size.height * 0.02),
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.1,
color: Colors.green.withOpacity(0.3),
child: Material(
color: Colors.transparent,
child: Text('I will disappear in 7 seconds',
style: TextStyle(
fontSize: MediaQuery.of(context).size.height * 0.03,
fontWeight: FontWeight.bold,
color: Colors.white)),
),
),
),
);
});
// Inserting the OverlayEntry into the Overlay
overlayState.insertAll([overlayEntry1, overlayEntry2, overlayEntry3]);
// Awaiting for 3 seconds
await Future.delayed(Duration(seconds: 3));
// Removing the first OverlayEntry from the Overlay
overlayEntry1.remove();
// Awaiting for 2 seconds more
await Future.delayed(Duration(seconds: 2));
// Removing the second OverlayEntry from the Overlay
overlayEntry2.remove();
// Awaiting for 2 seconds more
await Future.delayed(Duration(seconds: 2));
// Removing the third OverlayEntry from the Overlay
overlayEntry3.remove();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(
'GeeksForGeeks Example 3',
style: TextStyle(fontWeight: FontWeight.bold),
),
),
body: SafeArea(
child: Center(
child: MaterialButton(
color: Colors.green,
minWidth: MediaQuery.of(context).size.width * 0.4,
height: MediaQuery.of(context).size.height * 0.06,
child: Text(
'show Overlay',
style: TextStyle(color: Colors.white),
),
onPressed: () {
//calling the _showOverlay method
// when Button is pressed
_showOverlay(context);
},
))),
);
}
}
输出:
解释:
在这个例子中,我在MaterialButton的onPressed回调中调用了一个函数_showOverlay 。在_showOverlay函数,我声明并初始化了OverlayState和三个OverlayEntry对象。在这些OverlayEntries 中,我有不同颜色的Container并且它们都显示一个Text 。在OverlayEntries初始化之后,我为OverlayState调用了insertAll方法并传入了 OverlayEntries 列表,这会将所有条目添加到Overlay 中。之后,我等待Future.delayed延迟 3 秒,然后调用remove方法从Overlay 中删除第一个 OverlayEntry ,然后类似地我延迟了两秒钟,然后为第二个OverlayEntry调用了remove ,然后再次延迟了2 秒并为第三个也是最后一个OverlayEntry调用remove ,这使得OverlayEntries一个接一个消失。