Flutter悬浮窗管理插件flutter_overlay_window_fork的使用

Flutter悬浮窗管理插件flutter_overlay_window_fork的使用

auto_route_logo

MIT License stars pub version

Buy Me A Coffee

Flutter插件,用于在其他应用程序之上显示您的Flutter应用程序。


Preview

TrueCaller overlay example click-through overlay example Messanger chat-head example

Installation

pubspec.yaml文件中添加此包:

dependencies:
  flutter_overlay_window: any # 或者使用最新版本

Android

您需要在AndroidManifest.xml中添加SYSTEM_ALERT_WINDOW权限和OverlayService。将explanation_for_special_use替换为您自己的自定义说明。

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />

    <application>
        ...
        <service android:name="flutter.overlay.window.flutter_overlay_window.OverlayService" 
            android:exported="false"
            android:foregroundServiceType="specialUse">
            <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
                android:value="explanation_for_special_use"/>
        </service>
    </application>

Entry point

main.dart中创建一个用于Overlay小部件的入口点:

// overlay入口点
@pragma("vm:entry-point")
void overlayMain() {
  runApp(const MaterialApp(
    debugShowCheckedModeBanner: false,
    home: Material(child: Text("My overlay"))
  ));
}

Usage

检查是否已授予悬浮窗权限:

/// 检查是否已授予悬浮窗权限
final bool status = await FlutterOverlayWindow.isPermissionGranted();

请求悬浮窗权限:

/// 请求悬浮窗权限
/// 它会打开悬浮窗设置页面,并在权限授予后返回 `true`
final bool status = await FlutterOverlayWindow.requestPermission();

打开悬浮窗内容:

/// 打开悬浮窗内容
///
/// - 可选参数:
///
/// `height` 悬浮窗高度,默认为 [WindowSize.fullCover]
///
/// `width` 悬浮窗宽度,默认为 [WindowSize.matchParent]
///
/// `alignment` 屏幕上的对齐位置,默认为 [OverlayAlignment.center]
///
/// `visibilitySecret` 锁屏时通知详细信息的可见性,默认为 [NotificationVisibility.visibilitySecret]
///
/// `OverlayFlag` 悬浮窗标志,默认为 [OverlayFlag.defaultFlag]
///
/// `overlayTitle` 通知消息,默认为 "overlay activated"
///
/// `overlayContent` 通知内容
///
/// `enableDrag` 是否允许拖动悬浮窗,默认为 `false`
///
/// `positionGravity` 拖动后的悬浮窗位置,默认为 [PositionGravity.none]
///
/// `startPosition` 悬浮窗初始位置,默认为 `null`
await FlutterOverlayWindow.showOverlay();

关闭悬浮窗:

/// 如果悬浮窗已打开,则关闭
await FlutterOverlayWindow.closeOverlay();

在主应用和悬浮窗之间广播数据:

/// 在主应用和悬浮窗之间共享数据
await FlutterOverlayWindow.shareData("Hello from the other side");

监听主应用和悬浮窗之间的共享事件:

/// 监听悬浮窗和主应用之间共享的消息
FlutterOverlayWindow.overlayListener.listen((event) {
  print("当前事件: $event");
});

当需要使用键盘输入字段时启用焦点模式:

/// 当您想要使用需要键盘输入的字段时启用 [OverlayFlag.focusPointer]
await FlutterOverlayWindow.showOverlay(flag: OverlayFlag.focusPointer);

更新悬浮窗标志:

/// 在悬浮窗运行时更新标志
await FlutterOverlayWindow.updateFlag(OverlayFlag.defaultFlag);

更新悬浮窗在屏幕上的大小:

/// 更新悬浮窗在屏幕上的大小
await FlutterOverlayWindow.resizeOverlay(80, 120);

更新悬浮窗在屏幕上的位置:

/// 更新悬浮窗在屏幕上的位置
///
/// `position` 新的位置
///
/// `return` 如果位置成功更新则返回 `true`
await FlutterOverlayWindow.moveOverlay(OverlayPosition(0, 156));

获取当前悬浮窗的位置:

/// 获取当前悬浮窗的位置
///
/// `return` 当前悬浮窗的位置
await FlutterOverlayWindow.getOverlayPosition();

枚举类定义

悬浮窗标志枚举:

enum OverlayFlag {
  /// 窗口标志:该窗口永远不会接收触摸事件。
  /// 如果您想显示点击通过的悬浮窗,可以使用此标志。
  clickThrough,

  /// 窗口标志:该窗口不会接收任何按键输入焦点。
  /// 用户无法向其发送键或其他按钮事件。
  defaultFlag,

  /// 窗口标志:允许任何窗口外的指针事件被发送到其后面的窗口。
  /// 当您希望使用需要键盘输入的字段时,可以使用此标志。
  focusPointer,
}

悬浮窗拖动行为枚举:

/// 悬浮窗拖动行为类型。
enum PositionGravity {
  /// `PositionGravity.none` 允许悬浮窗放置在屏幕上的任意位置。
  none,

  /// `PositionGravity.right` 允许悬浮窗粘附在屏幕右侧。
  right,

  /// `PositionGravity.left` 允许悬浮窗粘附在屏幕左侧。
  left,

  /// `PositionGravity.auto` 允许悬浮窗根据当前位置粘附在屏幕左侧或右侧。
  auto,
}

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

1 回复

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


flutter_overlay_window_fork 是一个用于在 Flutter 应用中创建和管理悬浮窗的插件。它允许你在应用的其他界面之上显示一个独立的悬浮窗口,适用于需要常驻显示一些信息或操作的情况,比如显示当前播放的音乐、悬浮球等。

以下是如何使用 flutter_overlay_window_fork 插件的基本步骤:

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 flutter_overlay_window_fork 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_overlay_window_fork: ^0.0.1 # 请查看最新版本

然后运行 flutter pub get 来获取依赖。

2. 请求悬浮窗权限

在 Android 上,使用悬浮窗功能需要请求 SYSTEM_ALERT_WINDOW 权限。你需要在 AndroidManifest.xml 文件中添加以下权限声明:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

然后,你需要在运行时请求这个权限:

import 'package:flutter_overlay_window_fork/flutter_overlay_window_fork.dart';

Future<void> requestOverlayPermission() async {
  if (!await FlutterOverlayWindowFork.isPermissionGranted()) {
    await FlutterOverlayWindowFork.requestPermission();
  }
}

3. 创建悬浮窗

你可以使用 FlutterOverlayWindowFork.showOverlay 方法来创建一个悬浮窗。这个方法需要传递一个 OverlayWindowConfig 对象来配置悬浮窗的行为和外观。

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

void showOverlayWindow() async {
  await FlutterOverlayWindowFork.showOverlay(
    overlayWindowConfig: OverlayWindowConfig(
      width: 200,
      height: 200,
      alignment: OverlayAlignment.center,
      gravity: OverlayGravity.center,
      overlayContent: (BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            backgroundColor: Colors.transparent,
            body: Center(
              child: Container(
                width: 200,
                height: 200,
                color: Colors.blue,
                child: Center(
                  child: Text(
                    '悬浮窗内容',
                    style: TextStyle(color: Colors.white),
                  ),
                ),
              ),
            ),
          ),
        );
      },
    ),
  );
}

4. 关闭悬浮窗

你可以使用 FlutterOverlayWindowFork.closeOverlay 方法来关闭悬浮窗:

void closeOverlayWindow() async {
  await FlutterOverlayWindowFork.closeOverlay();
}

5. 处理悬浮窗的生命周期

你可以通过监听悬浮窗的生命周期事件来处理悬浮窗的显示和隐藏:

FlutterOverlayWindowFork.overlayWindowLifecycleStream.listen((event) {
  if (event == OverlayWindowLifecycleEvent.show) {
    print('悬浮窗显示');
  } else if (event == OverlayWindowLifecycleEvent.hide) {
    print('悬浮窗隐藏');
  }
});

6. 处理悬浮窗的点击事件

你可以在悬浮窗的内容中添加点击事件处理逻辑:

overlayContent: (BuildContext context) {
  return MaterialApp(
    home: Scaffold(
      backgroundColor: Colors.transparent,
      body: Center(
        child: GestureDetector(
          onTap: () {
            print('悬浮窗被点击');
          },
          child: Container(
            width: 200,
            height: 200,
            color: Colors.blue,
            child: Center(
              child: Text(
                '悬浮窗内容',
                style: TextStyle(color: Colors.white),
              ),
            ),
          ),
        ),
      ),
    ),
  );
},

7. 处理悬浮窗的拖拽事件

你可以在悬浮窗的内容中添加拖拽事件处理逻辑:

overlayContent: (BuildContext context) {
  return MaterialApp(
    home: Scaffold(
      backgroundColor: Colors.transparent,
      body: Center(
        child: Draggable(
          child: Container(
            width: 200,
            height: 200,
            color: Colors.blue,
            child: Center(
              child: Text(
                '拖拽我',
                style: TextStyle(color: Colors.white),
              ),
            ),
          ),
          feedback: Container(
            width: 200,
            height: 200,
            color: Colors.blue.withOpacity(0.5),
            child: Center(
              child: Text(
                '拖拽中',
                style: TextStyle(color: Colors.white),
              ),
            ),
          ),
          childWhenDragging: Container(),
          onDragEnd: (details) {
            print('拖拽结束');
          },
        ),
      ),
    ),
  );
},

8. 其他配置

你还可以通过 OverlayWindowConfig 对象配置悬浮窗的其他行为,比如悬浮窗的位置、大小、是否可拖动等。

9. 注意事项

  • 在 Android 上,悬浮窗功能需要用户手动授予 SYSTEM_ALERT_WINDOW 权限。
  • 悬浮窗的内容是一个独立的 Flutter 应用,因此它有自己的 MaterialAppScaffold
  • 悬浮窗的生命周期与主应用的生命周期是独立的,因此需要注意资源的管理和释放。

10. 示例代码

以下是一个完整的示例代码,展示了如何创建、显示、关闭悬浮窗,并处理悬浮窗的生命周期和点击事件:

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: HomeScreen(),
    );
  }
}

class HomeScreen extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('悬浮窗示例'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () async {
                await requestOverlayPermission();
                showOverlayWindow();
              },
              child: Text('显示悬浮窗'),
            ),
            ElevatedButton(
              onPressed: () {
                closeOverlayWindow();
              },
              child: Text('关闭悬浮窗'),
            ),
          ],
        ),
      ),
    );
  }
}

Future<void> requestOverlayPermission() async {
  if (!await FlutterOverlayWindowFork.isPermissionGranted()) {
    await FlutterOverlayWindowFork.requestPermission();
  }
}

void showOverlayWindow() async {
  await FlutterOverlayWindowFork.showOverlay(
    overlayWindowConfig: OverlayWindowConfig(
      width: 200,
      height: 200,
      alignment: OverlayAlignment.center,
      gravity: OverlayGravity.center,
      overlayContent: (BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            backgroundColor: Colors.transparent,
            body: Center(
              child: GestureDetector(
                onTap: () {
                  print('悬浮窗被点击');
                },
                child: Container(
                  width: 200,
                  height: 200,
                  color: Colors.blue,
                  child: Center(
                    child: Text(
                      '悬浮窗内容',
                      style: TextStyle(color: Colors.white),
                    ),
                  ),
                ),
              ),
            ),
          ),
        );
      },
    ),
  );
}

void closeOverlayWindow() async {
  await FlutterOverlayWindowFork.closeOverlay();
}
回到顶部