PaxLee
PaxLee学无止境
Back to list
Complete Guide: Porting Flutter Apps to HarmonyOS — From Zero to AppGallery
FlutterHarmonyOS从零到上架的踩坑指南

Complete Guide: Porting Flutter Apps to HarmonyOS — From Zero to AppGallery

Published July 14, 202618 min read
> For engineers who already ship Flutter apps on Android / iOS.
> After this guide you should be able to: set up the HarmonyOS toolchain → produce your first HAP from an existing project → handle plugin and feature compatibility → build a store-ready `.app`.

This is not another Hello World. It is a practical path based on shipping a real app. Sample package name is com.example.myapp — replace it with yours.

Also available: Chinese · Russian


What you will learn

  • Why stock Flutter is not enough, and which toolchain you must use
  • Step by step what to install, change, and run
  • How to wire plugins so the project actually compiles (the longest pain point)
  • How to trim features: login, storage, WebView, notifications, push, etc.
  • How debug HAP differs from store APP packages
  • How signing + Huawei Account clientId alignment prevents a launch-day outage

Build the right mental model first (read before coding)

Moving an existing Flutter app to HarmonyOS is not “add a flavor”. It is closer to “add an entire new platform toolchain”:

ExpectationReality
flutter build apk is enoughStock Flutter has no hap / HarmonyOS app commands
Android plugin implementations are enoughMost federated plugins have no OHOS impl — you need full-chain overrides
A few Platform.isAndroid checksYou need a capability matrix: Firebase / notifications / some native libs must degrade
A release build can go to the storeDebug uses .hap; the store wants .app; signing and OAuth must match

One-liner to remember:

OHOS Flutter fork + ohos native project + full-chain plugin overrides + Dart capability gating + release signing aligned with Huawei Account.

Follow the steps in order. Many people stall on day 3 because steps 1–2 were wrong and they already started rewriting business code.


Roadmap at a glance

Step 1   Install DevEco Studio and prepare the HarmonyOS SDK layout
Step 2   Install / configure the OpenHarmony Flutter fork
Step 3   Generate / verify the ohos project for your app
Step 4   Write local.properties (SDK + Flutter paths)
Step 5   Configure debug signing so SignHap works
Step 6   Update pubspec: *_ohos packages + dependency_overrides
Step 7   Add AppPlatform abstraction; keep analysis green
Step 8   First flutter build hap --debug; install on device
Step 9   Adapt features (login / storage / WebView / TTS / payments…)
Step 10  Release signing + flutter build app --release for AppGallery

Step 1: Install DevEco and prepare the SDK directory

1.1 What to install

  1. Install DevEco Studio from Huawei’s developer site (common macOS path: /Applications/DevEco-Studio.app)
  2. Open DevEco and download the HarmonyOS SDK via the wizard (many projects use API 24 / HarmonyOS 6.1.1-class versions — use what DevEco offers you)
  3. Confirm CLI tools work:
# hdc: HarmonyOS device tool, usually under DevEco sdk/.../toolchains
hdc list targets

Seeing a connected device — or an empty list while the command runs — means the toolchain is basically present.

1.2 Pitfall: SDK folder name must match hvigor conventions

hvigor 6.x often expects something like:

~/Library/OpenHarmony/Sdk/HarmonyOS-6.1.1/
  ├── openharmony/
  │   ├── ets
  │   ├── native
  │   ├── toolchains
  │   └── ...
  └── hms/
      ├── ets
      ├── native
      └── ...

Symptom: Build cannot find HarmonyOS-6.1.1 / API 24. Cause: DevEco’s built-in path is often Contents/sdk/default/openharmony, which is not the same as the layout above. Fix: Symlink (or copy) DevEco components into the expected tree:

#!/usr/bin/env bash
set -euo pipefail

```text
DEVECO_APP="${DEVECO_APP:-/Applications/DevEco-Studio.app}"
SDK_HOME="${HOME}/Library/OpenHarmony/Sdk"
SOURCE_OH="${DEVECO_APP}/Contents/sdk/default/openharmony"
SOURCE_HMS="${DEVECO_APP}/Contents/sdk/default/hms"
TARGET_SDK="${SDK_HOME}/HarmonyOS-6.1.1"

mkdir -p "${TARGET_SDK}/openharmony" "${TARGET_SDK}/hms"

for component in ets js native previewer toolchains; do
  ln -sfn "${SOURCE_OH}/${component}" "${TARGET_SDK}/openharmony/${component}"
done
for component in ets native previewer toolchains; do
  ln -sfn "${SOURCE_HMS}/${component}" "${TARGET_SDK}/hms/${component}"
done

echo "SDK ready: ${TARGET_SDK}" ls -la "${TARGET_SDK}"


> On Windows, follow DevEco docs for the equivalent SDK. The point is that hvigor must resolve a complete SDK with both `openharmony/` and `hms/`.

**Done when:** `~/Library/OpenHarmony/Sdk/HarmonyOS-6.1.1` (or the folder name matching your `compileSdkVersion`) exists with the expected children.

---

## Step 2: Install the OpenHarmony Flutter fork

### 2.1 Why you must switch Flutter

**On stock Flutter:**

```bash
flutter build -h

You usually see apk / appbundle / ios / web… — no hap, and no HarmonyOS-oriented app.

A community OpenHarmony fork (commonly sources like openharmony-tpc/flutter_flutter — check current official docs) adds:

flutter build hap    # debug / installable HAP
flutter build app    # AppGallery .app package

Plus engine support for:

  • Platform.operatingSystem == 'ohos'
  • TargetPlatform.ohos

2.2 How to install

Use FVM or a manual clone in a separate directory. Do not overwrite your day-to-day official Flutter for Android/iOS.

# Example: OHOS-specific Flutter via FVM (use the currently recommended version)
export OHOS_FLUTTER="$HOME/fvm/versions/openharmony_tpc/xxx/bin/flutter"

$OHOS_FLUTTER --version
$OHOS_FLUTTER build -h | grep -E 'hap|app'

Done when: $OHOS_FLUTTER build -h lists both hap and app.

2.3 Always use this Flutter for HarmonyOS commands

# ❌ don't
flutter pub get
flutter build hap

```text
# ✅ do
$OHOS_FLUTTER pub get
$OHOS_FLUTTER build hap --debug

In the IDE, configure a separate Flutter SDK for HarmonyOS builds so one-click runs do not pick the wrong engine.

---

## Step 3: Prepare the `ohos/` project for an existing app

### 3.1 Adding HarmonyOS to an existing Flutter app

**Typical layout:**

```text
your_flutter_app/
├── android/
├── ios/
├── lib/
├── pubspec.yaml
└── ohos/                    # new: HarmonyOS Stage project
    ├── AppScope/
       └── app.json5        # bundleName, versionCode, versionName
    ├── entry/               # main module (like android/app)
    ├── build-profile.json5  # signing, compileSdk, products
    ├── hvigorfile.ts
    ├── local.properties     # local SDK / Flutter paths
    └── oh-package.json5

Depending on your OHOS Flutter tooling:

cd your_flutter_app
$OHOS_FLUTTER create --platforms=ohos .
# or the “enable ohos on existing project” command from your fork’s docs

Then open the ohos/ folder in DevEco Studio (open the project — do not merely browse it as loose files).

3.2 Verify three critical pieces

AppScope/app.json5

{
  "app": {
    "bundleName": "com.example.myapp",
    "vendor": "example",
    "versionCode": 100,
    "versionName": "1.0.0",
    "icon": "$media:app_icon",
    "label": "$string:app_name"
  }
}

② product in build-profile.json5

{
  "app": {
    "products": [
      {
        "name": "default",
        "compileSdkVersion": "6.1.1(24)",
        "targetSdkVersion": "6.1.1(24)",
        "compatibleSdkVersion": "6.0.0(20)",
        "runtimeOS": "HarmonyOS",
        "signingConfig": "debug"   // later: your debug / release config name
      }
    ]
  }
}

hvigorfile.ts must load the Flutter plugin

import path from 'path'
import { appTasks } from '@ohos/hvigor-ohos-plugin'
import { flutterHvigorPlugin } from 'flutter-hvigor-plugin'

```text
export default {
  system: appTasks,
  plugins: [flutterHvigorPlugin(path.dirname(__dirname))],
}

**Done when:** DevEco opens `ohos/` with a complete project tree (compiling is not required yet).

---

## Step 4: Configure `local.properties`

In `ohos/local.properties` (adjust paths):

```properties
hwsdk.dir=/Users/YOUR_NAME/Library/OpenHarmony/Sdk
sdk.dir=/Users/YOUR_NAME/Library/OpenHarmony/Sdk
flutter.sdk=/Users/YOUR_NAME/path/to/openharmony_tpc/flutter
flutter.versionName=1.0.0
flutter.versionCode=100

Notes:

  • flutter.sdk points to the root of the OHOS fork from Step 2 (parent of bin/flutter)
  • Keep versionName / versionCode in sync across pubspec.yaml (x.y.z+code), AppScope/app.json5, and this file

Done when: Paths exist and flutter.sdk/bin/flutter --version works.


Step 5: Configure debug signing (or you never get a signed package)

5.1 Auto debug signing in DevEco

  1. Open the ohos project in DevEco
  2. FileProject StructureSigning Configs (menu names vary by version)
  3. Enable automatic signing, sign in with a Huawei account, generate debug cert / profile
  4. Confirm build-profile.json5 has a debug signingConfigs entry and the product points to it

5.2 Common pitfall: missing signing/material

Symptom: SignHap ENOENT for material. Cause: DevEco stores encrypted material under the user home (e.g. ~/.ohos/config/material), while the project expects another path. Fix: Copy/symlink material per DevEco docs, or re-select correct paths in Signing Configs.

5.3 Do not touch release signing yet

Use debug signing only. Goal:

$OHOS_FLUTTER build hap --debug

should produce a signed HAP. Release signing comes in Step 10.

Done when: DevEco or CLI can complete a signed build attempt (other compile errors are OK if signing itself works).


Step 6: Rewrite pubspec.yaml (longest, easiest to get wrong)

6.1 Understand the problem

HarmonyOS porting time is mostly spent on plugins:

  • Many pub.dev plugins only implement android / ios / web
  • Community hosts *_ohos implementations (e.g. on gitcode)
  • Only adding foo_ohos to dependencies is often not enough — the resolver may still pick stock foo without OHOS code

Correct approach:

  1. Declare *_ohos when needed
  2. Use dependency_overrides to pin foo and foo_platform_interface (if any) to the ohos branch

6.2 Recommended template

dependencies:
  flutter:
    sdk: flutter

```text
shared_preferences: ^2.5.4
  shared_preferences_ohos:
    git:
      url: https://gitcode.com/openharmony-tpc/flutter_packages.git
      path: packages/shared_preferences/shared_preferences_ohos
      ref: br_shared_preferences-v2.5.4_ohos   # use current community ref
path_provider: ^2.1.5
  path_provider_ohos:
    git:
      url: https://gitcode.com/openharmony-sig/flutter_packages.git
      path: packages/path_provider/path_provider_ohos
      ref: br_path_provider-v2.1.5_ohos
permission_handler: ^12.0.1
  permission_handler_ohos:
    git:
      url: https://gitcode.com/CPF-Flutter/flutter_permission_handler.git
      path: permission_handler_ohos
      ref: br_v12.0.1_ohos
# Add as needed:
  # audioplayers_ohos / open_file_ohos / file_picker_ohos / record ohos branch, etc.
dependency_overrides:
  shared_preferences:
    git:
      url: https://gitcode.com/openharmony-tpc/flutter_packages.git
      path: packages/shared_preferences/shared_preferences
      ref: br_shared_preferences-v2.5.4_ohos
path_provider:
    git:
      url: https://gitcode.com/openharmony-sig/flutter_packages.git
      path: packages/path_provider/path_provider
      ref: br_path_provider-v2.1.5_ohos
permission_handler:
    git:
      url: https://gitcode.com/CPF-Flutter/flutter_permission_handler.git
      path: permission_handler
      ref: br_v12.0.1_ohos
# Also override platform_interface packages together when they exist, e.g.:
  # record + record_platform_interface
  # share_plus + share_plus_platform_interface
  # webview_flutter + webview_flutter_platform_interface
  # audioplayers + audioplayers_platform_interface

**Always resolve with OHOS Flutter:**

```bash
$OHOS_FLUTTER pub get

6.3 How to decide which packages still need overrides

  1. List every native-backed plugin in your pubspec.yaml
  2. Search gitcode / openharmony-sig / openharmony-tpc / community repos for *_ohos or br_*_ohos
  3. When build hap fails with missing impl / MissingPluginException / platform analyzer errors, add an override for that package
  4. Keep main package and platform_interface on the same ref family

6.4 Special landmine: exhaustive switch on TargetPlatform breaks

The fork adds TargetPlatform.ohos. Third-party code like:

switch (Theme.of(context).platform) {
  case TargetPlatform.android:
  case TargetPlatform.iOS:
  // ...
  case TargetPlatform.linux:
    ...
  // no ohos → analyze / compile failure
}

Common victims include some video control packages (e.g. chewie).

Fix:

  1. Vendor the package under third_party/xxx
  2. Replace exhaustive switch with if + default
  3. Point dependency_overrides to the local path
Widget build(BuildContext context) {
  final TargetPlatform platform = Theme.of(context).platform;
  if (platform == TargetPlatform.iOS) {
    return const CupertinoControls(/* ... */);
  }
  if (platform == TargetPlatform.macOS ||
      platform == TargetPlatform.windows ||
      platform == TargetPlatform.linux) {
    return const MaterialDesktopControls();
  }
  // Android / ohos / others → Material
  return const MaterialControls();
}

Done when: $OHOS_FLUTTER pub get succeeds and analyzer no longer complains about non-exhaustive TargetPlatform.


Step 7: Add a platform abstraction — do not sprinkle if (ohos) everywhere

Create this early; all feature adaptation should go through it.

7.1 Conditional export (Web-safe)

// lib/utils/app_platform.dart
export 'app_platform_stub.dart'
    if (dart.library.io) 'app_platform_io.dart';

7.2 IO implementation

// lib/utils/app_platform_io.dart
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart';

```dart
abstract final class AppPlatform {
  AppPlatform._();
static bool get isOhos => Platform.operatingSystem == 'ohos';
  static bool get isWeb => kIsWeb;
  static String get operatingSystem => Platform.operatingSystem;
/// Adjust these flags for your product
  static bool get supportsFirebase =>
      Platform.isAndroid ||
      Platform.isIOS ||
      Platform.isMacOS ||
      Platform.isWindows;
static bool get supportsLocalNotifications =>
      Platform.isAndroid ||
      Platform.isIOS ||
      Platform.isLinux ||
      Platform.isWindows;
static bool get supportsInAppWebView =>
      Platform.isAndroid ||
      Platform.isIOS ||
      isOhos;
}

### 7.3 Web stub

```dart
// lib/utils/app_platform_stub.dart
import 'package:flutter/foundation.dart';

```dart
abstract final class AppPlatform {
  AppPlatform._();
static bool get isOhos => false;
  static bool get isWeb => kIsWeb;
  static String get operatingSystem => 'web';
  static bool get supportsFirebase => true;
  static bool get supportsLocalNotifications => false;
  static bool get supportsInAppWebView => false;
}

### 7.4 Decide the capability matrix before coding

| Capability | HarmonyOS approach | Typical reason |
|------------|--------------------|----------------|
| Firebase | Do not initialize | Not covered by the official stack |
| Local notifications | Early-return in business code | Plugin may register but behave poorly |
| In-app WebView | OK via fork + adapter | APIs may lag pub.dev |
| Push | Use Huawei Push (or similar) | Do not assume FCM |
| Some offline native libs | Force online fallback | `.so` / engine not ported |
| Social login | Evaluate; Huawei recommends Account Kit | Google / others may be unavailable |

```dart
Future<void> initializeNotifications() async {
  if (!AppPlatform.supportsLocalNotifications) {
    debugPrint('skip local notifications on ${AppPlatform.operatingSystem}');
    return;
  }
  // Android / iOS init...
}

Done when: The app imports AppPlatform and startup early-returns unsupported SDKs so it does not crash on launch.


Step 8: First real device build and install

8.1 Environment

export DEVECO_APP="/Applications/DevEco-Studio.app"
export DEVECO_SDK_HOME="$DEVECO_APP/Contents/sdk"
export OHOS_BASE_SDK_HOME="$HOME/Library/OpenHarmony/Sdk"
export OHOS_FLUTTER="$HOME/path/to/ohos-flutter/bin/flutter"
export PATH="$DEVECO_APP/Contents/tools/hvigor/bin:$DEVECO_APP/Contents/tools/ohpm/bin:$PATH"

cd your_flutter_app
$OHOS_FLUTTER pub get

8.2 Build a debug HAP

$OHOS_FLUTTER build hap --debug

Typical output:

build/ohos/hap/entry-default-signed.hap

8.3 Install and start

hdc list targets
hdc -t <deviceId> install -r build/ohos/hap/entry-default-signed.hap
hdc -t <deviceId> shell aa start -a EntryAbility -b com.example.myapp

Replace EntryAbility / com.example.myapp with values from your module.json5 / app.json5.

8.4 Failure triage

FailureCheck first
SDK not foundSteps 1 and 4 paths
Signing failureStep 5 Signing Configs / material
MissingPlugin / native compile errorsStep 6 overrides
Analyzer TargetPlatform.ohosStep 6.4 vendor patch
White screen / crash on launchStep 7 capability gating (Firebase etc.)

Done when: The device opens your home screen (features may be incomplete, but it starts).


Step 9: Feature adaptation (priority order)

Do not rewrite ten modules in parallel. Suggested order:

9.1 Startup / crash gating
9.2 Local storage
9.3 Network + account login
9.4 WebView / file paths
9.5 Payments + deep links
9.6 A/V, TTS, push, and other verticals

9.1 Startup: stop the crash first

Future<void> bootstrap() async {
  if (AppPlatform.supportsFirebase) {
    await initFirebase();
  }
  if (AppPlatform.supportsLocalNotifications) {
    await initNotifications();
  }
  // Shared init...
}

Rule: anything Android always inits but HarmonyOS has no equivalent must be gated.

9.2 Storage: wrap SharedPreferences quirks

Some OHOS implementations throw RangeError after a successful write. Shared wrapper:

Future<bool> setOhosCompatible(
  String key,
  Future<bool> Function() action,
) async {
  try {
    return await action();
  } catch (e) {
    if (AppPlatform.isOhos && e is RangeError) {
      debugPrint('OHOS preferences write quirk, treat as success: $key');
      return true;
    }
    rethrow;
  }
}

```dart
Future<bool> setString(String key, String value) {
  return setOhosCompatible(key, () => prefs.setString(key, value));
}

Do not blindly copy Android directories:

```dart
Future<Directory> getAppDirectory() async {
  if (AppPlatform.isOhos) {
    final dir = Directory(
      '${Directory.systemTemp.path}${Platform.pathSeparator}my_app_cache',
    );
    if (!dir.existsSync()) {
      await dir.create(recursive: true);
    }
    return dir;
  }
  if (Platform.isIOS) {
    return getApplicationDocumentsDirectory();
  }
  return getApplicationSupportDirectory();
}

9.3 Huawei Account login (recommended path)

Google Sign-In is usually unavailable. Prefer Account Kit:

  1. ArkTS plugin — call Account Kit; return authCode / idToken via MethodChannel
  2. Dart service — invoke channel; validate state
  3. UI — show Huawei button only when AppPlatform.isOhos
  4. Backend — exchange code / ID token for your session

Critical: custom plugins are not in GeneratedPluginRegistrant — register manually:

// entry/src/main/ets/entryability/EntryAbility.ets
import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos'
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant'
import HuaweiLoginPlugin from '../plugins/HuaweiLoginPlugin'

```text
export default class EntryAbility extends FlutterAbility {
  configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    flutterEngine.getPlugins()?.add(new HuaweiLoginPlugin())
  }
}

**Dart channel sketch:**

```dart
class HuaweiLoginService {
  static const _channel = MethodChannel('com.example.myapp/huawei_login');

```dart
Future<Map<String, String>?> auth() async {
    final state = _randomState();
    final raw = await _channel.invokeMethod<Map<dynamic, dynamic>>(
      'authById',
      {
        'forceLogin': false,
        'state': state,
        'nonce': _randomState(),
        'idTokenSignAlgorithm': 'PS256',
      },
    );
    if (raw == null) return null;
    if ((raw['errorCode'] as int? ?? -1) != 0) return null;
    if (raw['state'] != null && raw['state'] != state) return null;
    return {
      'authorizationCode': '${raw['authCode'] ?? ''}',
      'idToken': '${raw['idToken'] ?? ''}',
      'openId': '${raw['openID'] ?? ''}',
      'unionId': '${raw['unionID'] ?? ''}',
    };
  }
}

**UI:**

```dart
if (AppPlatform.isOhos)
  HuaweiLoginButton(onTap: controller.loginWithHuawei),

Store-level pitfall: Ability metadata client_id in module.json5 must equal the current signing profile’s appIdentifier. Debug and release IDs differ. See Step 10.

"metadata": [
  { "name": "client_id", "value": "appIdentifier from current p7b profile" },
  { "name": "app_id", "value": "same as above" }
]

9.4 WebView: adapter layer for API drift

OHOS fork of webview_flutter may lack newer pub.dev APIs (e.g. some loadFileWithParams). Centralize:

class LocalHtmlLoader {
  static Future<void> loadFile({
    required WebViewController controller,
    required Uri fileUri,
  }) async {
    if (fileUri.scheme != 'file') {
      await controller.loadRequest(fileUri);
      return;
    }
    await controller.loadFile(fileUri.toFilePath());
  }
}

9.5 Payments and deep links

  1. Configure querySchemes and Ability skills / URIs in module.json5
  2. If a payment SDK ships routes without ArkTS pages, you may need a stub to compile
  3. Product-wise, ship only verified payment rails first; enable others later

9.6 A/V, TTS, other native libraries

Future<void> speak(String text) async {
  if (AppPlatform.isOhos) {
    // Do not load unported offline .so
    return speakWithCloudTts(text);
  }
  return speakWithOfflineEngine(text);
}

Hide unavailable engines in settings so users cannot tap into a crash.

Done when: Login, basic persistence, and core screens work on device; unsupported features degrade explicitly.


Step 10: Release signing and store .app

10.1 HAP vs APP

ArtifactCommandUse
.hapflutter build hap --releaseDevice install / beta
.appflutter build app --releaseAppGallery submission

Submitting a release HAP to the store is wrong — the store wants an APP package.

10.2 Obtain and configure release certificates

  1. Create the app in AppGallery Connect with the same bundleName
  2. Apply for release cert + profile (release / app_gallery)
  3. Configure a release (or custom) profile in DevEco Signing Configs
  4. Point product signingConfig in build-profile.json5 to it

Keep at least two configs:

NameWhen
Debug signingDaily device builds
Release signingStore packages

10.3 After switching to release signing, sync Huawei clientId immediately

  1. Read appIdentifier from the current .p7b profile
  2. Write it into module.json5 client_id / app_id
  3. Update resource strings such as huawei_client_id if present
  4. Point backend OAuth to the release credentials

Otherwise: debug Huawei login works, every store user fails login.

Automate “switch signing + sync clientId”; forbid hand edits.

10.4 Build the release APP

# 1) signingConfig is release
# 2) client_id matches release profile
# 3) pubspec / AppScope / local.properties versions match

```text
$OHOS_FLUTTER build app --release \
  --build-name=1.0.0 \
  --build-number=100

**Success looks like:**

```text
✓ Built build/ohos/app/ohos-default-signed.app

Upload that .app. Keep a versioned local archive for rollback.

10.5 Pre-submit checklist

  • Versions match in three places (pubspec / AppScope / build args)
  • Artifact is a release-signed .app
  • Huawei login verified on the release package (debug does not count)
  • Payments / deep-link returns verified on release
  • Degraded capabilities match product intent
  • Privacy / permissions match store listing

Extra pitfalls cheatsheet

A. Commercial SDKs missing route page sources

**Symptom:** ohpm package installs, but routes point to missing `.ts` pages → ArkTS compile fails.
**Approach:** Generate a minimal stub page before build; hide unfinished payment options in product UI.

B. HarmonyOS deps “bleed” into Android

E.g. after adding a social SDK, Android Manifest Merger fails on usesCleartextTraffic:

<application
    android:usesCleartextTraffic="false"
    tools:replace="android:usesCleartextTraffic">

Lesson: OHOS-only dependency choices still affect other platforms — regress the whole matrix.

C. Registered plugin ≠ supported capability

A notifications plugin in GeneratedPluginRegistrant does not mean HarmonyOS notifications work. Product promises should follow your Dart supports* flags.

D. Long-lived dual-branch cost

If main development is on dev and HarmonyOS lives on a long branch:

  • Merge often, or you will lag by months of features
  • After merges, re-test login, payments, WebView, storage, and manual plugin registration

Minimum success path (three milestones)

Milestone A — Environment works

  1. DevEco + SDK layout ready
  2. OHOS Flutter can build hap
  3. DevEco opens ohos/
  4. Debug signing works

Milestone B — Package installs

  1. pubspec overrides let the project compile
  2. AppPlatform + startup gating: no instant crash
  3. build hap --debug installs and launches

Milestone C — Store-ready

  1. Core flows work (at least login + main feature)
  2. Release signing + clientId aligned
  3. build app --release produces .app that passes store validation

Closing

Porting Flutter to HarmonyOS is hard because of:

  1. Toolchain change (fork Flutter + DevEco + SDK layout)
  2. Plugin supply chain rebuild (full-chain dependency_overrides)
  3. Capability redraw (what to support vs degrade)
  4. Release signing aligned with account systems (especially Huawei clientId)
Follow **Step 1 → Step 10**. Each step has a “done when” bar — use it.
If stuck, classify the failure as **environment / signing / dependencies / feature gating** before rewriting more business code.

Good luck shipping your first store-ready HarmonyOS build.