如何在颤振应用程序之上覆盖小部件?
Posted
技术标签:
【中文标题】如何在颤振应用程序之上覆盖小部件?【英文标题】:How to overlay a widget on top of a flutter App? 【发布时间】:2019-11-25 22:06:17 【问题描述】:我想要一个位于整个应用程序之上的小部件。当我尝试使用Overlay.of(context).insert
执行此操作时,覆盖将在替换该路线后消失。即使稍后弹出屏幕,有没有办法在我的应用程序顶部添加一个小部件?
【问题讨论】:
你有没有想过在你的 MaterialApp 中使用 Stack 作为home
?
【参考方案1】:
也许存在更优化的方式,但作为一个选项,这是一个包含两个页面的示例,本地导航器和覆盖。
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget
@override
_MyAppState createState() => _MyAppState();
class _MyAppState extends State<MyApp>
final _navigatorKey = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context)
return MaterialApp(
debugShowCheckedModeBanner: false,
home: WillPopScope(
onWillPop: () async => !await _navigatorKey.currentState.maybePop(),
child: LayoutBuilder(
builder: (context, constraints)
WidgetsBinding.instance.addPostFrameCallback((_) => _insertOverlay(context));
return Navigator(
key: _navigatorKey,
onGenerateRoute: (RouteSettings settings)
switch (settings.name)
case '/page2':
return MaterialPageRoute(builder: (_) => Page2());
default:
return MaterialPageRoute(builder: (_) => Page1(_navigatorKey));
,
);
,
),
),
);
void _insertOverlay(BuildContext context)
return Overlay.of(context).insert(
OverlayEntry(builder: (context)
final size = MediaQuery.of(context).size;
print(size.width);
return Positioned(
width: 56,
height: 56,
top: size.height - 72,
left: size.width - 72,
child: Material(
color: Colors.transparent,
child: GestureDetector(
onTap: () => print('ON TAP OVERLAY!'),
child: Container(
decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.redAccent),
),
),
),
);
),
);
class Page1 extends StatelessWidget
final GlobalKey<NavigatorState> navigatorKey;
Page1(this.navigatorKey);
@override
Widget build(BuildContext context)
return Scaffold(
backgroundColor: Colors.green[200],
appBar: AppBar(title: Text('Page1')),
body: Container(
alignment: Alignment.center,
child: RaisedButton(
child: Text('go to Page2'),
onPressed: () => navigatorKey.currentState.pushNamed('/page2'),
),
),
);
class Page2 extends StatelessWidget
@override
Widget build(BuildContext context)
return Scaffold(
backgroundColor: Colors.yellow[200],
appBar: AppBar(title: Text('back to Page1')),
body: Container(
alignment: Alignment.center,
child: Text('Page 2'),
),
);
【讨论】:
这个例子将我推向了我的用例的正确方向。 我知道它不是(MaterialApp 的)“原始”导航器,而是一个嵌套的新导航器。这种方法有什么缺点吗? 这不会在每一帧之后继续插入叠加层吗?另外,你想从_insertOverlay
方法返回什么? .insert(...)
返回无效。【参考方案2】:
看完cmets,找到github-repo-link
-
创建了一个覆盖所有内容的叠加层
可以从任何地方调用。
只需 4 个简单的步骤即可完成
flutterflutter-layout
STEP-1:在 main.dart 中:
class MyApp extends StatelessWidget
// This widget is the root of your application.
@override
Widget build(BuildContext context)
return MaterialApp(
home: Stack( <-- using stack
children: [
MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
),
OverlayView(),<-- my overlay widget
],
),
);
第 2 步:OverLayView.dart
class OverlayView extends StatelessWidget
const OverlayView(
Key key,
) : super(key: key);
@override
Widget build(BuildContext context)
return ValueListenableBuilder<bool>( <--- IMP , using ValueListenableBuilder for showing/removing overlay
valueListenable: Loader.appLoader.loaderShowingNotifier,
builder: (context, value, child)
if (value)
return yourOverLayWidget(); <-- your awesome overlay
else
return Container();
,
);
STEP-3:loder_controller.dart(显示/隐藏)
class Loader
static final Loader appLoader = Loader(); <-- singleton
ValueNotifier<bool> loaderShowingNotifier = ValueNotifier(false);
ValueNotifier<String> loaderTextNotifier = ValueNotifier('error message');
void showLoader() <-- using to show from anywhere
loaderShowingNotifier.value = true;
void hideLoader() <-- using to hide from anywhere
loaderShowingNotifier.value = false;
void setText(String errorMessage) <-- using to change error message from anywhere
loaderTextNotifier.value = errorMessage;
void setImage() <-- DIY
// same as that of setText //
最后一步 4:显示/隐藏加载器
我正在展示它,在 boilerplate 代码增量方法上显示加载器
void _incrementCounter() async
Loader.appLoader.showLoader(); <-- show loder
Loader.appLoader.setText(errorMessage: 'this is custom error message');<-- set custom message
await Future.delayed(Duration(seconds: 5)); <-- im hiding it after 5 sec
Loader.appLoader.hideLoader(); <-- do whatever you want
【讨论】:
【参考方案3】:截图(空安全):
完整代码:
void main() => runApp(MyApp());
class MyApp extends StatefulWidget
@override
_MyAppState createState() => _MyAppState();
class _MyAppState extends State<MyApp>
Offset _offset = Offset.zero;
@override
Widget build(BuildContext context)
return MaterialApp(
home: LoginPage(),
builder: (context, child)
return Stack(
children: [
child!,
Positioned(
left: _offset.dx,
top: _offset.dy,
child: GestureDetector(
onPanUpdate: (d) => setState(() => _offset += Offset(d.delta.dx, d.delta.dy)),
child: FloatingActionButton(
onPressed: () ,
backgroundColor: Colors.black,
child: Icon(Icons.add),
),
),
),
],
);
,
);
LoginPage
:
class LoginPage extends StatelessWidget
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(title: Text('LoginPage')),
body: Center(
child: ElevatedButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(builder: (_) => HomePage())),
child: Text('Page2'),
),
),
);
HomePage
:
class HomePage extends StatelessWidget
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(title: Text('HomePage')),
body: FlutterLogo(size: 300),
);
【讨论】:
不是我要寻找的,但我喜欢这个实现【参考方案4】:您是否尝试将Navigator
添加为您的Scaffold
的子/后代?据我记得,默认导航器在MaterialApp
中,它高于一切。当您添加自己的Navigator
时,您的路由将发生在Scaffold
之下,而不是在树中它之上。
【讨论】:
这样做会使使用 android 上的后退按钮关闭应用程序... :(以上是关于如何在颤振应用程序之上覆盖小部件?的主要内容,如果未能解决你的问题,请参考以下文章