From da84977c7164154c717fb1600efd8ab4daf28f89 Mon Sep 17 00:00:00 2001 From: Huan Lin Date: Tue, 21 Jul 2026 14:27:25 -0700 Subject: [PATCH 1/2] [ios]add flutter-ios-uiscene-root-vc-migration --- README.md | 1 + resources/flutter_skills.yaml | 59 ++++++++- .../SKILL.md | 112 ++++++++++++++++++ 3 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 skills/flutter-ios-uiscene-root-vc-migration/SKILL.md 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..88a0f16d 100644 --- a/resources/flutter_skills.yaml +++ b/resources/flutter_skills.yaml @@ -4,7 +4,8 @@ # 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 +# 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,58 @@ - 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-uiscene-root-vc-migration + description: Migrates iOS Flutter plugins from fetching the root view controller + via UIApplication.shared.delegate.window.rootViewController to using + registrar.viewController. + examplePrompt: "Migrate the iOS plugin from accessing rootViewController via UIApplication delegate to using registrar.viewController" + instructions: | + 1. **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;` + + 2. **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 + ``` + + 3. **Verification & Output Notice:** + * Include an explicit disclaimer stating that the migration was AI-generated and should be verified. + * Advise the user to build and run affected iOS targets to confirm the migration compiles and operates correctly. + resources: + - https://docs.flutter.dev/release/breaking-changes/uiscenedelegate \ No newline at end of file diff --git a/skills/flutter-ios-uiscene-root-vc-migration/SKILL.md b/skills/flutter-ios-uiscene-root-vc-migration/SKILL.md new file mode 100644 index 00000000..f00e0a80 --- /dev/null +++ b/skills/flutter-ios-uiscene-root-vc-migration/SKILL.md @@ -0,0 +1,112 @@ +--- +name: flutter-ios-uiscene-root-vc-migration +description: Migrates iOS Flutter plugins from fetching the root view controller via UIApplication.shared.delegate.window.rootViewController to using registrar.viewController. +metadata: + model: models/gemini-3.1-pro-preview + last_modified: Tue, 21 Jul 2026 21:20:01 GMT +--- +# Migrating Flutter iOS Plugins to UIScene + +## Contents +- [Legacy Root View Controller Pattern](#legacy-root-view-controller-pattern) +- [Registrar-Based View Controller Access](#registrar-based-view-controller-access) +- [Migrating Plugin Implementations](#migrating-plugin-implementations) +- [Examples](#examples) +- [Verification & Output Notice](#verification--output-notice) + +## Legacy Root View Controller Pattern + +Identify usages of direct app-delegate window root view controller access. With the adoption of the `UIScene` lifecycle in iOS, `UIApplication.shared.delegate.window` evaluates to `nil`. + +Locate and replace the following legacy patterns in the codebase: + +**Swift:** +```swift +let vc = UIApplication.shared.delegate?.window??.rootViewController +``` + +**Objective-C:** +```objc +UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController; +``` + +## Registrar-Based View Controller Access + +Update the plugin class to save the `FlutterPluginRegistrar` during registration. Retrieve the active view controller via `registrar.viewController` instead of querying the application delegate. + +If editing existing content, ensure the registrar is passed into the plugin's initializer and stored as a private property. + +## Migrating Plugin Implementations + +Follow this workflow to migrate a Flutter iOS plugin to support the `UIScene` lifecycle. + +**Task Progress Checklist:** +- [ ] 1. Scan the plugin's iOS source files (`.swift` or `.m`) for legacy `UIApplication` window access. +- [ ] 2. Modify the plugin's registration method to inject the `FlutterPluginRegistrar` into the plugin instance. +- [ ] 3. Store the registrar as a private property on the plugin class. +- [ ] 4. Replace all legacy root view controller access with `registrar.viewController`. +- [ ] 5. Run validator -> review errors -> fix (Build the iOS target to ensure no syntax or type errors were introduced). + +## Examples + +Implement the registrar pattern using the following language-specific examples. + +### Swift Implementation + +```swift +public class FooPlugin: NSObject, FlutterPlugin { + private let registrar: FlutterPluginRegistrar + + init(registrar: FlutterPluginRegistrar) { + self.registrar = registrar + } + + public static func register(with registrar: FlutterPluginRegistrar) { + let instance = FooPlugin(registrar: registrar) + registrar.addMethodCallDelegate(instance, channel: /* your channel */) + } + + func accessVC() { + // Access the view controller via the stored registrar + registrar.viewController?.present(/* your view controller */, animated: true, completion: nil) + } +} +``` + +### Objective-C Implementation + +Declare a private property in a class extension within the implementation (`.m`) file. + +```objc +@interface FooPlugin () +@property(nonatomic) NSObject *registrar; +@end + +@implementation FooPlugin + +- (instancetype)initWithRegistrar:(NSObject *)registrar { + self = [super init]; + if (self) { + _registrar = registrar; + } + return self; +} + ++ (void)registerWithRegistrar:(NSObject *)registrar { + FooPlugin *plugin = [[FooPlugin alloc] initWithRegistrar:registrar]; + [registrar addMethodCallDelegate:plugin channel:/* your channel */]; +} + +- (void)accessVC { + // Access the view controller via the stored registrar + [self.registrar.viewController presentViewController:/* your view controller */ animated:YES completion:nil]; +} + +@end +``` + +## Verification & Output Notice + +**Disclaimer:** This migration was AI-generated and must be verified for accuracy and completeness. + +Build and run the affected iOS targets (`flutter build ios` or `flutter run`) to confirm the migration compiles successfully and operates correctly within the new `UIScene` lifecycle. Verify that any presented view controllers appear on the active screen as expected. From 903e66506c5dd2412ae7d720c430b8982f843133 Mon Sep 17 00:00:00 2001 From: Huan Lin Date: Thu, 23 Jul 2026 14:01:08 -0700 Subject: [PATCH 2/2] remove reference to UIScene --- resources/flutter_skills.yaml | 25 +-- .../SKILL.md | 142 ++++++++++++++++++ .../SKILL.md | 112 -------------- 3 files changed, 156 insertions(+), 123 deletions(-) create mode 100644 skills/flutter-ios-adopt-registrar-view-controller-api/SKILL.md delete mode 100644 skills/flutter-ios-uiscene-root-vc-migration/SKILL.md diff --git a/resources/flutter_skills.yaml b/resources/flutter_skills.yaml index 88a0f16d..97e6d075 100644 --- a/resources/flutter_skills.yaml +++ b/resources/flutter_skills.yaml @@ -3,7 +3,7 @@ # 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): +# 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 @@ -433,18 +433,21 @@ - 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 -- name: flutter-ios-uiscene-root-vc-migration +- 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. - examplePrompt: "Migrate the iOS plugin from accessing rootViewController via UIApplication delegate to using registrar.viewController" + via `UIApplication.shared.delegate.window.rootViewController` to using + `registrar.viewController` API. + examplePrompt: "Adopt registrar.viewController API in iOS" instructions: | - 1. **Identify Legacy Root View Controller Pattern:** + 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;` - 2. **Migrate Plugin Implementation to Use Registrar:** + 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 @@ -482,8 +485,8 @@ @end ``` - 3. **Verification & Output Notice:** - * Include an explicit disclaimer stating that the migration was AI-generated and should be verified. - * Advise the user to build and run affected iOS targets to confirm the migration compiles and operates correctly. + 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://docs.flutter.dev/release/breaking-changes/uiscenedelegate \ No newline at end of file + - 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 diff --git a/skills/flutter-ios-uiscene-root-vc-migration/SKILL.md b/skills/flutter-ios-uiscene-root-vc-migration/SKILL.md deleted file mode 100644 index f00e0a80..00000000 --- a/skills/flutter-ios-uiscene-root-vc-migration/SKILL.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -name: flutter-ios-uiscene-root-vc-migration -description: Migrates iOS Flutter plugins from fetching the root view controller via UIApplication.shared.delegate.window.rootViewController to using registrar.viewController. -metadata: - model: models/gemini-3.1-pro-preview - last_modified: Tue, 21 Jul 2026 21:20:01 GMT ---- -# Migrating Flutter iOS Plugins to UIScene - -## Contents -- [Legacy Root View Controller Pattern](#legacy-root-view-controller-pattern) -- [Registrar-Based View Controller Access](#registrar-based-view-controller-access) -- [Migrating Plugin Implementations](#migrating-plugin-implementations) -- [Examples](#examples) -- [Verification & Output Notice](#verification--output-notice) - -## Legacy Root View Controller Pattern - -Identify usages of direct app-delegate window root view controller access. With the adoption of the `UIScene` lifecycle in iOS, `UIApplication.shared.delegate.window` evaluates to `nil`. - -Locate and replace the following legacy patterns in the codebase: - -**Swift:** -```swift -let vc = UIApplication.shared.delegate?.window??.rootViewController -``` - -**Objective-C:** -```objc -UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController; -``` - -## Registrar-Based View Controller Access - -Update the plugin class to save the `FlutterPluginRegistrar` during registration. Retrieve the active view controller via `registrar.viewController` instead of querying the application delegate. - -If editing existing content, ensure the registrar is passed into the plugin's initializer and stored as a private property. - -## Migrating Plugin Implementations - -Follow this workflow to migrate a Flutter iOS plugin to support the `UIScene` lifecycle. - -**Task Progress Checklist:** -- [ ] 1. Scan the plugin's iOS source files (`.swift` or `.m`) for legacy `UIApplication` window access. -- [ ] 2. Modify the plugin's registration method to inject the `FlutterPluginRegistrar` into the plugin instance. -- [ ] 3. Store the registrar as a private property on the plugin class. -- [ ] 4. Replace all legacy root view controller access with `registrar.viewController`. -- [ ] 5. Run validator -> review errors -> fix (Build the iOS target to ensure no syntax or type errors were introduced). - -## Examples - -Implement the registrar pattern using the following language-specific examples. - -### Swift Implementation - -```swift -public class FooPlugin: NSObject, FlutterPlugin { - private let registrar: FlutterPluginRegistrar - - init(registrar: FlutterPluginRegistrar) { - self.registrar = registrar - } - - public static func register(with registrar: FlutterPluginRegistrar) { - let instance = FooPlugin(registrar: registrar) - registrar.addMethodCallDelegate(instance, channel: /* your channel */) - } - - func accessVC() { - // Access the view controller via the stored registrar - registrar.viewController?.present(/* your view controller */, animated: true, completion: nil) - } -} -``` - -### Objective-C Implementation - -Declare a private property in a class extension within the implementation (`.m`) file. - -```objc -@interface FooPlugin () -@property(nonatomic) NSObject *registrar; -@end - -@implementation FooPlugin - -- (instancetype)initWithRegistrar:(NSObject *)registrar { - self = [super init]; - if (self) { - _registrar = registrar; - } - return self; -} - -+ (void)registerWithRegistrar:(NSObject *)registrar { - FooPlugin *plugin = [[FooPlugin alloc] initWithRegistrar:registrar]; - [registrar addMethodCallDelegate:plugin channel:/* your channel */]; -} - -- (void)accessVC { - // Access the view controller via the stored registrar - [self.registrar.viewController presentViewController:/* your view controller */ animated:YES completion:nil]; -} - -@end -``` - -## Verification & Output Notice - -**Disclaimer:** This migration was AI-generated and must be verified for accuracy and completeness. - -Build and run the affected iOS targets (`flutter build ios` or `flutter run`) to confirm the migration compiles successfully and operates correctly within the new `UIScene` lifecycle. Verify that any presented view controllers appear on the active screen as expected.