Skip to content

Fix Android stutter: async updateWidget using compute#403

Open
Aroojsarfraz90 wants to merge 1 commit intoABausG:mainfrom
Aroojsarfraz90:fix-android-stutter
Open

Fix Android stutter: async updateWidget using compute#403
Aroojsarfraz90 wants to merge 1 commit intoABausG:mainfrom
Aroojsarfraz90:fix-android-stutter

Conversation

@Aroojsarfraz90
Copy link

Description

Replace this text.

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

docs-page bot commented Feb 26, 2026

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

docs.page/abausg/home_widget~403

Documentation is deployed and generated using docs.page.

@coderabbitai
Copy link

coderabbitai bot commented Feb 26, 2026

Walkthrough

The updateWidget method is refactored to execute asynchronously using compute for argument preparation before channel invocation. The renderFlutterWidget method signature is expanded with optional parameters for logical size and pixel ratio to control rendering behavior.

Changes

Cohort / File(s) Summary
Home Widget Updates
packages/home_widget/lib/src/home_widget.dart
updateWidget converted to async with compute-wrapped argument preparation via new _prepareUpdateArgs helper. renderFlutterWidget signature expanded with logicalSize (default 200x200) and pixelRatio optional parameters.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description contains only placeholder text and no actual description of the changes, motivation, or affected APIs, failing to meet the required template structure. Replace placeholder text with a meaningful description of the changes, explain the Android stutter issue being fixed, describe how async/compute addresses it, and complete all relevant checklist items.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'Fix Android stutter: async updateWidget using compute' is specific and clearly describes the main changes in the changeset—making updateWidget asynchronous and using compute.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

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

Warning

Review ran into problems

🔥 Problems

Git: Failed to clone repository. Please run the @coderabbitai full review command to re-trigger a full review. If the issue persists, set path_filters to include or exclude specific files.

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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.

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: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/home_widget/lib/src/home_widget.dart`:
- Line 196: Add an explicit null check for the byteData returned from
image.toByteData() before using the forced unwrap; in the method where you call
await file.writeAsBytes(byteData!.buffer.asUint8List()), verify byteData != null
and handle the null case (throw a clear exception or return an error) so you
don't dereference a null value—use the existing byteData, file, and image
variables (or the containing function) to locate and implement this check and
provide a descriptive error message if encoding fails.
- Around line 27-52: The use of compute in updateWidget adds isolate overhead
for a trivial map copy; change updateWidget to build the args synchronously
(either inline or by calling _prepareUpdateArgs directly) and then call
_channel.invokeMethod('updateWidget', args) without compute, and remove
_prepareUpdateArgs if it becomes unused; additionally, check any
native/platform-channel code if stutter persists.
- Around line 198-201: The platform channel call to save widget data is
fire-and-forget; update the caller (likely renderFlutterWidget) to await the
result of _channel.invokeMethod<bool>('saveWidgetData', { 'id': key, 'data':
path }) so persistence completes before returning: make the function async (or
return a Future), await the invokeMethod, and handle failures (check the
returned bool or catch PlatformException) to log or propagate errors instead of
allowing silent failures.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fa7453a and d6417ad.

📒 Files selected for processing (1)
  • packages/home_widget/lib/src/home_widget.dart

Comment on lines +27 to 52
/// --- Updated to async + compute to avoid stutter on Android ---
static Future<bool?> updateWidget({
String? name,
String? androidName,
String? iOSName,
String? qualifiedAndroidName,
}) {
return _channel.invokeMethod('updateWidget', {
}) async {
final Map<String, dynamic> args = await compute(_prepareUpdateArgs, {
'name': name,
'android': androidName,
'ios': iOSName,
'qualifiedAndroidName': qualifiedAndroidName,
});

return _channel.invokeMethod('updateWidget', args);
}

static Map<String, dynamic> _prepareUpdateArgs(Map<String, dynamic> input) {
// Heavy computation can go here in future if needed
return {
'name': input['name'],
'android': input['android'],
'ios': input['ios'],
'qualifiedAndroidName': input['qualifiedAndroidName'],
};
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

compute overhead likely exceeds benefit for trivial map copying.

compute spawns a new isolate with significant overhead (isolate creation, serialization/deserialization of arguments and results). The _prepareUpdateArgs function only copies a map with no actual computation, so this adds latency rather than reducing it.

If Android stutter originates from the platform channel call itself, this change won't help since _channel.invokeMethod still runs on the main isolate. Consider:

  1. Investigating the actual source of stutter (likely native side or platform channel)
  2. Removing the compute wrapper if there's no CPU-intensive work to offload
  3. If heavy computation is needed in the future, add compute at that time
Suggested simplification (revert to direct call)
-  /// --- Updated to async + compute to avoid stutter on Android ---
   static Future<bool?> updateWidget({
     String? name,
     String? androidName,
     String? iOSName,
     String? qualifiedAndroidName,
-  }) async {
-    final Map<String, dynamic> args = await compute(_prepareUpdateArgs, {
+  }) {
+    return _channel.invokeMethod<bool>('updateWidget', {
       'name': name,
       'android': androidName,
       'ios': iOSName,
       'qualifiedAndroidName': qualifiedAndroidName,
     });
-
-    return _channel.invokeMethod('updateWidget', args);
   }
-
-  static Map<String, dynamic> _prepareUpdateArgs(Map<String, dynamic> input) {
-    // Heavy computation can go here in future if needed
-    return {
-      'name': input['name'],
-      'android': input['android'],
-      'ios': input['ios'],
-      'qualifiedAndroidName': input['qualifiedAndroidName'],
-    };
-  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// --- Updated to async + compute to avoid stutter on Android ---
static Future<bool?> updateWidget({
String? name,
String? androidName,
String? iOSName,
String? qualifiedAndroidName,
}) {
return _channel.invokeMethod('updateWidget', {
}) async {
final Map<String, dynamic> args = await compute(_prepareUpdateArgs, {
'name': name,
'android': androidName,
'ios': iOSName,
'qualifiedAndroidName': qualifiedAndroidName,
});
return _channel.invokeMethod('updateWidget', args);
}
static Map<String, dynamic> _prepareUpdateArgs(Map<String, dynamic> input) {
// Heavy computation can go here in future if needed
return {
'name': input['name'],
'android': input['android'],
'ios': input['ios'],
'qualifiedAndroidName': input['qualifiedAndroidName'],
};
}
static Future<bool?> updateWidget({
String? name,
String? androidName,
String? iOSName,
String? qualifiedAndroidName,
}) {
return _channel.invokeMethod<bool>('updateWidget', {
'name': name,
'android': androidName,
'ios': iOSName,
'qualifiedAndroidName': qualifiedAndroidName,
});
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/home_widget/lib/src/home_widget.dart` around lines 27 - 52, The use
of compute in updateWidget adds isolate overhead for a trivial map copy; change
updateWidget to build the args synchronously (either inline or by calling
_prepareUpdateArgs directly) and then call _channel.invokeMethod('updateWidget',
args) without compute, and remove _prepareUpdateArgs if it becomes unused;
additionally, check any native/platform-channel code if stutter persists.

if (!await file.exists()) {
await file.create(recursive: true);
}
await file.writeAsBytes(byteData!.buffer.asUint8List());
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

Add explicit null check for byteData before force unwrap.

image.toByteData() can return null if encoding fails. The force unwrap will throw, but adding an explicit check provides a clearer error message.

Suggested fix
+      if (byteData == null) {
+        throw Exception('Failed to encode image to PNG');
+      }
-      await file.writeAsBytes(byteData!.buffer.asUint8List());
+      await file.writeAsBytes(byteData.buffer.asUint8List());
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
await file.writeAsBytes(byteData!.buffer.asUint8List());
if (byteData == null) {
throw Exception('Failed to encode image to PNG');
}
await file.writeAsBytes(byteData.buffer.asUint8List());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/home_widget/lib/src/home_widget.dart` at line 196, Add an explicit
null check for the byteData returned from image.toByteData() before using the
forced unwrap; in the method where you call await
file.writeAsBytes(byteData!.buffer.asUint8List()), verify byteData != null and
handle the null case (throw a clear exception or return an error) so you don't
dereference a null value—use the existing byteData, file, and image variables
(or the containing function) to locate and implement this check and provide a
descriptive error message if encoding fails.

Comment on lines +198 to +201
_channel.invokeMethod<bool>('saveWidgetData', {
'id': key,
'data': path,
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Platform channel call is not awaited, causing potential race condition.

The invokeMethod call is fire-and-forget. Callers may expect that widget data is persisted when renderFlutterWidget completes, but without await, the save operation may still be in progress (or fail silently) when the function returns.

Suggested fix
-      _channel.invokeMethod<bool>('saveWidgetData', {
+      await _channel.invokeMethod<bool>('saveWidgetData', {
         'id': key,
         'data': path,
       });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
_channel.invokeMethod<bool>('saveWidgetData', {
'id': key,
'data': path,
});
await _channel.invokeMethod<bool>('saveWidgetData', {
'id': key,
'data': path,
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/home_widget/lib/src/home_widget.dart` around lines 198 - 201, The
platform channel call to save widget data is fire-and-forget; update the caller
(likely renderFlutterWidget) to await the result of
_channel.invokeMethod<bool>('saveWidgetData', { 'id': key, 'data': path }) so
persistence completes before returning: make the function async (or return a
Future), await the invokeMethod, and handle failures (check the returned bool or
catch PlatformException) to log or propagate errors instead of allowing silent
failures.

Copy link
Owner

@ABausG ABausG left a comment

Choose a reason for hiding this comment

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

Please provide more Info on why you would want this change. Also I don't see any optimization in moving the map creation into a dedicated compute.

Plus please do not delete documentation

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