flutter_android_widgets

Live Updates

The package supports two kinds of live refresh:

  1. Data updates — new text/numbers from HomeWidgetData.save() appear on the widget instantly
  2. Style updates — changed colors, sizes, and layout properties appear on hot restart, no rebuild required

Setup

Pass your widget list to initialize() so the updater can sync styles on every hot restart:

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  WidgetUpdater.initialize(widgets: [myWidget]); // ← required for style sync
  HomeWidgetData.autoUpdate = true;              // ← auto-refresh after save
  runApp(const MyApp());
}

Three automatic triggers

TriggerHow it firesWhat it does
Hot restart (Ctrl+Shift+F5)main() re-runsSyncs all styles to SharedPreferences → broadcasts widget refresh
App resumes from backgroundAppLifecycleState.resumedRe-syncs styles → broadcasts widget refresh
Data saveHomeWidgetData.save() / saveAll() with autoUpdate = trueBroadcasts widget refresh immediately after writing

Use hot restart, not hot reload. Hot reload does not re-evaluate top-level final variables, so your widget definition changes are not visible to it.


What updates on hot restart

✅ Updates immediately (no rebuild needed)

These properties are written to SharedPreferences by _syncStyles() on every hot restart, then applied by the generated Kotlin via the RemoteViews API:

PropertyApplies toAndroid API used
backgroundColorAll nodessetInt("setBackgroundColor", ...)
paddingAll nodessetViewPadding(...) with dp→px conversion
gravityWColumn, WRow, WTextsetInt("setGravity", ...)
textColorWText, WButtonsetTextColor(...)
textSizeWText, WButtonsetFloat("setTextSize", ...)
maxLinesWTextsetInt("setMaxLines", ...)
label (button text)WButtonsetTextViewText(...)
progressWProgressBarsetInt("setProgress", ...)
Data valuesWText with \${key} bindingssetTextViewText(...)

❌ Requires rebuild + re-add widget

These properties are compiled into APK resources. Android provides no runtime API to change them — this is an OS-level constraint:

PropertyWhy it cannot update at runtime
minWidth / minHeightStored in appwidget_provider_*.xml — a compiled APK resource. The Android launcher reads this once at widget placement to allocate grid cells. No API exists to resize a widget after it's placed.
resizeModeSame provider XML. Controls whether the user can drag-resize the widget — read by the launcher, not the app.
updatePeriodMillisAlso in provider XML. Registered as a system alarm when the widget is first added.
widgetNameThe picker label comes from android:label in AndroidManifest.xml.
bold (text style)The RemoteViews API has no method to change Typeface or android:textStyle at runtime. Bold is only settable via XML.

To apply any of the above changes:

# 1. Edit my_widgets.dart
# 2. Regenerate native files
dart run build_runner build
# 3. Reinstall the APK
flutter run
# 4. For minWidth/minHeight/resizeMode: remove widget from home screen and re-add it
#    (The launcher caches grid size at placement time)

How hot restart refresh works (step by step)

1. You press Ctrl+Shift+F5 in VS Code
2. Flutter engine restarts
3. main() re-runs
4. WidgetUpdater.initialize(widgets: [myWidget]) is called
5. _syncStyles() walks the layout tree
6. Writes backgroundColor, padding, textColor, etc. to SharedPreferences
7. Calls updateAll() → MethodChannel → Kotlin
8. FlutterAndroidWidgetsChannel sends APPWIDGET_UPDATE broadcast
9. Android delivers it to all AppWidgetProvider instances
10. onUpdate() reads SharedPreferences (data + style keys)
11. RemoteViews API applies the new values
12. Widget on home screen refreshes

Manual update

You can trigger updates programmatically at any time:

// Refresh all widgets
await WidgetUpdater.updateAll();

// Refresh a specific widget by class name
await WidgetUpdater.updateWidget('MyWidgetProvider');

How the channel works

The generator creates a FlutterAndroidWidgetsChannel.kt that registers a MethodChannel("flutter_android_widgets") in your app's MainActivity. The Dart WidgetUpdater calls this channel, which sends APPWIDGET_UPDATE broadcasts to all registered widget providers.

// Generated: FlutterAndroidWidgetsChannel.kt
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "flutter_android_widgets")
    .setMethodCallHandler { call, result ->
        when (call.method) {
            "updateAll" -> { updateAllWidgets(context); result.success(null) }
            "updateWidget" -> { updateWidgetByName(context, call.argument("className")!!); result.success(null) }
        }
    }

The channel uses broadcasts (not direct calls) so the update goes through the Android system — consistent with updates triggered by the system timer or button taps.

On this page

No Headings