Flutter系统弹窗管理插件system_alert_window的使用

发布于 1周前 作者 bupafengyu 来自 Flutter

Flutter系统弹窗管理插件system_alert_window的使用

插件简介

system_alert_window 是一个Flutter插件,用于显示类似Truecaller的覆盖窗口,可以覆盖在所有其他应用程序之上,并带有回调事件。对于Android Go或Android 11及以上版本,此插件会显示通知气泡;而在其他Android版本中,则会显示覆盖窗口。

Android平台配置

权限声明

为了正常使用system_alert_window,需要在AndroidManifest.xml文件中添加如下权限:

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

版本适配说明

  • Android 10及以下, Android GO (API 27):使用’draw on top’权限并作为覆盖窗口显示。
  • Android 11及以上:根据SystemWindowPrefMode的不同设置,可能会以气泡形式展示或者直接覆盖其他应用。
  • Android GO (API 29):用户需手动从开发者选项启用气泡功能。

iOS平台支持

目前iOS端仅能作为通知中心的通知显示,该部分功能尚需社区贡献代码完善。

使用示例

下面是一个完整的示例程序,演示了如何请求权限、创建和控制覆盖窗口以及与之交互。

示例代码

main.dart

import 'dart:async';
import 'dart:developer';
import 'dart:isolate';
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:share_plus/share_plus.dart';
import 'package:system_alert_window/system_alert_window.dart';
import 'custom_overlay.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

@pragma("vm:entry-point")
void overlayMain() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      home: CustomOverlay(),
    ),
  );
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _platformVersion = 'Unknown';
  bool _isShowingWindow = false;
  bool _isUpdatedWindow = false;
  SystemWindowPrefMode prefMode = SystemWindowPrefMode.OVERLAY;
  static const String _mainAppPort = 'MainApp';
  final _receivePort = ReceivePort();
  SendPort? homePort;
  String? latestMessageFromOverlay;

  @override
  void initState() {
    super.initState();
    _initPlatformState();
    _requestPermissions();
    if (homePort != null) return;
    final res = IsolateNameServer.registerPortWithName(
      _receivePort.sendPort,
      _mainAppPort,
    );
    log("$res: OVERLAY");
    _receivePort.listen((message) {
      log("message from OVERLAY: $message");
    });
  }

  @override
  void dispose() {
    super.dispose();
  }

  Future<void> _initPlatformState() async {
    await SystemAlertWindow.enableLogs(true);
    String? platformVersion;
    try {
      platformVersion = await SystemAlertWindow.platformVersion;
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    if (!mounted) return;
    if (platformVersion != null)
      setState(() {
        _platformVersion = platformVersion!;
      });
  }

  Future<void> _requestPermissions() async {
    await SystemAlertWindow.requestPermissions(prefMode: prefMode);
  }

  void _showOverlayWindow() async {
    if (!_isShowingWindow) {
      await SystemAlertWindow.sendMessageToOverlay('show system window');
      SystemAlertWindow.showSystemWindow(
          height: 200,
          width: MediaQuery.of(context).size.width.floor(),
          gravity: SystemWindowGravity.CENTER,
          prefMode: prefMode,
          isFlagFocusable: false);
      setState(() {
        _isShowingWindow = true;
      });
    } else if (!_isUpdatedWindow) {
      await SystemAlertWindow.sendMessageToOverlay('update system window');
      SystemAlertWindow.updateSystemWindow(
          height: 200,
          width: MediaQuery.of(context).size.width.floor(),
          gravity: SystemWindowGravity.CENTER,
          prefMode: prefMode,
          isFlagFocusable: true,
          isDisableClicks: true);
      setState(() {
        _isUpdatedWindow = true;
        SystemAlertWindow.sendMessageToOverlay(_isUpdatedWindow.toString());
      });
    } else {
      setState(() {
        _isShowingWindow = false;
        _isUpdatedWindow = false;
        SystemAlertWindow.sendMessageToOverlay(_isUpdatedWindow.toString());
      });
      SystemAlertWindow.closeSystemWindow(prefMode: prefMode);
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('System Alert Window Example App \n with flutterview'),
        ),
        body: Center(
          child: Column(
            children: <Widget>[
              Text('Running on: $_platformVersion\n'),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 8.0),
                child: MaterialButton(
                  onPressed: _showOverlayWindow,
                  textColor: Colors.white,
                  child: !_isShowingWindow
                      ? Text("Show system alert window")
                      : !_isUpdatedWindow
                          ? Text("Update system alert window")
                          : Text("Close system alert window"),
                  color: Colors.deepOrange,
                  padding: const EdgeInsets.symmetric(vertical: 8.0),
                ),
              ),
              Padding(
                padding: const EdgeInsets.symmetric(vertical: 8.0),
                child: MaterialButton(
                  onPressed: () => SystemAlertWindow.sendMessageToOverlay("message from main"),
                  textColor: Colors.white,
                  child: Text("send message to overlay"),
                  color: Colors.deepOrange,
                  padding: const EdgeInsets.symmetric(vertical: 8.0),
                ),
              ),
              TextButton(
                  onPressed: () async {
                    String? logFilePath = await SystemAlertWindow.getLogFile;
                    if (logFilePath != null && logFilePath.isNotEmpty) {
                      Share.shareFiles([logFilePath]);
                    } else {
                      print("Path is empty");
                    }
                  },
                  child: Text("Share Log file"))
            ],
          ),
        ),
      ),
    );
  }
}

custom_overlay.dart

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

class CustomOverlay extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.transparent,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text("This is a custom overlay"),
          ElevatedButton(
            onPressed: () {
              SystemAlertWindow.sendMessageToOverlay("Button clicked in overlay");
            },
            child: Text("Click me"),
          ),
        ],
      ),
    );
  }
}

以上代码展示了如何使用system_alert_window插件来实现跨应用的覆盖窗口显示,并且包含了基本的操作如显示、更新、关闭窗口以及发送消息给覆盖窗口等功能。希望这对您有所帮助!


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

1 回复

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


当然,以下是如何在Flutter中使用system_alert_window插件来管理系统弹窗的一个示例代码案例。这个插件允许你在Android设备上显示一个悬浮在系统其他应用之上的窗口。请注意,由于这个特性涉及到系统级权限,因此在实现时需要考虑权限请求和处理。

首先,确保在你的pubspec.yaml文件中添加system_alert_window依赖:

dependencies:
  flutter:
    sdk: flutter
  system_alert_window: ^x.y.z  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

接下来是具体的实现步骤:

  1. 请求权限:在Android上,显示系统弹窗需要SYSTEM_ALERT_WINDOW权限。这个权限在Android 6.0(API级别23)及以上版本需要用户手动授予。

  2. 创建和显示弹窗:使用system_alert_window插件来创建并显示弹窗。

以下是一个完整的示例代码:

import 'package:flutter/material.dart';
import 'package:system_alert_window/system_alert_window.dart';
import 'dart:ui' as ui;
import 'package:permission_handler/permission_handler.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('System Alert Window Example'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: _showSystemAlert,
            child: Text('Show System Alert'),
          ),
        ),
      ),
    );
  }

  Future<void> _showSystemAlert() async {
    if (Platform.isAndroid) {
      // 请求 SYSTEM_ALERT_WINDOW 权限
      var status = await Permission.systemAlertWindow.status;
      if (!status.isGranted) {
        var result = await Permission.systemAlertWindow.request();
        if (!result.isGranted) {
          return; // 用户拒绝了权限请求
        }
      }

      // 创建 SystemAlertWindowController
      final window = SystemAlertWindowController(
        // 设置窗口大小(这里设置为全屏,你可以根据需要调整)
        size: MediaQuery.of(context).size,
        // 设置窗口的背景颜色
        backgroundColor: Colors.transparent,
        // 窗口的标题栏(可选)
        title: 'System Alert',
        // 窗口内容
        child: Container(
          color: Colors.black54.withOpacity(0.7),
          alignment: Alignment.center,
          child: Text(
            'This is a system alert window!',
            style: TextStyle(color: Colors.white, fontSize: 24),
          ),
        ),
        // 点击窗口外部时是否关闭窗口
        shouldDismissOnTapOutside: true,
      );

      // 显示窗口
      await window.show();

      // 这里可以添加一些逻辑来处理窗口的关闭,比如用户点击了某个按钮
      // window.close();
    } else {
      // 目前只支持Android
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('System alert windows are only supported on Android.')),
      );
    }
  }
}

注意事项

  • 在实际项目中,请确保处理所有可能的权限请求结果,包括用户拒绝的情况。
  • 上面的代码示例中使用了permission_handler插件来处理权限请求。你需要在pubspec.yaml中添加这个依赖,并运行flutter pub get来安装它。
  • 由于SYSTEM_ALERT_WINDOW权限的敏感性,一些设备或Android版本可能会限制或阻止这个权限的授予,特别是在没有用户明确指示的情况下自动请求。

希望这个示例能帮助你理解如何在Flutter中使用system_alert_window插件来管理系统弹窗。如果有进一步的问题,欢迎继续提问!

回到顶部