floaty_chatheads 3.0.0
floaty_chatheads: ^3.0.0 copied to clipboard
Floating chathead plugin for Android & iOS with draggable bubbles, expandable content panels, theming, snap-to-edge physics, and bidirectional app-overlay messaging.
Changelog #
3.0.0 #
Breaking release. Minimum Flutter 3.44 / Dart 3.12, iOS 15, Android API 24. Built-in Kotlin end to end. See the migration notes at the end.
Simpler integration #
Floatyis now the single entry point.Floaty.show/Floaty.togglehandle the overlay permission and expose every option the plugin supports (panel size and anchor, theming, snapping, widget and animated icons, notification text,autoLaunchOnBackground,persistOnAppClose,debugMode).Floaty.expand,Floaty.collapse,Floaty.updateBadge,Floaty.onClosedandFloaty.ensurePermissionwere added.FloatyLauncherremoved. ReplaceFloatyLauncher.show(sizePreset: x)withFloaty.show(size: x); every other parameter keeps its name.FloatyControllernow routes throughFloatyand gainedautoLaunchOnBackground,persistOnAppCloseandentryPointLibrary.- Entry point in any file. New
entryPointLibraryparameter ('package:my_app/overlay.dart') lets the overlay entry point live outsidemain.dart. Previously only root-library functions resolved. - Passing
contentWidth/contentHeighttoFloaty.shownow takes precedence over thesizepreset instead of being ignored.
Reliability #
show()returns once the overlay is listening. The main app pings the overlay after the native window is up, so data sent right afterawait Floaty.show()is delivered. A broken entry point (wrong name, wrong library, missing@pragma('vm:entry-point')in release, or noFloatyOverlay.setUp()call) no longer shows a blank overlay and reports success: the overlay is closed andFloatyOverlayNotReadyExceptionis thrown afterreadyTimeout(10 seconds by default,Duration.zerodisables the check).- Raw messages that reach either isolate before anything listens to
onDataare kept and delivered to the first listener. FloatyChatheads.disposeandFloatyOverlay.disposeremove only their own handlers. They used to wipe every handler on the shared channel, silently disconnecting a liveFloatyHostKit,FloatyProxyStreamor connection tracking (for example when aFloatyControllerWidgetunmounted).- iOS:
Floaty.send()and every Kit call no longer hang forever when no overlay is showing, and showing the chathead no longer fires a falseonClosedevent.
Performance #
- Showing the chathead again while it is visible reuses the overlay engine when the entry point is unchanged instead of cold-starting a new one (Android and iOS).
- Animated icons: Android renders frames into reused buffers with a shadow blurred once, drops frames that arrive while one is still rendering, and no longer races two frames on one bitmap. iOS no longer renders or transfers icon frames at all, since its bubble is a Flutter view.
- Android: the built-in bubble icon is decoded once at its native size instead of on every redraw; close-target assets decode at native size.
- Android: the touch tracker window is only moved when its position changes, removing a WindowManager call on nearly every frame at the end of each settle animation.
- Android: debug mode no longer leaks the overlay view tree after close.
Native fixes #
- Android 12+: the collapsed bubble now draws at full opacity. Android caps an untouchable overlay window at 80 percent opacity, which let the app or wallpaper show through the bubble. The collapsed bubble stack now lives in its own small touchable window that follows it.
- Android 10+: dragging a bubble that sits on a screen edge no longer triggers the system Back gesture.
- Android 13+:
POST_NOTIFICATIONSis declared and requested, so the foreground-service notification actually appears. - Android:
NotificationConfig.visibilityis honored; the notification channel name and "is running" text are localizable string resources consumers can override; the foreground service declares its type explicitly on Android 14+. - Android:
showChatHeadno longer reports success while doing nothing when no Activity is attached, and the overlay engine restore after process death no longer hijacks the main app's plugin instance. - Android: unused
WAKE_LOCKpermission and a 300-line unused color resource file no longer merge into consumer apps; consumer ProGuard rules ship with the plugin. - iOS: the overlay engine now registers the app's plugins, so
shared_preferences,path_providerand friends work inside the overlay isolate.
API shape #
- Public classes that were never meant to be extended are now
final:AnimatedWidgetIcon,StreamKey,ActionKey,ChatHeadDragEvent,OverlayColorPalette,FloatyScopeData,FloatySimplePanel,FloatyMiniPlayer,FloatyNotificationCard,FloatyNotificationAction,FakeOverlayDataSource, and the concreteFloatyProxyTimeoutException,FloatyProxyDisconnectedExceptionandFloatyProxyErrorException. Constructing them is unchanged.FloatyAction,FloatyProxyExceptionandFakeFloatyPlatformstay open for subclassing. FloatyOverlayno longer implements the generatedFloatyOverlayFlutterApi. Code that only calls its static members is unaffected.FloatyProxyClient.callnow waits for the request to be sent, so a send failure reaches the caller as a failed Future instead of being dropped.
Toolchain #
- Flutter 3.44+ / Dart 3.12+; Pigeon 29.0.11; very_good_analysis 11.
- Android: AGP 9.5, built-in Kotlin (no Kotlin Gradle Plugin applied), Java 17, compileSdk 36, androidx.core 1.18, Material 1.14. androidx.core stays on 1.18 because 1.19 forces apps onto compileSdk 37, above the Flutter default of 36.
- Example app: Gradle 9.8.2, Kotlin 2.4.21, and current releases of flutter_map, geolocator, latlong2 and shared_preferences.
- iOS: deployment target 15.0 (Flutter 3.47 minimum), SwiftPM package
declares the
FlutterFrameworkdependency per the current template.
Migration from 2.x #
- Bump your app to Flutter 3.44+ and iOS 15.
FloatyLauncher.show(...)becomesFloaty.show(...)(renamesizePreset:tosize:).FloatyLauncher.togglebecomesFloaty.toggle.- If your overlay entry point is not in
main.dart, passentryPointLibrary: 'package:<your_app>/<file>.dart'. - Remove any
<uses-permission>lines you added for this plugin: the plugin manifest declares them all. - Overlay entry points must call
FloatyOverlay.setUp().FloatyOverlayApp.runalready does. If yours deliberately does not, passreadyTimeout: Duration.zerotoFloaty.show. - Classes you extended or implemented that are now
final(see API shape) must be wrapped or composed instead.
2.1.0 #
New: PanelAnchor #
- feat: new
PanelAnchorenum (below/above/auto) onChatHeadConfig.panelAnchorcontrols where the expanded content panel opens relative to the bubble. The panel is always clamped to the screen bounds;autopicks below when there is room, otherwise above.
Behavior change (non-breaking API) #
-
The content panel now opens below the bubble by default. Previously the panel rendered from the top of the screen with the bubble row overlapping its top edge (Android) or grew down-right from the bubble's corner (iOS). Full-screen panels are unaffected (clamping keeps them at the top). Overlays that reserved top padding for the bubble row on card-sized panels should remove it.
-
FloatyChatheads.showChatHead,FloatyLauncher.show/toggle, andFloatyControlleraccept the newpanelAnchorparameter. -
fix: calling
FloatyOverlay.getOverlayPosition()from the overlay isolate crashed with a decode error because the overlay isolate's Pigeon codec assigns different type tags than the platform contract serving the same channels. Shared overlay channels now use only standard-codec payloads (getOverlayPositionreturns a map,updateFlagtakes the enum index).
Chores #
- Example app migrated to AGP 9.2 / Gradle 9.4.1. The
android.builtInKotlin/android.newDslopt-outs remain, since Flutter 3.44's Gradle tooling still applies KGP to plugin projects and does not support AGP 9's new DSL yet. - Pigeon regenerated with 26.3.x.
2.0.0 #
2.0 is a breaking release. It modernizes the toolchain, simplifies the public surface, and adds compile-time type safety to action and stream routing. See the migration notes at the end of this entry.
New: minimal happy-path API #
Two new additions bring the simplest setup down to a top-level entry point and one method call:
Floaty: a static facade that wraps the most common workflow (check permission, then request if needed, then show with sensible defaults). One-liners forshow,close,toggle,isActive,send,onData. Drops down to [FloatyLauncher] / [FloatyChatheads] for power users.FloatySimplePanel: a pre-built styled Material card for overlay content, with optional title and a built-in close button wired to [FloatyOverlay.closeOverlay]. Eliminates the Card/Material/Padding boilerplate for the common case.
See the README "Hello World" section for the full 30-second setup.
Toolchain & platform floors (BREAKING) #
- Dart SDK floor raised to
^3.0.0(was^3.4.0). This is the minimum that supportsfinal class,sealed class, records, and the pattern matching used by the typedActionKey/StreamKeyAPI. - Flutter floor raised to
>=3.10.0(was>=3.22.0) to pair with Dart 3.0.
Picked as the bare minimum that the library code actually requires. Anyone on Flutter 3.10+ (released May 2023) can consume the plugin.
Trade-offs deliberately taken to keep the floor low:
- The two pre-built widgets (
FloatyMiniPlayer,FloatyNotificationCard) useColor.withOpacity(x)rather than the newerColor.withValues(alpha: x)(introduced in Flutter 3.27).withOpacityis deprecated on Flutter 3.27+ but still functional. A file-level// ignore_for_file: deprecated_member_usein each widget suppresses the lint. - The Android plugin keeps applying
id "kotlin-android"explicitly with the legacyandroid { kotlinOptions {} }block. On Flutter 3.44+ this triggers the "Future versions of Flutter will fail to build if your app uses plugins that apply KGP" warning. Migrating to Built-in Kotlin would lock out the Flutter 3.10 to 3.43 install base, so the warning is accepted for now. - Android
minSdkraised to 24 (was 23). Android 7.0 (Nougat) is now the oldest supported OS.compileSdkbumped to 35 (Android 15). Plugin Java target raised to 11 (was 1.8) to match the example app. - iOS deployment target raised to 14.0 (was 13.0) in both
Package.swiftand the CocoaPods podspec. - CI matrix bumped to Flutter
3.44.0across all four workflows.
Typed action and stream keys (BREAKING) #
-
New
ActionKey<A extends FloatyAction>colocates the actiontypestring with itsfromJson, removing the chance of mismatched pairs. -
New
StreamKey<T>colocates a streamnamewith bothtoJsonandfromJson, used by both producer and consumer. -
FloatyActionRouter.onandFloatyActionRouter.off(and the matching delegate methods onFloatyHostKit/FloatyOverlayKit) now take a key instead of separateString type+fromJsonarguments. -
FloatyProxyStream(...)andFloatyProxyStream.overlay(...)now take a singleStreamKey<T>instead ofname+toJson/fromJsonnamed parameters.Migration:
+static const navigateKey = ActionKey<NavigateAction>( + 'navigate', NavigateAction.fromJson); -router.on<NavigateAction>( - 'navigate', - fromJson: NavigateAction.fromJson, - handler: (a) => map.move(a.target), -); +router.on(navigateKey, (a) => map.move(a.target));+static final gpsKey = StreamKey<GpsCoord>( + name: 'gps', + toJson: (c) => c.toJson(), + fromJson: GpsCoord.fromJson, +); -final gps = FloatyProxyStream<GpsCoord>( - name: 'gps', toJson: (c) => c.toJson()); +final gps = FloatyProxyStream(gpsKey);
Slimmer public surface (BREAKING) #
-
The main barrel (
package:floaty_chatheads/floaty_chatheads.dart) no longer exports the low-level primitivesFloatyActionRouter,FloatyStateChannel,FloatyProxyHost, orFloatyProxyClient. -
A new opt-in barrel
package:floaty_chatheads/advanced.dartexposes them for callers that need direct access (most apps should useFloatyHostKit/FloatyOverlayKitfrom the main barrel instead).Migration: add
import 'package:floaty_chatheads/advanced.dart';alongside the existing main-barrel import in files that instantiate the primitives directly.
De-duplication & documentation (Phase B) #
FloatyChannelsconstant infloaty_chatheads_platform_interfaceis now the single source of truth for the messenger channel name (ni.devotion.floaty_head/messenger). The Dart channel implementation references it instead of hard-coding the string.- New
ChatHeadConfigResolverinfloaty_chatheads_platform_interfaceremoves ~30 lines of duplicated default-value resolution between the Android and iOS Dart shims. - Documented iOS-only behavior on
ChatHeadAssetsandFloatyChatheadsIOS: icon-source overrides, secondary chathead icons, and notification description are intentionally not forwarded to the Swift side because the iOS chathead renders as a Flutter view. Wiring them through would silently send data into a void.
1.5.0 #
Widget-Based Icons #
-
Any Flutter widget as the chathead icon. Pass
iconWidgettoshowChatHead()and the widget is rendered to a PNG image via an offscreen pipeline (separateRenderView/PipelineOwner/BuildOwner, so it does not block the main widget tree).await FloatyChatheads.showChatHead( entryPoint: 'overlayMain', iconWidget: const CircleAvatar(child: Text('JD')), ); -
Animated widget icons. Pass
iconBuilder(receives a 0.0 to 1.0 animation value) and setanimateIcon: true. Frames are rendered at configurable FPS and pushed to the native layer as raw RGBA bytes. Control playback withstartIconAnimation()/stopIconAnimation().await FloatyChatheads.showChatHead( entryPoint: 'overlayMain', iconBuilder: (v) => Transform.rotate( angle: v * 2 * 3.14159, child: const Icon(Icons.sync, size: 50), ), animateIcon: true, ); -
Widget-based close icons.
closeIconWidgetandcloseBackgroundWidgetlet you replace the default close target with any Flutter widget. On Android, widget-rendered close icons are scaled to the full close-target size (64 dp) instead of the small 28 dp default. -
New parameters on
showChatHead():iconWidget,iconBuilder,closeIconWidget,closeBackgroundWidget,animateIcon,iconAnimationFps,iconSize,iconPixelRatio,iconAnimationDuration. -
addChatHead()now acceptsiconWidget. Additional bubbles can also use widget-based icons. -
New public API:
AnimatedWidgetIcon,widgetToIconSource(),renderWidgetToImage(),renderWidgetToBytes(),renderWidgetToRgbaByteData(),renderWidgetToPngByteData(),updateChatHeadIcon(),isIconAnimating,startIconAnimation(),stopIconAnimation().
Performance #
- Android: Eliminated bitmap allocations in
onDraw()(ChatHead). The circular-crop + shadow pipeline ran on every frame (~240 allocations/second during 60 fps drag). Processed bitmaps are now cached and only recomputed when the source icon reference changes. - Android: Cached close-target resource bitmap (Close).
BitmapFactory.decodeResource()was called on every spring animation frame. The source bitmap is now decoded once at construction and scaled from cache. - Dart: Optimised
updateShouldNotifyinFloatyScopeandFloatyOverlayScope. Changed from unconditionaltrueto!identical()identity checks, avoiding unnecessary widget rebuilds when the data object has not changed. - Dart: Parallelised independent dispose futures with
Future.wait. Sequential awaits inFloatyMessenger, both Kit classes,FloatyOverlayScope, andFakeOverlayDataSourcenow run concurrently.
Robustness #
- Dart: Added
mountedguards to all stream listeners inFloatyScope. PreventssetState-after-dispose crashes if a stream event fires between unmount and subscription cancellation. - Android: Fixed potential
velocityTracker!!crash (ChatHeads). Replaced force-unwrap with safe?: returnto guard against null velocity tracker onACTION_UP. - Android: Added
@Volatileto thread-shared bitmap properties (OverlayConfig). Icon bitmaps written fromDispatchers.IOare now guaranteed visible to the main-threadonDraw(). - Android: Replaced
getString()!!with safe defaults (ConfigPersistence).SharedPreferences.getString()can returnnulleven with a non-null default on some OEM ROMs; three restore calls now use?: "DEFAULT"fallbacks.
Code Quality #
- Dart: Replaced
unawaited()antipattern with properasync/await. All non-widget dispose methods (FloatyActionRouter,FloatyStateChannel,FloatyMessenger,FloatyProxyStream,FloatyHostKit,FloatyOverlayKit,FakeOverlayDataSource) now returnFuture<void>and properly await their cleanup futures. Reduced from 25 scatteredunawaited()calls to 9 at unavoidable sync framework boundaries (State.dispose(), constructors, lifecycle callbacks). - Android: Removed dead
getRunningServiceInfo()(ChatHeads) and unusedregistrarproperty (iOS plugin). - iOS: Extracted channel name constant and changed
bubbleSizetolet.
Bug Fixes #
- Fixed icon animation not stopping on native close. When the
chathead was dismissed via the drag-to-close gesture, the animation
timer continued running. The
onClosedhandler now callsstopIconAnimation()automatically.
Dependencies #
- Bumped
floaty_chatheads_platform_interfaceto^1.0.5. - Bumped
floaty_chatheads_androidto^1.1.0. - Bumped
floaty_chatheads_iosto^1.1.6.
1.4.0+1 #
Metadata #
- Upgraded package version for meta to 1.18.
- Upgraded package version for pigeon to 26.3.3
1.4.0 #
Enhancements #
- Added
autoLaunchOnBackgroundparameter. Whentrue, the chathead is automatically shown when the app moves to the background and dismissed when the app returns to the foreground. Useful for music players, call UIs, or any overlay that should appear while the user is in another app. (Android only.) - Added
persistOnAppCloseparameter. Whentrue, the foreground service usesSTART_STICKYand persists its configuration so the overlay survives app death and is recreated by the system. Whenfalse(the default), the service usesSTART_NOT_STICKYand stops itself when the main app disconnects. (Android only.) - New example: Auto-Launch & Persist, which demonstrates both parameters with toggle switches and step-by-step instructions.
Performance #
- Android: Migrated native plugin to Kotlin Coroutines. Icon loading
and all async operations now use structured concurrency with
Dispatchers.IO, eliminating main-thread blocking. - iOS: Migrated native plugin to Swift Concurrency (
@MainActor). Compile-time main-actor isolation replaces runtime GCD dispatch.
Bug Fixes #
- Fixed chathead freeze on both platforms.
- Android:
showChatHeadandaddChatHeadare now async Pigeon methods. The DartFutureresolves only after the overlay window is fully created, preventing the half-initialized state caused by the coroutine race condition. - iOS: The
@MainActorgesture handler was routing through the actor executor instead of firing synchronously. Now usesnonisolated+MainActor.assumeIsolatedfor zero-overhead drag.
- Android:
Dependencies #
- Bumped
floaty_chatheads_platform_interfaceto^1.0.4. - Bumped
floaty_chatheads_androidto^1.0.7. - Bumped
floaty_chatheads_iosto^1.1.5.
1.3.2 #
Bug Fixes #
FloatyProxyStreamnow throwsStateErroron duplicate names. Creating a second stream with the samenamewithout disposing the first previously caused silent overwrites and broken dispose semantics. A clear error message now guides the developer.
Tests #
- Added duplicate-name guard tests for
FloatyProxyStream. - Added 3 message-buffering tests for
FloatyChannel(replay on handler registration, max-pending overflow, no buffering for previously-removed handlers).floaty_channel.dartis now at 100% line coverage.
1.3.1 #
Enhancements #
- Added
FloatyProxyStream<T>. A typed, reactive, unidirectional stream that pushes values from the main app to the overlay. The overlay subscribes once and receives automatic updates, which is useful for GPS coordinates, sensor data, or any high-frequency state.
Tests #
- Added 9 tests for
FloatyProxyStream.
1.3.0 #
Enhancements #
- Added
FloatyChatheads.onClosedstream. A broadcast stream that emits the chathead ID when the overlay is closed by the native drag-to-close gesture or from the overlay itself. This lets the main app update its UI state without pollingisActive(). - Added
NotificationConfig.description. Custom body text for the Android foreground-service notification. When set, the notification displays a separate title and body instead of the default"<title> is running"format. - Added
FloatyChannelmessage buffering for reconnection reliability. System messages arriving before their handler is registered are now buffered (up to 200 per prefix) and replayed in order when the handler attaches. A_everRegisteredset ensures messages for intentionally-removed handlers still flow to the raw stream. - New examples: Grouping, Move Tracking, Notification Actions, Resizable Panel, and Todo Survival examples added to the gallery app. The Todo Survival example demonstrates add / toggle / remove actions, optimistic local updates, proxy fallback, and full queue-flush reliability after app death.
Bug Fixes #
- Fixed queued actions lost on reconnection. When the main app
restarted, the overlay flushed its action queue before the Dart side
had registered channel handlers, silently dropping every message.
FloatyChannelnow buffers system messages for not-yet-registered prefixes and replays them in order whenregisterHandler()is called. Bumpedfloaty_chatheads_androidto^1.0.5. - Fixed counter survival replaying all queued increments as a single value. Buffered actions are now stored as individual objects and replayed one-by-one with per-action log entries.
- Fixed chathead close crash when GPS is streaming (Android). The
overlay
FlutterViewwas removed from its parent while the engine was still processing GPS data, causing an orphaned rendering window. Bumpedfloaty_chatheads_androidto^1.0.5. - Fixed foreground service persisting after chathead close
(Android). The service teardown now completes before the overlay
engine is destroyed. Bumped
floaty_chatheads_androidto^1.0.5. - Fixed example UI state not updating on drag-to-close. All
example screens now listen to
FloatyChatheads.onClosedto update_chatheadActivewhen the chathead is dismissed via the native close gesture.
1.2.3 #
Bug Fixes #
- Fixed overlay to main app communication broken on Android. The
overlay
FlutterEngineauto-registers all plugins, which overwrote the sharedactiveInstanceinFloatyChatheadsPluginwith the overlay's instance. Messages sent from the overlay viashareData()looped back to the overlay instead of reaching the main app'sonDatastream. This affected all examples using rawFloatyOverlay.shareData/FloatyChatheads.onDatacommunication (basic chathead, messenger, mini player, etc.). Bumpedfloaty_chatheads_androidto^1.0.4.
Metadata #
- Added
example/example.mdfor the pub.dev example tab.
1.2.2 #
Enhancements #
- Upgraded Pigeon to 26.2.3. Regenerated overlay Pigeon bindings across all platform packages. No API surface changes.
- Updated Android demo GIF in the media folder.
Bug Fixes #
- Fixed survival overlay counter adding +1 on every reconnection. The overlay's manual re-dispatch logic fired even though the framework's automatic queue flush already delivered the queued actions, causing every increment to be applied twice. Removed the redundant re-dispatch; the queue flush is reliable on its own.
- Fixed counter race condition on app restart. Queued actions
arriving via auto-flush before
SharedPreferencesrestore could increment from zero instead of the persisted value. Increment actions are now buffered until the counter is restored.
1.2.1 #
Enhancements #
- Debug logs are now optional and silent by default (Android). All
native logcat output from the plugin is gated behind
debugMode. SetdebugMode: trueinshowChatHead()to enable verbose logging during development; production builds produce zero log noise.
Bug Fixes #
- Fixed content panel rendering fullscreen on subsequent launches
(Android). When the foreground service had not started yet,
showChatHead()only saved the entry point to SharedPreferences, omitting content dimensions. On service startup,restoreConfig()then overwrote the in-memory values withnull, causing the panel to fall back toMATCH_PARENT. The plugin now persists the full config and the service guards against overwriting values that the plugin already set. - Fixed content panel dimensions leaking between chathead sessions on Android. Switching from a larger overlay (e.g. 300x400) to a smaller one (e.g. 220x220) no longer inherits the previous session's size.
- Fixed missing touch interaction on the content panel after switching
between chathead sessions. The plugin now explicitly recreates the
overlay window with fresh dimensions instead of relying on
onStartCommand(), which could skip recreation when a stale window from aSTART_STICKYrestart was still present. - Fixed iOS content size not resetting between sessions. Dimensions now default to 300x400 before applying the new config.
1.2.0 #
Higher-Level Convenience Widgets #
-
Added
FloatyDataBuilder<T>, a reactive builder for the main app side that subscribes toFloatyChatheads.onData, reduces incoming messages into typed state via aT Function(T current, Object? raw)reducer, and rebuilds automatically. Eliminates manualStreamSubscription,setState, anddisposeboilerplate. -
Added
FloatyOverlayBuilder<T>, a zero-boilerplate builder for the overlay side that handlesFloatyOverlay.setUp(), stream subscriptions,mountedguards, andFloatyOverlay.dispose()automatically. SupportsonDatareducer, optionalonTappedreducer, andonInitcallback. Turns overlay widgets intoStatelessWidgetdeclarations. -
Added
FloatyOverlayApp.runScoped(), a variant ofrun()that wraps the child inFloatyScope, soFloatyScope.of(context)works everywhere inside the overlay without manual wiring. -
Added
builderparameter toFloatyControllerWidget. It accepts aWidget Function(BuildContext, FloatyController)callback with reactive re-rendering viaListenableBuilder. Thechildparameter is now optional whenbuilderis provided.
Grouped Configuration Objects #
- Added
ChatHeadAssets, which groups chathead icon, close icon, and close background into a single object. Old flat parameters are deprecated. - Added
NotificationConfig, which groups notification title, icon, and visibility into a single object. Old flat parameters are deprecated. - Added
SnapConfig, which groups snap edge, margin, and position persistence into a single object. Old flat parameters are deprecated. - Added
IconSource, a polymorphic icon source withIconSource.asset(),IconSource.network(), andIconSource.bytes()constructors for flexible icon loading from assets, URLs, or raw byte data.
Simplified Examples #
- Simplified all 14 overlay entry points from verbose 6-line blocks to
single-line
FloatyOverlayApp.run()calls (from 84 to 14 lines total). - Rewrote
NotificationCounterOverlayusingFloatyOverlayBuilder<int>; it is now aStatelessWidgetwith zero lifecycle code. - Rewrote
QuickActionOverlayusingFloatyOverlayBuilder<bool>; it is now aStatelessWidgetwith tap-to-toggle expand/collapse. - Rewrote
QuickActionExampleusingFloatyDataBuilder<List<LogEntry>>: incoming actions accumulate into a log via reducer, no manual subscription.
Tests #
- Added 10 new tests for
FloatyDataBuilder(5) andFloatyOverlayBuilder(5).
1.1.0 #
iOS Feature Parity #
- iOS now supports entrance animations, snap-to-edge, position persistence, badge counter, theming, expand/collapse, drag events, and VoiceOver accessibility.
- Removed "Android only" notes from platform interface docs.
- Bumped
floaty_chatheads_iosdependency to^1.1.0.
Shared State Channel #
- Added
FloatyStateChannel<T>, a generic, type-safe channel for synchronizing arbitrary state between the main app and the overlay. - Supports partial updates via
update()and full replacements viaset(). - Provides a reactive
streamof state changes and synchronousstateaccess. - Works with any serializable Dart model, not limited to a specific domain.
Bidirectional Action Routing #
- Added
FloatyActionRouter, a typed, extensible action bus for dispatching structured commands between app and overlay. - Register strongly-typed handlers with
on<A>()and send actions withsend(). - Actions implement
FloatyActionwithtypeandtoJson()for automatic serialization over the platform channel.
Overlay Proxy #
- Added
FloatyProxy, which lets the overlay call app-side services (e.g. APIs, databases, repositories) without direct dependencies. FloatyProxyHostregisters named service handlers in the main app.FloatyProxyClientinvokes them from the overlay and receivesFuture-based responses.- Enables clean separation of concerns between the overlay UI and app-side business logic.
Overlay Survival After App Death (Android) #
- The Android
FloatyContentJobServicenow owns the Flutter engine lifecycle. The overlay survives when the main app is killed and automatically restores from persisted config on service restart. - Added
FloatyConnectionState, an overlay-side utility that tracks whether the main app is connected, with a reactive stream and synchronous getter. FloatyActionRouternow queues dispatched actions while the main app is disconnected and flushes them in order on reconnection. ConfigurablemaxQueueSizeandQueueOverflowStrategy.FloatyProxyClientfails fast withFloatyProxyDisconnectedExceptionwhen the main app is unavailable, with an optionalfallbackparameter to provide default values instead of throwing.FloatyScopeDataexposesisMainAppConnectedfor reactive overlay UI updates.- The plugin automatically reconnects to an existing overlay when the main app restarts (hot-restart or cold launch).
1.0.1 #
- Added Android and iOS demo GIFs to README.
- Fixed foreground notification on service reuse.
- Documentation and metadata updates.
1.0.0 #
Initial Release: Complete Rewrite #
A ground-up rewrite of the floating chathead plugin, inspired by flutter_overlay_window.
Platform Support
- Android:
SYSTEM_ALERT_WINDOWoverlay with foreground service, Facebook Rebound spring physics, and fullFlutterEnginecontent panels. - iOS:
UIWindow-based PiP overlay at.alert + 1window level. No special permissions required. Supports iOS 13+.
Core Features
- Floating chathead bubbles rendered as system-level overlays.
- Expand / collapse Flutter widget content panels.
- Bidirectional data messaging between the main app and overlay isolate
via
BasicMessageChannel. - Drag-and-drop with spring-animated snap-to-edge.
- Close target with magnetic snap and animated reveal.
- Badge counter on chathead bubbles.
- Multi-bubble support: add / remove individual chatheads by ID.
Theming API
ChatHeadThememodel with badge colors, bubble border, shadow color, close tint, and overlay color palette.- Automatic palette delivery to overlay isolate on engine start.
FloatyOverlay.paletteandonPaletteChangedstream for overlay-side consumption.
Size Presets
ContentSizePresetenum:compact,card,halfScreen,fullScreen.- Preset dimensions resolved before passing to the native layer.
Debug Inspector
debugModeflag onChatHeadConfig.DebugOverlayView(Android) with bounds rectangles, position labels, spring velocity HUD, and FPS counter.- Pigeon message log (circular buffer, 50 entries).
FloatyOverlay.getDebugInfo()for Dart-side debug panels.
Accessibility (Android)
- Full TalkBack support with content descriptions, state announcements, and accessibility actions on all interactive views.
- Focus management: content panel receives focus on expand, bubble receives focus on collapse.
ACTION_CLICK("Expand / Collapse chat") andACTION_DISMISS("Close chat bubble") custom actions.AccessibilityLiveRegion.POLITEon close target for automatic announcements.
Snap & Animation
SnapEdgeenum:left,right,nearest,none.- Configurable snap margin in dp.
persistPosition: remembers last bubble position across sessions.EntranceAnimationenum:none,slideInFromLeft,slideInFromRight,fadeIn.
Developer Experience
- Federated plugin architecture (main package on top of platform_interface, with android / ios implementations).
- Pigeon-generated type-safe communication between Dart and Kotlin / Swift.
- VGV-style
{@template}/{@macro}documentation across all packages. FloatyPermissionGatewidget for declarative permission handling.- 12 runnable example screens in the gallery app.
Architecture
floaty_chatheads: app-facing APIfloaty_chatheads_platform_interface: abstract contract + modelsfloaty_chatheads_android: Kotlin + Pigeon implementationfloaty_chatheads_ios: Swift + Pigeon implementation