如何在 flutter 中在整个应用程序顶部显示叠加层?

问题描述 投票:0回答:4

我想在整个应用程序上显示覆盖,因此我尝试在 MaterialApp(根小部件)的上下文中插入覆盖条目,但问题是我在调用以下方法时得到空值:

Overlay.of(context);
GetMaterialApp.router(
          debugShowCheckedModeBanner: false,
          theme: AppTheme.lightTheme,
          scaffoldMessengerKey: Keys.scaffold,
          scrollBehavior: MyCustomScrollBehavior(),
          routeInformationParser: WebRoutes.goRouter.routeInformationParser,
          routerDelegate: WebRoutes.goRouter.routerDelegate,
          routeInformationProvider: WebRoutes.goRouter.routeInformationProvider,
          builder: (context, child) {

          WidgetsBinding.instance.addPostFrameCallback((_){
              addOverlay(context);
            });
           
            return child;

}

void addOverlay(BuildContext context) {
  print(Overlay.of(context));

  return Overlay.of(context)?.insert(OverlayEntry(
    builder: (context) {
      return SomeWidget();
    },
  ));
}

有什么方法可以使用此根小部件的上下文来获取叠加层的状态,因为我想全局显示叠加层。

非常感谢,如果有人帮助我,我真的很感激。

flutter overlay
4个回答
2
投票
MaterialApp(
        navigatorKey: getIt.get<NavigatorService>().navigatorKey,
        theme: AppTheme.defaultTheme,
        initialRoute: AppRoutes.splashScreen,
        builder: (context, child) {
          return Scaffold(
            body: Stack(
              children: [
                child!,
                Positioned(
                  top: 15,
                  child: Container(
                    color: Colors.red,
                    height: 50,
                    width: MediaQuery.of(context).size.width,
                    child: const Center(child: Text("HI I AM AN OVERLAY")),
                  ),
                ),
              ],
            ),
          );
        },
        onGenerateRoute: AppRoutes.onGenerateRoute,
      ),


2
投票

您可以通过创建一个负责显示/删除覆盖的类来实现,该类在创建时需要接收一个

BuildContext
才能创建
Overlay
的实例。

基本上你需要做的是:

  1. 创建一个类

    OverlayScreen
    来构建
    OverlayState
    &&
    OverlayEntry
    (在这种情况下,
    OverylayEntry
    将是一个OverlayEntry列表,因为我们可能在屏幕上有多个Overlay,所以我们可以将它们全部删除)立刻)。

  2. 先在您的应用程序中创建此类的实例(例如 MyApp)。在您的情况下,您需要在 Material.router...builder param 中调用它。

  3. 在主页中访问此

    overlayScreen
    以显示|删除所有叠加层

让我们创建我们的 OverlayScreen

import 'package:flutter/material.dart';

class OverlayScreen {
  /// Create an Overlay on the screen
  /// Declared [overlayEntrys] as List<OverlayEntry> because we might have
  /// more than one Overlay on the screen, so we keep it on a list and remove all at once
  BuildContext _context;
  OverlayState? overlayState;
  List<OverlayEntry>? overlayEntrys;

  void closeAll() {
    for (final overlay in overlayEntrys ?? <OverlayEntry>[]) {
      overlay.remove();
    }
    overlayEntrys?.clear();
  }

  void show() {
    overlayEntrys?.add(
      OverlayEntry(
        builder: (context) {
          return _buildOverlayWidget();
        },
      ),
    );

    overlayState?.insert(overlayEntrys!.last);
  }

  OverlayScreen._create(this._context) {
    overlayState = Overlay.of(_context);
    overlayEntrys = [];
  }

  factory OverlayScreen.of(BuildContext context) {
    return OverlayScreen._create(context);
  }

  Widget _buildOverlayWidget() {
    return Positioned(
      top: 20,
      left: 20,
      right: 20,
      child: Container(
        width: 300,
        color: Colors.black,
        height: 300,
        child: const Text("MY CHAT"),
      ),
    );
  }
}

现在让我们在

MyApp

上创建一个实例

// Need to have it global to be able to access everywhere
OverlayScreen? overlayScreen;

void main() {
  runApp(
    const MyApp(),
  );
}


class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: const HomePage(),
      builder: (context, child) {
        return Overlay(
          initialEntries: [
            OverlayEntry(
              builder: (context) {
// Create an instance of `OverlayScreen` to be accessed globally
                overlayScreen = OverlayScreen.of(context);
                return child ?? const SizedBox();
              },
            ),
          ],
        );
      },
    );
  }
}

为了最终确定,让我们创建我们的

HomePage
并在那里访问我们的
overlayScreen

import 'package:flutter/material.dart';
import 'package:overlay_all_app/src/overlay_screen.dart';

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    // Create an instance of OverlayScreen
    final overlayScreen = OverlayScreen.of(context);

    return Scaffold(
      appBar: AppBar(
        title: const Text('Home'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            TextButton(
              onPressed: () {
                // display the overlay
                overlayScreen.show();
              },
              child: const Text('Display Overlay'),
            ),
            const SizedBox(height: 30),
            TextButton(
              onPressed: () {
                // Call your next screen here
              },
              child: const Text('Go to next page'),
            ),
            const SizedBox(height: 30),
            TextButton(
              onPressed: () {
                // removed all overlays on the screen
                overlayScreen.closeAll();
              },
              child: const Text('Close Overlay'),
            ),
          ],
        ),
      ),
    );
  }
}

就是这样。您可以使用此类 OverlayScreen 在任何您想要的地方显示/删除All。

我使用示例代码创建了一个 PR,请查看 https://github.com/antonio-nicolau/flutter-working-with-overlay


0
投票
import 'package:flutter/material.dart';
import 'package:get/get_navigation/src/root/get_material_app.dart';
import 'package:go_router/go_router.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  runApp(App2());
}

class App2 extends StatelessWidget {
  App2({super.key});
  final _router = GoRouter(
    routes: [
      GoRoute(
        path: '/',
        builder: (context, state) => const OverlayWrapper(),
      ),
    ],
  );

  @override
  Widget build(BuildContext context) {
    return GetMaterialApp.router(
      routeInformationParser: _router.routeInformationParser,
      routerDelegate: _router.routerDelegate,
      routeInformationProvider: _router.routeInformationProvider,
    );
  }
}

class OverlayWrapper extends StatefulWidget {
  const OverlayWrapper({Key? key}) : super(key: key);

  @override
  State<OverlayWrapper> createState() => _OverlayWrapperState();
}

class _OverlayWrapperState extends State<OverlayWrapper> {
  @override
  void initState() {
    super.initState();
  }

  showOverLay() {
    OverlayEntry overlayEntry = OverlayEntry(
      builder: (context) => Container(
        color: Colors.red,
        child: const Text('data'),
      ),
    );
    Overlay.of(context).insert(overlayEntry);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            showOverLay();
          },
          child: const Text(
            'ShowOverlay',
            style: TextStyle(),
          ),
        ),
      ),
    );
  }
}

0
投票

鉴于问题是关于如何添加 使用 .router Navigator 2.0 进行覆盖。最好的方法是将导航器小部件包装在 RouterDelegate 的构建方法中的覆盖层中,如下所示:

class AppRouterDelegate extends RouterDelegate<AppRoute> with ChangeNotifier, PopNavigatorRouterDelegateMixin<AppRoute> {
  

  final PageNotifier notifier;
  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
  final SideMenu _sidMenu = const SideMenu();
  final GlobalKey<NavigatorState> _navigatorKey;

  AppRouterDelegate({required this.notifier}) : _navigatorKey = GlobalKey<NavigatorState>();
  
  @override
  GlobalKey<NavigatorState> get navigatorKey => _navigatorKey;


  @override
  Widget build(BuildContext context) {


   //THIS EXAMPLE SHOWS USING A SCAFFOLD IN THE OVERLAY WITH A SIDE MENU AND BUTTON


   return Overlay(
              initialEntries: [
                    OverlayEntry(
                      builder: (context) => Scaffold(
                        key: _scaffoldKey,
                        drawer:  _sidMenu,  //UTILIZE THE DRAWER PROPERTY FOR A SIDE MENU 
                        body: Stack(
                          children: [
                            Navigator(
                                key: navigatorKey,
                                pages: [
                                  //YOUR PAGE LOGIC
                                ]
                            ),
                            Positioned( // ADD AN ICON TO OPEN SIDE MENU
                              top: 10.0, 
                              left: 10.0,
                              child: IconButton(
                                icon: const Icon(Icons.menu, size: 30.0),
                                onPressed: () {
                                  _scaffoldKey.currentState!.openDrawer();
                                },
                              ),
                            ),
                          ],
                        ),
                      ),
                    ),
                  ],
              );

     
  } 

  //Rest of your router delegate code

}
© www.soinside.com 2019 - 2024. All rights reserved.