Flutter覆盖层管理插件x_overlay的使用

Flutter覆盖层管理插件x_overlay的使用

如果你觉得这个包对你有帮助,请在pub.dev上给它点赞!↗️

你的支持和鼓励激励我们继续改进和维护这个包。

感谢你认可我们的工作!👏👏


功能

轻松支持覆盖层


使用方法

自定义覆盖层数据

你可以通过继承扩展来自定义变量。

/// 定义
class MyOverlayData extends XOverlayData {
  MyOverlayData({
    required this.myVariable,
  });

  final String myVariable;
}

/// 转换
final overlayData = XOverlayData();
late MyOverlayData myOverlayData;
if (data is MyOverlayData) {
  myOverlayData = data;
}
myOverlayData.myVariable;

定义一个 XOverlayController 对象

final overlayController = XOverlayController();

在你的应用主页面中使用 XOverlayPopScope

这将替换直接退出应用并返回到桌面,否则页面会被销毁。

MaterialApp(
  ...
  home: XOverlayPopScope(
    child: YourHomePage(),
  ),
)

在 MaterialApp 的 builder 中嵌套 XOverlayPage

  • 在 builder 中读取覆盖层数据并返回覆盖层页面。
  • 确保在 contextQuery 中返回正确的上下文。
  • 默认情况下,点击覆盖层页面会返回到内容页面。因此,在 restoreWidgetQuery 中读取覆盖层数据并返回内容页面。
MaterialApp(
  ...
  navigatorKey: widget.navigatorKey,
  builder: (BuildContext context, Widget? child) {
    return Stack(
      children: [
        child!,

        /// 添加覆盖层页面在 MaterialApp.builder 中
        XOverlayPage(
          controller: overlayController,
          contextQuery: () {
            return widget.navigatorKey.currentState!.context;
          },
          restoreWidgetQuery: (
            XOverlayData data,
          ) {
            /// 默认情况下,点击覆盖层页面会返回到内容页面。
            /// 因此,读取你的覆盖层数据,并返回你的内容页面
            late MyOverlayData myOverlayData;
            if (data is MyOverlayData) {
              myOverlayData = data;
            }

            /// 然后恢复目标小部件
            return ContentPage(
              overlayController: overlayController,

              /// 当从覆盖层页面返回时,可以同步在覆盖过程中更改的结果。
              customVariable: myOverlayData.myVariable,
            );
          },
          builder: (XOverlayData data) {
            /// 读取你的覆盖层数据并返回覆盖层页面
            late MyOverlayData myOverlayData;
            if (data is MyOverlayData) {
              myOverlayData = data;
            }

            return OverlayPage(
              overlayController: overlayController,

              /// 从内容页面读取的数据,并可能在覆盖过程中更改
              customVariable: myOverlayData.myVariable,
            );
          },
        ),
      ],
    );
  },
)

在你的内容小部件中添加 XOverlayButton

或者自定义一个按钮并调用控制器的 overlay 方法。

XOverlayButton(
  controller: widget.overlayController,
  dataQuery: () {
    /// 需要返回到覆盖层页面的数据
    return YourOverlayData();
  },
)

示例

以下是完整的示例代码:

import 'package:flutter/material.dart';
import 'package:x_overlay/x_overlay.dart';

class MyOverlayData extends XOverlayData {
  MyOverlayData({
    required this.myVariable,
  });

  final String myVariable;
}

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  final navigatorKey = GlobalKey<NavigatorState>();

  runApp(MyApp(
    navigatorKey: navigatorKey,
  ));
}

class MyApp extends StatefulWidget {
  final GlobalKey<NavigatorState> navigatorKey;

  const MyApp({
    required this.navigatorKey,
    super.key,
  });

  [@override](/user/override)
  State<StatefulWidget> createState() => MyAppState();
}

class MyAppState extends State<MyApp> {
  /// 1/4. 定义一个 XOverlayController 对象
  final overlayController = XOverlayController();

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: widget.navigatorKey,
      title: 'Flutter Demo',
      color: Colors.white,

      /// 2/4. 替换直接退出应用并返回到桌面,否则页面会被销毁。
      home: XOverlayPopScope(
        child: HomePage(
          overlayController: overlayController,
        ),
      ),
      builder: (BuildContext context, Widget? child) {
        return Stack(
          children: [
            child!,

            /// 3/4. 在 MaterialApp.builder 中添加覆盖层页面
            XOverlayPage(
              controller: overlayController,
              contextQuery: () {
                return widget.navigatorKey.currentState!.context;
              },
              restoreWidgetQuery: (
                XOverlayData data,
              ) {
                /// 默认情况下,点击覆盖层页面会返回到内容页面。
                /// 因此,读取你的覆盖层数据,并返回你的内容页面
                late MyOverlayData myOverlayData;
                if (data is MyOverlayData) {
                  myOverlayData = data;
                }

                /// 然后恢复目标小部件
                return ContentPage(
                  overlayController: overlayController,

                  /// 当从覆盖层页面返回时,可以同步在覆盖过程中更改的结果。
                  customVariable: myOverlayData.myVariable,
                );
              },
              builder: (XOverlayData data) {
                /// 读取你的覆盖层数据并返回覆盖层页面
                late MyOverlayData myOverlayData;
                if (data is MyOverlayData) {
                  myOverlayData = data;
                }

                return OverlayPage(
                  overlayController: overlayController,

                  /// 从内容页面读取的数据,并可能在覆盖过程中更改
                  customVariable: myOverlayData.myVariable,
                );
              },
            ),
          ],
        );
      },
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({
    super.key,
    required this.overlayController,
  });

  final XOverlayController overlayController;

  [@override](/user/override)
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      /// 防止UI因文本编辑器移动
      resizeToAvoidBottomInset: false,
      body: SafeArea(
        child: Stack(
          children: [
            Container(color: Colors.white),
            const Align(
              alignment: Alignment.topCenter,
              child: Text("Home Page"),
            ),
            Center(child: contentButton()),
          ],
        ),
      ),
    );
  }

  Widget contentButton() {
    return ValueListenableBuilder<XOverlayPageState>(
      valueListenable: widget.overlayController.pageStateNotifier,
      builder: (context, overlayPageState, _) {
        return ElevatedButton(
          onPressed: () {
            if (XOverlayPageState.overlaying == overlayPageState) {
              /// 在覆盖过程中,直接恢复内容页面
              widget.overlayController.restore(context);
            } else {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => ContentPage(
                    overlayController: widget.overlayController,
                  ),
                ),
              );
            }
          },
          child: const Text("Content Page"),
        );
      },
    );
  }
}

class ContentPage extends StatefulWidget {
  final XOverlayController overlayController;

  /// 当从覆盖层页面返回时,可以设置在覆盖过程中更改的结果。
  final String? customVariable;

  const ContentPage({
    super.key,
    required this.overlayController,
    this.customVariable,
  });

  [@override](/user/override)
  State<ContentPage> createState() => _ContentPageState();
}

class _ContentPageState extends State<ContentPage> {
  final textController = TextEditingController();

  [@override](/user/override)
  void initState() {
    super.initState();

    textController.text = widget.customVariable ?? 'Custom Variable';
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Stack(
          children: [
            Container(color: Colors.blue),
            const Align(
              alignment: Alignment.topCenter,
              child: Text("Content Page"),
            ),
            Positioned(
              top: 10,
              left: 10,
              child:

                  /// 4.1/4. 你可以直接使用 XOverlayButton
                  XOverlayButton(
                buttonSize: const Size(30, 30),
                controller: widget.overlayController,
                dataQuery: () {
                  /// 需要返回到覆盖层页面的数据
                  return MyOverlayData(
                    myVariable: textController.text,
                  );
                },
              ),
            ),
            Positioned(
              top: 10,
              right: 10,
              child: SizedBox(
                height: 50,
                child: ElevatedButton(
                  /// 4.2/4. 或者自定义一个覆盖按钮并调用API
                  /// 使用 `overlayController.overlay(context, data: MyOverlayData);`
                  /// 在按钮的 onClick 事件上覆盖当前目标小部件
                  onPressed: () {
                    /// 需要返回到覆盖层页面的数据
                    final overlayData = MyOverlayData(
                      myVariable: textController.text,
                    );

                    /// 缓存数据并覆盖目标小部件
                    widget.overlayController.overlay(
                      context,
                      data: overlayData,
                    );
                  },
                  child: const Text("Overlay Button"),
                ),
              ),
            ),
            Center(child: TextField(controller: textController))
          ],
        ),
      ),
    );
  }
}

class OverlayPage extends StatefulWidget {
  final String customVariable;
  final XOverlayController overlayController;

  const OverlayPage({
    super.key,
    required this.overlayController,

    /// 从内容页面读取的数据,并可能在覆盖过程中更改
    required this.customVariable,
  });

  [@override](/user/override)
  State<OverlayPage> createState() => _OverlayPageState();
}

class _OverlayPageState extends State<OverlayPage> {
  final textController = TextEditingController();

  [@override](/user/override)
  void initState() {
    super.initState();

    textController.text = widget.customVariable;
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      /// 防止UI因文本编辑器移动
      resizeToAvoidBottomInset: false,
      body: SafeArea(
        child: Container(
          decoration: BoxDecoration(
            color: Colors.blue,
            borderRadius: BorderRadius.circular(10),
          ),
          child: Stack(
            children: [
              const Align(
                alignment: Alignment.topCenter,
                child: Text("Overlay Page"),
              ),
              Center(
                child: TextField(
                  controller: textController,
                  textAlign: TextAlign.center,
                  onChanged: (text) {
                    widget.overlayController.updateData(
                      MyOverlayData(myVariable: text),
                    );
                  },
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

更多关于Flutter覆盖层管理插件x_overlay的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter覆盖层管理插件x_overlay的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,关于Flutter中的x_overlay插件(假设这是一个用于管理覆盖层的第三方插件,尽管这不是一个官方或广泛认知的插件名,但我会基于假设给出一个通用的覆盖层管理示例),以下是一个可能的代码案例来展示如何使用一个覆盖层管理插件。请注意,具体实现可能会根据插件的实际API有所不同,以下代码仅供参考。

首先,假设x_overlay插件提供了一个OverlayManager类来管理覆盖层,并且有一个showOverlay方法来显示覆盖层。以下是一个简单的示例代码:

1. 添加依赖

首先,确保在pubspec.yaml文件中添加了x_overlay插件的依赖(这里假设插件名为x_overlay,实际使用时请替换为正确的插件名):

dependencies:
  flutter:
    sdk: flutter
  x_overlay: ^latest_version  # 替换为实际版本号

2. 导入插件并使用

在你的Flutter应用中,导入x_overlay插件并使用它。以下是一个完整的示例,包括如何在按钮点击时显示一个覆盖层:

import 'package:flutter/material.dart';
import 'package:x_overlay/x_overlay.dart';  // 假设这是插件的导入路径

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Overlay Manager Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: OverlayDemoScreen(),
    );
  }
}

class OverlayDemoScreen extends StatefulWidget {
  @override
  _OverlayDemoScreenState createState() => _OverlayDemoScreenState();
}

class _OverlayDemoScreenState extends State<OverlayDemoScreen> {
  final OverlayManager _overlayManager = OverlayManager();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Overlay Manager Demo'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: () {
            _showOverlay(context);
          },
          child: Text('Show Overlay'),
        ),
      ),
    );
  }

  void _showOverlay(BuildContext context) {
    // 假设OverlayManager有一个showOverlay方法,接收一个OverlayEntry和一个context
    _overlayManager.showOverlay(
      context,
      OverlayEntry(
        builder: (context) => Material(
          elevation: 4.0,
          child: Container(
            width: double.infinity,
            height: 200,
            color: Colors.white,
            child: Center(
              child: Text('This is an overlay!'),
            ),
          ),
        ),
      ),
    );
  }
}

// 假设这是插件中定义的OverlayManager类(实际上这个类应该由插件提供)
class OverlayManager {
  OverlayState? _overlayState;

  void showOverlay(BuildContext context, OverlayEntry overlayEntry) {
    _overlayState ??= Overlay.of(context)!;
    _overlayState!.insert(overlayEntry);
  }

  // 可以添加更多管理覆盖层的方法,如隐藏、移除等
}

注意事项

  1. 插件API:上面的代码是基于假设的OverlayManager类及其showOverlay方法。实际使用时,请查阅x_overlay插件的文档以了解正确的API用法。

  2. OverlayEntry管理:在实际应用中,你可能需要管理多个OverlayEntry,例如隐藏或移除它们。这通常涉及到在OverlayEntry上调用remove()方法。

  3. 错误处理:确保处理可能的空值或异常,特别是在调用插件方法时。

  4. 插件版本:检查插件的最新版本和更新日志,以确保你的代码与插件的最新API兼容。

由于x_overlay不是一个官方或广泛认知的插件名,上述代码是一个基于假设的示例。如果x_overlay是一个特定的第三方插件,请查阅其官方文档以获取准确的用法示例。

回到顶部