仓库地址:https://github.com/qiuxiang/flutter-android-window
pub 地址:https://pub.dartlang.org/packages/android_window
功能
- 使用 android 原生接口实现,可以让 flutter app 覆盖在其他 app 上面
- 浮窗作为 android 前台服务运行,可完全脱离主应用
- 默认支持拖拽移动浮窗,且可以与 flutter 手势共存
- 提供浮窗大小、位置控制接口
- 提供主应用与浮窗应用相互通信接口
安装与配置
flutter pub add android_window
修改 MainActivity.kt
让 MainActivity
继承 qiuxiang.android_window.AndroidWindowActivity
:
class MainActivity : qiuxiang.android_window.AndroidWindowActivity()
创建 MainApplication.kt
:
package your_package // 和 MainActivity.kt 保持一致即可
class MainApplication : qiuxiang.android_window.AndroidWindowApplication()
修改 AndroidManifest.xml
的 <application>
新增属性 android:name=".MainApplication"
:
<application
android:name=".MainApplication"
...
>
用法
main.dart:
import 'package:android_window/main.dart' as android_window;
import 'package:flutter/material.dart';
import 'android_window.dart';
@pragma('vm:entry-point')
void androidWindow() {
runApp(const AndroidWindowApp());
}
void main() {
runApp(const App());
}
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(title: 'Flutter Demo', home: HomePage());
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => android_window.open(size: const Size(300, 200)),
child: const Icon(Icons.add),
),
);
}
}
我们需要用 @pragma('vm:entry-point')
声明一个入口函数,默认函数名是 androidWindow
,当然你可以随意指定一个,只是调用 open
的时候需要同时指定参数 entryPoint:
。
android_window.dart:
import 'package:android_window/android_window.dart';
import 'package:flutter/material.dart';
class AndroidWindowApp extends StatelessWidget {
const AndroidWindowApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: HomePage(),
debugShowCheckedModeBanner: false,
);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return AndroidWindow(
child: Scaffold(
backgroundColor: Colors.lightGreen.withOpacity(0.9),
body: const Padding(
padding: EdgeInsets.all(8),
child: Text('Hello android window'),
),
),
);
}
}
浮窗 app 的写法就和我们平时写的 app 没什么区别了,如果需要支持窗口拖拽移动,则要在最外层使用 AndroidWindow
。
最终效果:
image更完整的示例请参考:https://github.com/qiuxiang/flutter-android-window/tree/main/example
浮窗与主应用的通信
主应用和浮窗都有 post
和 setHandler
方法用于发送消息以及设置监听处理函数。用法举例:
主应用发送消息到浮窗:
final response = await android_window.post('message_name', 'data');
浮窗监听并处理主应用消息:
AndroidWindow.setHandler((name, data) async {
switch(name) {
case 'bar':
return 'data';
}
});
反过来同理。
聊聊这个库的一些实现细节
- 核心功能参考了官方文档 Adding a Flutter View to an Android app。
- 为了让主应用与浮窗进行通信,需要自定义
Application
,并存储两个互通的 channel。 - 这个库并没有直接用 MethodChannel,而是实验性地用了 pigeon,总的来说,MethodChannel 更灵活高效,而 pigeon 更规范且类型安全,之后我应该都会考虑用 pigeon 而不是 MethodChannel。
网友评论