diff --git a/README.md b/README.md index 06a30a06..1416a39d 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,7 @@ npx skills@1.5.17 update | [flutter-build-responsive-layout](skills/flutter-build-responsive-layout/SKILL.md) | Use `LayoutBuilder`, `MediaQuery`, or `Expanded/Flexible` to create a layout that adapts to different screen sizes. Use when you need the UI to look good on both mobile and tablet/desktop form factors. | Make the home screen responsive so it displays a grid on tablets and a list on phones | | [flutter-fix-layout-issues](skills/flutter-fix-layout-issues/SKILL.md) | Fixes Flutter layout errors (overflows, unbounded constraints) using Dart and Flutter MCP tools. Use when addressing "RenderFlex overflowed", "Vertical viewport was given unbounded height", or similar layout issues. | Fix the overflow error on the profile page when the keyboard is visible | | [flutter-implement-json-serialization](skills/flutter-implement-json-serialization/SKILL.md) | Create model classes with `fromJson` and `toJson` methods using `dart:convert`. Use when manually mapping JSON keys to class properties for simple data structures. | Implement JSON serialization for the User model class | +| [flutter-ios-uiscene-root-vc-migration](skills/flutter-ios-uiscene-root-vc-migration/SKILL.md) | Migrates iOS Flutter plugins from fetching the root view controller via UIApplication.shared.delegate.window.rootViewController to using registrar.viewController. | Migrate the iOS plugin from accessing rootViewController via UIApplication delegate to using registrar.viewController | | [flutter-setup-declarative-routing](skills/flutter-setup-declarative-routing/SKILL.md) | Configure `MaterialApp.router` using a package like `go_router` for advanced URL-based navigation. Use when developing web applications or mobile apps that require specific deep linking and browser history support. | Set up GoRouter with paths for home, details, and settings | | [flutter-setup-localization](skills/flutter-setup-localization/SKILL.md) | Add `flutter_localizations` and `intl` dependencies, enable "generate true" in `pubspec.yaml`, and create an `l10n.yaml` configuration file. Use when initializing localization support for a new Flutter project. | Setup localization and add English and Spanish translations | | [flutter-use-http-package](skills/flutter-use-http-package/SKILL.md) | Use the `http` package to execute GET, POST, PUT, or DELETE requests. Use when you need to fetch from or send data to a REST API. | Use the http package to fetch the list of products from the API | diff --git a/resources/flutter_skills.yaml b/resources/flutter_skills.yaml index f055e473..97e6d075 100644 --- a/resources/flutter_skills.yaml +++ b/resources/flutter_skills.yaml @@ -3,8 +3,9 @@ # BSD-style license that can be found in the LICENSE file. # This is the configuration file for the skill generator. -# To generate skills, use the following command (from the `tool` directory): -# dart run skills generate-skill --config ../resources/flutter_skills.yaml --output ../skills +# To generate skills, use the following command (from repo's root directory): +# dart run skills generate-skill --skill [skill-name] +# Make sure you have already set up GEMINI_API_KEY in your path. - name: flutter-add-integration-test description: Configures Flutter Driver for app interaction and converts MCP actions into permanent integration tests. Use when adding integration @@ -431,4 +432,61 @@ - https://docs.flutter.dev/cookbook/networking/send-data - https://docs.flutter.dev/cookbook/networking/update-data - https://docs.flutter.dev/data-and-backend/serialization - - https://docs.flutter.dev/data-and-backend/serialization/json \ No newline at end of file + - https://docs.flutter.dev/data-and-backend/serialization/json +- name: flutter-ios-adopt-registrar-view-controller-api + description: Migrates iOS Flutter plugins from fetching the root view controller + via `UIApplication.shared.delegate.window.rootViewController` to using + `registrar.viewController` API. + examplePrompt: "Adopt registrar.viewController API in iOS" + instructions: | + 1. **Verify Scope:** + * This skill applies exclusively to a Flutter plugin's iOS implementation. Do not apply it to application code, such as standalone Flutter apps or example projects (e.g., `example/ios`). + + 2. **Identify Legacy Root View Controller Pattern:** + * Locate usages of direct app-delegate window root view controller access: + * Swift: `let vc = UIApplication.shared.delegate?.window??.rootViewController` + * Objective-C: `UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController;` + + 3. **Migrate Plugin Implementation to Use Registrar:** + * Update the plugin class to save the `FlutterPluginRegistrar` during registration and retrieve the active view controller via `registrar.viewController`. + * **Swift:** + ```swift + public class FooPlugin: NSObject, FlutterPlugin { + private let registrar: FlutterPluginRegistrar + + public static func register(with registrar: FlutterPluginRegistrar) { + let instance = FooPlugin(registrar: registrar) + // ... + } + + func accessVC() { + registrar.viewController?.present(...) + } + } + ``` + * **Objective-C:** + Declare a private property in a class extension in the implementation (`.m`) file: + ```objc + @interface FooPlugin () + @property(nonatomic) NSObject *registrar; + @end + + @implementation FooPlugin + + + (void)registerWithRegistrar:(NSObject *)registrar { + FooPlugin *plugin = [[FooPlugin alloc] initWithRegistrar:registrar]; + // ... + } + + - (void)accessVC { + [self.registrar.viewController presentViewController:... animated:YES completion:nil]; + } + + @end + ``` + + 4. **Verification & Output Notice:** + * After completing the code modifications: + * Include a explicit disclaimer stating that the migration was AI-generated and should be verified. + resources: + - https://api.flutter.dev/ios-embedder/protocol_flutter_plugin_registrar-p.html#a732bebcd9fcfe9f9f74ea695848d7ea7 \ No newline at end of file diff --git a/skills/flutter-ios-adopt-registrar-view-controller-api/SKILL.md b/skills/flutter-ios-adopt-registrar-view-controller-api/SKILL.md new file mode 100644 index 00000000..a2bb87a9 --- /dev/null +++ b/skills/flutter-ios-adopt-registrar-view-controller-api/SKILL.md @@ -0,0 +1,142 @@ +--- +name: flutter-ios-adopt-registrar-view-controller-api +description: Migrates iOS Flutter plugins from fetching the root view controller via `UIApplication.shared.delegate.window.rootViewController` to using `registrar.viewController` API. +metadata: + model: models/gemini-3.1-pro-preview + last_modified: Fri, 24 Jul 2026 03:57:06 GMT +--- +# Migrating Flutter iOS Plugins to registrar.viewController API + +## Contents +- [Scope and Applicability](#scope-and-applicability) +- [Migration Workflow](#migration-workflow) +- [Implementation Examples](#implementation-examples) +- [Verification and Feedback Loop](#verification-and-feedback-loop) + +## Scope and Applicability + +Apply this skill **exclusively** to a Flutter plugin's iOS implementation (typically found in `ios/Classes/`). + +**Conditional Logic for Scope:** +- **If modifying a Flutter plugin's iOS implementation:** Proceed with the migration workflow below. +- **If modifying application code (e.g., standalone Flutter apps, `ios/Runner/`, or `example/ios/`):** Abort this workflow. Do not apply these patterns to application-level code. +- **If no legacy root view controller access patterns are found:** Abort this workflow. Do not modify the code. + +## Migration Workflow + +Use the following checklist to track progress when migrating a plugin from legacy app-delegate window access to the modern registrar pattern. + +### Task Progress +- [ ] **Step 1: Identify Legacy Root View Controller Pattern** + - Scan the plugin's iOS source files for direct app-delegate window access. + - Swift target: `UIApplication.shared.delegate?.window??.rootViewController` + - Objective-C target: `[UIApplication sharedApplication].delegate.window.rootViewController` +- [ ] **Step 2: Update Plugin Registration** + - Modify the plugin class to accept and retain the `FlutterPluginRegistrar` instance during the `register(with:)` / `registerWithRegistrar:` phase. +- [ ] **Step 3: Refactor View Controller Access** + - Replace all legacy root view controller calls with `registrar.viewController`. +- [ ] **Step 4: Print Notice** + - Print a notice that the migration was AI-generated and requires manual verification. + +## Implementation Examples + +Implement the migration as shown in the following language-specific examples. The code may not be exactly the same. Do your best to adapt to the existing code structure. + +### Swift Implementation + +**Legacy (Anti-pattern):** +```swift +public class FooPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { + let instance = FooPlugin() + registrar.addMethodCallDelegate(instance, channel: channel) + } + + func accessVC() { + let vc = UIApplication.shared.delegate?.window??.rootViewController + vc?.present(...) + } +} +``` + +**Modern (Preferred):** +```swift +public class FooPlugin: NSObject, FlutterPlugin { + private let registrar: FlutterPluginRegistrar + + // 1. Initialize with registrar + init(registrar: FlutterPluginRegistrar) { + self.registrar = registrar + } + + public static func register(with registrar: FlutterPluginRegistrar) { + // 2. Pass registrar to instance + let instance = FooPlugin(registrar: registrar) + let channel = FlutterMethodChannel(name: "foo_plugin", binaryMessenger: registrar.messenger()) + registrar.addMethodCallDelegate(instance, channel: channel) + } + + func accessVC() { + // 3. Access view controller via registrar + registrar.viewController?.present(...) + } +} +``` + +### Objective-C Implementation + +**Legacy (Anti-pattern):** +```objc +@implementation FooPlugin ++ (void)registerWithRegistrar:(NSObject *)registrar { + FooPlugin *plugin = [[FooPlugin alloc] init]; + // ... +} + +- (void)accessVC { + UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController; + [vc presentViewController:... animated:YES completion:nil]; +} +@end +``` + +**Modern (Preferred):** +```objc +// 1. Declare a private property in the class extension (.m file) +@interface FooPlugin () +@property(nonatomic) NSObject *registrar; +@end + +@implementation FooPlugin + +// 2. Initialize with registrar +- (instancetype)initWithRegistrar:(NSObject *)registrar { + self = [super init]; + if (self) { + _registrar = registrar; + } + return self; +} + ++ (void)registerWithRegistrar:(NSObject *)registrar { + // 3. Pass registrar to instance + FooPlugin *plugin = [[FooPlugin alloc] initWithRegistrar:registrar]; + FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"foo_plugin" + binaryMessenger:[registrar messenger]]; + [registrar addMethodCallDelegate:plugin channel:channel]; +} + +- (void)accessVC { + // 4. Access view controller via registrar + [self.registrar.viewController presentViewController:... animated:YES completion:nil]; +} + +@end +``` + +## Print Notice + +After completing the code modifications, print the following notice: + +> **⚠️ AI Notice:** +> The code modifications generated by this migration workflow are AI-generated. You must manually verify that the migration is correct. \ No newline at end of file