簡體   English   中英

如何在 flutter App 上疊加一個小部件?

[英]How to overlay a widget on top of a flutter App?

我想要一個位於整個應用程序之上的小部件。 當我嘗試使用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'),
      ),
    );
  }
}

在此處輸入圖片說明

看完評論,找到github-repo-link

  1. 創建了一個覆蓋層,將位於一切之上
  2. 可以從任何地方調用。
  3. 只需 4 個簡單的步驟

在此處輸入圖片說明

步驟 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();
        }
      },
    );
  }

第 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:顯示/隱藏裝載機

我正在展示它,在增量方法的樣板代碼上顯示加載器

 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 
  }

屏幕截圖(空安全):

在此處輸入圖片說明


完整代碼:

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),
    );
  }
}

作為對其他答案的補充:如果你想顯示一些 overlaysflutter_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(),
)

請注意,它是聲明性的(與覆蓋相反,不是強制性的)。 此外,您得到的好處是 alignment 非常簡單,上下文也很直觀。

免責聲明:我是該庫的當前所有者。

您是否嘗試將Navigator添加為Scaffold子項/后代 據我所知,默認導航器位於MaterialApp ,它高於一切。 當您添加自己的Navigator ,您的路由將發生在Scaffold下,而不是在樹的上方。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM