{"id":13550472,"url":"https://github.com/jiusanzhou/flutter_floatwing","last_synced_at":"2025-04-05T23:18:43.048Z","repository":{"id":41260054,"uuid":"490972345","full_name":"jiusanzhou/flutter_floatwing","owner":"jiusanzhou","description":"A Flutter plugin that makes it easier to make floating/overlay window for Android with pure Flutter. 一个可以用纯粹的Flutter来开发Android浮动窗口的插件。","archived":false,"fork":false,"pushed_at":"2024-04-26T03:49:53.000Z","size":8289,"stargazers_count":177,"open_issues_count":16,"forks_count":29,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-03T00:34:03.503Z","etag":null,"topics":["alert-widget","android","background-service","display-over-other-apps","floating-window","flutter","flutter-plugin","overlay","overlay-window","popup-window","truecaller","zoe-lab"],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jiusanzhou.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"patreon":null,"open_collective":null,"ko_fi":"zoeim","tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":["https://payone.wencai.app/s/zoe"]}},"created_at":"2022-05-11T05:42:57.000Z","updated_at":"2025-03-11T13:32:04.000Z","dependencies_parsed_at":"2024-01-16T18:57:58.418Z","dependency_job_id":"7069ddef-91d3-4a79-9294-e48f47c4b040","html_url":"https://github.com/jiusanzhou/flutter_floatwing","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jiusanzhou%2Fflutter_floatwing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jiusanzhou%2Fflutter_floatwing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jiusanzhou%2Fflutter_floatwing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jiusanzhou%2Fflutter_floatwing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jiusanzhou","download_url":"https://codeload.github.com/jiusanzhou/flutter_floatwing/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247411538,"owners_count":20934697,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alert-widget","android","background-service","display-over-other-apps","floating-window","flutter","flutter-plugin","overlay","overlay-window","popup-window","truecaller","zoe-lab"],"created_at":"2024-08-01T12:01:33.571Z","updated_at":"2025-04-05T23:18:43.028Z","avatar_url":"https://github.com/jiusanzhou.png","language":"Dart","funding_links":["https://ko-fi.com/zoeim","https://payone.wencai.app/s/zoe"],"categories":["Dart"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# flutter_floatwing\n\n[![Version](https://img.shields.io/pub/v/flutter_floatwing.svg)](https://pub.dartlang.org/packages/flutter_floatwing)\n[![pub points](https://badges.bar/flutter_floatwing/pub%20points)](https://pub.dev/packages/flutter_floatwing/score)\n[![popularity](https://badges.bar/flutter_floatwing/popularity)](https://pub.dev/packages/flutter_floatwing/score)\n[![likes](https://badges.bar/flutter_floatwing/likes)](https://pub.dev/packages/flutter_floatwing/score)\n[![License](https://img.shields.io/badge/license-AL2-blue.svg)](https://github.com/jiusanzhou/flutter_floatwing/blob/master/LICENSE)\n\nA Flutter plugin that makes it easier to make floating/overlay windows for Android with pure Flutter. **Android only**\n\n\u003c/div\u003e\n\n---\n\n## Features\n\n- **Pure Flutter**: you can write your whole overlay windows in pure Flutter.\n- **Simple**: at least only 1 line of code to start your overlay window.\n- **Auto Resize**: just care about your Flutter widget size, it will auto resize for Android view.\n- **Multi-window**: support create multiple overlay windows in one App, and window can has child windows.\n- **Communicable**: your main App can talk with windows, and also supported between windows.\n- **Event Mechanism**: fire the events of window lifecyle and other actions like drag, you can controll your window more flexible.\n- *and more features are coming ...*\n\n## Previews\n\n|Night mode|Simpe example|Assistive touch mock|\n|:-:|:-:|:-:|\n|![](./assets/flutter-floatwing-example-1.gif)|![](./assets/flutter-floatwing-example-2.gif)|![](./assets/flutter-floatwing-example-3.gif)|\n\n## Installtion\n\nOpen the `pubspec.yaml` file located inside the app folder, and add `flutter_floatwing` under `dependencies`.\n```yaml\ndependencies:\n  flutter_floatwing: \u003clatest_version\u003e\n```\n\nThe latest version is \n[![Version](https://img.shields.io/pub/v/flutter_floatwing.svg)](https://pub.dartlang.org/packages/flutter_floatwing)\n\nThen you should install it,\n- From the terminal: Run `flutter pub get`.\n- From Android Studio/IntelliJ: Click Packages get in the action ribbon at the top of `pubspec.yaml`.\n- From VS Code: Click Get Packages located in right side of the action ribbon at the top of `pubspec.yaml`.\n\n\nOr simply add it in your command line:\n```bash\nflutter pub add flutter_floatwing\n```\n\n## Quick Started\n\nWe use the android's system alert window to display, so need to add the permission in `AndroidManifest.xml` first:\n```xml\n    \u003cuses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\" /\u003e\n```\n\nAdd a route for your widget which will be displayed in the overlay window:\n```dart\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      debugShowCheckedModeBanner: false,\n      initialRoute: \"/\",\n      routes: {\n          \"/\": (_) =\u003e HomePage(),\n          // add a route as entrypoint for your overlay window.\n          \"/my-overlay-window\": (_) =\u003e MyOverlayWindow(),\n      },\n    );\n  }\n```\n\nBefore we start the floating window,\nwe need to check and request the permission, and initialize the `flutter_floatwing` plugin in `initState` or any button's callback function:\n```dart\n// check and grant the system alert window permission.\nFloatwingPlugin().checkPermission().then((v) {\n    if (!v) FloatwingPlugin().openPermissionSetting();\n});\n\n// initialize the plugin at first.\nFloatwingPlugin().initialize();\n```\n\nNext to start create and start your overlay window:\n```dart\n// define window config and start the window from config.\nWindowConfig(route: \"/my-overlay-window\") \n    .to() // create a window object\n    .create(start: true); // create the window and start the overlay window.\n```\n\n---\n\nNotes:\n\n- `route` is one of 3 ways to define entrypoint for overlay window.\nPlease check the [Entrypoint section](#entrypoint) for more information.\n- See [Usage section](#usage) for more functions.\n\n## Architecture\n\nBefore we see how `flutter_floatwing` manage windows in detail,\nwe need to know some things about the design of the plugin.\n- `id` is the unique identifier for the window, and all operations on the window are based on this `id`, you must provide one before creating.\n- We consider the first engine created by opening the main application as the `main engine` or `plugin engine`. The other engines created by service are `window engine`.\n- Different `engine` are different `threads` and cannot communicate directly.\n- Subscribe events of all windows from `main engine` is allowed, it's also allowed to subscribe events of own and child windows in `window engine`. But we can not subscribe events of sibling or parent windows.\n- `share` data is the only way to communicate between `window engine` or `plugin engine`, there are no restrictions on it, except that the data needs to be serializable. Which means you can share data from anywhere to anywhere.\n\n\nA floatwing window object contains: a flutter engine which run a widget by `runApp` and a view which add to window manager.\n\n![floatwing window](./assets/flutter-floatwing-window.png)\n\nThe whole view hierarchy like below:\n\n![flutter floatwing architecture](./assets/flutter-floatwing-arch.png)\n\n## Usage\n\nBefore we start how to use `flutter_floatwing` in detail,\nlet's talk about how the `flutter_floatwing` create a new overlay window:\n- First of all we need to start a service as manager by main app.\n- Then create window request send to the service.\n- In the service, we start the flutter engine with entrypoint.\n- Create a new flutter view and attach it to the flutter engine.\n- Add the view to android window manager.\n\n### Window \u0026 Config\n\n`WindowConfig` contains all configuration for window.\nWe can use configuration to create a window like below:\n```dart\nvoid _createWindow() {\n    var config = WindowConfig();\n    w = Window(config, id=\"my-window\");\n    w.create();\n}\n```\n\nIf you have no need to register event or data handler,\nyou can just use config to create a window.\n```dart\nvoid _createWindow() {\n    WindowConfig(id=\"my-window\").create();\n}\n```\n\nBut as you can see, if you want to provide a id for window,\nmust provide in `WindowConfig`.\n\nIf want to register handler, you can use a `to()` function to turn a config to a window at first,\nthis is every useful when you want to make code simple.\n```dart\nvoid _createWindow() {\n    WindowConfig(id=\"my-window\").to()\n        .on(EventType.WindowCreated, (w, _) {})\n        .create();\n}\n```\n\n#### Lifecyle of Window\n\n- created\n- started\n- paused\n- resumed\n- destroy\n\n*TODO*\n\n### Entrypoint\n\nEntrypoint is where the engine execute from. We support 3 modes of configuration:\n\n|Name|Config|How to use|\n|:--|:--|:--|\n|`route`|`WindowConfig(route: \"/my-overlay\")`|- Add a route for overlay window in your main routes\u003cbr /\u003e- Start window with config: `WindowConfig(route: \"/my-overlay\")`|\n|`staic function`|`WindowConfig(callback: myOverlayMain)`|- Define a static function `void Function()` which calling `runApp` to start a widget.\u003cbr /\u003e- Start window with config: `WindowConfig(callback: myOverlayMain)`|\n|`entry-point`|`WindowConfig(entry: \"myOverlayMain\")`|- First step is same as `staic function`.\u003cbr /\u003e- Add `@pragma(\"vm:entry-point\")` above the static function.\u003cbr /\u003e- Start window with config: `WindowConfig(entry: \"myOverlayMain\")`\u003cbr /\u003e- *like `static function`, but use string of function name as parameter*|\n\n#### Example for `route`\n\n1. Add route for your overlay widget in the main application.\n```dart\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      debugShowCheckedModeBanner: false,\n      initialRoute: \"/\",\n      routes: {\n          \"/\": (_) =\u003e HomePage(),\n          // add a route as entrypoint for your overlay window.\n          \"/my-overlay-window\": (_) =\u003e MyOverlayWindow(),\n      },\n    );\n  }\n```\n\n2. Start window with `route` as config.\n```dart\n    void _startWindow() {\n        // define window config and start the window from config.\n        WindowConfig(route: \"/my-overlay-window\") \n            .to() // create a window object\n            .create(start: true); // create the window and start the overlay window.\n    }\n```\n\n#### Example for `static function`\n\n1. Define a static function which called `runApp`\n```dart\nvoid myOverlayMain() {\n    runApp(MaterialApp(\n        home: AssistivePannel(),\n    ));\n    // or simply use `floatwing` method to inject `MaterialApp`\n    // runApp(AssistivePannel().floatwing(app: true));\n}\n```\n\n2. Start window with `callback` as config.\n```dart\n    void _startWindow() {\n        // define window config and start the window from config.\n        WindowConfig(callback: myOverlayMain) \n            .to() // create a window object\n            .create(start: true); // create the window and start the overlay window.\n    }\n```\n\n#### Example for `entry-point`\n\n1. Define as static function which called `runApp` and add `prama`\n```dart\n@pragma(\"vm:entry-point\")\nvoid myOverlayMain() {\n    runApp(MaterialApp(\n        home: AssistivePannel(),\n    ));\n    // or simply use `floatwing` method to inject `MaterialApp`\n    // runApp(AssistivePannel().floatwing(app: true));\n}\n```\n\n2. Start window with `entry` as config.\n```dart\n    void _startWindow() {\n        // define window config and start the window from config.\n        WindowConfig(entry: \"myOverlayMain\") \n            .to() // create a window object\n            .create(start: true); // create the window and start the overlay window.\n    }\n```\n\n### Wrap your widget\n\nFor simple widget, you have no need to do with your widget.\nBut if you want more functions and make your code clean,\nwe provide a injector for your widget.\n\nFor now there are some functions listed below,\n- Auto resize the window view.\n- Auto sync and ensure the window.\n- Wrap a `MaterialApp`\n- *more features are coming*\n\nBefore, we write our overlay main function, like below,\n```dart\nvoid overlayMain() {\n  runApp(MaterialApp(\n    home: MyOverView(),\n  ))\n}\n```\n\nNow, we can code simply,\n```dart\nvoid overlayMain() {\n  runApp(MyOverView().floatwing(app: true)))\n}\n```\n\nWe can wrap to a `Widget` and a `WidgetBuilder`,\nwrap the `WidgetBuilder`, we can access the window instance\nwith `Window.of(context)`, while `FloatwingPlugin().currentWindow` \nis the only to get window instance for wrap `Widget`.\n\nIf we want to access the window with `Window.of(context)`,\nchange the code like below,\n```dart\nvoid overlayMain() {\n  runApp(((_)=\u003eMyOverView()).floatwing(app: true).make()))\n}\n```\n\n### Access window in overlay window\n\nIn your window engine, you can access the window object in 2 ways:\n- Directly access the cache field of plugin: `FloatwingPlugin().currentWindow`.\n- If widget injected by `.floatwing()`, you can take window with `Window.of(context)`.\n\n`FloatwingPlugin().currentWindow` will return `null` unless initialize has been completed.\n\nIf you inject `WidgetBuilder` with `.floatwing()`,\nthen you can access the current window.\nIt will always return non-value, unless you enable debug with `.floatwing(debug: true)`.\n\nFor example, if we want to get the `id` of current window,\nwe can do it like below:\n```dart\n/// ...\nimport 'package:flutter_floatwing/flutter_floatwing.dart';\n\nclass _ExampleViewState extends State\u003cExampleView\u003e {\n    Window? w;\n\n    @override\n    void initState() {\n        super.initState();\n        SchedulerBinding.instance?.addPostFrameCallback((_) {\n            w = Window.of(context);\n            print(\"my window id is ${w.id}\");\n        });\n    }\n}\n```\n\n### Subscribe events\n\nWe can subscribe events from windows, and trigger actions when events fired.\nEvents of window will be sent to `main engine`, self `window engine` and the parent `window engine.\nWhich means you can subscribe events of window from flutter of main application,\noverlay window and parent overlay window.\n\nCurrently we support events for window lifecycle and drag action.\n```dart\nenum EventType {\n  WindowCreated,\n  WindowStarted,\n  WindowPaused,\n  WindowResumed,\n  WindowDestroy,\n\n  WindowDragStart,\n  WindowDragging,\n  WindowDragEnd,\n}\n```\n\n*More events type are coming, and contributtions are welcome!*\n\nFor example, we want to do something when the window is started,\nwe can code like below:\n```dart\n  @override\n  void initState() {\n    super.initState();\n\n    SchedulerBinding.instance?.addPostFrameCallback((_) {\n      w = Window.of(context);\n      w?.on(EventType.WindowStarted, (window, _) {\n          print(\"$w has been started.\");\n      }).on(EventType.WindowDestroy, (window, data) {\n          // data is a boolean value, which means that the window\n          // are destroyed force or not.\n          print(\"$w has been destroy, force $data\");\n      });\n    });\n  }\n```\n\n### Share data with windows\n\nSharing data is the only way to communicate with windows.\nWe provide a simple way to do this: `window.share(data)`.\n\nFor example, if you want to share data to overlay window from main application.\n\nFirst get the target window in main application,\nusually the created one can be used or you can get one from `windows` cache by `id`,\n```dart\n\n    Window w;\n\n    void _startWindow() {\n        w = WindowConfig(route: \"/my-overlay-window\").to();\n    }\n\n    void _shareData(dynamic data) {\n        w.share(data).then((value) {\n            // and window can return value.\n        });\n        // or just take one from cache\n        // FloatwingPlugin().windows[\"default\"]?.share(data);\n    }\n```\n\nIf you want to share data with a name, yon can add the name parameter:\n``w.share(data, name=\"name-1\")`.\n\nAnd then you should listen the data in window by register the data handler.\n```dart\n  @override\n  void initState() {\n    super.initState();\n\n    SchedulerBinding.instance?.addPostFrameCallback((_) {\n      w = Window.of(context);\n      w?.onData((source, name, data) async {\n          print(\"get $name data from $source: $data\");\n      });\n    });\n  }\n```\n\nThe function signature of handler is `Future\u003cdynamic\u003e Function(String? source, String? name, dynamic data)`.\n- `source` is where the data comes from, `null` if from main application, from window will be the `id` of window.\n- `name` is the data name, you can share data for different purposes.\n- `data` is the data that you get.\n- return some value if you want to do.\n\nThere are restrictions for directions of communication, unless you send data to self, which will not be allowed. Which means you can send data as long as you know the id of window. *Currently share to main application is not implemented.*\n\n**Note: The data you are sharing should be serializable.**\n\n## API References\n\n### `FloatwingPlugin` instance\n\n`FloatwingPlugin` is a singleton class that returns the same instance every time it called `FloatwingPlugin()` factory method.\n\n\n\n### `WindowConfig` Object\n\n*TODO*\n\n### `Window` Object\n\n*TODO*\n\n### Events\n\n#### Window lifecycle\n\n#### Action\n\n\n*More events type are coming, and comtributions are welcome!*\n\n## Support\n\nDid you find this plugin useful? Please consider to make a donation to help improve it!\n\n## Contributing\n\nContributions are always welcome!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjiusanzhou%2Fflutter_floatwing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjiusanzhou%2Fflutter_floatwing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjiusanzhou%2Fflutter_floatwing/lists"}