Flutter 如何在颤振应用程序上覆盖小部件?

Flutter 如何在颤振应用程序上覆盖小部件?,flutter,flutter-layout,Flutter,Flutter Layout,我想要一个小部件,将坐在整个应用程序的顶部。当我尝试使用Overlay.of(context).insert执行此操作时,覆盖将在替换该路由后消失。是否有一种方法可以让我的应用程序上有一个小部件,即使屏幕稍后弹出?您是否尝试添加一个导航器作为脚手架的子/后代?据我所知,默认导航器位于MaterialApp中,它高于一切。当您添加自己的导航器时,您的路由将发生在树中的脚手架下,而不是其上方 可能存在一种更优化的方式,但作为一种选择,这是一个具有两个页面的示例,本地导航器和覆盖 import 'pa

我想要一个小部件,将坐在整个应用程序的顶部。当我尝试使用
Overlay.of(context).insert执行此操作时,覆盖将在替换该路由后消失。是否有一种方法可以让我的应用程序上有一个小部件,即使屏幕稍后弹出?

您是否尝试添加一个
导航器作为
脚手架的子/后代?据我所知,默认导航器位于
MaterialApp
中,它高于一切。当您添加自己的
导航器
时,您的路由将发生在树中的
脚手架
下,而不是其上方

可能存在一种更优化的方式,但作为一种选择,这是一个具有两个页面的示例,本地导航器和覆盖

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());
类MyApp扩展了StatefulWidget{
@凌驾
_MyAppState createState()=>\u MyAppState();
}
类MyAppState扩展了状态{
最终_navigatorKey=GlobalKey();
@凌驾
小部件构建(构建上下文){
返回材料PP(
debugShowCheckedModeBanner:false,
家:威尔波普(
onWillPop:()异步=>!等待_navigatorKey.currentState.maybePop(),
子:布局生成器(
生成器:(上下文、约束){
WidgetsBinding.instance.addPostFrameCallback(()=>insertOverlay(上下文));
返回导航器(
键:_导航工作,
onGenerateRoute:(路由设置){
开关(设置.名称){
案例“/page2”:
返回MaterialPackageRoute(生成器:()=>Page2());
违约:
返回物料管理路线(生成器:()=>Page1(navigatorKey));
}
},
);
},
),
),
);
}
void\u insertOverlay(构建上下文){
返回Overlay.of(上下文).insert(
覆盖入口(生成器:(上下文){
最终大小=MediaQuery.of(context).size;
打印(大小、宽度);
返回定位(
宽度:56,
身高:56,
顶部:尺寸。高度-72,
左:size.width-72,
儿童:材料(
颜色:颜色。透明,
儿童:手势检测器(
onTap:()=>打印('ON-TAP OVERLAY!'),
子:容器(
装饰:框装饰(形状:BoxShape.circle,颜色:Colors.redAccent),
),
),
),
);
}),
);
}
}
类Page1扩展了无状态小部件{
最后的环球航行;
第1页(本页为导航工作);
@凌驾
小部件构建(构建上下文){
返回脚手架(
背景颜色:颜色。绿色[200],
appBar:appBar(标题:文本('Page1')),
主体:容器(
对齐:对齐.center,
孩子:升起按钮(
子项:文本(“转到第2页”),
onPressed:()=>navigatorKey.currentState.PushName('/page2'),
),
),
);
}
}
类Page2扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回脚手架(
背景颜色:颜色。黄色[200],
appBar:appBar(标题:文本(“返回到第1页”),
主体:容器(
对齐:对齐.center,
子项:文本(“第2页”),
),
);
}
}

阅读评论后,找到

  • 创建了一个覆盖,将坐在上面的一切
  • 可以从任何地方调用
  • 只需遵循4个简单步骤
  • 步骤1:在main.dart中:
    类MyApp扩展了无状态小部件{
    //此小部件是应用程序的根。
    @凌驾
    小部件构建(构建上下文){
    返回材料PP(
    
    home:Stack(这样做会使使用android上的back按钮关闭应用程序…:(这个例子将我推向了我的用例的正确方向。我知道它不是MaterialApp的“原始”导航器),而是一个嵌套的新方法。这种方法有什么缺点吗?这会不会在每一帧后继续在其顶部插入覆盖层?还有,您试图从
    \u insertOverlay
    方法返回什么?
    。insert(…)
    返回void。您是否想过在MaterialApp中使用堆栈作为
    主页
    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 
      }