You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter开发:bubble_overlay包已停用,求Android端气泡悬浮窗替代方案

Flutter Android气泡悬浮窗替代方案与实现方式

一、替代包推荐

  • flutter_windowmanager:支持Android端窗口管理,可实现悬浮窗功能,维护状态相对活跃,能处理权限请求与基础悬浮视图交互。
  • system_alert_window:专注于创建系统级悬浮窗口,支持自定义布局,适配点击、拖动等交互逻辑,兼容较新Android版本。
  • flutter_float_widget:轻量级悬浮窗实现包,提供基础的悬浮视图创建、拖动及生命周期管理,适合简单场景需求。

二、原生Android自定义实现(MethodChannel通信)

若不想依赖第三方包,可通过Flutter与原生Android通信自行实现:

  1. 申请权限
    在AndroidManifest.xml中添加权限声明:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

针对Android 6.0+动态请求权限,检查并跳转至设置页面:

if (!Settings.canDrawOverlays(context)) {
    val intent = Intent(
        Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
        Uri.parse("package:" + context.packageName)
    )
    startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE)
}
  1. 创建悬浮窗Service
    在原生端编写Service构建悬浮窗布局与交互逻辑:
class FloatWindowService : Service() {
    private lateinit var windowManager: WindowManager
    private lateinit var floatView: View

    override fun onCreate() {
        super.onCreate()
        windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
        floatView = LayoutInflater.from(this).inflate(R.layout.float_window, null)

        val params = WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 
                WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY 
            else 
                WindowManager.LayoutParams.TYPE_PHONE,
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
            PixelFormat.TRANSLUCENT
        )
        params.gravity = Gravity.TOP or Gravity.LEFT
        params.x = 100
        params.y = 100

        // 实现拖动功能
        floatView.setOnTouchListener(object : View.OnTouchListener {
            private var lastX = 0
            private var lastY = 0
            private var initialX = 0
            private var initialY = 0
            private var isDragging = false

            override fun onTouch(v: View, event: MotionEvent): Boolean {
                when (event.action) {
                    MotionEvent.ACTION_DOWN -> {
                        lastX = event.rawX.toInt()
                        lastY = event.rawY.toInt()
                        initialX = params.x
                        initialY = params.y
                        isDragging = false
                    }
                    MotionEvent.ACTION_MOVE -> {
                        val dx = event.rawX.toInt() - lastX
                        val dy = event.rawY.toInt() - lastY
                        params.x = initialX + dx
                        params.y = initialY + dy
                        windowManager.updateViewLayout(floatView, params)
                        isDragging = true
                    }
                    MotionEvent.ACTION_UP -> {
                        if (!isDragging) {
                            // 通过MethodChannel通知Flutter点击事件
                            methodChannel.invokeMethod("onFloatWindowClick", null)
                        }
                    }
                }
                return true
            }
        })

        windowManager.addView(floatView, params)
    }

    override fun onDestroy() {
        super.onDestroy()
        windowManager.removeView(floatView)
    }

    override fun onBind(intent: Intent): IBinder? = null
}
  1. Flutter与原生通信
    Flutter端通过MethodChannel调用原生方法并监听事件:
static const MethodChannel _channel = MethodChannel('float_window_channel');

// 启动悬浮窗
Future<void> startFloatWindow() async {
  await _channel.invokeMethod('startFloatWindow');
}

// 关闭悬浮窗
Future<void> stopFloatWindow() async {
  await _channel.invokeMethod('stopFloatWindow');
}

// 监听悬浮窗点击
void _setupListener() {
  _channel.setMethodCallHandler((call) async {
    if (call.method == 'onFloatWindowClick') {
      // 处理点击逻辑
      print('悬浮窗被点击');
    }
  });
}

原生端初始化MethodChannel并处理Flutter调用:

private val methodChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "float_window_channel")

methodChannel.setMethodCallHandler { call, result ->
    when (call.method) {
        "startFloatWindow" -> {
            startService(Intent(this, FloatWindowService::class.java))
            result.success(null)
        }
        "stopFloatWindow" -> {
            stopService(Intent(this, FloatWindowService::class.java))
            result.success(null)
        }
        else -> result.notImplemented()
    }
}

三、注意事项

  • Android 10及以上版本需使用TYPE_APPLICATION_OVERLAY窗口类型,旧版本用TYPE_PHONE。
  • 悬浮窗默认设置FLAG_NOT_FOCUSABLE,若需输入功能需调整窗口参数。
  • 后台启动悬浮窗可能受厂商后台限制,需适配不同品牌设备的策略。

内容的提问来源于stack exchange,提问作者Rishigesan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 12:45:39