Live Updates
The package supports two kinds of live refresh:
- Data updates — new text/numbers from
HomeWidgetData.save()appear on the widget instantly - 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
| Trigger | How it fires | What it does |
|---|---|---|
Hot restart (Ctrl+Shift+F5) | main() re-runs | Syncs all styles to SharedPreferences → broadcasts widget refresh |
| App resumes from background | AppLifecycleState.resumed | Re-syncs styles → broadcasts widget refresh |
| Data save | HomeWidgetData.save() / saveAll() with autoUpdate = true | Broadcasts widget refresh immediately after writing |
Use hot restart, not hot reload. Hot reload does not re-evaluate top-level
finalvariables, 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:
| Property | Applies to | Android API used |
|---|---|---|
backgroundColor | All nodes | setInt("setBackgroundColor", ...) |
padding | All nodes | setViewPadding(...) with dp→px conversion |
gravity | WColumn, WRow, WText | setInt("setGravity", ...) |
textColor | WText, WButton | setTextColor(...) |
textSize | WText, WButton | setFloat("setTextSize", ...) |
maxLines | WText | setInt("setMaxLines", ...) |
label (button text) | WButton | setTextViewText(...) |
progress | WProgressBar | setInt("setProgress", ...) |
| Data values | WText with \${key} bindings | setTextViewText(...) |
❌ 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:
| Property | Why it cannot update at runtime |
|---|---|
minWidth / minHeight | Stored 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. |
resizeMode | Same provider XML. Controls whether the user can drag-resize the widget — read by the launcher, not the app. |
updatePeriodMillis | Also in provider XML. Registered as a system alarm when the widget is first added. |
widgetName | The 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 refreshesManual 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.