From flutter-skills
Adds home screen widgets to a Flutter app for Android and iOS. Use when providing glanceable app information or quick actions on the device home screen.
How this skill is triggered — by the user, by Claude, or both
Slash command
/flutter-skills:flutter-adding-home-screen-widgetsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
- [Architecture & Data Flow](#architecture--data-flow)
Home Screen Widgets require native UI implementation (SwiftUI for iOS, XML/Kotlin for Android). The Flutter app communicates with these native widgets via shared local storage (UserDefaults on iOS, SharedPreferences on Android) using the home_widget package.
Use this checklist to implement the Dart side of the Home Screen Widget integration.
HomeWidget.setAppGroupId('<YOUR_APP_GROUP>') in initState() or app startup.HomeWidget.saveWidgetData<T>('key', value) to write data to shared storage.HomeWidget.updateWidget(iOSName: 'YourIOSWidget', androidName: 'YourAndroidWidget') to notify the OS.If targeting iOS, implement the widget using Xcode and SwiftUI.
ios/Runner.xcworkspace in Xcode. Add a new Widget Extension target. Disable "Include Live Activity" and "Include Configuration Intent" unless explicitly required.TimelineEntry to hold the data passed from shared storage.getSnapshot and getTimeline, instantiate UserDefaults(suiteName: "<YOUR_APP_GROUP>").userDefaults?.string(forKey: "your_key").TimelineEntry.View to display the data from the TimelineEntry.If targeting Android, implement the widget using Android Studio and XML/Kotlin.
android folder in Android Studio. Right-click the app directory -> New -> Widget -> App Widget.res/layout/<widget_name>.xml to define the UI using standard Android XML layouts (e.g., RelativeLayout, TextView, ImageView).AppWidgetProvider.onUpdate method, retrieve shared data using HomeWidgetPlugin.getData(context).widgetData.getString("your_key", null).RemoteViews and setTextViewText or setImageViewBitmap.appWidgetManager.updateAppWidget(appWidgetId, views).If the UI is too complex to recreate natively (e.g., custom charts), render the Flutter widget to an image and display the image in the native widget.
GlobalKey.HomeWidget.renderFlutterWidget(), passing the widget, a filename, and the key.UserDefaults and render using UIImage(contentsOfFile:) inside a SwiftUI Image.SharedPreferences, decode using BitmapFactory.decodeFile(), and render using setImageViewBitmap().If utilizing custom fonts defined in Flutter on iOS Home Screen Widgets:
CTFontManagerRegisterFontsForURL.Font.custom().import 'package:home_widget/home_widget.dart';
const String appGroupId = 'group.com.example.app';
const String iOSWidgetName = 'NewsWidgets';
const String androidWidgetName = 'NewsWidget';
Future<void> updateWidgetData(String title, String description) async {
await HomeWidget.setAppGroupId(appGroupId);
await HomeWidget.saveWidgetData<String>('headline_title', title);
await HomeWidget.saveWidgetData<String>('headline_description', description);
await HomeWidget.updateWidget(
iOSName: iOSWidgetName,
androidName: androidWidgetName,
);
}
import WidgetKit
import SwiftUI
struct NewsArticleEntry: TimelineEntry {
let date: Date
let title: String
let description: String
}
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> NewsArticleEntry {
NewsArticleEntry(date: Date(), title: "Loading...", description: "Loading...")
}
func getSnapshot(in context: Context, completion: @escaping (NewsArticleEntry) -> ()) {
let userDefaults = UserDefaults(suiteName: "group.com.example.app")
let title = userDefaults?.string(forKey: "headline_title") ?? "No Title"
let description = userDefaults?.string(forKey: "headline_description") ?? "No Description"
let entry = NewsArticleEntry(date: Date(), title: title, description: description)
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
getSnapshot(in: context) { (entry) in
let timeline = Timeline(entries: [entry], policy: .atEnd)
completion(timeline)
}
}
}
struct NewsWidgetsEntryView : View {
var entry: Provider.Entry
var body: some View {
VStack(alignment: .leading) {
Text(entry.title).font(.headline)
Text(entry.description).font(.subheadline)
}
}
}
package com.example.app.widgets
import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.widget.RemoteViews
import es.antonborri.home_widget.HomeWidgetPlugin
import com.example.app.R
class NewsWidget : AppWidgetProvider() {
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray,
) {
for (appWidgetId in appWidgetIds) {
val widgetData = HomeWidgetPlugin.getData(context)
val views = RemoteViews(context.packageName, R.layout.news_widget).apply {
val title = widgetData.getString("headline_title", "No Title")
setTextViewText(R.id.headline_title, title)
val description = widgetData.getString("headline_description", "No Description")
setTextViewText(R.id.headline_description, description)
}
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}
// Add this to your SwiftUI View struct
var bundle: URL {
let bundle = Bundle.main
if bundle.bundleURL.pathExtension == "appex" {
var url = bundle.bundleURL.deletingLastPathComponent().deletingLastPathComponent()
url.append(component: "Frameworks/App.framework/flutter_assets")
return url
}
return bundle.bundleURL
}
init(entry: Provider.Entry) {
self.entry = entry
CTFontManagerRegisterFontsForURL(
bundle.appending(path: "/fonts/YourCustomFont.ttf") as CFURL,
CTFontManagerScope.process,
nil
)
}
npx claudepluginhub gsmlg-dev/code-agent --plugin flutter-skillsGuides expert Flutter 3.x and Dart 3.x development for multi-platform apps, including advanced widgets, state management with Riverpod/Bloc/GetX, performance optimization, and architecture patterns.
Provides Flutter patterns for widget architecture, state management, Impeller renderer, and platform-adaptive design, avoiding common mistakes.
Builds Home Screen, Lock Screen, StandBy, and CarPlay widgets with timeline providers, interactive controls, deep links, and Smart Stack relevance for iOS 26+.