Skip to content

feat: Configurable widgets for Android#396

Open
jonmarkhall wants to merge 8 commits intoABausG:mainfrom
BerryPress:main
Open

feat: Configurable widgets for Android#396
jonmarkhall wants to merge 8 commits intoABausG:mainfrom
BerryPress:main

Conversation

@jonmarkhall
Copy link

Description

Android supports "reconfigurable" widgets via a Settings item in the widget menu (opened via long-press on the widget), and also on initial widget creation. This PR adds a method, initiallyLaunchedFromHomeWidgetConfigure(), which can be used to detect if the app was launched from a widget configuration requests, and returns the corresponding widget ID if so. This can then be handled in the app to load a widget settings UI.

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

N/A

@docs-page
Copy link

docs-page bot commented Jan 30, 2026

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

docs.page/abausg/home_widget~396

Documentation is deployed and generated using docs.page.

@coderabbitai
Copy link

coderabbitai bot commented Jan 30, 2026

Walkthrough

Adds Android widget-configuration support: new Dart APIs to detect configure-time launches and finish configuration, Android plugin handlers for APPWIDGET_CONFIGURE intents, updated (duplicated) documentation, and tests exercising the configure pathway.

Changes

Cohort / File(s) Summary
Documentation
docs/features/configurable-widgets.mdx
Adds an Android section describing android:widgetFeatures="reconfigurable", android:configure="...", required APPWIDGET_CONFIGURE intent-filter, using initiallyLaunchedFromHomeWidgetConfigure() to detect configure launches, and calling finishHomeWidgetConfigure(); the guidance is inserted twice.
Dart API
packages/home_widget/lib/src/home_widget.dart
Adds static Future<String?> initiallyLaunchedFromHomeWidgetConfigure() and static Future<void> finishHomeWidgetConfigure() which call platform channel methods.
Android Native Implementation
packages/home_widget/android/src/main/kotlin/.../HomeWidgetPlugin.kt
Adds method-call handlers: initiallyLaunchedFromHomeWidgetConfigure (reads ACTION_APPWIDGET_CONFIGURE and EXTRA_APPWIDGET_ID, returns ID or null; sets result canceled) and finishHomeWidgetConfigure (sets RESULT_OK with EXTRA_APPWIDGET_ID and finishes the activity).
Tests
packages/home_widget/test/home_widget_test.dart
Extends mock MethodChannel handler for initiallyLaunchedFromHomeWidgetConfigure and adds tests for valid widget ID and null scenarios; integrates with existing test setup.

Sequence Diagram(s)

sequenceDiagram
    participant App as Flutter App
    participant Plugin as Android Plugin
    participant Activity as Android Activity
    participant System as Android Home Launcher

    Note over System,Activity: Launcher starts Activity for widget configuration
    System->>Activity: start (action=APPWIDGET_CONFIGURE, EXTRA_APPWIDGET_ID)
    Activity->>Plugin: activity attached / intent available
    App->>Plugin: invokeMethod("initiallyLaunchedFromHomeWidgetConfigure")
    Plugin->>Activity: read intent.action and EXTRA_APPWIDGET_ID
    Plugin-->>App: return widgetId (string) or null
    App->>Plugin: invokeMethod("finishHomeWidgetConfigure")
    Plugin->>Activity: setResult(RESULT_OK, EXTRA_APPWIDGET_ID) and finish()
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 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
Title check ✅ Passed The title accurately summarizes the main change: adding configurable widget support for Android with a clear, concise feat: prefix.
Description check ✅ Passed The description covers the feature purpose, includes completed checklist items for tests and documentation, clarifies it's not a breaking change, and explains the new method's behavior.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

@jonmarkhall jonmarkhall changed the title feat:Configurable widgets for Android feat: Configurable widgets for Android Jan 30, 2026
Copy link

@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.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetPlugin.kt`:
- Around line 139-147: The handler for
"initiallyLaunchedFromHomeWidgetConfigure" in HomeWidgetPlugin returns "-1" when
the EXTRA_APPWIDGET_ID is missing because it converts the int directly to
string; change the logic to read the int via activity?.intent?.getIntExtra(...),
check if it equals AppWidgetManager.INVALID_APPWIDGET_ID and call
result.success(null) in that case, otherwise call result.success(id.toString())
so Dart receives null for invalid/missing widget IDs.
🧹 Nitpick comments (1)
docs/features/configurable-widgets.mdx (1)

440-462: Add documentation about android:exported attribute and configure activity return requirements.

The current snippet omits two critical implementation details. On Android 12+, the configure activity must declare android:exported="true" (otherwise the app cannot be installed). Additionally, the configure activity must return RESULT_OK with EXTRA_APPWIDGET_ID for the widget to actually be added; without this, the widget won't appear even if configuration completes successfully.

📌 Suggested doc addition
 <intent-filter>
   <action android:name="android.intent.action.APPWIDGET_CONFIGURE"/>
 </intent-filter>

+> **Note:** On Android 12+ the configure activity must include `android:exported="true"`.
+> The activity must also return `RESULT_OK` with an Intent containing `EXTRA_APPWIDGET_ID`
+> set to the widget ID received from the launch intent; without this, the widget won't be added.

@ABausG
Copy link
Owner

ABausG commented Feb 2, 2026

Thanks for this! Can you maybe add some more links to official Android Documentation to home_widget's docs?

Will try to find some time to review this proper by end of week

@codecov
Copy link

codecov bot commented Feb 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (fa7453a) to head (fbb1bd6).

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #396   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            3         3           
  Lines          115       117    +2     
=========================================
+ Hits           115       117    +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jonmarkhall
Copy link
Author

@ABausG I've added a link to the Android docs page on this feature. I also made a minor change to the code based on the CodeRabbit suggestion in this thread.

Copy link

@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.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetPlugin.kt`:
- Around line 151-160: The finishHomeWidgetConfigure branch should validate the
AppWidget ID and default the activity result to RESULT_CANCELED early; read the
ID from activity!!.intent?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID), if the ID is INVALID_APPWIDGET_ID then
call activity!!.setResult(Activity.RESULT_CANCELED) (or leave the default) and
finish, otherwise set activity!!.setResult(Activity.RESULT_OK,
Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)) and finish;
update the finishHomeWidgetConfigure flow to perform this check before returning
result.success(null).

Comment on lines +151 to +160
"finishHomeWidgetConfigure" -> {
if (
activity != null
&& activity!!.intent?.action?.equals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE) ==
true
) {
activity!!.setResult(Activity.RESULT_OK, Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, activity!!.intent?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID)));
activity!!.finish()
}
return result.success(null)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Android AppWidget configuration activity RESULT_OK EXTRA_APPWIDGET_ID requirements

💡 Result:

For an AppWidget configuration Activity (the one launched via ACTION_APPWIDGET_CONFIGURE / android:configure=...), the host/system expects:

  • You must always return a result to the caller (the widget host). (developer.android.com)
  • The result must include the same AppWidget ID you were launched with, in an intent extra named AppWidgetManager.EXTRA_APPWIDGET_ID. (developer.android.com)
  • To actually add the widget, you must call:
    • setResult(Activity.RESULT_OK, Intent().putExtra(EXTRA_APPWIDGET_ID, appWidgetId))
    • then finish() (developer.android.com)
  • Best practice: set a default result of RESULT_CANCELED early (with the extra), so if the user backs out, the host knows to cancel and not add the widget. (developer.android.com)
  • When a widget has a configuration activity, onUpdate() is not called for the initial add; your config activity should perform the initial AppWidgetManager.updateAppWidget(...) before returning RESULT_OK. (developer.android.com)

Citations:


🌐 Web query:

Android AppWidget host behavior INVALID_APPWIDGET_ID configuration activity

💡 Result:

AppWidgetManager.INVALID_APPWIDGET_ID (-1) in a widget configuration Activity almost always means the host did not pass a real widget ID in Intent extra AppWidgetManager.EXTRA_APPWIDGET_ID (or you didn’t read it from the same launching intent). Per the platform contract, the config activity is started with ACTION_APPWIDGET_CONFIGURE and must receive EXTRA_APPWIDGET_ID identifying the instance being configured. (developer.android.com)

Correct host flow (what the host must do)

  1. Allocate an ID: val appWidgetId = appWidgetHost.allocateAppWidgetId(). (developer.android.com)
  2. Bind the provider to that ID (often via ACTION_APPWIDGET_BIND, or bindAppWidgetIdIfAllowed(...) when permitted). (developer.android.com)
  3. If the provider has a config activity, start it for that same ID, e.g.:
    • AppWidgetHost.startAppWidgetConfigureActivityForResult(activity, appWidgetId, ...) (recommended; also handles cross-profile). (developer.android.com)

If the host skips (1) or doesn’t pass the ID through, the config activity will read INVALID_APPWIDGET_ID.

Correct configuration Activity behavior (what your config Activity must do)

  • Read the ID from the launch intent extras, defaulting to INVALID_APPWIDGET_ID. (developer.android.com)
  • If it’s invalid, finish (usually RESULT_CANCELED), because you can’t configure/update without a real ID.
  • When the user completes configuration, return RESULT_OK and include the same EXTRA_APPWIDGET_ID in the result intent; otherwise many hosts will treat it as failure and won’t add the widget. (developer.android.com)

Minimal pattern:

class WidgetConfigActivity : Activity() {
  private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    appWidgetId = intent?.getIntExtra(
      AppWidgetManager.EXTRA_APPWIDGET_ID,
      AppWidgetManager.INVALID_APPWIDGET_ID
    ) ?: AppWidgetManager.INVALID_APPWIDGET_ID

    // Default to canceled so Back = not added
    setResult(RESULT_CANCELED, Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId))

    if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
      finish()
      return
    }

    // ... UI ...
  }

  private fun done() {
    // ... update widget via AppWidgetManager.updateAppWidget(appWidgetId, views) ...
    setResult(RESULT_OK, Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId))
    finish()
  }
}

Common causes of INVALID_APPWIDGET_ID

  • Host launches your config activity with a plain intent (e.g., startActivity(...)) and forgets putExtra(EXTRA_APPWIDGET_ID, id).
  • Host launches config before allocating/binding an ID.
  • Config activity reads from the wrong intent (e.g., uses getIntent() after being relaunched without propagating extras).

If you share your host code that launches the config activity (and your manifest/provider XML android:configure=...), I can point out exactly where the ID gets lost.

Citations:


Validate widget ID before returning RESULT_OK.

The Android AppWidget configuration activity contract requires returning RESULT_OK only with a valid widget ID. If getIntExtra(EXTRA_APPWIDGET_ID, INVALID_APPWIDGET_ID) returns -1, the activity must return RESULT_CANCELED instead. Hosts treat RESULT_OK with an invalid ID as a configuration failure and will not add the widget. Additionally, per best practices, set a default RESULT_CANCELED result early to ensure the widget isn't added if the user backs out.

🔧 Suggested hardening
       "finishHomeWidgetConfigure" -> {
-        if (
-		    activity != null
-              && activity!!.intent?.action?.equals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE) ==
-                  true
-        ) {
-			  activity!!.setResult(Activity.RESULT_OK, Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, activity!!.intent?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID)));
-			  activity!!.finish()
-        }
-        return result.success(null)
+        val act = activity
+        if (
+            act?.intent?.action?.equals(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE) ==
+                true
+        ) {
+          val widgetId =
+              act.intent?.getIntExtra(
+                  AppWidgetManager.EXTRA_APPWIDGET_ID,
+                  AppWidgetManager.INVALID_APPWIDGET_ID,
+              )
+          if (widgetId != null && widgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
+            act.setResult(
+                Activity.RESULT_OK,
+                Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId),
+            )
+            act.finish()
+          } else {
+            act.setResult(Activity.RESULT_CANCELED)
+          }
+        }
+        return result.success(null)
       }
🤖 Prompt for AI Agents
In
`@packages/home_widget/android/src/main/kotlin/es/antonborri/home_widget/HomeWidgetPlugin.kt`
around lines 151 - 160, The finishHomeWidgetConfigure branch should validate the
AppWidget ID and default the activity result to RESULT_CANCELED early; read the
ID from activity!!.intent?.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID), if the ID is INVALID_APPWIDGET_ID then
call activity!!.setResult(Activity.RESULT_CANCELED) (or leave the default) and
finish, otherwise set activity!!.setResult(Activity.RESULT_OK,
Intent().putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId)) and finish;
update the finishHomeWidgetConfigure flow to perform this check before returning
result.success(null).

@jonmarkhall
Copy link
Author

@ABausG just checking in on this PR. If you need any more info, let me know!

@ABausG
Copy link
Owner

ABausG commented Feb 19, 2026

Hi sorry. Super busy both with work and a new (really big, really awesome) home_widget feature!

To understand the full flow better would you be able to add the android implementation and the suggested usage of these new apis to the configurable_widget?

This would help me (but I guess also all users of this) to directly see how they can use it to add configuration to their widgets!

@jonmarkhall
Copy link
Author

Hey @ABausG, unfortunately I'm swamped and I don't know if or when I would get to that since it looks like that project doesn't have an Android widget implementation at all yet. However, I'd be happy to answer questions and/or expand on the documentation if that would be helpful in understanding how to use this feature. We've implemented it in our own app and it seems to work pretty well.

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.

2 participants