How to Build Home Screen Widgets for iOS and Android with Flutter

Flutter

Engineering

Guide

Summary

This article provides a comprehensive guide to building home screen widgets for iOS and Android with Flutter. It explains what widgets are, why they are essential, and offers step-by-step instructions for creating widgets on both platforms using WidgetKit for iOS and App Widgets for Android. The article also includes design tips to enhance usability and performance, ensuring a seamless user experience across devices.

Key insights:
  • Quick Access: Home screen widgets allow users to view real-time app information without opening the app, enhancing convenience and engagement.

  • iOS Widgets: Built using WidgetKit, iOS widgets require SwiftUI for layout and data sharing via App Groups.

  • Android Widgets: Leverage the App Widget Framework, using XML layouts for UI and Kotlin for logic and updates.

  • Flutter Integration: Flutter does not natively support widgets; developers must use platform-specific tools like Xcode and Android Studio.

  • Data Sharing: Use UserDefaults on iOS and SharedPreferences on Android to share data between the Flutter app and widgets.

  • Design Principles: Widgets should prioritize simplicity, readability, accessibility, and battery efficiency for optimal performance.

  • Design Guidelines: Following Apple’s Human Interface Guidelines and Google’s Material Design principles ensures consistency and better integration.

Introduction

Home screen widgets have become an essential feature for mobile applications, providing users with quick access to app information without needing to open the app. For Flutter developers, creating home screen widgets involves leveraging platform-specific tools and integrating them with Flutter apps. This article provides a detailed guide on building home screen widgets for both iOS and Android using Flutter, covering their purpose, implementation steps, design tips, and best practices.

What Are Home Screen Widgets and Why Are They Needed?

Home screen widgets are small, interactive components that display app information directly on a device's home screen or lock screen. Unlike traditional app interfaces, widgets provide users with a glanceable view of critical data without requiring them to open the app. Some key benefits of home screen widgets include:

Convenience: Widgets allow users to access real-time updates or perform quick actions directly from the home screen.

User Engagement: By keeping key information readily available, widgets encourage frequent interaction with the app.

Personalization: Widgets can be customized in size, appearance, and content to suit user preferences.

On Android, widgets are placed on the home screen and can include interactive elements like buttons. On iOS, widgets can appear on the home screen, lock screen, or Today View but are more limited in interactivity due to platform constraints. Despite these differences, widgets serve as a powerful tool for enhancing user experience across both platforms.

How to Build Home Screen Widgets for iOS

Creating home screen widgets for iOS involves using WidgetKit, Apple's framework for building widgets. Since Flutter does not natively support widget creation, developers must use platform-specific tools like Xcode alongside Flutter.

0. Prerequisites

  • A macOS system with the Xcode IDE installed. This installs the necessary compiler for building the iOS version of your app.

  • A physical iOS device or simulator for testing.

  • The Flutter SDK configured with your preferred IDE (e.g., Visual Studio Code with the Dart and Flutter extensions or IntelliJ with the Dart and Flutter plugins).

  • Have your initial starter code ready, potentially by cloning a GitHub repository. Open this starter app into the preferred IDE and run flutter pub get to make sure all dependencies are installed.

1. Add a Widget Extension

  • Open your Flutter project in Xcode by running open ios/Runner.xcworkspace from the terminal or by right-clicking the ios folder in your IDE and selecting "Open in Xcode."

  • In Xcode, navigate to File → New → Target.

  • From the list of templates, select Widget Extension.

  • Name the widget (e.g., "WeatherWidgets") and deselect the options for "Include Live Activity" and "Include Configuration Intent."

  • Xcode will generate sample code for the widget based on the selected template.

2. Debug and Test the Widget

  • Update your Flutter app's configuration by running flutter build ios --config-only in your project directory.

  • In Xcode, select the widget target (e.g., "WeatherWidgets") from the target list under Runner and click Run.

  • Add the widget to the simulator or device's home screen by long-pressing on the home screen, clicking the "+" icon in the top-left corner, and searching for your app's widget.

How to Build Home Screen Widgets for Android

Android supports more interactive widgets compared to iOS. Building a home screen widget for Android involves creating a new widget class and configuring it within your Flutter project.

0. Prerequisites

  • A development environment configured with Android Studio. Doing so installs the compiler required for building the Android version of the app.

  • A physical Android device or emulator for testing.

  • The Flutter SDK installed on your system, along with your choice of IDE (Visual Studio Code, Android Studio or IntelliJ with the necessary Flutter and Dart extensions/plugins).

  • Have your starter code ready. Clone the GitHub repository, open the starter app in your preferred IDE and run flutter pub get to ensure all dependencies are installed.

1. Add a Widget Class

  • Open your Flutter project in Android Studio by right-clicking on the android folder in VSCode and selecting "Open in Android Studio." You can also find the build file directly in Android Studio at android/build.gradle.

  • Navigate to the app directory within Android Studio.

  • Right-click on this directory and select New → Widget → App Widget.

2. Configure Widget Properties

In the form that appears:

  • Set a class name (e.g., "WeatherWidget").

  • Define minimum width and height in cells (e.g., 3x3).

Android Studio will generate several files:

  1. An updated receiver file that registers the widget. (AndroidManifest.xml)

  2. A layout file that defines its UI structure. (res/layout/weather_widget.xml)

  3. A configuration file where dimensions or names can be adjusted. (res/xml/weather_widget_info.xml)

  4. A Kotlin file containing logic for updating widget content. (java/com/example/homescreen_widgets/WeatherWidget.kt)

3. Debug and Test

After building your project:

  • Run it on an emulator or physical device.

  • Long press on your app icon from the application selection menu and select Widgets from the popup menu.

  • Drag and drop your widget onto the home screen.

Sharing Data Between App and Widget

The previous sections describe how to add a basic widget in both the native environments. To customize the widget, sharing information from your Flutter app to the widget would be a typical route. This requires some common Dart code, and then configuring the native code for both iOS and Android.

1. Common Dart Code

Both iOS and Android apps can share data with a Flutter app through the device's local key/value store. iOS uses UserDefaults, while Android uses SharedPreferences. The home_widget package simplifies this process, allowing seamless data updates for Home Screen widgets by wrapping both APIs.

The weather data for this example comes from weather_data.dart, which contains mock data and a WeatherReport class.

lib/weather_data.dart
class WeatherReport {
  final String temperature;
  final String description;
  WeatherReport({
    required this.temperature,
    required this.description,
  });
}

Update Weather Data in the Widget: To enable updating the Home Screen widget from your Flutter app, modify the home_screen.dart file.

  1. Replace the contents of lib/home_screen.dart with the following code.

  2. Replace <YOUR APP GROUP> with the identifier of your App Group.

lib/home_screen.dart
import 'package:flutter/material.dart';
import 'package:home_widget/home_widget.dart';
import 'weather_data.dart';
// Replace with your App Group ID
const String appGroupId = '<YOUR APP GROUP>';
const String iOSWidgetName = 'WeatherWidgets';
const String androidWidgetName = 'WeatherWidget';
void updateWeather(WeatherReport newWeather) {
  // Save weather data to the widget
  HomeWidget.saveWidgetData<String>('weather_temperature', newWeather.temperature);
  HomeWidget.saveWidgetData<String>('weather_description', newWeather.description);
  HomeWidget.updateWidget(
    iOSName: iOSWidgetName,
    androidName: androidWidgetName,
  );
}
class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
    HomeWidget.setAppGroupId(appGroupId);
    // Mock data update
    final newWeather = WeatherReport(temperature: "72°F", description: "Sunny");
    updateWeather(newWeather);
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
            title: const Text('Weather Updates'),
            centerTitle: false,
            titleTextStyle: const TextStyle(
                fontSize: 30,
                fontWeight: FontWeight.bold,
                color: Colors.black)),
        body: Center(child: const Text('Weather Widget Example')),
      );
  }
}

The updateWeather function saves weather data (temperature and description) as key/value pairs to local storage. It also triggers the platform to update the Home Screen widget.

Modify the FloatingActionButton: Update the floatingActionButton to call updateWeather when pressed.

lib/weather_screen.dart
import 'home_screen.dart';
...
floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
            content: Text('Updating Home Screen widget...'),
          ));
          updateWeather(widget.weatherReport);
        },
        label: const Text('Update Weather Widget'),
      ),

Pressing the button updates the Home Screen widget with the latest weather data.

2. iOS-specific Steps

To enable data sharing between an iOS parent app and a widget extension for WeatherWidgets, both targets must belong to the same app group. Refer to Apple's App Groups Documentation for more details. These are the steps to follow to enable the same:

Prerequisites: Ensure you are signed in to your Apple Developer account to configure an app group.

Update the Bundle Identifier:

  1. Open your project in Xcode.

  2. Go to the target's Signing & Capabilities tab and ensure your team and bundle identifier are correctly configured.

Add the App Group:

  1. In Xcode, select + Capability -> App Groups.

  2. Create a new App Group and add it to both the Runner (parent app) and the WeatherWidgetExtension targets.

Note that the widget's bundle identifier must use the parent app's bundle identifier as the prefix.

Updating the iOS Widget: Modify the Swift code for the widget in Xcode.

First, open WeatherWidgets.swift.

Then, we need to configure the TimelineEntry. Replace the SimpleEntry struct with the following:

struct WeatherEntry: TimelineEntry {
    let date: Date
    let temperature: String
    let description: String
}

This struct defines the data (temperature and description) passed to the widget.

Then, update the widget view to display weather data.

struct WeatherWidgetsEntryView: View {
    var entry: Provider.Entry
    var body: some View {
      VStack {
        Text(entry.temperature)
          .font(.largeTitle)
          .bold()
        Text(entry.description)
          .font(.subheadline)
      }
    }
}

Then, update the Provider implementation by replacing the existing code with the following:

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> WeatherEntry {
        WeatherEntry(date: Date(), temperature: "72°F", description: "Sunny")
    }
    func getSnapshot(in context: Context, completion: @escaping (WeatherEntry) -> ()) {
        let entry: WeatherEntry
        if context.isPreview {
            entry = placeholder(in: context)
        } else {
            let userDefaults = UserDefaults(suiteName: "<YOUR APP GROUP>")
            let temperature = userDefaults?.string(forKey: "weather_temperature") ?? "No Data"
            let description = userDefaults?.string(forKey: "weather_description") ?? "No Data"
            entry = WeatherEntry(date: Date(), temperature: temperature, 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)
        }
    }
}

Lastly, comment out the WeatherWidgets_Previews section, as previews are out of scope for this example.

Final Steps:

  1. Save all files and re-run the app and widget targets:

    • Select the app schema in Xcode to run the app target.

    • Select the extension schema to run the widget target.

  2. Navigate to a page in the app, then press the update button to validate that the Home Screen widget reflects the latest weather data.

3. Android-specific Steps

Make the following changes to your Android code:

Add the Home Screen Widget XML:

  1. In Android Studio, locate the file res/layout/weather_widget.xml. This file defines the structure and layout of your Home Screen widget.

  2. Replace its content with the following XML to create the layout for WeatherWidget:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:id="@+id/widget_container"
   style="@style/Widget.Android.AppWidget.Container"
   android:layout_width="wrap_content"
   android:layout_height="match_parent"
   android:background="@android:color/white"
   android:theme="@style/Theme.Android.AppWidgetContainer">
   <TextView
       android:id="@+id/weather_temperature"
       style="@style/Widget.Android.AppWidget.InnerView"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_alignParentStart="true"
       android:layout_marginStart="8dp"
       android:background="@android:color/white"
       android:text="Temperature"
       android:textSize="20sp"
       android:textStyle="bold" />
   <TextView
       android:id="@+id/weather_condition"
       style="@style/Widget.Android.AppWidget.InnerView"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_below="@+id/weather_temperature"
       android:layout_alignParentStart="true"
       android:layout_marginStart="8dp"
       android:layout_marginTop="4dp"
       android:background="@android:color/white"
       android:text="Condition"
       android:textSize="16sp"

This XML defines two TextView elements:

  • weather_temperature: Displays the current temperature.

  • weather_condition: Displays the weather condition (e.g., "Sunny").

This XML defines the UI for WeatherWidget. For additional details, refer to the Android Developer Guide.

Update WeatherWidget Functionality:

  1. Open the WeatherWidget.kt file located in the android/app/java/com.mydomain.weather_widgets/ directory.

  2. Replace its content with the following updated code:

// Import will depend on App ID.  
package com.mydomain.weather_widgets  
import android.appwidget.AppWidgetManager  
import android.appwidget.AppWidgetProvider  
import android.content.Context  
import android.widget.RemoteViews  
// New import.  
import es.antonborri.home_widget.HomeWidgetPlugin  
/**
 * Implementation of App Widget functionality.
 */
class WeatherWidget : AppWidgetProvider() {
    override fun onUpdate(
            context: Context,
            appWidgetManager: AppWidgetManager,
            appWidgetIds: IntArray,
    ) {
        for (appWidgetId in appWidgetIds) {
            // Get reference to SharedPreferences
            val widgetData = HomeWidgetPlugin.getData(context)
            val views = RemoteViews(context.packageName, R.layout.weather_widget).apply {
                val temperature = widgetData.getString("weather_temperature", null)
                setTextViewText(R.id.weather_temperature, temperature ?: "No temperature set")
                val condition = widgetData.getString("weather_condition", null)
                setTextViewText(R.id.weather_condition, condition ?: "No condition set")
            }
            appWidgetManager.updateAppWidget(appWidgetId, views

This code modifies the onUpdate method to:

  • Fetch the latest weather data (temperature and condition) from local storage using widgetData.getString().

  • Update the TextView elements (weather_temperature and weather_condition) with the fetched data using setTextViewText.

Note that Android calls onUpdate at fixed intervals (default: 24 hours) or when triggered programmatically via the updateWidget method in Dart code.

3. Test the Updates

Run the app and test its functionality to ensure WeatherWidget updates with new data.

To test updates, use a button or mechanism (e.g., a FloatingActionButton) in your app to update the weather data programmatically.

Verify that the widget displays the updated temperature and condition on the Home Screen.

With these steps, your widget is fully functional and capable of displaying real-time weather updates, whether on iOS or Android Home Screens.

Additional Steps

1. Display Flutter Widgets as Images

You can showcase graphics such as charts from your Flutter app within a Home Screen widget. Rendering the Flutter chart as an image offers a simpler solution compared to recreating it using native UI components. By converting the Flutter chart into a PNG file, you can easily display it in your Home Screen widget. For detailed instructions on achieving this in both iOS and Android, check out this guide.

2. Use Flutter App Custom Fonts for iOS

You can apply custom fonts from your Flutter app to your iOS Home Screen widget by following these steps:

Access Flutter Assets: Create a helper function in WeatherWidgetsEntryView to get the path to the Flutter asset directory.

Register the Font: Use CTFontManagerRegisterFontsForURL in the init method of WeatherWidgetsEntryView to register the custom font using its file URL.

Apply the Font: Update the Text view in the widget to use the custom font with Font.custom.

This configuration allows the headline in the widget to display using the custom font.

3. Link to Content in your Flutter App

You can direct users to specific pages in your app based on widget interactions.

For example in our weather widget example, clicking on the temperature displayed could open the app to a full hourly breakdown of the day’s temperatures. Refer to Flutter's deep linking documentation for implementation.

4. Update Your Widget in the Background

In this guide, widget updates were triggered manually using a button, which is suitable for testing. For production, background updates are recommended. The workmanager plugin can schedule background tasks to update widget resources for both Android and iOS. On iOS, the widget can also make network requests for updates, with the Timeline feature managing update frequency and conditions. Refer to the Background update section in the home_widget package and Apple’s “Keeping a widget up to date” documentation for further guidance.

Design Tips and Best Practices for Home Screen Widgets

When designing home screen widgets, it is essential to balance functionality with simplicity due to platform constraints. Below are some tips:

Keep It Minimal: Display only essential information that users need at a glance.

Optimize Layouts: Ensure layouts adapt well to different widget sizes (small, medium, large).

Use Platform Guidelines: Follow Apple’s Human Interface Guidelines and Google’s Material Design principles.

Ensure Accessibility: Use readable fonts, sufficient contrast, and support system-wide accessibility settings like larger text sizes.

Reduce Battery Usage: Avoid frequent updates or animations that could drain battery life unnecessarily.

For platform-specific widget design best practices, check out the information provided by Apple and Android.

Conclusion

Building home screen widgets for iOS and Android with Flutter involves leveraging platform-specific tools like Xcode’s WidgetKit integration for iOS and Android Studio’s App Widget framework for Android. While each platform has unique requirements and limitations, both provide opportunities to enhance user engagement by offering quick access to essential app features directly from the home screen.

By following this guide, developers can create functional, visually appealing widgets that improve user experience while adhering to best practices for design and performance optimization.

References

“Adding a Home Screen Widget to Your Flutter App  |  Google Codelabs.” Google Codelabs, codelabs.developers.google.com/flutter-home-screen-widgets.

“Widgets | Apple Developer Documentation.” Apple Developer Documentation, developer.apple.com/design/human-interface-guidelines/widgets#Best-practices.

“Widgets on Android | UI Design.” Android Developers, developer.android.com/design/ui/widget.

Other Insights

Got an app?

We build and deliver stunning mobile products that scale

Got an app?

We build and deliver stunning mobile products that scale

Got an app?

We build and deliver stunning mobile products that scale

Got an app?

We build and deliver stunning mobile products that scale

Got an app?

We build and deliver stunning mobile products that scale

Our mission is to harness the power of technology to make this world a better place. We provide thoughtful software solutions and consultancy that enhance growth and productivity.

The Jacx Office: 16-120

2807 Jackson Ave

Queens NY 11101, United States

Book an onsite meeting or request a services?

© Walturn LLC • All Rights Reserved 2024

Our mission is to harness the power of technology to make this world a better place. We provide thoughtful software solutions and consultancy that enhance growth and productivity.

The Jacx Office: 16-120

2807 Jackson Ave

Queens NY 11101, United States

Book an onsite meeting or request a services?

© Walturn LLC • All Rights Reserved 2024

Our mission is to harness the power of technology to make this world a better place. We provide thoughtful software solutions and consultancy that enhance growth and productivity.

The Jacx Office: 16-120

2807 Jackson Ave

Queens NY 11101, United States

Book an onsite meeting or request a services?

© Walturn LLC • All Rights Reserved 2024

Our mission is to harness the power of technology to make this world a better place. We provide thoughtful software solutions and consultancy that enhance growth and productivity.

The Jacx Office: 16-120

2807 Jackson Ave

Queens NY 11101, United States

Book an onsite meeting or request a services?

© Walturn LLC • All Rights Reserved 2024

Our mission is to harness the power of technology to make this world a better place. We provide thoughtful software solutions and consultancy that enhance growth and productivity.

The Jacx Office: 16-120

2807 Jackson Ave

Queens NY 11101, United States

Book an onsite meeting or request a services?

© Walturn LLC • All Rights Reserved 2024