reis-mobile 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/.claude-plugin/marketplace.json +24 -0
  2. package/.claude-plugin/plugin.json +12 -0
  3. package/CHANGELOG.md +103 -0
  4. package/LICENSE +21 -0
  5. package/README.md +631 -0
  6. package/THIRD_PARTY_NOTICES.md +105 -0
  7. package/agents/flutter-architect.md +92 -0
  8. package/agents/flutter-performance-engineer.md +103 -0
  9. package/agents/flutter-test-engineer.md +100 -0
  10. package/agents/lead-mobile.md +107 -0
  11. package/agents/mobile-code-reviewer.md +66 -0
  12. package/agents/mobile-staff-engineer.md +85 -0
  13. package/agents/plugin-native-expert.md +97 -0
  14. package/bin/reis-mobile.mjs +256 -0
  15. package/commands/debate.md +135 -0
  16. package/commands/doctor.md +23 -0
  17. package/commands/reis-mobile.md +50 -0
  18. package/commands/review.md +53 -0
  19. package/core/config/language.mjs +83 -0
  20. package/core/context/context-engine.mjs +113 -0
  21. package/core/detection/project-probe.mjs +104 -0
  22. package/core/detection/stack-detector.mjs +204 -0
  23. package/core/diagnostics/doctor.mjs +181 -0
  24. package/core/install/claude-plugin.mjs +76 -0
  25. package/core/paths.mjs +4 -0
  26. package/core/registry/frontmatter.mjs +81 -0
  27. package/core/registry/registry.mjs +140 -0
  28. package/core/router/intent-detector.mjs +75 -0
  29. package/core/router/intents.mjs +91 -0
  30. package/core/router/router.mjs +83 -0
  31. package/core/security/redact.mjs +24 -0
  32. package/package.json +35 -0
  33. package/skills/architecture-feature-first/SKILL.md +116 -0
  34. package/skills/bloc/SKILL.md +354 -0
  35. package/skills/code-review/SKILL.md +89 -0
  36. package/skills/dart-3-updates/SKILL.md +223 -0
  37. package/skills/dart-add-unit-test/SKILL.md +126 -0
  38. package/skills/dart-collect-coverage/SKILL.md +145 -0
  39. package/skills/dart-fix-runtime-errors/SKILL.md +170 -0
  40. package/skills/dart-generate-test-mocks/SKILL.md +159 -0
  41. package/skills/dart-migrate-to-checks-package/SKILL.md +532 -0
  42. package/skills/dart-resolve-package-conflicts/SKILL.md +120 -0
  43. package/skills/dart-run-static-analysis/SKILL.md +108 -0
  44. package/skills/dart-use-pattern-matching/SKILL.md +240 -0
  45. package/skills/dart-use-pattern-matching/examples/json_patterns.dart +85 -0
  46. package/skills/detect-mobile-stack/SKILL.md +33 -0
  47. package/skills/effective-dart/SKILL.md +187 -0
  48. package/skills/firebase-ai/SKILL.md +93 -0
  49. package/skills/firebase-analytics/SKILL.md +129 -0
  50. package/skills/firebase-app-check/SKILL.md +124 -0
  51. package/skills/firebase-auth/SKILL.md +224 -0
  52. package/skills/firebase-cloud-firestore/SKILL.md +165 -0
  53. package/skills/firebase-cloud-functions/SKILL.md +148 -0
  54. package/skills/firebase-crashlytics/SKILL.md +182 -0
  55. package/skills/firebase-data-connect/SKILL.md +80 -0
  56. package/skills/firebase-database/SKILL.md +224 -0
  57. package/skills/firebase-in-app-messaging/SKILL.md +111 -0
  58. package/skills/firebase-messaging/SKILL.md +201 -0
  59. package/skills/firebase-remote-config/SKILL.md +131 -0
  60. package/skills/firebase-storage/SKILL.md +158 -0
  61. package/skills/flutter-add-integration-test/SKILL.md +167 -0
  62. package/skills/flutter-add-widget-preview/SKILL.md +149 -0
  63. package/skills/flutter-add-widget-test/SKILL.md +158 -0
  64. package/skills/flutter-adding-home-screen-widgets/SKILL.md +205 -0
  65. package/skills/flutter-animating-apps/SKILL.md +185 -0
  66. package/skills/flutter-app-architecture/SKILL.md +80 -0
  67. package/skills/flutter-apply-architecture-best-practices/SKILL.md +166 -0
  68. package/skills/flutter-build-responsive-layout/SKILL.md +143 -0
  69. package/skills/flutter-building-forms/SKILL.md +125 -0
  70. package/skills/flutter-building-layouts/SKILL.md +131 -0
  71. package/skills/flutter-building-plugins/SKILL.md +209 -0
  72. package/skills/flutter-caching-data/SKILL.md +171 -0
  73. package/skills/flutter-change-notifier/SKILL.md +138 -0
  74. package/skills/flutter-embedding-native-views/SKILL.md +200 -0
  75. package/skills/flutter-errors/SKILL.md +126 -0
  76. package/skills/flutter-fix-layout-issues/SKILL.md +134 -0
  77. package/skills/flutter-handling-concurrency/SKILL.md +182 -0
  78. package/skills/flutter-implement-json-serialization/SKILL.md +157 -0
  79. package/skills/flutter-improving-accessibility/SKILL.md +113 -0
  80. package/skills/flutter-interoperating-with-native-apis/SKILL.md +130 -0
  81. package/skills/flutter-login-usecase/SKILL.md +38 -0
  82. package/skills/flutter-managing-state/SKILL.md +206 -0
  83. package/skills/flutter-project-audit/SKILL.md +66 -0
  84. package/skills/flutter-reducing-app-size/SKILL.md +103 -0
  85. package/skills/flutter-secure-token-store/SKILL.md +35 -0
  86. package/skills/flutter-setting-up-on-macos/SKILL.md +78 -0
  87. package/skills/flutter-setup-declarative-routing/SKILL.md +259 -0
  88. package/skills/flutter-setup-localization/SKILL.md +214 -0
  89. package/skills/flutter-theming-apps/SKILL.md +163 -0
  90. package/skills/flutter-use-http-package/SKILL.md +178 -0
  91. package/skills/flutter-widget-review/SKILL.md +54 -0
  92. package/skills/flutter-working-with-databases/SKILL.md +203 -0
  93. package/skills/flutterfire-configure/SKILL.md +143 -0
  94. package/skills/mobile-security-audit/SKILL.md +101 -0
  95. package/skills/mockito/SKILL.md +149 -0
  96. package/skills/mocktail/SKILL.md +122 -0
  97. package/skills/patrol-e2e-testing/SKILL.md +224 -0
  98. package/skills/provider/SKILL.md +139 -0
  99. package/skills/riverpod/SKILL.md +246 -0
  100. package/skills/testing/SKILL.md +50 -0
  101. package/stacks/android/stack.json +8 -0
  102. package/stacks/flutter/stack.json +8 -0
  103. package/stacks/ios/stack.json +8 -0
  104. package/stacks/kotlin-multiplatform/stack.json +8 -0
  105. package/stacks/react-native/stack.json +8 -0
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: flutter-add-widget-preview
3
+ description: Adds interactive widget previews to the project using the previews.dart system. Use when creating new UI components or updating existing screens to ensure consistent design and interactive testing.
4
+ metadata:
5
+ model: models/gemini-3.1-pro-preview
6
+ last_modified: Tue, 21 Apr 2026 20:05:23 GMT
7
+ routing: manual
8
+ stacks: [flutter]
9
+ source: https://github.com/flutter/skills
10
+ license: BSD-3-Clause
11
+ ---
12
+ # Previewing Flutter Widgets
13
+
14
+ ## Contents
15
+ - [Preview Guidelines](#preview-guidelines)
16
+ - [Handling Limitations](#handling-limitations)
17
+ - [Workflows](#workflows)
18
+ - [Examples](#examples)
19
+
20
+ ## Preview Guidelines
21
+
22
+ Use the Flutter Widget Previewer to render widgets in real-time, isolated from the full application context.
23
+
24
+ - **Target Elements:** Apply the `@Preview` annotation to top-level functions, static methods within a class, or public widget constructors/factories that have no required arguments and return a `Widget` or `WidgetBuilder`.
25
+ - **Imports:** Always import `package:flutter/widget_previews.dart` to access the preview annotations.
26
+ - **Custom Annotations:** Extend the `Preview` class to create custom annotations that inject common properties (e.g., themes, wrappers) across multiple widgets.
27
+ - **Multiple Configurations:** Apply multiple `@Preview` annotations to a single target to generate multiple preview instances. Alternatively, extend `MultiPreview` to encapsulate common multi-preview configurations.
28
+ - **Runtime Transformations:** Override the `transform()` method in custom `Preview` or `MultiPreview` classes to modify preview configurations dynamically at runtime (e.g., generating names based on dynamic values, which is impossible in a `const` context).
29
+
30
+ ## Handling Limitations
31
+
32
+ Adhere to the following constraints when authoring previewable widgets, as the Widget Previewer runs in a web environment:
33
+
34
+ - **No Native APIs:** Do not use native plugins or APIs from `dart:io` or `dart:ffi`. Widgets with transitive dependencies on `dart:io` or `dart:ffi` will throw exceptions upon invocation. Use conditional imports to mock or bypass these in preview mode.
35
+ - **Asset Paths:** Use package-based paths for assets loaded via `dart:ui` `fromAsset` APIs (e.g., `packages/my_package_name/assets/my_image.png` instead of `assets/my_image.png`).
36
+ - **Public Callbacks:** Ensure all callback arguments provided to preview annotations are public and constant to satisfy code generation requirements.
37
+ - **Constraints:** Apply explicit constraints using the `size` parameter in the `@Preview` annotation if your widget is unconstrained, as the previewer defaults to constraining them to approximately half the viewport.
38
+
39
+ ## Workflows
40
+
41
+ ### Creating a Widget Preview
42
+ Copy and track this checklist when implementing a new widget preview:
43
+
44
+ - [ ] Import `package:flutter/widget_previews.dart`.
45
+ - [ ] Identify a valid target (top-level function, static method, or parameter-less public constructor).
46
+ - [ ] Apply the `@Preview` annotation to the target.
47
+ - [ ] Configure preview parameters (`name`, `group`, `size`, `theme`, `brightness`, etc.) as needed.
48
+ - [ ] If applying the same configuration to multiple widgets, extract the configuration into a custom class extending `Preview`.
49
+
50
+ ### Interacting with Previews
51
+ Follow the appropriate conditional workflow to launch and interact with the Widget Previewer:
52
+
53
+ **If using a supported IDE (Android Studio, IntelliJ, VS Code with Flutter 3.38+):**
54
+ 1. Launch the IDE. The Widget Previewer starts automatically.
55
+ 2. Open the "Flutter Widget Preview" tab in the sidebar.
56
+ 3. Toggle "Filter previews by selected file" at the bottom left if you want to view previews outside the currently active file.
57
+
58
+ **If using the Command Line:**
59
+ 1. Navigate to the Flutter project's root directory.
60
+ 2. Run `flutter widget-preview start`.
61
+ 3. View the automatically opened Chrome environment.
62
+
63
+ **Feedback Loop: Preview Iteration**
64
+ 1. Modify the widget code or preview configuration.
65
+ 2. Observe the automatic update in the Widget Previewer.
66
+ 3. If global state (e.g., static initializers) was modified: Click the global hot restart button at the bottom right.
67
+ 4. If only the local widget state needs resetting: Click the individual hot restart button on the specific preview card.
68
+ 5. Review errors in the IDE/CLI console -> fix -> repeat.
69
+
70
+ ## Examples
71
+
72
+ ### Basic Preview
73
+ ```dart
74
+ import 'package:flutter/widget_previews.dart';
75
+ import 'package:flutter/material.dart';
76
+
77
+ @Preview(name: 'My Sample Text', group: 'Typography')
78
+ Widget mySampleText() {
79
+ return const Text('Hello, World!');
80
+ }
81
+ ```
82
+
83
+ ### Custom Preview with Runtime Transformation
84
+ ```dart
85
+ import 'package:flutter/widget_previews.dart';
86
+ import 'package:flutter/material.dart';
87
+
88
+ final class TransformativePreview extends Preview {
89
+ const TransformativePreview({
90
+ super.name,
91
+ super.group,
92
+ });
93
+
94
+ PreviewThemeData _themeBuilder() {
95
+ return PreviewThemeData(
96
+ materialLight: ThemeData.light(),
97
+ materialDark: ThemeData.dark(),
98
+ );
99
+ }
100
+
101
+ @override
102
+ Preview transform() {
103
+ final originalPreview = super.transform();
104
+ final builder = originalPreview.toBuilder();
105
+
106
+ builder
107
+ ..name = 'Transformed - ${originalPreview.name}'
108
+ ..theme = _themeBuilder;
109
+
110
+ return builder.toPreview();
111
+ }
112
+ }
113
+
114
+ @TransformativePreview(name: 'Custom Themed Button')
115
+ Widget myButton() => const ElevatedButton(onPressed: null, child: Text('Click'));
116
+ ```
117
+
118
+ ### MultiPreview Implementation
119
+ ```dart
120
+ import 'package:flutter/widget_previews.dart';
121
+ import 'package:flutter/material.dart';
122
+
123
+ /// Creates light and dark mode previews automatically.
124
+ final class MultiBrightnessPreview extends MultiPreview {
125
+ const MultiBrightnessPreview({required this.name});
126
+
127
+ final String name;
128
+
129
+ @override
130
+ List<Preview> get previews => const [
131
+ Preview(brightness: Brightness.light),
132
+ Preview(brightness: Brightness.dark),
133
+ ];
134
+
135
+ @override
136
+ List<Preview> transform() {
137
+ final previews = super.transform();
138
+ return previews.map((preview) {
139
+ final builder = preview.toBuilder()
140
+ ..group = 'Brightness'
141
+ ..name = '$name - ${preview.brightness!.name}';
142
+ return builder.toPreview();
143
+ }).toList();
144
+ }
145
+ }
146
+
147
+ @MultiBrightnessPreview(name: 'Primary Card')
148
+ Widget cardPreview() => const Card(child: Padding(padding: EdgeInsets.all(8.0), child: Text('Content')));
149
+ ```
@@ -0,0 +1,158 @@
1
+ ---
2
+ name: flutter-add-widget-test
3
+ description: Implement a component-level test using `WidgetTester` to verify UI rendering and user interactions (tapping, scrolling, entering text). Use when validating that a specific widget displays correct data and responds to events as expected.
4
+ metadata:
5
+ model: models/gemini-3.1-pro-preview
6
+ last_modified: Tue, 21 Apr 2026 21:15:41 GMT
7
+ intents: [test]
8
+ stacks: [flutter]
9
+ source: https://github.com/flutter/skills
10
+ license: BSD-3-Clause
11
+ ---
12
+ # Writing Flutter Widget Tests
13
+
14
+ ## Contents
15
+ - [Setup & Configuration](#setup--configuration)
16
+ - [Core Components](#core-components)
17
+ - [Workflow: Implementing a Widget Test](#workflow-implementing-a-widget-test)
18
+ - [Interaction & State Management](#interaction--state-management)
19
+ - [Examples](#examples)
20
+
21
+ ## Setup & Configuration
22
+
23
+ Ensure the testing environment is properly configured before authoring widget tests.
24
+
25
+ 1. Add the `flutter_test` dependency to the `dev_dependencies` section of `pubspec.yaml`.
26
+ 2. Place all test files in the `test/` directory at the root of the project.
27
+ 3. Suffix all test file names with `_test.dart` (e.g., `widget_test.dart`).
28
+
29
+ ## Core Components
30
+
31
+ Utilize the following `flutter_test` components to interact with and validate the widget tree:
32
+
33
+ * **`WidgetTester`**: The primary interface for building and interacting with widgets in the test environment. Provided automatically by the `testWidgets()` function.
34
+ * **`Finder`**: Locates widgets in the test environment (e.g., `find.text('Submit')`, `find.byType(TextField)`, `find.byKey(Key('submit_btn'))`).
35
+ * **`Matcher`**: Verifies the presence or state of widgets located by a `Finder` (e.g., `findsOneWidget`, `findsNothing`, `findsNWidgets(2)`, `matchesGoldenFile`).
36
+
37
+ ## Workflow: Implementing a Widget Test
38
+
39
+ Copy the following checklist to track progress when implementing a new widget test.
40
+
41
+ ### Task Progress
42
+ - [ ] **Step 1: Define the test.** Use `testWidgets('description', (WidgetTester tester) async { ... })`.
43
+ - [ ] **Step 2: Build the widget.** Call `await tester.pumpWidget(MyWidget())` to render the UI. Wrap the widget in a `MaterialApp` or `Directionality` widget if it requires inherited directional or theme data.
44
+ - [ ] **Step 3: Locate elements.** Instantiate `Finder` objects for the target widgets.
45
+ - [ ] **Step 4: Verify initial state.** Use `expect(finder, matcher)` to validate the initial render.
46
+ - [ ] **Step 5: Simulate interactions.** Execute gestures or inputs (e.g., `await tester.tap(buttonFinder)`).
47
+ - [ ] **Step 6: Rebuild the tree.** Call `await tester.pump()` or `await tester.pumpAndSettle()` to process state changes.
48
+ - [ ] **Step 7: Verify updated state.** Use `expect()` to validate the UI after the interaction.
49
+ - [ ] **Step 8: Run and validate.** Execute `flutter test test/your_test_file_test.dart`.
50
+ - [ ] **Step 9: Feedback Loop.** Review test output -> identify failing matchers -> adjust widget logic or test assertions -> re-run until passing.
51
+
52
+ ## Interaction & State Management
53
+
54
+ Apply the following conditional logic based on the type of interaction or state change being tested:
55
+
56
+ * **If testing static rendering:** Call `await tester.pumpWidget()` once, then immediately run `expect()` assertions.
57
+ * **If testing standard state changes (e.g., button taps):**
58
+ 1. Call `await tester.tap(finder)`.
59
+ 2. Call `await tester.pump()` to trigger a single frame rebuild.
60
+ * **If testing animations, transitions, or asynchronous UI updates:**
61
+ 1. Trigger the action (e.g., `await tester.drag(finder, Offset(500, 0))`).
62
+ 2. Call `await tester.pumpAndSettle()` to repeatedly pump frames until no more frames are scheduled (animation completes).
63
+ * **If testing text input:** Call `await tester.enterText(textFieldFinder, 'Input string')`.
64
+ * **If testing items in a dynamic or long list:** Call `await tester.scrollUntilVisible(itemFinder, 500.0, scrollable: listFinder)` to ensure the target widget is rendered before interacting with it.
65
+
66
+ ## Examples
67
+
68
+ ### High-Fidelity Widget Test Implementation
69
+
70
+ **Target Widget (`lib/todo_list.dart`):**
71
+ ```dart
72
+ import 'package:flutter/material.dart';
73
+
74
+ class TodoList extends StatefulWidget {
75
+ const TodoList({super.key});
76
+
77
+ @override
78
+ State<TodoList> createState() => _TodoListState();
79
+ }
80
+
81
+ class _TodoListState extends State<TodoList> {
82
+ final todos = <String>[];
83
+ final controller = TextEditingController();
84
+
85
+ @override
86
+ Widget build(BuildContext context) {
87
+ return MaterialApp(
88
+ home: Scaffold(
89
+ body: Column(
90
+ children: [
91
+ TextField(controller: controller),
92
+ Expanded(
93
+ child: ListView.builder(
94
+ itemCount: todos.length,
95
+ itemBuilder: (context, index) {
96
+ final todo = todos[index];
97
+ return Dismissible(
98
+ key: Key('$todo$index'),
99
+ onDismissed: (_) => setState(() => todos.removeAt(index)),
100
+ child: ListTile(title: Text(todo)),
101
+ );
102
+ },
103
+ ),
104
+ ),
105
+ ],
106
+ ),
107
+ floatingActionButton: FloatingActionButton(
108
+ onPressed: () {
109
+ setState(() {
110
+ todos.add(controller.text);
111
+ controller.clear();
112
+ });
113
+ },
114
+ child: const Icon(Icons.add),
115
+ ),
116
+ ),
117
+ );
118
+ }
119
+ }
120
+ ```
121
+
122
+ **Test Implementation (`test/todo_list_test.dart`):**
123
+ ```dart
124
+ import 'package:flutter/material.dart';
125
+ import 'package:flutter_test/flutter_test.dart';
126
+ import 'package:my_app/todo_list.dart';
127
+
128
+ void main() {
129
+ testWidgets('Add and remove a todo item', (WidgetTester tester) async {
130
+ // 1. Build the widget
131
+ await tester.pumpWidget(const TodoList());
132
+
133
+ // 2. Verify initial state
134
+ expect(find.byType(ListTile), findsNothing);
135
+
136
+ // 3. Enter text into the TextField
137
+ await tester.enterText(find.byType(TextField), 'Buy groceries');
138
+
139
+ // 4. Tap the add button
140
+ await tester.tap(find.byType(FloatingActionButton));
141
+
142
+ // 5. Rebuild the widget to reflect the new state
143
+ await tester.pump();
144
+
145
+ // 6. Verify the item was added
146
+ expect(find.text('Buy groceries'), findsOneWidget);
147
+
148
+ // 7. Swipe the item to dismiss it
149
+ await tester.drag(find.byType(Dismissible), const Offset(500, 0));
150
+
151
+ // 8. Build the widget until the dismiss animation ends
152
+ await tester.pumpAndSettle();
153
+
154
+ // 9. Verify the item was removed
155
+ expect(find.text('Buy groceries'), findsNothing);
156
+ });
157
+ }
158
+ ```
@@ -0,0 +1,205 @@
1
+ ---
2
+ name: flutter-adding-home-screen-widgets
3
+ description: 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.
4
+ metadata:
5
+ model: models/gemini-3.1-pro-preview
6
+ last_modified: Thu, 12 Mar 2026 22:23:50 GMT
7
+ routing: manual
8
+ stacks: [flutter]
9
+ source: https://github.com/flutter/skills
10
+ license: BSD-3-Clause
11
+ ---
12
+ # Implementing Flutter Home Screen Widgets
13
+
14
+ ## Contents
15
+ - [Architecture & Data Flow](#architecture--data-flow)
16
+ - [Flutter Integration Workflow](#flutter-integration-workflow)
17
+ - [iOS Implementation Workflow](#ios-implementation-workflow)
18
+ - [Android Implementation Workflow](#android-implementation-workflow)
19
+ - [Advanced Techniques](#advanced-techniques)
20
+ - [Examples](#examples)
21
+
22
+ ## Architecture & Data Flow
23
+ 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.
24
+
25
+ - **Data Write:** Flutter app writes key-value pairs or renders images to a shared container.
26
+ - **Trigger:** Flutter app signals the native OS to update the widget.
27
+ - **Data Read:** Native widget wakes up, reads the key-value pairs or images from the shared container, and updates its UI.
28
+
29
+ ## Flutter Integration Workflow
30
+
31
+ Use this checklist to implement the Dart side of the Home Screen Widget integration.
32
+
33
+ - [ ] **Step 1: Initialize the App Group.** Call `HomeWidget.setAppGroupId('<YOUR_APP_GROUP>')` in `initState()` or app startup.
34
+ - [ ] **Step 2: Save Data.** Use `HomeWidget.saveWidgetData<T>('key', value)` to write data to shared storage.
35
+ - [ ] **Step 3: Trigger Update.** Call `HomeWidget.updateWidget(iOSName: 'YourIOSWidget', androidName: 'YourAndroidWidget')` to notify the OS.
36
+ - [ ] **Step 4: Validate.** Run Flutter build -> review console for missing plugin registrations -> fix.
37
+
38
+ ## iOS Implementation Workflow
39
+
40
+ If targeting iOS, implement the widget using Xcode and SwiftUI.
41
+
42
+ - [ ] **Step 1: Create Target.** Open `ios/Runner.xcworkspace` in Xcode. Add a new **Widget Extension** target. Disable "Include Live Activity" and "Include Configuration Intent" unless explicitly required.
43
+ - [ ] **Step 2: Configure App Groups.** Add the **App Groups** capability to *both* the Runner target and the Widget Extension target. Ensure the App Group ID matches the one used in Dart.
44
+ - [ ] **Step 3: Define TimelineEntry.** Create a struct conforming to `TimelineEntry` to hold the data passed from shared storage.
45
+ - [ ] **Step 4: Implement TimelineProvider.**
46
+ - In `getSnapshot` and `getTimeline`, instantiate `UserDefaults(suiteName: "<YOUR_APP_GROUP>")`.
47
+ - Extract values using `userDefaults?.string(forKey: "your_key")`.
48
+ - Return the populated `TimelineEntry`.
49
+ - [ ] **Step 5: Build UI.** Implement the SwiftUI `View` to display the data from the `TimelineEntry`.
50
+ - [ ] **Step 6: Validate.** Run Xcode build for the Widget Extension -> review provisioning/App Group errors -> fix.
51
+
52
+ ## Android Implementation Workflow
53
+
54
+ If targeting Android, implement the widget using Android Studio and XML/Kotlin.
55
+
56
+ - [ ] **Step 1: Create App Widget.** Open the `android` folder in Android Studio. Right-click the app directory -> **New -> Widget -> App Widget**.
57
+ - [ ] **Step 2: Define Layout.** Edit `res/layout/<widget_name>.xml` to define the UI using standard Android XML layouts (e.g., `RelativeLayout`, `TextView`, `ImageView`).
58
+ - [ ] **Step 3: Implement AppWidgetProvider.**
59
+ - Open the generated Kotlin class extending `AppWidgetProvider`.
60
+ - In the `onUpdate` method, retrieve shared data using `HomeWidgetPlugin.getData(context)`.
61
+ - Extract values using `widgetData.getString("your_key", null)`.
62
+ - Update the UI using `RemoteViews` and `setTextViewText` or `setImageViewBitmap`.
63
+ - Call `appWidgetManager.updateAppWidget(appWidgetId, views)`.
64
+ - [ ] **Step 4: Validate.** Run Android build -> review Manifest registration errors -> fix.
65
+
66
+ ## Advanced Techniques
67
+
68
+ ### Rendering Flutter Widgets as Images
69
+ 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.
70
+
71
+ 1. Wrap the target Flutter widget with a `GlobalKey`.
72
+ 2. Call `HomeWidget.renderFlutterWidget()`, passing the widget, a filename, and the key.
73
+ 3. **iOS:** Read the file path from `UserDefaults` and render using `UIImage(contentsOfFile:)` inside a SwiftUI `Image`.
74
+ 4. **Android:** Read the file path from `SharedPreferences`, decode using `BitmapFactory.decodeFile()`, and render using `setImageViewBitmap()`.
75
+
76
+ ### Using Custom Flutter Fonts (iOS Only)
77
+ If utilizing custom fonts defined in Flutter on iOS Home Screen Widgets:
78
+
79
+ 1. Extract the Flutter asset bundle path in Swift.
80
+ 2. Register the font using `CTFontManagerRegisterFontsForURL`.
81
+ 3. Apply the font in SwiftUI using `Font.custom()`.
82
+
83
+ ## Examples
84
+
85
+ ### Example: Flutter Data Update
86
+ ```dart
87
+ import 'package:home_widget/home_widget.dart';
88
+
89
+ const String appGroupId = 'group.com.example.app';
90
+ const String iOSWidgetName = 'NewsWidgets';
91
+ const String androidWidgetName = 'NewsWidget';
92
+
93
+ Future<void> updateWidgetData(String title, String description) async {
94
+ await HomeWidget.setAppGroupId(appGroupId);
95
+ await HomeWidget.saveWidgetData<String>('headline_title', title);
96
+ await HomeWidget.saveWidgetData<String>('headline_description', description);
97
+ await HomeWidget.updateWidget(
98
+ iOSName: iOSWidgetName,
99
+ androidName: androidWidgetName,
100
+ );
101
+ }
102
+ ```
103
+
104
+ ### Example: iOS SwiftUI Provider & View
105
+ ```swift
106
+ import WidgetKit
107
+ import SwiftUI
108
+
109
+ struct NewsArticleEntry: TimelineEntry {
110
+ let date: Date
111
+ let title: String
112
+ let description: String
113
+ }
114
+
115
+ struct Provider: TimelineProvider {
116
+ func placeholder(in context: Context) -> NewsArticleEntry {
117
+ NewsArticleEntry(date: Date(), title: "Loading...", description: "Loading...")
118
+ }
119
+
120
+ func getSnapshot(in context: Context, completion: @escaping (NewsArticleEntry) -> ()) {
121
+ let userDefaults = UserDefaults(suiteName: "group.com.example.app")
122
+ let title = userDefaults?.string(forKey: "headline_title") ?? "No Title"
123
+ let description = userDefaults?.string(forKey: "headline_description") ?? "No Description"
124
+
125
+ let entry = NewsArticleEntry(date: Date(), title: title, description: description)
126
+ completion(entry)
127
+ }
128
+
129
+ func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
130
+ getSnapshot(in: context) { (entry) in
131
+ let timeline = Timeline(entries: [entry], policy: .atEnd)
132
+ completion(timeline)
133
+ }
134
+ }
135
+ }
136
+
137
+ struct NewsWidgetsEntryView : View {
138
+ var entry: Provider.Entry
139
+
140
+ var body: some View {
141
+ VStack(alignment: .leading) {
142
+ Text(entry.title).font(.headline)
143
+ Text(entry.description).font(.subheadline)
144
+ }
145
+ }
146
+ }
147
+ ```
148
+
149
+ ### Example: Android Kotlin Provider
150
+ ```kotlin
151
+ package com.example.app.widgets
152
+
153
+ import android.appwidget.AppWidgetManager
154
+ import android.appwidget.AppWidgetProvider
155
+ import android.content.Context
156
+ import android.widget.RemoteViews
157
+ import es.antonborri.home_widget.HomeWidgetPlugin
158
+ import com.example.app.R
159
+
160
+ class NewsWidget : AppWidgetProvider() {
161
+ override fun onUpdate(
162
+ context: Context,
163
+ appWidgetManager: AppWidgetManager,
164
+ appWidgetIds: IntArray,
165
+ ) {
166
+ for (appWidgetId in appWidgetIds) {
167
+ val widgetData = HomeWidgetPlugin.getData(context)
168
+ val views = RemoteViews(context.packageName, R.layout.news_widget).apply {
169
+ val title = widgetData.getString("headline_title", "No Title")
170
+ setTextViewText(R.id.headline_title, title)
171
+
172
+ val description = widgetData.getString("headline_description", "No Description")
173
+ setTextViewText(R.id.headline_description, description)
174
+ }
175
+ appWidgetManager.updateAppWidget(appWidgetId, views)
176
+ }
177
+ }
178
+ }
179
+ ```
180
+
181
+ <details>
182
+ <summary>Example: iOS Custom Font Registration Helper</summary>
183
+
184
+ ```swift
185
+ // Add this to your SwiftUI View struct
186
+ var bundle: URL {
187
+ let bundle = Bundle.main
188
+ if bundle.bundleURL.pathExtension == "appex" {
189
+ var url = bundle.bundleURL.deletingLastPathComponent().deletingLastPathComponent()
190
+ url.append(component: "Frameworks/App.framework/flutter_assets")
191
+ return url
192
+ }
193
+ return bundle.bundleURL
194
+ }
195
+
196
+ init(entry: Provider.Entry) {
197
+ self.entry = entry
198
+ CTFontManagerRegisterFontsForURL(
199
+ bundle.appending(path: "/fonts/YourCustomFont.ttf") as CFURL,
200
+ CTFontManagerScope.process,
201
+ nil
202
+ )
203
+ }
204
+ ```
205
+ </details>