Overlay是一个类似Stack的Widget,可以将OverlayEntry插入到Overlay中,将OverlayEntry中构建的小部件叠加悬浮在其他顶部小部件之上。可以实现类似悬浮小d窗的效果,如Toast,安卓的PopupWindow.
另外Overlay本身使用的是Stack布局,因此OverlayEntry可以使用Position或者AnimatedPosition定位自己的位置。
最常见的用例是:有WidgetsApp或MaterialApp中的Navigator创建的Overlay.
const Overlay({
Key? key,
this.initialEntries = const <OverlayEntry>[],//包含在叠加层中的Widget
this.clipBehavior = Clip.hardEdge,
})
通常我们可以直接使用Overlay.of(context)方法获取该context最近的OverlayState,然后通过OverlayState中的insert()或者insertAll()方法插入一个或多个悬浮的Widget。如果想移除悬浮Widget,可以通过OverlayEntry的remove()方法实现。
OverlayEntry[Overlay] 中可以包含小部件的位置。
OverlayEntry({
required this.builder,//构建在Overlay中显示的Widget
bool opaque = false,//builger中构建的widget是否遮挡整个Overlay,如果opaque=true,Overlay将不在构建该widget下面的widget,除非maintainState=true
bool maintainState = false,//该widget是否必须显示在树种。即使opaque=true
})
Overlay的使用
该示例实现如下效果:
点击“show icon”按钮,在右上角图标下显示一个悬浮提示。
![在这里插入图片描述](http://www.kaotop.com/file/tupian/20220505/579872c28cdd4975b6e6df2a02615f56.png#pic_center
![在这里插入图片描述](http://www.kaotop.com/file/tupian/20220505/d1acf50917d143a7b2448d6044a10fee.png#pic_center
class _SimpleOverlayPageState extends State<SimpleOverlayPage> {
final GlobalKey _noticeKey = GlobalKey();
OverlayEntry? _overlayEntry;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Overlay+OverlayEntry"),
actions: [
Padding(
key: _noticeKey,
padding: const EdgeInsets.all(16),
child: const Icon(Icons.notifications),
)
],
),
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text("该示例在AppBar右上角的图标出显示一个提示信息"),
TextButton(
onPressed: () {
showOverlay(context);
},
child: const Text(
"show icon",
style: TextStyle(
fontSize: 30,
),
),
),
],
),
),
);
}
///显示浮层
void showOverlay(BuildContext context) async {
if (_overlayEntry != null) {
_overlayEntry?.remove();
_overlayEntry = null;
return;
}
//1、获取OverlayState
OverlayState? overlayState = Overlay.of(context);
//2、创建OverlayEntry
_overlayEntry = OverlayEntry(
builder: (ctx) {
//获取_noticeKey Widget的位置,用于设置OverlayEntry将悬浮的位置
RenderBox? renderBox =
_noticeKey.currentContext?.findRenderObject() as RenderBox?;
Size size = renderBox?.size ?? Size.zero;
Offset offset = renderBox?.localToGlobal(Offset.zero) ?? Offset.zero;
final double _top = offset.dy + size.height;
double radius = 20.0;
double right = offset.dx + size.width / 2 - radius;
return Positioned(
top: _top,
left: right,
child: CircleAvatar(
radius: radius,
backgroundColor: Colors.orange,
child: const Text("20"),
),
);
},
opaque: false,
);
//3、将OverlayEntry插入到Overlay中。
overlayState?.insert(_overlayEntry!);
}
}
源码地址:
Overlay + OverlayEntry 演示用例2
实现悬浮窗,类似安卓中的PopupWindow
class _PopupDialogPageState extends State<PopupDialogPage> {
final GlobalKey nameKey = GlobalKey();
final TextEditingController _controller = TextEditingController();
final FocusNode _focusNode = FocusNode();
OverlayEntry? _overlayEntry;
final LayerLink _layerLink = LayerLink();
@override
void initState() {
super.initState();
_focusNode.addListener(() {
if (_focusNode.hasFocus) {
showOverlay(context);
} else {
dismissOverlay();
}
});
}
@override
void dispose() {
// _focusNode.removeListener(() { })
super.dispose();
_controller.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Overlay"),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
height: 60,
color: Colors.orange,
),
Container(
height: 40,
color: Colors.yellow,
child: const Text("在输入框下显示可以输入的用户名"),
),
Container(
margin: const EdgeInsets.symmetric(horizontal: 30),
child: CompositedTransformTarget(
link: _layerLink,
child: TextField(
key: nameKey,
focusNode: _focusNode,
controller: _controller,
decoration: InputDecoration(
label: const Text("输入用户名"),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
),
),
),
),
),
Container(
height: 100,
color: Colors.green,
),
],
),
),
);
}
void showOverlay(BuildContext context) {
final overlay = Overlay.of(context);
_overlayEntry = OverlayEntry(builder: (ctx) {
final renderBox =
nameKey?.currentContext?.findRenderObject() as RenderBox;
final size = renderBox.size;
final offset = renderBox.localToGlobal(Offset.zero);
///如果使用了CompositedTransformFollower,就不在在对Positioned 设置left,top
return Positioned(
// left: offset.dx,
// top: offset.dy + size.height,
width: size.width,
child: CompositedTransformFollower(
link: _layerLink,
showWhenUnlinked: false,
offset: Offset(0, size.height + 8),
child: Material(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
title: Text("ZhangSan"),
subtitle: Text("CFO"),
onTap: () {
_controller.text = "ZhangSan";
dismissOverlay();
_focusNode.unfocus();
},
),
ListTile(
title: Text("HuShuo"),
subtitle: Text("CEO"),
onTap: () {
_controller.text = "HuShuo";
dismissOverlay();
_focusNode.unfocus();
},
),
ListTile(
title: Text("LiMing"),
subtitle: Text("Common"),
onTap: () {
_controller.text = "LiMing";
dismissOverlay();
_focusNode.unfocus();
},
),
],
),
),
),
);
});
overlay?.insert(_overlayEntry!);
}
void dismissOverlay() {
if (_overlayEntry != null) {
_overlayEntry?.remove();
_overlayEntry = null;
}
}
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)