Skip to content

feat: Add Android example for configurable widgets#411

Merged
ABausG merged 3 commits into
mainfrom
feat/configurable-widget-example
Apr 12, 2026
Merged

feat: Add Android example for configurable widgets#411
ABausG merged 3 commits into
mainfrom
feat/configurable-widget-example

Conversation

@ABausG
Copy link
Copy Markdown
Owner

@ABausG ABausG commented Apr 12, 2026

Description

Add an Android Example for Configurable Widgets following the work of #396

Checklist

  • I have updated/added tests for ALL new/updated/fixed functionality.
  • I have updated/added relevant documentation and added code (documentation) comments where necessary.
  • I have updated/added relevant examples in example or documentation.

Breaking Change?

  • Yes, this PR is a breaking change.
  • No, this PR is not a breaking change.

Related Issues

@docs-page
Copy link
Copy Markdown

docs-page Bot commented Apr 12, 2026

To view this pull requests documentation preview, visit the following URL:

docs.page/abausg/home_widget~411

Documentation is deployed and generated using docs.page.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 12, 2026

Walkthrough

Introduces documentation and a full Flutter example that implement configurable Android home widgets using a dedicated FlutterActivity that launches a separate Dart entrypoint configureMain, per-widget persisted state, Glance-based widget rendering, and explicit configuration finish/refresh APIs.

Changes

Cohort / File(s) Summary
Documentation
docs/features/configurable-widgets.mdx
Rewrote Android configuration guidance to use a dedicated FlutterActivity + configureMain entrypoint, explain initiallyLaunchedFromHomeWidgetConfigure() / finishHomeWidgetConfigure(), per-widget data keys, and widget update semantics.
Example project metadata & ignore
examples/configurable_widget/.metadata, examples/configurable_widget/android/.gitignore
Updated Flutter tooling metadata revisions and added Android/Gradle ignore rules.
Gradle / build setup
examples/configurable_widget/android/settings.gradle.kts, examples/configurable_widget/android/build.gradle.kts, examples/configurable_widget/android/gradle.properties, examples/configurable_widget/android/gradle/wrapper/gradle-wrapper.properties, examples/configurable_widget/android/app/build.gradle.kts
Added Kotlin-DSL build scripts, plugin declarations, repositories, Gradle wrapper, buildDirectory relocation, Java 17/Kotlin/Compose configuration, and Glance dependency.
AndroidManifests (variants)
examples/configurable_widget/android/app/src/main/AndroidManifest.xml, .../src/debug/AndroidManifest.xml, .../src/profile/AndroidManifest.xml
Declared MainActivity launcher, WidgetConfigurationActivity with APPWIDGET_CONFIGURE intent filter, Glance receiver registration, and debug/profile INTERNET permission entries.
Android Kotlin code
examples/configurable_widget/android/app/src/main/kotlin/.../MainActivity.kt, .../WidgetConfigurationActivity.kt, .../ConfigurableWidgetHomeWidget.kt, .../ConfigurableWidgetHomeWidgetReceiver.kt
Added MainActivity and WidgetConfigurationActivity (overrides getDartEntrypointFunctionName()configureMain), Glance AppWidget implementation that reads per-widget preferences, and a receiver linking to the Glance widget.
Android resources
examples/configurable_widget/android/app/src/main/res/drawable*/launch_background.xml, .../res/values*/styles.xml, .../res/xml/configurable_widget_home_widget.xml
Added launch background drawables, light/dark themes, and appwidget-provider XML pointing android:configure to the WidgetConfigurationActivity.
Flutter/Dart UI & logic
examples/configurable_widget/lib/main.dart, examples/configurable_widget/lib/android_configuration_page.dart
Added configureMain() entrypoint routing to AndroidConfigurationPage via initiallyLaunchedFromHomeWidgetConfigure(), converted main() to async init, implemented per-widget data persistence (name.{id}, punctuation.{id}), widget list rendering, and save/update/finish flows that call HomeWidget.updateWidget and HomeWidget.finishHomeWidgetConfigure().

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Android as Android System
    participant ConfigActivity as WidgetConfigurationActivity
    participant Dart as Dart (configureMain)
    participant HomeWidget as HomeWidget Plugin
    participant Storage as Local Storage
    participant Widget as Glance Widget

    User->>Android: Initiate widget placement/configuration
    Android->>ConfigActivity: Start activity with appWidgetId
    ConfigActivity->>Dart: Launch Dart entrypoint configureMain()
    Dart->>HomeWidget: initiallyLaunchedFromHomeWidgetConfigure()
    HomeWidget-->>Dart: Return widgetId (or null)
    alt widgetId present
      Dart->>Dart: Show AndroidConfigurationPage(widgetId)
      User->>Dart: Enter config & Tap Save
      Dart->>HomeWidget: saveWidgetData(name.{id}, value)
      Dart->>HomeWidget: saveWidgetData(punctuation.{id}, value)
      HomeWidget->>Storage: Persist per-widget keys
      Dart->>HomeWidget: updateWidget(qualifiedAndroidName)
      HomeWidget->>Widget: Trigger refresh
      Dart->>HomeWidget: finishHomeWidgetConfigure()
      HomeWidget-->>ConfigActivity: Return RESULT_OK
      ConfigActivity-->>Android: Close activity
    else fallback
      Dart->>Dart: Call main() (normal app flow)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description provides context referencing issue #396, completes all required checklist items, and confirms no breaking changes, but lacks detail on the specific changes made to the widget configuration approach.
Title check ✅ Passed The title accurately summarizes the main change: adding an Android example for configurable widgets, which is the primary focus of this PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/configurable-widget-example

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@ABausG ABausG linked an issue Apr 12, 2026 that may be closed by this pull request
@ABausG ABausG changed the title feat: configurable widget example feat: Add Android example for configurable widgets Apr 12, 2026
@ABausG ABausG changed the title feat: Add Android example for configurable widgets docs: Add Android example for configurable widgets Apr 12, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
examples/configurable_widget/android/settings.gradle.kts (1)

4-7: Add an explicit local.properties existence guard for clearer failure mode.

Line 5 opens the file before validation, so missing-file failures are less actionable than they could be.

Proposed refinement
 pluginManagement {
     val flutterSdkPath =
         run {
             val properties = java.util.Properties()
-            file("local.properties").inputStream().use { properties.load(it) }
+            val localProperties = file("local.properties")
+            require(localProperties.exists()) { "local.properties not found" }
+            localProperties.inputStream().use { properties.load(it) }
             val flutterSdkPath = properties.getProperty("flutter.sdk")
             require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
             flutterSdkPath
         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/configurable_widget/android/settings.gradle.kts` around lines 4 - 7,
The code opens local.properties before checking, so missing-file errors are
unclear; update the block around val properties,
file("local.properties").inputStream(), and val flutterSdkPath to first check
file("local.properties").exists() and throw or require with a clear message if
missing, then load the properties only when the file exists and finally
require(flutterSdkPath != null) with the existing message; this ensures a clear,
explicit failure when local.properties is absent and preserves the
flutterSdkPath null check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@examples/configurable_widget/android/settings.gradle.kts`:
- Around line 4-7: The code opens local.properties before checking, so
missing-file errors are unclear; update the block around val properties,
file("local.properties").inputStream(), and val flutterSdkPath to first check
file("local.properties").exists() and throw or require with a clear message if
missing, then load the properties only when the file exists and finally
require(flutterSdkPath != null) with the existing message; this ensures a clear,
explicit failure when local.properties is absent and preserves the
flutterSdkPath null check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: eb26b858-3e82-4af4-898e-cbf9e92d86fe

📥 Commits

Reviewing files that changed from the base of the PR and between faf1689 and 4b7071d.

⛔ Files ignored due to path filters (5)
  • examples/configurable_widget/android/app/src/main/res/mipmap-hdpi/ic_launcher.png is excluded by !**/*.png
  • examples/configurable_widget/android/app/src/main/res/mipmap-mdpi/ic_launcher.png is excluded by !**/*.png
  • examples/configurable_widget/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png is excluded by !**/*.png
  • examples/configurable_widget/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png is excluded by !**/*.png
  • examples/configurable_widget/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png is excluded by !**/*.png
📒 Files selected for processing (22)
  • docs/features/configurable-widgets.mdx
  • examples/configurable_widget/.metadata
  • examples/configurable_widget/android/.gitignore
  • examples/configurable_widget/android/app/build.gradle.kts
  • examples/configurable_widget/android/app/src/debug/AndroidManifest.xml
  • examples/configurable_widget/android/app/src/main/AndroidManifest.xml
  • examples/configurable_widget/android/app/src/main/kotlin/es/antonborri/configurable_widget/ConfigurableWidgetHomeWidget.kt
  • examples/configurable_widget/android/app/src/main/kotlin/es/antonborri/configurable_widget/ConfigurableWidgetHomeWidgetReceiver.kt
  • examples/configurable_widget/android/app/src/main/kotlin/es/antonborri/configurable_widget/MainActivity.kt
  • examples/configurable_widget/android/app/src/main/kotlin/es/antonborri/configurable_widget/WidgetConfigurationActivity.kt
  • examples/configurable_widget/android/app/src/main/res/drawable-v21/launch_background.xml
  • examples/configurable_widget/android/app/src/main/res/drawable/launch_background.xml
  • examples/configurable_widget/android/app/src/main/res/values-night/styles.xml
  • examples/configurable_widget/android/app/src/main/res/values/styles.xml
  • examples/configurable_widget/android/app/src/main/res/xml/configurable_widget_home_widget.xml
  • examples/configurable_widget/android/app/src/profile/AndroidManifest.xml
  • examples/configurable_widget/android/build.gradle.kts
  • examples/configurable_widget/android/gradle.properties
  • examples/configurable_widget/android/gradle/wrapper/gradle-wrapper.properties
  • examples/configurable_widget/android/settings.gradle.kts
  • examples/configurable_widget/lib/android_configuration_page.dart
  • examples/configurable_widget/lib/main.dart
👮 Files not reviewed due to content moderation or server errors (5)
  • examples/configurable_widget/lib/android_configuration_page.dart
  • examples/configurable_widget/android/app/src/main/kotlin/es/antonborri/configurable_widget/ConfigurableWidgetHomeWidget.kt
  • examples/configurable_widget/android/app/src/main/AndroidManifest.xml
  • docs/features/configurable-widgets.mdx
  • examples/configurable_widget/lib/main.dart

@ABausG ABausG changed the title docs: Add Android example for configurable widgets feat: Add Android example for configurable widgets Apr 12, 2026
@ABausG ABausG merged commit 2c86b55 into main Apr 12, 2026
14 of 15 checks passed
@ABausG ABausG deleted the feat/configurable-widget-example branch April 12, 2026 21:53
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
examples/configurable_widget/lib/main.dart (1)

92-112: Consider adding kIsWeb guard for consistency.

Unlike configureMain() which guards Platform checks with !kIsWeb, this method accesses Platform.isAndroid and Platform.isIOS directly. If the app were ever run on web, this would throw at runtime since dart:io's Platform is unavailable there.

While unlikely for a home widget example, adding a guard would be consistent with the pattern used elsewhere in this file.

♻️ Suggested guard
  Future<void> _getInstalledWidgets() async {
    try {
      final installedWidgets = await HomeWidget.getInstalledWidgets();
      final entries = <_InstalledWidgetEntry>[];
      for (final w in installedWidgets) {
        Map<String, dynamic>? configuration;
-       if (Platform.isAndroid && w.androidWidgetId != null) {
+       if (!kIsWeb && Platform.isAndroid && w.androidWidgetId != null) {
          configuration =
              await _androidConfigurationFromStorage(w.androidWidgetId!);
-       } else if (Platform.isIOS && w.configuration != null) {
+       } else if (!kIsWeb && Platform.isIOS && w.configuration != null) {
          configuration = Map<String, dynamic>.from(w.configuration!);
        }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/configurable_widget/lib/main.dart` around lines 92 - 112, The
Platform.isAndroid / Platform.isIOS checks used while processing
HomeWidget.getInstalledWidgets can throw on web; guard them with a kIsWeb check
(e.g. only access Platform when !kIsWeb) so the configuration lookup via
_androidConfigurationFromStorage and the Map copy from w.configuration are only
executed on non-web. Add the package:flutter/foundation.dart import for kIsWeb
if missing and update the conditional logic around Platform.isAndroid /
Platform.isIOS to use !kIsWeb && Platform.isAndroid / !kIsWeb && Platform.isIOS
before calling _androidConfigurationFromStorage or reading w.configuration,
keeping the rest of the setState flow unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@examples/configurable_widget/lib/main.dart`:
- Around line 92-112: The Platform.isAndroid / Platform.isIOS checks used while
processing HomeWidget.getInstalledWidgets can throw on web; guard them with a
kIsWeb check (e.g. only access Platform when !kIsWeb) so the configuration
lookup via _androidConfigurationFromStorage and the Map copy from
w.configuration are only executed on non-web. Add the
package:flutter/foundation.dart import for kIsWeb if missing and update the
conditional logic around Platform.isAndroid / Platform.isIOS to use !kIsWeb &&
Platform.isAndroid / !kIsWeb && Platform.isIOS before calling
_androidConfigurationFromStorage or reading w.configuration, keeping the rest of
the setState flow unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a1d2f208-fb70-4700-8948-233c982f33d2

📥 Commits

Reviewing files that changed from the base of the PR and between 4b7071d and f2fb03e.

📒 Files selected for processing (6)
  • examples/configurable_widget/android/app/build.gradle.kts
  • examples/configurable_widget/android/app/src/main/kotlin/es/antonborri/configurable_widget/ConfigurableWidgetHomeWidget.kt
  • examples/configurable_widget/android/app/src/main/kotlin/es/antonborri/configurable_widget/ConfigurableWidgetHomeWidgetReceiver.kt
  • examples/configurable_widget/android/build.gradle.kts
  • examples/configurable_widget/android/settings.gradle.kts
  • examples/configurable_widget/lib/main.dart
✅ Files skipped from review due to trivial changes (5)
  • examples/configurable_widget/android/app/src/main/kotlin/es/antonborri/configurable_widget/ConfigurableWidgetHomeWidgetReceiver.kt
  • examples/configurable_widget/android/settings.gradle.kts
  • examples/configurable_widget/android/app/build.gradle.kts
  • examples/configurable_widget/android/build.gradle.kts
  • examples/configurable_widget/android/app/src/main/kotlin/es/antonborri/configurable_widget/ConfigurableWidgetHomeWidget.kt

@coderabbitai coderabbitai Bot mentioned this pull request May 25, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Android : configurable widget

1 participant