From 595e863c977a918008e49446c449b21932c25d8e Mon Sep 17 00:00:00 2001 From: Jaime Wren Date: Tue, 15 Sep 2026 13:57:00 -0700 Subject: [PATCH] feat: add flutter-convert-to-flutter skill for native Android and iOS migrations Add a unified Agent Skill `flutter-convert-to-flutter` to guide migrating native mobile applications from Android (Java, Kotlin) and iOS (Objective-C, Swift) to Dart and Flutter. - Structure skill using progressive disclosure: - Root `SKILL.md` contains core Flutter paradigms, declarative UI mental model, Dart 3 type system, universal subsystem mapping, and interop decision trees. - `reference/android.md`: Android Java migration guide (Activities, XML, Room, Retrofit, Threads/RxJava, Pigeon/JNI). - `reference/kotlin.md`: Android Kotlin migration guide (Compose, Coroutines, Flow, data/sealed classes, Drift, Dio). - `reference/swift.md`: iOS Swift & SwiftUI migration guide (SwiftUI, async/await, Combine, structs, Codable, Pigeon). - `reference/objective-c.md`: iOS Objective-C & UIKit migration guide (UIKit, Foundation types, GCD, ffigen FFI interop). --- README.md | 1 + resources/flutter_skills.yaml | 57 +- skills/flutter-convert-to-flutter/SKILL.md | 176 +++ .../reference/android.md | 1246 +++++++++++++++++ .../reference/kotlin.md | 1076 ++++++++++++++ .../reference/objective-c.md | 1139 +++++++++++++++ .../reference/swift.md | 1048 ++++++++++++++ 7 files changed, 4742 insertions(+), 1 deletion(-) create mode 100644 skills/flutter-convert-to-flutter/SKILL.md create mode 100644 skills/flutter-convert-to-flutter/reference/android.md create mode 100644 skills/flutter-convert-to-flutter/reference/kotlin.md create mode 100644 skills/flutter-convert-to-flutter/reference/objective-c.md create mode 100644 skills/flutter-convert-to-flutter/reference/swift.md diff --git a/README.md b/README.md index 364cbcc4..73ca411b 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,7 @@ Refer to [Get started developing with AI](https://docs.flutter.dev/ai/get-starte | [flutter-add-widget-test](skills/flutter-add-widget-test/SKILL.md) | 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. | Add a widget test for the CustomButton to verify the onTap callback is called | | [flutter-apply-architecture-best-practices](skills/flutter-apply-architecture-best-practices/SKILL.md) | Architects a Flutter application using the recommended layered approach (UI, Logic, Data). Use when structuring a new project or refactoring for scalability. | Refactor the authentication flow to follow the recommended layered architecture | | [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-convert-to-flutter](skills/flutter-convert-to-flutter/SKILL.md) | Migrate and convert native iOS (Swift, Objective-C) and Android (Kotlin, Java) applications, architecture patterns, concurrency, UI components, and subsystems to cross-platform Dart and Flutter. Use when converting native mobile codebases or UI screens to Flutter, translating language features and idioms to Dart 3, establishing platform channel or Pigeon interop, or modernizing mobile architectures. | Convert this native Android and iOS mobile application code to idiomatic Flutter and Dart | | [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-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 | diff --git a/resources/flutter_skills.yaml b/resources/flutter_skills.yaml index f055e473..b45e4101 100644 --- a/resources/flutter_skills.yaml +++ b/resources/flutter_skills.yaml @@ -431,4 +431,59 @@ - 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-convert-to-flutter + description: Migrate and convert native iOS (Swift, Objective-C) and Android (Kotlin, + Java) applications, architecture patterns, concurrency, UI components, and + subsystems to cross-platform Dart and Flutter. Use when converting native mobile + codebases or UI screens to Flutter, translating language features and idioms + to Dart 3, establishing platform channel or Pigeon interop, or modernizing mobile + architectures. + examplePrompt: "Convert this native Android and iOS mobile application code to idiomatic Flutter and Dart" + instructions: | + 1. **Analyze and Classify Native Components:** + * Audit source files across iOS (Swift, Objective-C) and Android (Kotlin, Java) to categorize domain models, business logic, UI screens, and platform SDK dependencies. + * Separate code candidates for a 100% pure Dart rewrite from hardware/OS-specific components requiring native bridges. + + 2. **Consult Language-Specific References:** + * For Android Java: refer to `reference/android.md`. + * For Android Kotlin: refer to `reference/kotlin.md`. + * For iOS Swift & SwiftUI: refer to `reference/swift.md`. + * For iOS Objective-C: refer to `reference/objective-c.md`. + + 3. **Convert Data Models and Logic to Dart 3:** + * Convert native structs, POJOs, and classes into immutable Dart classes with `final` fields, `const` constructors, and `copyWith()` methods (or `package:freezed`). + * Map state hierarchies and algebraic data types to Dart 3 `sealed class` hierarchies with exhaustive pattern matching. + * Enforce sound null safety throughout. + + 4. **Translate Concurrency to Dart Primitives:** + * Replace GCD, Threads, Handlers, Coroutines, and completion blocks with `Future`, `Stream`, and `async`/`await`. + * Dart code on the root isolate runs on the main UI thread; do not perform redundant main thread dispatching. + * Offload CPU-bound tasks to `Isolate.run()`. + + 5. **Build Declarative Flutter UI:** + * Replace imperative ViewControllers, Activities, Fragments, Compose functions, and XML layouts with `StatelessWidget` and `StatefulWidget`. + * Virtually recycle lists using `ListView.builder` or `GridView.builder`. + + 6. **Implement Native Interoperability:** + * Use `package:pigeon` for type-safe IPC across iOS (Swift/Obj-C) and Android (Kotlin/Java). + * Use `package:ffigen` for in-process C/ObjC FFI or `package:jni` for Java/Kotlin. + * Use `AndroidView` or `UiKitView` for embedded native UI views. + + 7. **Verify and Test:** + * Dispose all controllers, timers, and stream subscriptions in `State.dispose()`. + * Verify zero errors with `dart analyze` and run automated unit/widget tests with `flutter test`. + resources: + - https://docs.flutter.dev/get-started/flutter-for/ios-devs + - https://docs.flutter.dev/get-started/flutter-for/android-devs + - https://docs.flutter.dev/get-started/fundamentals + - https://docs.flutter.dev/ui/layout + - https://docs.flutter.dev/data-and-backend/state-mgmt/intro + - https://docs.flutter.dev/platform-integration/ios + - https://docs.flutter.dev/platform-integration/android + - https://docs.flutter.dev/platform-integration/platform-channels + - https://dart.dev/language/patterns + - https://dart.dev/language/class-modifiers + - https://pub.dev/packages/pigeon + - https://pub.dev/packages/ffigen + - https://pub.dev/packages/jni diff --git a/skills/flutter-convert-to-flutter/SKILL.md b/skills/flutter-convert-to-flutter/SKILL.md new file mode 100644 index 00000000..2372f028 --- /dev/null +++ b/skills/flutter-convert-to-flutter/SKILL.md @@ -0,0 +1,176 @@ +--- +name: flutter-convert-to-flutter +description: Migrate and convert native iOS (Swift, Objective-C) and Android (Kotlin, Java) applications, architecture patterns, concurrency, UI components, and subsystems to cross-platform Dart and Flutter. Use when converting native mobile codebases or UI screens to Flutter, translating language features and idioms to Dart 3, establishing platform channel or Pigeon interop, or modernizing mobile architectures. +metadata: + model: models/gemini-3.1-pro-preview + last_modified: Tue, 15 Sep 2026 20:50:00 GMT +--- + +# Migrating Native Mobile (Android & iOS) to Flutter + +This skill guides the end-to-end migration of native mobile applications from **Android (Java, Kotlin)** and **iOS (Objective-C, Swift)** to cross-platform **Dart and Flutter**. + +--- + +## Contents + +- [Flutter Core Knowledge & Paradigms](#flutter-core-knowledge--paradigms) + - [Declarative UI: UI as a Function of State](#declarative-ui-ui-as-a-function-of-state) + - [The Three Trees: Widget, Element, and RenderObject](#the-three-trees-widget-element-and-renderobject) + - [Concurrency Model: Single-Threaded Event Loop & Isolates](#concurrency-model-single-threaded-event-loop--isolates) + - [Dart 3 Type System & Null Safety](#dart-3-type-system--null-safety) +- [Progressive Disclosure: Platform & Language Guides](#progressive-disclosure-platform--language-guides) +- [Universal Subsystem Mapping Table](#universal-subsystem-mapping-table) +- [Native Interoperability Decision Matrix](#native-interoperability-decision-matrix) +- [Universal Migration Workflow](#universal-migration-workflow) +- [Universal Pitfalls & Gotchas](#universal-pitfalls--gotchas) +- [Migration Verification Checklist](#migration-verification-checklist) + +--- + +## Flutter Core Knowledge & Paradigms + +Before migrating native code, align with Flutter's foundational design principles: + +### Declarative UI: UI as a Function of State +- **Native Android/iOS (Imperative)**: Views are created and mutated directly via references (e.g. `textView.setText()`, `label.text = @"..."`, `findViewById()`, `@IBOutlet`). +- **Flutter (Declarative)**: The user interface is described as `UI = f(state)`. When state changes, widgets are rebuilt immutably. + - **`StatelessWidget`**: Use for presentation widgets that depend solely on their constructor parameters. + - **`StatefulWidget`**: Use when a widget owns local mutable state that triggers rebuilds via `setState()`. + - **Composition over Inheritance**: Complex UIs are created by nesting simple single-purpose widgets (`Padding`, `Center`, `DecoratedBox`, `ConstrainedBox`), rather than configuring dozens of properties on a monolithic view class. + +### The Three Trees: Widget, Element, and RenderObject +1. **Widget Tree**: Lightweight, immutable blueprints of UI elements instantiated on every rebuild. +2. **Element Tree**: Persistent lifecycle managers that manage widget updates, state retention, and tree diffing. +3. **RenderObject Tree**: Mutable objects that handle sizing, layout constraints, painting, and hit testing. +- **Rule of Thumb**: Creating widgets is extremely cheap in Dart; avoid caching widget instances manually unless optimizing static subtrees with `const`. + +### Concurrency Model: Single-Threaded Event Loop & Isolates +- **Main UI Thread**: Dart runs in a single-threaded **isolate** powered by an event loop. +- **Resuming on Main**: Any code resuming after an `await` in the root isolate **already executes on the main UI thread**. There is **no need** for `runOnUiThread()`, `Dispatchers.Main`, `DispatchQueue.main.async`, or `Handler(Looper.getMainLooper())`. +- **CPU-Bound Tasks**: To execute heavy compute (e.g. image processing, massive JSON parsing, cryptography) without dropping frames, offload to a background isolate using `Isolate.run(() async => ...)`. + +### Dart 3 Type System & Null Safety +- **Sound Null Safety**: Types are non-nullable by default (`String` vs `String?`). The compiler guarantees that a non-nullable variable will never hold `null`. +- **Sealed Classes & Pattern Matching**: Use `sealed class` hierarchies to represent finite state machines (e.g. `Loading`, `Success`, `Error`), and handle variants exhaustively via `switch` expressions. +- **Records & Destructuring**: Return multiple strongly typed values without ad-hoc tuple classes: `(int id, String name)`. + +--- + +## Progressive Disclosure: Platform & Language Guides + +Load the dedicated deep-dive reference document corresponding to the source language and platform: + +| Source Language & Platform | Deep-Dive Reference File | Key Topics Covered | +| :--- | :--- | :--- | +| **Android (Java)** | [reference/android.md](reference/android.md) | Activities/Fragments $\rightarrow$ Widgets, XML layouts $\rightarrow$ `Column`/`Row`/`Stack`, `RecyclerView` $\rightarrow$ `ListView.builder`, `AsyncTask`/`Handler` $\rightarrow$ `Future`/`Stream`, Room $\rightarrow$ Drift/Sqflite, Retrofit $\rightarrow$ Dio/Http, Pigeon/JNI. | +| **Android (Kotlin)** | [reference/kotlin.md](reference/kotlin.md) | `data class` $\rightarrow$ immutable models, `sealed class`/`interface` $\rightarrow$ Dart 3 sealed classes, Coroutines/`Flow` $\rightarrow$ `async`/`await`/`Stream`, Jetpack Compose $\rightarrow$ Widgets, Compose Modifiers $\rightarrow$ composition, Hilt/Koin $\rightarrow$ Riverpod/GetIt. | +| **iOS (Swift / SwiftUI)** | [reference/swift.md](reference/swift.md) | Swift `struct` $\rightarrow$ Dart models, `Codable` $\rightarrow$ `json_serializable`, Swift Concurrency (`Task`/`actor`) $\rightarrow$ `Future`/`Isolate.run()`, Combine $\rightarrow$ `Stream`, SwiftUI `View` $\rightarrow$ `StatelessWidget`, `@State`/`@Binding`/`@Observable` $\rightarrow$ `StatefulWidget`/`ChangeNotifier`. | +| **iOS (Objective-C)** | [reference/objective-c.md](reference/objective-c.md) | `@interface`/`@implementation` $\rightarrow$ Dart classes, Foundation types $\rightarrow$ Dart core types, GCD / Blocks $\rightarrow$ `Future`/Closures, `UIViewController` $\rightarrow$ `StatefulWidget`, `UITableView` $\rightarrow$ `ListView.builder`, direct C/ObjC FFI via `ffigen`. | + +--- + +## Universal Subsystem Mapping Table + +| Application Subsystem | Android (Java/Kotlin) | iOS (Obj-C/Swift) | Flutter / Dart Ecosystem | +| :--- | :--- | :--- | :--- | +| **State Management** | ViewModel, LiveData, StateFlow | ObservableObject, @Observable, Combine | [`ChangeNotifier`](https://api.flutter.dev/flutter/foundation/ChangeNotifier-class.html), [`ValueNotifier`](https://api.flutter.dev/flutter/foundation/ValueNotifier-class.html), [Riverpod](https://pub.dev/packages/flutter_riverpod), [BLoC](https://pub.dev/packages/flutter_bloc) | +| **HTTP Networking** | Retrofit, OkHttp, Ktor | URLSession, Alamofire | [`package:http`](https://pub.dev/packages/http), [`package:dio`](https://pub.dev/packages/dio) | +| **JSON Serialization** | Gson, Moshi, Kotlinx Serialization | Codable, NSJSONSerialization | `dart:convert`, [`package:json_serializable`](https://pub.dev/packages/json_serializable), [`package:freezed`](https://pub.dev/packages/freezed) | +| **Key-Value Storage** | SharedPreferences, DataStore | UserDefaults, Keychain | [`package:shared_preferences`](https://pub.dev/packages/shared_preferences), [`package:flutter_secure_storage`](https://pub.dev/packages/flutter_secure_storage) | +| **Relational Database** | Room, SQLiteDatabase | Core Data, SwiftData, GRDB | [`package:drift`](https://pub.dev/packages/drift), [`package:sqflite`](https://pub.dev/packages/sqflite) | +| **NoSQL / Document DB** | Realm, Firebase | Realm, Firebase | [`package:hive_ce`](https://pub.dev/packages/hive_ce), `firebase_database` / `cloud_firestore` | +| **Dependency Injection** | Hilt, Dagger, Koin | Factory, Swinject, Dependencies | [`package:get_it`](https://pub.dev/packages/get_it), Riverpod Providers | +| **Navigation & Routing** | Navigation Component, Intents | NavigationStack, UINavigationController | [`package:go_router`](https://pub.dev/packages/go_router), [`Navigator`](https://api.flutter.dev/flutter/widgets/Navigator-class.html) | +| **Image Loading/Caching** | Glide, Coil, Picasso | Kingfisher, SDWebImage | [`package:cached_network_image`](https://pub.dev/packages/cached_network_image), `Image.network` | +| **Background Scheduling** | WorkManager, JobScheduler | BGAppRefreshTask, BGProcessingTask | [`package:workmanager`](https://pub.dev/packages/workmanager), platform channels | + +--- + +## Native Interoperability Decision Matrix + +When migrating an existing app, evaluate whether to rewrite completely or bridge retained native code: + +``` + ┌─────────────────────────────────────┐ + │ Can this code be written in Dart? │ + └──────────────────┬──────────────────┘ + │ + ┌───────────────────┴───────────────────┐ + ▼ YES ▼ NO (Requires OS SDK / C++ / Driver) + ┌───────────────────────┐ ┌─────────────────────────────────────┐ + │ 100% Pure Dart Rewrite │ │ Interoperability Strategy: │ + │ (Portable, testable, │ └──────────────────┬──────────────────┘ + │ zero bridging overhead)│ │ + └───────────────────────┘ ┌────────────────────────┼────────────────────────┐ + ▼ ▼ ▼ + ┌───────────────────┐ ┌───────────────────┐ ┌───────────────────┐ + │ Cross-Platform IPC│ │ In-Process Native │ │ Embedded Native UI│ + │ (Asynchronous) │ │ (Synchronous FFI) │ │ (Platform Views) │ + ├───────────────────┤ ├───────────────────┤ ├───────────────────┤ + │ package:pigeon │ │ Dart FFI (C/ObjC) │ │ AndroidView │ + │ Generates typesafe│ │ via package:ffigen│ │ UiKitView │ + │ Swift, Obj-C, Java│ │ or package:jni │ │ For map engines, │ + │ & Kotlin bridges. │ │ for Java/Kotlin. │ │ complex webviews. │ + └───────────────────┘ └───────────────────┘ └───────────────────┘ +``` + +--- + +## Universal Migration Workflow + +Follow this step-by-step checklist to migrate modules systematically: + +- [ ] **Step 1: Audit and Categorize Source Code** + - Identify domain models, business logic/services, UI screens, and hardware/platform dependencies. + - Separate candidates for 100% Dart rewrite from code requiring native bridging. +- [ ] **Step 2: Read Language-Specific Reference** + - Read [reference/android.md](reference/android.md), [reference/kotlin.md](reference/kotlin.md), [reference/swift.md](reference/swift.md), or [reference/objective-c.md](reference/objective-c.md). +- [ ] **Step 3: Convert Data Models & Business Logic** + - Translate native structs/classes/POJOs to immutable Dart classes with `final` fields, `const` constructors, and `fromJson`/`toJson` methods. + - Implement algebraic data types using Dart 3 `sealed class` hierarchies. +- [ ] **Step 4: Translate Asynchronous & Concurrency Workflows** + - Replace GCD queues, Android Handlers, Threads, Coroutines, and completion blocks with `Future`, `Stream`, and `async`/`await`. + - Offload heavy CPU processing to `Isolate.run()`. +- [ ] **Step 5: Translate UI to Declarative Flutter Widgets** + - Convert ViewControllers, Activities, Fragments, Compose functions, or XML layouts into `StatelessWidget` or `StatefulWidget`. + - Compose layouts using `Scaffold`, `AppBar`, `Column`, `Row`, `Stack`, and `ListView.builder`. +- [ ] **Step 6: Implement Native Interop (If Retaining Code)** + - Define Pigeon interface specifications (`pigeon/schema.dart`) and generate bridges. + - For embedded native views, wrap with `AndroidView` or `UiKitView`. +- [ ] **Step 7: Validation and Testing** + - Ensure all controllers (`TextEditingController`, `AnimationController`, `ScrollController`) and subscriptions are disposed in `State.dispose()`. + - Run `dart analyze` to verify zero static analysis errors and warnings. + - Write and run unit and widget tests: `flutter test`. + +--- + +## Universal Pitfalls & Gotchas + +1. **Redundant UI Thread Dispatching**: + - *Mistake*: Calling platform channel methods or awaiting futures, then attempting to dispatch back to the "main thread". + - *Correction*: In Dart, code that resumes after `await` on the root isolate runs directly on the platform UI thread. + +2. **Leaking Controller and Subscription Resources**: + - *Mistake*: Creating `TextEditingController`, `AnimationController`, or `StreamSubscription` without cleaning them up. + - *Correction*: Always override `State.dispose()` and dispose all controllers, timers, and subscriptions. + +3. **Overusing Deep Widget Trees Instead of Builder Patterns**: + - *Mistake*: Mapping `UITableView` or `RecyclerView` by mapping an entire collection into children: `Column(children: list.map(...).toList())`. + - *Correction*: Always use `ListView.builder` or `GridView.builder` to enable virtualization and render object recycling. + +4. **Public Mutable Getters and Sound Null Safety Promotion**: + - *Mistake*: Expecting a public getter to type-promote after a null-check: `if (obj.field != null) print(obj.field.length);`. + - *Correction*: Dart cannot promote public getters because a subclass could override them. Assign to a local variable first: `final f = obj.field; if (f != null) print(f.length);`. + +--- + +## Migration Verification Checklist + +Before finalizing any native-to-Flutter migration, verify: + +- [ ] **Sound Null Safety**: All variables and parameters have strict types with zero unsafe `!` force-unwraps. +- [ ] **Memory & Resource Disposal**: Every controller, stream subscription, and timer is disposed in `State.dispose()`. +- [ ] **Asynchronous Safety**: No blocking computations on the root isolate; CPU-heavy work is delegated to `Isolate.run()`. +- [ ] **Zero Static Analysis Errors**: `dart analyze` reports 0 issues. +- [ ] **Automated Test Coverage**: Unit tests for models/repositories and widget tests for screens pass via `flutter test`. diff --git a/skills/flutter-convert-to-flutter/reference/android.md b/skills/flutter-convert-to-flutter/reference/android.md new file mode 100644 index 00000000..68ebc7b7 --- /dev/null +++ b/skills/flutter-convert-to-flutter/reference/android.md @@ -0,0 +1,1246 @@ +# Android Java to Flutter Migration Guide (`flutter-android-java-to-flutter`) + +## Contents +- [Overview & Core Paradigms](#android-overview--core-paradigms) +- [Language & Syntax Mapping (Java to Dart)](#java-to-dart-language--syntax-mapping) + - [Classes, Interfaces, and Mixins](#classes-interfaces-and-mixins) + - [Constructors, Initializers, and Named Constructors](#constructors-initializers-and-named-constructors) + - [Getters, Setters, and Encapsulation](#getters-setters-and-encapsulation) + - [Null Safety vs NullPointerException](#null-safety-vs-nullpointerexception) + - [Collections and Streams API Mapping](#collections-and-streams-api-mapping) + - [Anonymous Classes vs First-Class Functions](#anonymous-classes-vs-first-class-functions) + - [Enums and Constant Sets](#enums-and-constant-sets) +- [Concurrency: Android Threads & Handlers to Dart](#concurrency-android-threads--handlers-to-dart) + - [Thread, ExecutorService, and Handler vs Dart Event Loop](#thread-executorservice-and-handler-vs-dart-event-loop) + - [runOnUiThread vs Automatic Main Isolate Resumption](#runonuithread-vs-automatic-main-isolate-resumption) + - [Heavy Background Work: Isolate.run vs Background Threads](#heavy-background-work-isolaterun-vs-background-threads) + - [RxJava to Dart Streams and RxDart](#rxjava-to-dart-streams-and-rxdart) +- [UI Architecture: Android Views & XML to Flutter](#ui-architecture-android-views--xml-to-flutter) + - [Activity / Fragment Lifecycle to StatefulWidget Lifecycle](#activity--fragment-lifecycle-to-statefulwidget-lifecycle) + - [XML Layout ViewGroups to Flutter Layout Widgets](#xml-layout-viewgroups-to-flutter-layout-widgets) + - [RecyclerView & ViewHolder to ListView.builder](#recyclerview--viewholder-to-listviewbuilder) + - [Component Equivalents Table](#android-component-equivalents-table) + - [Themes, Colors, and Drawables to BoxDecoration & ThemeData](#themes-colors-and-drawables-to-boxdecoration--themedata) +- [Android Architecture & Subsystem Mapping](#android-architecture--subsystem-mapping) + - [ViewModel & LiveData to ChangeNotifier / Riverpod](#viewmodel--livedata-to-changenotifier--riverpod) + - [SharedPreferences & KeyStore](#sharedpreferences--keystore) + - [Room & SQLiteDatabase to sqflite / drift](#room--sqlitedatabase-to-sqflite--drift) + - [Retrofit & OkHttp to package:http / dio](#retrofit--okhttp-to-packagehttp--dio) + - [Intents (Internal & External)](#intents-internal--external) +- [Native Interoperability Strategies (When Keeping Java Code)](#native-interoperability-strategies-when-keeping-java-code) + - [Pigeon Code Generation for Type-Safe Java IPC](#pigeon-code-generation-for-type-safe-java-ipc) + - [Direct In-Process Interop via package:jni](#direct-in-process-interop-via-packagejni) + - [Platform Views (AndroidView)](#platform-views-androidview) +- [Step-by-Step Migration Workflow](#android-step-by-step-migration-workflow) +- [Concrete Migration Examples](#android-concrete-migration-examples) + - [Example 1: POJO Model with Gson to Dart Model with fromJson/toJson](#android-example-1-pojo-model-with-gson-to-dart-model-with-fromjsontojson) + - [Example 2: Retrofit/OkHttp Service with Callback to Dart Async Service](#android-example-2-retrofitokhttp-service-with-callback-to-dart-async-service) + - [Example 3: Activity with RecyclerView & ViewModel to Flutter StatefulWidget](#android-example-3-activity-with-recyclerview--viewmodel-to-flutter-statefulwidget) +- [Common Pitfalls & Anti-Patterns](#android-common-pitfalls--anti-patterns) +- [Migration Verification Checklist](#android-migration-verification-checklist) + +--- + +## Android Overview & Core Paradigms + +Migrating from native Android (Java) to Flutter involves transitioning from a **multi-component, XML-bound, imperative framework** to a **unified, declarative widget tree**: + +``` +Android Java Architecture (Imperative & XML-driven) +┌──────────────────────────────────────────────────────────────┐ +│ • Layouts declared in XML: res/layout/activity_main.xml │ +│ • Component lifecycle: Activity / Fragment / Service │ +│ • Imperative mutation: findViewById -> textView.setText(...) │ +│ • Multi-threading: Handler, Looper, runOnUiThread(...) │ +│ • Manifest registration for every screen and permission │ +└──────────────────────────────────────────────────────────────┘ + │ + ▼ Paradigm Shift +Flutter / Dart Architecture (Declarative & Code-driven) +┌──────────────────────────────────────────────────────────────┐ +│ • UI = f(state): Declarative widget tree inside build() │ +│ • Everything is a Widget (layout, styling, animations) │ +│ • Single-threaded isolate event loop (no runOnUiThread) │ +│ • Lightweight element tree diffing via Skia / Impeller │ +│ • Single-entry point: void main() => runApp(const MyApp()) │ +└──────────────────────────────────────────────────────────────┘ +``` + +--- + +## Java to Dart Language & Syntax Mapping + +### Classes, Interfaces, and Mixins + +- **In Java**: Interfaces require explicit `implements`, and classes can only extend one superclass. Default methods exist, but true mixin composition is unavailable. +- **In Dart**: **Every class implicitly defines an interface**. Multiple interfaces can be implemented without `interface` keywords. Reusable behavior across class hierarchies is achieved via **`mixin`**. + +```java +// Java: Interface and Class +public interface Identifiable { + String getId(); +} + +public class User extends BaseEntity implements Identifiable { + private final String id; + private String name; + + public User(String id, String name) { + this.id = id; + this.name = name; + } + + @Override + public String getId() { + return id; + } +} +``` + +```dart +// Dart: Class implementing implicit interface and using mixin +abstract interface class Identifiable { + String get id; +} + +mixin TimestampMixin { + DateTime createdAt = DateTime.now(); + bool get isRecent => DateTime.now().difference(createdAt).inDays < 7; +} + +class User extends BaseEntity with TimestampMixin implements Identifiable { + @override + final String id; + String name; + + User({ + required this.id, + required this.name, + }); +} +``` + +### Constructors, Initializers, and Named Constructors + +Java requires verbose assignment boilerplate (`this.field = field;`) and method overloading for constructors. Dart provides **initializing formals**, **named constructors**, and **factory constructors**: + +```java +// Java: Constructor overloading +public class Product { + private final String id; + private final String title; + private final double price; + + public Product(String id, String title, double price) { + this.id = id; + this.title = title; + this.price = price; + } + + public Product(String id, String title) { + this(id, title, 0.0); + } +} +``` + +```dart +// Dart: Initializing formals & named constructors +class Product { + final String id; + final String title; + final double price; + + // Generative constructor with default value + const Product({ + required this.id, + required this.title, + this.price = 0.0, + }); + + // Named constructor + const Product.free({ + required this.id, + required this.title, + }) : price = 0.0; +} +``` + +### Getters, Setters, and Encapsulation + +Java uses boilerplate getters and setters (`getId()`, `setId(...)`). In Dart: +- Public fields automatically provide implicit getters and setters. +- Custom getters and setters can be introduced later without changing the public contract. +- Privacy is library-scoped via a leading underscore (`_`). + +```java +// Java: Getters and setters +public class Counter { + private int count = 0; + + public int getCount() { + return count; + } + + public void setCount(int count) { + if (count >= 0) { + this.count = count; + } + } +} +``` + +```dart +// Dart: Idiomatic getters and setters +class Counter { + int _count = 0; + + int get count => _count; + set count(int value) { + if (value >= 0) { + _count = value; + } + } +} +``` + +### Null Safety vs NullPointerException + +- In Java, any object reference can be `null`, resulting in frequent `NullPointerException` (NPE) crashes at runtime. +- In Dart, **Sound Null Safety** guarantees that non-nullable types (`String`) can never be `null`. Nullable types (`String?`) require explicit handling. + +```java +// Java: Defensive null checks +public String formatUser(User user) { + if (user != null) { + String name = user.getName(); + if (name != null) { + return name.trim(); + } + } + return "Guest"; +} +``` + +```dart +// Dart: Sound Null Safety with null-aware operators +String formatUser(User? user) { + return user?.name.trim() ?? 'Guest'; +} +``` + +### Collections and Streams API Mapping + +| Java Collection / API | Dart Equivalent | Notes | +|---|---|---| +| `java.util.List` / `ArrayList` | `List` | Literal: `[1, 2, 3]` | +| `java.util.Map` / `HashMap` | `Map` | Literal: `{'key': 'value'}` | +| `java.util.Set` / `HashSet` | `Set` | Literal: `{1, 2, 3}` | +| `Collections.unmodifiableList(...)` | `List.unmodifiable(...)` | Read-only wrapper | +| `list.stream().filter(p).collect(...)` | `list.where(p).toList()` | Dart Iterable methods | +| `list.stream().map(f).collect(...)` | `list.map(f).toList()` | Lazy transformation | +| `list.stream().findFirst().orElse(d)` | `list.firstWhere(p, orElse: () => d)` | Search element | +| `list.stream().reduce(0, Integer::sum)` | `list.fold(0, (sum, val) => sum + val)` | Aggregation | + +```java +// Java: Streams API +List activeNames = users.stream() + .filter(User::isActive) + .map(User::getName) + .sorted() + .collect(Collectors.toList()); +``` + +```dart +// Dart: Fluent Iterables +final activeNames = (users + .where((u) => u.isActive) + .map((u) => u.name) + .toList() + ..sort()); +``` + +### Anonymous Classes vs First-Class Functions + +Java uses anonymous classes (e.g. `new View.OnClickListener() { ... }`) or lambda expressions. In Dart, functions are first-class objects: + +```java +// Java: Event Listener +button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + submitForm(); + } +}); +``` + +```dart +// Dart: First-class function callback +ElevatedButton( + onPressed: submitForm, + child: const Text('Submit'), +) +``` + +### Enums and Constant Sets + +Java uses `public enum Status`. Dart supports **Enhanced Enums** with properties, constructors, and methods, as well as Dart 3 **`sealed class`** hierarchies for pattern-matching state machines. + +```java +// Java: Enum with properties +public enum Priority { + LOW(1), MEDIUM(2), HIGH(3); + + private final int level; + Priority(int level) { this.level = level; } + public int getLevel() { return level; } +} +``` + +```dart +// Dart: Enhanced Enum +enum Priority { + low(1), + medium(2), + high(3); + + final int level; + const Priority(this.level); + + bool get isUrgent => level == 3; +} +``` + +--- + +## Concurrency: Android Threads & Handlers to Dart + +### Thread, ExecutorService, and Handler vs Dart Event Loop + +In Android Java: +- CPU and I/O work are dispatched to background threads using `ExecutorService`, `ThreadPoolExecutor`, or `Thread`. +- To modify Views, background threads must post Runnables back to the main thread via `Handler` or `Activity.runOnUiThread(...)`. + +In Flutter: +- The **main isolate** processes all UI building, rendering coordination, and user events on a single thread. +- Asynchronous I/O (network calls, database operations, file reads) is non-blocking via `Future` and `async`/`await`. +- Code that resumes after `await` **already executes on the main UI isolate**. No `Handler` or `runOnUiThread` is needed! + +``` +Android Java Multi-threading: +[Main Thread] ──> ExecutorService ──> [Worker Thread (I/O or CPU)] + │ +[Main Thread] <── Handler.post(Runnable) <────┘ + +Dart Event Loop: +[Root Isolate UI Thread] ──> await http.get() (non-blocking OS I/O) + │ +[Root Isolate UI Thread] <─────────┘ (resumes on main thread automatically!) +``` + +### runOnUiThread vs Automatic Main Isolate Resumption + +```java +// Android Java +new Thread(new Runnable() { + @Override + public void run() { + final String result = networkService.fetchData(); + runOnUiThread(new Runnable() { + @Override + public void run() { + textView.setText(result); + } + }); + } +}).start(); +``` + +```dart +// Dart & Flutter +final result = await networkService.fetchData(); +// Automatically resumes on main thread! +setState(() { + _statusText = result; +}); +``` + +### Heavy Background Work: Isolate.run vs Background Threads + +For **heavy CPU-bound operations** (e.g. image processing, large JSON decryption/parsing, cryptographic hashing): + +```java +// Java: ExecutorService background execution +ExecutorService executor = Executors.newSingleThreadExecutor(); +Handler mainHandler = new Handler(Looper.getMainLooper()); + +executor.execute(() -> { + byte[] compressed = processImage(rawBytes); + mainHandler.post(() -> updateImageView(compressed)); +}); +``` + +```dart +// Dart: Isolate.run transfers execution to a worker isolate +final compressed = await Isolate.run(() => processImage(rawBytes)); +updateImageView(compressed); +``` + +### RxJava to Dart Streams and RxDart + +| RxJava | Dart Streams / RxDart | +|---|---| +| `Observable` / `Flowable` | `Stream` | +| `Single` | `Future` | +| `Completable` | `Future` | +| `PublishSubject` | `StreamController.broadcast()` | +| `BehaviorSubject` | `BehaviorSubject` (`package:rxdart`) or `ValueNotifier` | +| `Schedulers.io()` | Unnecessary (Dart I/O is non-blocking) | +| `AndroidSchedulers.mainThread()` | Unnecessary (resumes on root isolate) | +| `.subscribe(onSuccess, onError)` | `stream.listen((v) => ..., onError: (e) => ...)` | +| `CompositeDisposable` | `List` cancelled in `dispose()` | + +--- + +## UI Architecture: Android Views & XML to Flutter + +### Activity / Fragment Lifecycle to StatefulWidget Lifecycle + +``` +Android Activity / Fragment Flutter State +┌───────────────────────────┐ ┌───────────────────────────┐ +│ onCreate(Bundle saved) │ ───> │ void initState() │ +│ │ │ │ +│ onStart() / onResume() │ ───> │ didChangeDependencies() │ +│ │ │ didChangeAppLifecycleState│ +│ │ │ │ +│ onCreateView() / XML │ ───> │ Widget build(context) │ +│ │ │ │ +│ onPause() / onStop() │ ───> │ didChangeAppLifecycleState│ +│ │ │ │ +│ onDestroy() │ ───> │ void dispose() │ +└───────────────────────────┘ └───────────────────────────┘ +``` + +### XML Layout ViewGroups to Flutter Layout Widgets + +In Android, layouts are defined in XML files with constraint equations or nested ViewGroups. In Flutter, layout is achieved using composable widgets: + +```xml + + + + + +