在 Flutter 中使用 pushNamed 导航后如何使 OverlayEntry 消失
Posted
技术标签:
【中文标题】在 Flutter 中使用 pushNamed 导航后如何使 OverlayEntry 消失【英文标题】:How to make OverlayEntry disappear after navigating with pushNamed in Flutter 【发布时间】:2019-12-04 16:11:37 【问题描述】:我正在尝试制作如下所示的叠加层:
https://www.didierboelens.com/2018/06/how-to-create-a-toast-or-notifications-notion-of-overlay/
使用OverlayEntry
。
import 'package:flutter/material.dart';
import 'dart:async';
class ShowNotificationIcon
void show(BuildContext context) async
OverlayState overlayState = Overlay.of(context);
OverlayEntry overlayEntry = new OverlayEntry(builder: _build);
overlayState.insert(overlayEntry);
Widget _build(BuildContext context)
return new Positioned(
top: 50.0,
left: 50.0,
child: new Material(
color: Colors.transparent,
child: new Icon(Icons.warning, color: Colors.purple),
),
);
调用:
ShowNotificationIcon _icon = new ShowNotificationIcon();
_icon.show(context);
但是,当我尝试导航到其他屏幕时,覆盖仍保留在屏幕中。
如何只在被调用的屏幕中显示覆盖而不在其他屏幕中显示?
以防万一,这是我在有状态小部件中尝试过的:
ShowNotificationIcon _icon = new ShowNotificationIcon();
@override
void initState()
WidgetsBinding.instance.addPostFrameCallback((_)
_icon.show(context);
);
super.initState();
@override
void dispose()
_icon.remove();
super.dispose();
【问题讨论】:
有一个包调用overlay_containergithub.com/MustansirZia/overlay_container/blob/master/lib/…,或许你可以从中得到灵感 如果您只需要显示通知。你可以使用包flushbar pub.dev/packages/flushbar @chunhunghan 感谢您的输入,但我正在寻找一种方法来创建自己的代码。 @chunhunghan 我会看看overlay_container
。谢谢你的建议。
【参考方案1】:
这通常使用RouteAware
+RouteObserver
执行。
RouteObserver
是一个对象,它允许实现RouteAware
的对象对与路由相关的一些更改做出反应,其中包括:
然后您可以使用这两个事件来隐藏/显示您的叠加层
首先,您需要RouteObserver
。
这可以创建为全局变量,需要传递给您的Navigator
。在基于MaterialApp
的应用程序中,它通常如下所示:
final RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();
void main()
runApp(MaterialApp(
home: Container(),
navigatorObservers: [routeObserver],
));
然后,拥有OverlayEntry
的小部件现在可以像这样实现RouteAware
:
class RouteAwareWidget extends StatefulWidget
State<RouteAwareWidget> createState() => RouteAwareWidgetState();
// Implement RouteAware in a widget's state and subscribe it to the RouteObserver.
class RouteAwareWidgetState extends State<RouteAwareWidget> with RouteAware
@override
void didChangeDependencies()
super.didChangeDependencies();
// routeObserver is the global variable we created before
routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute);
@override
void dispose()
routeObserver.unsubscribe(this);
super.dispose();
@override
void didPush()
// Route was pushed onto navigator and is now topmost route.
@override
void didPopNext()
// Covering route was popped off the navigator.
@override
Widget build(BuildContext context) => Container();
此时,您可以使用didPush
和didPopNext
来显示/隐藏您的OverlayEntry:
OverlayEntry myOverlay;
@override
void didPush()
myOverlay.remove();
@override
void didPopNext()
Overlay.of(context).insert(myOverlay);
【讨论】:
如何在小部件中使用routeObserver
?复制上面的代码给了我routeObserver
的undefined_identifier
错误。
这是我们在上一步中定义的全局变量。你必须导入它
好的,到目前为止效果非常好。但是我使用了didPushNext()
而不是didPush()
,因为后者在当前路由已被推送且OverlayEntry
尚未插入时被调用。
但是,我希望找到一种更直接的方法,例如 Snackbar 的 showSnackBar()
函数。它不必担心在路由更改时将其删除。有没有办法检测函数本身内的路由变化形式?如我上面的示例,在ShowNotificationIcon
类中?
没有这样的东西。如果你愿意,你可以在 RouteAware 上构建你自己的,但你至少需要它。【参考方案2】:
我想建议使用软件包flushbar。 https://github.com/AndreHaueisen/flushbar 正如包所说:如果您在通知用户时需要更多自定义,请使用此包。对于 android 开发人员来说,它可以替代 toasts 和snackbars。
您也可以将flushbarPosition设置为TOP或BOTTOM
Flushbar(
title: "Hey Ninja",
message: "Lorem Ipsum is simply dummy text of the printing and typesetting industry",
flushbarPosition: FlushbarPosition.TOP,
flushbarStyle: FlushbarStyle.FLOATING,
reverseAnimationCurve: Curves.decelerate,
forwardAnimationCurve: Curves.elasticOut,
backgroundColor: Colors.red,
boxShadows: [BoxShadow(color: Colors.blue[800], offset: Offset(0.0, 2.0), blurRadius: 3.0)],
backgroundGradient: LinearGradient(colors: [Colors.blueGrey, Colors.black]),
isDismissible: false,
duration: Duration(seconds: 4),
icon: Icon(
Icons.check,
color: Colors.greenAccent,
),
mainButton: FlatButton(
onPressed: () ,
child: Text(
"CLAP",
style: TextStyle(color: Colors.amber),
),
),
showProgressIndicator: true,
progressIndicatorBackgroundColor: Colors.blueGrey,
titleText: Text(
"Hello Hero",
style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 20.0, color: Colors.yellow[600], fontFamily: "ShadowsIntoLightTwo"),
),
messageText: Text(
"You killed that giant monster in the city. Congratulations!",
style: TextStyle(fontSize: 18.0, color: Colors.green, fontFamily: "ShadowsIntoLightTwo"),
),
)..show(context);
【讨论】:
感谢您的输入,但我正在尝试学习编写此类功能的方法。所以,没有第三方库。有什么想法吗? overlay_container 包构造就像你做的那样。也许你可以看到它。 好的,我去看看。感谢您的建议 我看过了。但是,它没有我一直在寻找的两个要求,即:1)它应该作为一个函数调用。 2)导航到下一个屏幕时应该隐藏它。【参考方案3】:你只需要CompositedTransformTarget
、CompositedTransformFollower
和LinkLayout
。
多亏了这些,如果附加了叠加层的小部件消失了,叠加层也会消失。
final key = GlobalKey();
OverlayEntry? floatingEntry ;
final layerLink = LayerLink();
void hideEntry()
floatingEntry ?.remove();
void displayOverlay()
final overlay = Overlay.of(context);
floatingEntry = OverlayEntry(builder: _buildFloatingButton);
overlay!.insert(floatingEntry!);
Widget _buildFloatingButton(BuildContext context)
final render = key.currentContext!.findRenderObject() as RenderBox;
final offset = render.localToGlobal(Offset.zero);
final size = render.size;
return Positioned(
width: floatinSize,
child: CompositedTransformFollower(
link: layerLink,
offset: Offset(0.0, -size.height / 2),
showWhenUnlinked: false,
child: Container(
width: floatinSize,
height: floatinSize,
decoration: BoxDecoration(
color: Get.theme.scaffoldBackgroundColor,
shape: BoxShape.circle,
),
padding: const EdgeInsets.all(10.0),
child: Container(
decoration: BoxDecoration(
color: Get.theme.primaryColor,
shape: BoxShape.circle,
),
child: Icon(
FontAwesomeIcons.plus,
color: Get.theme.primaryColorDark,
),
),
),
),
);
@override
Widget build(BuildContext context)
final size = MediaQuery.of(context).size;
return Container(
width: size.width,
height: _navigationHeight,
color: Get.theme.bottomNavigationBarTheme.backgroundColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
CompositedTransformTarget(
link:linkLayout,
child:Container(key:key),
)
],
),
);
【讨论】:
以上是关于在 Flutter 中使用 pushNamed 导航后如何使 OverlayEntry 消失的主要内容,如果未能解决你的问题,请参考以下文章
Flutter:如何将 PageRoute Animation 与 Navigator.of(context).pushNamed 结合起来
为啥 Navigator.pushNamed(context, "route");在我的 flutter-cupertino-app 代码示例中工作?