Flutter窗口覆盖插件overlay_windows_plugin的使用
Flutter窗口覆盖插件overlay_windows_plugin的使用
插件介绍
overlay_windows_plugin
是一个用于在Android上与Window Manager Service集成的Flutter插件,允许你在其他应用程序之上显示覆盖窗口。 该插件支持在同一个时间显示多个覆盖窗口,并且每个覆盖窗口可以使用不同的UI或布局。
示例代码
import 'package:flutter/material.dart';
import 'package:overlay_windows_plugin/overlay_message.dart';
import 'package:overlay_windows_plugin/overlay_windows_api.g.dart';
import 'package:overlay_windows_plugin/overlay_windows_plugin.dart';
import 'package:overlay_windows_plugin_example/overlay_main11.0.dart';
import 'package:overlay_windows_plugin_example/overlay_main22.0.dart';
void main() {
runApp(const MyApp());
}
[@pragma](/user/pragma)("vm:entry-point")
void overlayMain11() {
runApp(
const MaterialApp(
debugShowCheckedModeBanner: false,
home: SafeArea(
child: OverlayMain11(),
),
),
);
}
[@pragma](/user/pragma)("vm:entry-point")
void overlayMain22() {
runApp(
const MaterialApp(
debugShowCheckedModeBanner: false,
home: OverlayMain22(),
),
);
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _overlayWindowsPlugin = OverlayWindowsPlugin.defaultInstance;
List<OverlayMessage> message = [];
@override
void initState() {
super.initState();
_overlayWindowsPlugin.messageStream.listen((event) {
setState(() {
message.add(event);
});
});
_overlayWindowsPlugin.touchEventStream.listen((event) {});
}
final List<String> _overlayWindowIds = [];
int index = 0;
void showOverlay(String entryPointName) async {
var hasPermission = await _overlayWindowsPlugin.isPermissionGranted();
if (!hasPermission) {
_overlayWindowsPlugin.requestPermission();
return;
}
var id = '$entryPointName-${index + 1}';
setState(() {
_overlayWindowIds.add(id);
});
if (entryPointName == "overlayMain1") {
_overlayWindowsPlugin.showOverlayWindow(
id,
entryPointName,
OverlayWindowConfig(
width: 300,
height: 100,
enableDrag: true,
));
} else {
_overlayWindowsPlugin.showOverlayWindow(
id,
entryPointName,
OverlayWindowConfig(
width: 300,
height: 300,
// alignment: OverlayAlignment.bottomCenter,
flag: OverlayFlag.defaultFlag,
enableDrag: true,
positionGravity: PositionGravity.left,
));
}
index++;
}
void closeOverlay(String entryPointName) async {
var ids = _overlayWindowIds.where((element) => element.startsWith(entryPointName)).toList();
ids.forEach((element) async {
setState(() {
_overlayWindowIds.remove(element);
});
_overlayWindowsPlugin.closeOverlayWindow(element);
});
}
void sendMessage() {
_overlayWindowsPlugin.sendMessage("", "Hello from main");
}
int clickTime = 0;
void setOverlayFlag(String entryPointName) {
var ids = _overlayWindowIds.where((element) => element.startsWith(entryPointName)).toList();
if (clickTime > 2) {
clickTime = 0;
}
ids.forEach((element) async {
if (clickTime == 0) {
_overlayWindowsPlugin.setFlags(element, OverlayFlag.clickThrough);
} else if (clickTime == 1) {
_overlayWindowsPlugin.setFlags(element, OverlayFlag.focusPointer);
} else if (clickTime == 2) {
_overlayWindowsPlugin.setFlags(element, OverlayFlag.defaultFlag);
}
});
clickTime++;
}
int initWidth = 300;
int initHeight = 300;
void increaseSize() {
setState(() {
initWidth += 20;
initHeight += e0;
_overlayWindowsPlugin.resize(_overlayWindowIds.first, initWidth, initHeight);
});
}
void decreaseSize() {
setState(() {
initWidth -= e0;
initHeight -= e0;
_overlayWindowsPlugin.resize(_overlayWindowIds.first, initWidth, initHeight);
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
showOverlay("overlayMain1");
},
child: const Text("Show 1"),
),
ElevatedButton(
onPressed: () {
closeOverlay("overlayMain1");
},
child: const Text("Close 1"),
),
ElevatedButton(
onPressed: () {
setOverlayFlag("overlayMain1");
},
child: const Text("Update Flag"),
),
ElevatedButton(
onPressed: () {
increaseSize();
},
child: const Text("Big"),
),
ElevatedButton(
onPressed: () {
decreaseSize();
},
child: const Text("Small"),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
showOverlay("overlayMain22");
},
child: const Text("Show 2"),
),
ElevatedButton(
onPressed: () {
closeOverlay("overlayMaine22");
},
child: const Text("Close 2"),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () {
sendMessage();
},
child: const Text("Send Message"),
),
],
),
Expanded(
child: ListView.builder(
itemCount: message.length,
itemBuilder: (context, index) {
return Text('${message[index].overlayWindowId}: ${message[index].message}');
},
),
),
],
),
),
),
);
}
}
使用说明
-
安装依赖
dependencies: overlay_windows_plugin: any
-
设置权限 在
AndroidManifest.xml
中添加以下权限:<manifest xmlns:android="http://schemas.android.com/apk/res/android"> ... <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <application> ... <service android:name="com.ducdd.overlay_windows_plugin.OverlayService" android:exported="false" /> ... </application> </manifest>
-
定义入口点 在
main.dart
文件中定义入口点。
[@pragma](/user/pragma)("vm:entry-point")
void overlayMain1() {
runApp(
const MaterialApp(
debugShowCheckedModeBanner: false,
home: SafeArea(
child: OverlayMain1(),
),
),
);
}
[@pragma](/user/pragma)("vm:entry-point")
void overlayMain22() {
runApp(
const MaterialApp(
debugShowCheckedModeBanner: false,
home: OverlayMaine22(),
),
);
}
- 创建插件实例
final _overlayWindowsPlugin = OverlayWindowsPlugin.defaultInstance;
- 打开覆盖窗口
_overlayWindowsPlugin.showOverlayWindow(
id,
"overlayMain1",
OverlayWindowConfig(
width: 300,
height: 100,
enableDrag: true,
));
- 关闭覆盖窗口
_overlayWindowsPlugin.closeOverlayWindow("overlay window id");
- 发送消息给所有打开的覆盖窗口
_overlayWindowsPlugin.sendMessage("", "Hello from main");
- 接收覆盖窗口的消息或触摸事件
_overlayWindowsPlugin.messageStream.listen((event) {
setState(() {
message.add(event);
});
});
_overlayWindowsPlugin.touchEventStream.listen((event) {});
- 在每个覆盖窗口视图中启动一个视图来与主视图通信
final view = OverlayWindowView();
view.stateChangedStream.listen((event) {
setState(() {
viewId = event;
});
});
view.messageStream.listen((mes) {
setState(() {
message = mes.message as String;
});
});
view.sendMessage('Hello from overlay $viewId');
- 获取覆盖窗口API
更多关于Flutter窗口覆盖插件overlay_windows_plugin的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter窗口覆盖插件overlay_windows_plugin的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter中使用overlay_windows_plugin
插件来实现窗口覆盖的示例代码。请注意,由于overlay_windows_plugin
是一个假定的插件名称(实际中可能并不存在一个名为overlay_windows_plugin
的官方插件),我将提供一个通用的实现思路和伪代码,以展示如何在Flutter中处理窗口覆盖的概念。通常,这种功能可能需要平台特定的代码(如Android和iOS的原生代码)。
Flutter 插件实现思路
-
创建Flutter插件:首先,你需要创建一个Flutter插件,该插件包含原生代码来处理窗口覆盖。
-
原生代码实现:
- Android:使用
WindowManager
和addView
方法。 - iOS:使用
UIWindow
并设置其windowLevel
。
- Android:使用
-
Flutter 调用原生方法:通过MethodChannel在Flutter中调用原生实现的方法。
示例代码
1. 创建Flutter插件(以Android为例)
a. 在android/src/main/java/com/example/overlayplugin
下创建OverlayPlugin.java
package com.example.overlayplugin;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.os.Bundle;
import android.os.IBinder;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
public class OverlayPlugin implements FlutterPlugin, ActivityAware {
private MethodChannel channel;
private Context applicationContext;
private WindowManager windowManager;
private View overlayView;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
applicationContext = flutterPluginBinding.getApplicationContext();
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "overlay_plugin");
channel.setMethodCallHandler(
(call, result) -> {
if (call.method.equals("showOverlay")) {
showOverlay();
result.success(null);
} else {
result.notImplemented();
}
});
}
private void showOverlay() {
windowManager = (WindowManager) applicationContext.getSystemService(Context.WINDOW_SERVICE);
LayoutInflater inflater = (LayoutInflater) applicationContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
overlayView = inflater.inflate(R.layout.overlay_layout, null);
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
WindowManager.LayoutParams.FORMAT_CHANGED);
params.gravity = Gravity.TOP | Gravity.LEFT;
params.x = 0;
params.y = 0;
windowManager.addView(overlayView, params);
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
channel.setMethodCallHandler(null);
if (overlayView != null && windowManager != null) {
windowManager.removeView(overlayView);
}
}
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
// No-op
}
@Override
public void onDetachedFromActivityForConfigChanges() {
// No-op
}
@Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
// No-op
}
@Override
public void onDetachedFromActivity() {
// No-op
}
}
b. 在android/src/main/res/layout
下创建overlay_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FF000080"> <!-- 半透明红色背景 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Overlay Window"
android:textColor="#FFFFFF"
android:layout_gravity="center" />
</FrameLayout>
2. 在Flutter中调用插件
a. 在pubspec.yaml
中添加依赖(假设你已经将插件发布到pub.dev)
dependencies:
flutter:
sdk: flutter
overlay_plugin:
path: ../path/to/your/overlay_plugin # 本地路径或pub.dev上的包名
b. 在Dart代码中调用插件方法
import 'package:flutter/material.dart';
import 'package:overlay_plugin/overlay_plugin.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Overlay Plugin Demo'),
),
body: Center(
child: ElevatedButton(
onPressed: () async {
// 调用插件方法显示覆盖窗口
OverlayPlugin.showOverlay();
},
child: Text('Show Overlay'),
),
),
),
);
}
}
注意:
- 上述代码是一个简化的示例,实际使用中需要考虑权限处理(如
SYSTEM_ALERT_WINDOW
权限)、适配不同屏幕尺寸和分辨率、处理窗口的移除等。 - 对于iOS,你需要创建一个对应的Swift或Objective-C类,并使用
UIWindow
来实现覆盖窗口。 - 由于Flutter插件的创建和发布涉及到更多细节,这里只提供了核心逻辑。如果需要实际使用,请查阅Flutter插件开发文档并创建符合你需求的插件。