我想要一个位于整个应用程序之上的小部件。当我尝试使用
Overlay.of(context).insert
执行此操作时,覆盖层会在替换该路线后消失。有没有办法让我的应用程序顶部有一个小部件,即使屏幕稍后弹出?
也许存在更优化的方式,但作为一个选项,这是一个包含两个页面的示例,本地导航器和覆盖。
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'),
),
);
}
}
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),
);
}
}
看完评论后,找到github-repo-link
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
],
),
);
}
}
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();
}
},
);
}
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 //
}
}
我正在展示它,在样板增量方法代码上显示加载程序
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
}
作为其他答案的补充:如果您想显示一些叠加层,flutter_portal确实可能是一个更好的选择,更易于使用。
基本上,它看起来像:
PortalTarget(
// Declarative
portalFollower: MyAwesomeOverlayWidget(),
// Align anywhere you like. Now `portalFollower` floats at right of `child`
anchor: const Aligned(follower: Alignment.topLeft, target: Alignment.topRight),
child: MyChildWidget(),
)
请注意,它是声明性的(与 Overlay 不同,它不是命令性的)。此外,您还可以获得额外的好处,即对齐非常容易,并且上下文也很直观。
免责声明:我是该图书馆的当前所有者。
您是否尝试过添加
Navigator
作为您的 Scaffold
的 子/后代?据我记得,默认导航器位于
MaterialApp
中,它位于一切之上。当您添加自己的 Navigator
时,您的路由将发生在树中的 Scaffold
下方,而不是其上方。
这个包可能不错 flutter_portal