feat: harmonyOS初步支持

This commit is contained in:
chaoszhu
2026-06-25 23:04:43 +08:00
parent 8fcdc18673
commit 3bf7790046
47 changed files with 1160 additions and 191 deletions
+7 -22
View File
@@ -4,8 +4,8 @@
# This file should be version controlled and should not be manually edited.
version:
revision: "00b0c91f06209d9e4a41f71b7a512d6eb3b9c694"
channel: "stable"
revision: "cedbd616b9a5b055c9ad2c7acdf3f5a504d73652"
channel: "[user-branch]"
project_type: app
@@ -13,26 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
- platform: android
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
- platform: ios
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
- platform: linux
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
- platform: macos
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
- platform: web
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
- platform: windows
create_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
base_revision: 00b0c91f06209d9e4a41f71b7a512d6eb3b9c694
create_revision: cedbd616b9a5b055c9ad2c7acdf3f5a504d73652
base_revision: cedbd616b9a5b055c9ad2c7acdf3f5a504d73652
- platform: ohos
create_revision: cedbd616b9a5b055c9ad2c7acdf3f5a504d73652
base_revision: cedbd616b9a5b055c9ad2c7acdf3f5a504d73652
# User provided section
+119
View File
@@ -0,0 +1,119 @@
# EasyNode HarmonyOS (OHOS) 构建说明
## 前置环境
| 工具 | 版本要求 | 说明 |
|------|----------|------|
| DevEco Studio | 5.0+ | HarmonyOS IDE、SDK 管理、签名配置 |
| HarmonyOS SDK | 5.0.0(12)+ | 编译目标 |
| Flutter-OH SDK | 3.41.10-ohos | 社区 fork(CPF-Flutter),命令行工具为 `flutter-oh` |
| Node.js | ≥ 18.15 | hvigor 构建系统依赖 |
| JDK | 17 | 构建工具链依赖 |
## dependency_overrides 说明
Flutter 标准插件不识别 OHOS 平台,需要通过 `dependency_overrides` 指向 CPF-Flutter 社区适配版本:
| 插件 | 来源 | 分支/版本 | 说明 |
|------|------|-----------|------|
| `shared_preferences` | `CPF-Flutter/flutter_packages` | 默认分支 | 键值存储 |
| `path_provider` | `CPF-Flutter/flutter_packages` | 默认分支 | 文件路径 |
| `url_launcher` | `CPF-Flutter/flutter_packages` | 默认分支 | 打开外部链接 |
| `flutter_secure_storage` | `CPF-Flutter/fluttertpc_flutter_secure_storage` | `br_v9.2.4_ohos` | 加密存储 |
| `flutter_secure_storage_ohos` | 同上 | `br_v9.2.4_ohos` | OHOS 平台实现 |
| `package_info_plus` | `CPF-Flutter/flutter_plus_plugins` | `br_package_info_plus-v8.1.0_ohos` | 包信息 |
| `share_plus` | `CPF-Flutter/flutter_plus_plugins` | `br_share_plus-v12.0.1_ohos` | 文件分享 |
额外依赖(非 override,直接添加到 dependencies):
| 插件 | 来源 | 分支/版本 | 说明 |
|------|------|-----------|------|
| `file_picker_ohos` | `CPF-Flutter/fluttertpc_file_picker` | `br_v10.3.8_ohos` | 文件选择器,全平台替代包 |
## ohos_patch.sh 运行时补丁
部分 pub cache 中的包不识别 OHOS 平台,需要在 `pub get` 后运行 `ohos_patch.sh` 进行修补。**每次执行 `pub get` 后都需要重新运行。**
补丁内容:
| 目标包 | 修补内容 |
|--------|----------|
| `code_assets` os.dart | 添加 `OS.ohos` 枚举值到 OS 类和 values 列表 |
| `code_assets` syntax.g.dart | 添加 `OSSyntax.ohos` 枚举值到 OSSyntax 类和 values 列表 |
| `flutter_secure_storage` | 将 `throw UnsupportedError` 替换为 `return <String, String>{}` 兜底 |
| `xterm` shortcuts.dart | 在 switch 语句中添加 `default:` 分支避免 OHOS 平台抛异常 |
补丁覆盖两个 pub cache 目录:
- `~/.pub-cache/hosted/pub.dev/`
- `~/PUB/hosted/pub.flutter-io.cn/`(PUB_CACHE 环境变量指定)
- `~/PUB/git/`(git 依赖缓存)
## 构建步骤
### Debug 构建 & 运行
```bash
cd native
# 1. 获取依赖
flutter-oh pub get
# 2. 运行补丁(每次 pub get 后必须执行)
bash ohos_patch.sh
# 3. 连接设备后运行
flutter-oh run
```
### Release 构建(HAP)
```bash
cd native
flutter-oh pub get
bash ohos_patch.sh
flutter-oh build hap --release
```
构建产物位于 `ohos/entry/build/default/outputs/` 目录,后缀为 `.hap`。
### Release 构建(APP,用于上架应用市场)
上架华为应用市场(AppGallery)需要提交 `.app` 格式的包,而非 `.hap`。
**HAP vs APP 区别:**
- **HAP** (HarmonyOS Ability Package):模块级包,每个 module 编译产出一个 `.hap`,可用于本地安装测试
- **APP** (Application Package):应用级包,将一个或多个 HAP 打包在一起,**上架应用市场必须提交此格式**
构建方式:
```bash
flutter-oh build app --release
```
### 清理构建缓存
遇到编译异常时可尝试清理缓存后重新构建:
```bash
rm -rf .dart_tool/hooks_runner/
rm -rf build/
rm -rf ohos/entry/build/
flutter-oh pub get
bash ohos_patch.sh
flutter-oh run
```
## 签名配置
Debug 签名由 DevEco Studio 自动生成,配置在 `ohos/build-profile.json5` 中。
Release 签名需要华为开发者证书,请在 DevEco Studio 中配置:
- Project Structure → Signing Configs → 添加 Release 签名
- 或手动编辑 `ohos/build-profile.json5` 的 `signingConfigs`
## 注意事项
- Flutter-OH SDK 是社区维护的 fork(CPF-Flutter),非 Google 官方支持
- `build-profile.json5` 中的 debug 签名路径为本地路径,不应提交到仓库
- 当前 CI/CD 暂无 OHOS 构建流水线,需本地构建
@@ -1,3 +1,5 @@
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart';
bool? debugIosAppStoreComplianceOverride;
@@ -7,3 +9,8 @@ bool get isIosAppStoreCompliance {
if (override != null) return override;
return !kIsWeb && defaultTargetPlatform == TargetPlatform.iOS;
}
bool get isOhosPlatform {
if (kIsWeb) return false;
return Platform.operatingSystem == 'ohos';
}
@@ -12,6 +12,7 @@ const nativeVersionFeedFallbackUrl =
'https://raw.githubusercontent.com/chaos-zhu/easynode/refs/heads/main/server/version.json';
const nativeGitHubReleaseUrl = 'https://github.com/chaos-zhu/easynode/releases';
const nativeIosReleaseUrl = '';
const nativeOhosReleaseUrl = '';
class AppUpdateInfo {
const AppUpdateInfo({
@@ -66,6 +67,13 @@ class AppUpdateInfo {
if (isIosAppStoreCompliance) {
return (latest['iosReleaseUrl'] ?? nativeIosReleaseUrl).toString().trim();
}
if (isOhosPlatform) {
final ohosUrl = (latest['ohosReleaseUrl'] ?? '').toString().trim();
if (ohosUrl.isNotEmpty) return ohosUrl;
return (latest['nativeReleaseUrl'] ?? nativeGitHubReleaseUrl)
.toString()
.trim();
}
return (latest['nativeReleaseUrl'] ?? nativeGitHubReleaseUrl)
.toString()
.trim();
+17 -2
View File
@@ -4,6 +4,21 @@ import 'app.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final app = await EasyNodeApp.bootstrap();
runApp(app);
try {
final app = await EasyNodeApp.bootstrap();
runApp(app);
} catch (e, s) {
runApp(
MaterialApp(
home: Scaffold(
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: SelectableText('Bootstrap error:\n$e\n\n$s'),
),
),
),
),
);
}
}
@@ -6,10 +6,14 @@
#include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
flutter_secure_storage_linux
url_launcher_linux
)
@@ -6,18 +6,20 @@ import FlutterMacOS
import Foundation
import file_picker
import file_picker_ohos
import file_selector_macos
import flutter_secure_storage_macos
import package_info_plus
import path_provider_foundation
import share_plus
import shared_preferences_foundation
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
+23
View File
@@ -0,0 +1,23 @@
/node_modules
/oh_modules
/local.properties
/.idea
**/build
/.hvigor
.cxx
/.clangd
/.clang-format
/.clang-tidy
**/.test
**/BuildProfile.ets
**/oh-package-lock.json5
/package.json
/package-lock.json
# 签名配置包含本地证书路径和密钥,不应提交
/build-profile.json5
**/src/main/resources/rawfile/flutter_assets/
**/libs/**/libapp.so
**/libs/**/libflutter.so
**/libs/**/libvmservice_snapshot.so
+10
View File
@@ -0,0 +1,10 @@
{
"app": {
"bundleName": "io.github.chaoszhu.easynode_native",
"vendor": "easynode_native",
"versionCode": 1000000,
"versionName": "1.0.0",
"icon": "$media:app_icon",
"label": "$string:app_name"
}
}
@@ -0,0 +1,8 @@
{
"string": [
{
"name": "app_name",
"value": "EasyNode"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

+7
View File
@@ -0,0 +1,7 @@
/node_modules
/oh_modules
/.preview
/build
/.cxx
/.test
GeneratedPluginRegistrant.ets
+15
View File
@@ -0,0 +1,15 @@
{
"apiType": 'stageMode',
"buildOption": {
},
"targets": [
{
"name": "default",
"runtimeOS": "HarmonyOS"
},
{
"name": "ohosTest",
}
]
}
+7
View File
@@ -0,0 +1,7 @@
// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.
import { hapTasks } from '@ohos/hvigor-ohos-plugin';
export default {
system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins: [] /* Custom plugin to extend the functionality of Hvigor. */
}
+11
View File
@@ -0,0 +1,11 @@
{
"name": "entry",
"version": "1.0.0",
"description": "Please describe the basic information.",
"main": "",
"author": "",
"license": "",
"dependencies": {},
}
@@ -0,0 +1,10 @@
import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
export default class EntryAbility extends FlutterAbility {
configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
GeneratedPluginRegistrant.registerWith(flutterEngine)
}
}
@@ -0,0 +1,24 @@
import common from '@ohos.app.ability.common';
import { FlutterPage } from '@ohos/flutter_ohos'
let storage = LocalStorage.getShared()
const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS'
@Entry(storage)
@Component
struct Index {
private context = getContext(this) as common.UIAbilityContext
@LocalStorageLink('viewId') viewId: string = "";
build() {
Column() {
FlutterPage({ viewId: this.viewId })
}
}
onBackPress(): boolean {
this.context.eventHub.emit(EVENT_BACK_PRESS)
return true
}
}
+40
View File
@@ -0,0 +1,40 @@
{
"module": {
"name": "entry",
"type": "entry",
"description": "$string:module_desc",
"mainElement": "EntryAbility",
"deviceTypes": [
"phone"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:main_pages",
"abilities": [
{
"name": "EntryAbility",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"description": "$string:EntryAbility_desc",
"icon": "$media:icon",
"label": "$string:EntryAbility_label",
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"exported": true,
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
]
}
],
"requestPermissions": [
{"name" : "ohos.permission.INTERNET"},
]
}
}
@@ -0,0 +1,8 @@
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
}
]
}
@@ -0,0 +1,16 @@
{
"string": [
{
"name": "module_desc",
"value": "module description"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "EasyNode"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

@@ -0,0 +1,5 @@
{
"src": [
"pages/Index"
]
}
@@ -0,0 +1,16 @@
{
"string": [
{
"name": "module_desc",
"value": "module description"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "EasyNode"
}
]
}
@@ -0,0 +1,8 @@
{
"string": [
{
"name": "enable_impeller",
"value": "true"
}
]
}
@@ -0,0 +1,37 @@
{
"SWITCH": 1,
"TRANSLATE": [
{
"serial_number": 1,
"min": 800,
"max": -1,
"preferred_fps": 90
},
{
"serial_number": 2,
"min": 77,
"max": 800,
"preferred_fps": 120
},
{
"serial_number": 3,
"min": 46,
"max": 77,
"preferred_fps": 90
},
{
"serial_number": 4,
"min": 10,
"max": 46,
"preferred_fps": 72
},
{
"serial_number": 5,
"min": 0,
"max": 10,
"preferred_fps": 60
}
],
"SCALE": [],
"ROTATION": []
}
@@ -0,0 +1,16 @@
{
"string": [
{
"name": "module_desc",
"value": "模块描述"
},
{
"name": "EntryAbility_desc",
"value": "description"
},
{
"name": "EntryAbility_label",
"value": "EasyNode"
}
]
}
@@ -0,0 +1,35 @@
import hilog from '@ohos.hilog';
import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'
export default function abilityTest() {
describe('ActsAbilityTest', function () {
// Defines a test suite. Two parameters are supported: test suite name and test suite function.
beforeAll(function () {
// Presets an action, which is performed only once before all test cases of the test suite start.
// This API supports only one parameter: preset action function.
})
beforeEach(function () {
// Presets an action, which is performed before each unit test case starts.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: preset action function.
})
afterEach(function () {
// Presets a clear action, which is performed after each unit test case ends.
// The number of execution times is the same as the number of test cases defined by **it**.
// This API supports only one parameter: clear action function.
})
afterAll(function () {
// Presets a clear action, which is performed after all test cases of the test suite end.
// This API supports only one parameter: clear action function.
})
it('assertContain',0, function () {
// Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.
hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');
let a = 'abc'
let b = 'b'
// Defines a variety of assertion methods, which are used to declare expected boolean conditions.
expect(a).assertContain(b)
expect(a).assertEqual(a)
})
})
}
@@ -0,0 +1,5 @@
import abilityTest from './Ability.test'
export default function testsuite() {
abilityTest()
}
@@ -0,0 +1,48 @@
import UIAbility from '@ohos.app.ability.UIAbility';
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
import hilog from '@ohos.hilog';
import { Hypium } from '@ohos/hypium';
import testsuite from '../test/List.test';
import window from '@ohos.window';
export default class TestAbility extends UIAbility {
onCreate(want, launchParam) {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');
hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');
hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? '');
var abilityDelegator: any
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
var abilityDelegatorArguments: any
abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');
Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite)
}
onDestroy() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');
}
onWindowStageCreate(windowStage: window.WindowStage) {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');
windowStage.loadContent('testability/pages/Index', (err, data) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s',
JSON.stringify(data) ?? '');
});
}
onWindowStageDestroy() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');
}
onForeground() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');
}
onBackground() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');
}
}
@@ -0,0 +1,36 @@
import hilog from '@ohos.hilog';
@Entry
@Component
struct Index {
aboutToAppear() {
hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear');
}
@State message: string = 'Hello World'
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
Button() {
Text('next page')
.fontSize(20)
.fontWeight(FontWeight.Bold)
}.type(ButtonType.Capsule)
.margin({
top: 20
})
.backgroundColor('#0D9FFB')
.width('35%')
.height('5%')
.onClick(()=>{
})
}
.width('100%')
}
.height('100%')
}
}
@@ -0,0 +1,50 @@
import hilog from '@ohos.hilog';
import TestRunner from '@ohos.application.testRunner';
import AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';
var abilityDelegator = undefined
var abilityDelegatorArguments = undefined
async function onAbilityCreateCallback() {
hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');
}
async function addAbilityMonitorCallback(err: any) {
hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');
}
export default class OpenHarmonyTestRunner implements TestRunner {
constructor() {
}
onPrepare() {
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare ');
}
async onRun() {
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');
abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()
abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()
var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility'
let lMonitor = {
abilityName: testAbilityName,
onAbilityCreate: onAbilityCreateCallback,
};
abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)
var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName
var debug = abilityDelegatorArguments.parameters['-D']
if (debug == 'true')
{
cmd += ' -D'
}
hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd);
abilityDelegator.executeShellCommand(cmd,
(err: any, d: any) => {
hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? '');
hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? '');
hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? '');
})
hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');
}
}
@@ -0,0 +1,37 @@
{
"module": {
"name": "entry_test",
"type": "feature",
"description": "$string:module_test_desc",
"mainElement": "TestAbility",
"deviceTypes": [
"phone"
],
"deliveryWithInstall": true,
"installationFree": false,
"pages": "$profile:test_pages",
"abilities": [
{
"name": "TestAbility",
"srcEntry": "./ets/testability/TestAbility.ets",
"description": "$string:TestAbility_desc",
"icon": "$media:icon",
"label": "$string:TestAbility_label",
"exported": true,
"startWindowIcon": "$media:icon",
"startWindowBackground": "$color:start_window_background",
"skills": [
{
"actions": [
"action.system.home"
],
"entities": [
"entity.system.home"
]
}
]
}
]
}
}
@@ -0,0 +1,8 @@
{
"color": [
{
"name": "start_window_background",
"value": "#FFFFFF"
}
]
}
@@ -0,0 +1,16 @@
{
"string": [
{
"name": "module_test_desc",
"value": "test ability description"
},
{
"name": "TestAbility_desc",
"value": "the test ability"
},
{
"name": "TestAbility_label",
"value": "test label"
}
]
}
@@ -0,0 +1,5 @@
{
"src": [
"testability/pages/Index"
]
}
+6
View File
@@ -0,0 +1,6 @@
{
"modelVersion": "5.0.0",
"dependencies": {
}
}
+4
View File
@@ -0,0 +1,4 @@
import path from 'path'
import { injectNativeModules } from 'flutter-hvigor-plugin';
injectNativeModules(__dirname, path.dirname(__dirname))
+8
View File
@@ -0,0 +1,8 @@
import path from 'path'
import { appTasks } from '@ohos/hvigor-ohos-plugin';
import { flutterHvigorPlugin } from 'flutter-hvigor-plugin';
export default {
system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins:[flutterHvigorPlugin(path.dirname(__dirname))] /* Custom plugin to extend the functionality of Hvigor. */
}
+13
View File
@@ -0,0 +1,13 @@
{
"modelVersion": "5.0.0",
"name": "easynode_native",
"version": "1.0.0",
"description": "Please describe the basic information.",
"main": "",
"author": "",
"license": "",
"dependencies": {},
"devDependencies": {
"@ohos/hypium": "1.0.6"
}
}
+59
View File
@@ -0,0 +1,59 @@
#!/bin/bash
# Flutter OHOS 构建 Patch
# 为 code_assets 和 xterm 包添加 ohos 平台支持
# 重新执行 pub get 后可能需要再次运行
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
count=0
# 清除 hooks_runner 编译缓存,确保 patch 生效
if [ -d "$SCRIPT_DIR/.dart_tool/hooks_runner" ]; then
rm -rf "$SCRIPT_DIR/.dart_tool/hooks_runner"
echo "cleared: .dart_tool/hooks_runner cache"
fi
# patch code_assets os.dart
for f in ~/.pub-cache/hosted/*/code_assets-*/lib/src/code_assets/os.dart ~/PUB/hosted/*/code_assets-*/lib/src/code_assets/os.dart; do
[ -f "$f" ] || continue
grep -q "ohos" "$f" && continue
sed -i '' "s/static const OS windows = OS._('windows');/static const OS windows = OS._('windows');\n\n static const OS ohos = OS._('ohos');/" "$f"
sed -i '' "s/static const List<OS> values = \[android, fuchsia, iOS, linux, macOS, windows\];/static const List<OS> values = [android, fuchsia, iOS, linux, macOS, windows, ohos];/" "$f"
echo "patched: $f"
((count++))
done
# patch code_assets syntax.g.dart
for f in ~/.pub-cache/hosted/*/code_assets-*/lib/src/code_assets/syntax.g.dart ~/PUB/hosted/*/code_assets-*/lib/src/code_assets/syntax.g.dart; do
[ -f "$f" ] || continue
grep -q "ohos" "$f" && continue
sed -i '' "s/static const windows = OSSyntax._('windows');/static const windows = OSSyntax._('windows');\n\n static const ohos = OSSyntax._('ohos');/" "$f"
sed -i '' "s/static const List<OSSyntax> values = \[android, iOS, linux, macOS, windows\];/static const List<OSSyntax> values = [android, iOS, linux, macOS, windows, ohos];/" "$f"
echo "patched: $f"
((count++))
done
# patch flutter_secure_storage: _selectOptions 添加 OHOS fallback
for f in ~/.pub-cache/git/fluttertpc_flutter_secure_storage-*/flutter_secure_storage/lib/flutter_secure_storage.dart ~/PUB/git/fluttertpc_flutter_secure_storage-*/flutter_secure_storage/lib/flutter_secure_storage.dart; do
[ -f "$f" ] || continue
grep -q "return <String, String>{};" "$f" && continue
sed -i '' "s/throw UnsupportedError(UNSUPPORTED_PLATFORM);/return <String, String>{};/" "$f"
echo "patched: $f"
((count++))
done
# patch xterm shortcuts.dart
for f in ~/.pub-cache/hosted/*/xterm-*/lib/src/ui/shortcut/shortcuts.dart ~/PUB/hosted/*/xterm-*/lib/src/ui/shortcut/shortcuts.dart; do
[ -f "$f" ] || continue
grep -q "default:" "$f" && continue
sed -i '' 's/case TargetPlatform.macOS:/case TargetPlatform.macOS:\n default:/' "$f"
echo "patched: $f"
((count++))
done
if [ $count -eq 0 ]; then
echo "all files already patched."
else
echo "done. patched $count file(s)."
fi
+351 -163
View File
File diff suppressed because it is too large Load Diff
+44 -1
View File
@@ -4,7 +4,7 @@ publish_to: 'none'
version: 1.0.1+1
environment:
sdk: ^3.11.5
sdk: ^3.11.0
dependencies:
flutter:
@@ -16,6 +16,15 @@ dependencies:
cookie_jar: ^4.0.8
dio_cookie_manager: ^3.1.1
flutter_secure_storage: ^9.2.2
flutter_secure_storage_ohos:
git:
url: "https://gitcode.com/CPF-Flutter/fluttertpc_flutter_secure_storage.git"
ref: "br_v9.2.4_ohos"
path: "flutter_secure_storage_ohos"
file_picker_ohos:
git:
url: "https://gitcode.com/CPF-Flutter/fluttertpc_file_picker.git"
ref: "br_v10.3.8_ohos"
shared_preferences: ^2.3.2
pointycastle: ^3.9.1
basic_utils: ^5.7.0
@@ -59,6 +68,40 @@ flutter_launcher_icons:
generate: true
image_path: "assets/logo_v2_03.png"
dependency_overrides:
shared_preferences:
git:
url: "https://gitcode.com/CPF-Flutter/flutter_packages.git"
path: "packages/shared_preferences/shared_preferences"
path_provider:
git:
url: "https://gitcode.com/CPF-Flutter/flutter_packages.git"
path: "packages/path_provider/path_provider"
url_launcher:
git:
url: "https://gitcode.com/CPF-Flutter/flutter_packages.git"
path: "packages/url_launcher/url_launcher"
flutter_secure_storage:
git:
url: "https://gitcode.com/CPF-Flutter/fluttertpc_flutter_secure_storage.git"
ref: "br_v9.2.4_ohos"
path: "flutter_secure_storage"
flutter_secure_storage_ohos:
git:
url: "https://gitcode.com/CPF-Flutter/fluttertpc_flutter_secure_storage.git"
ref: "br_v9.2.4_ohos"
path: "flutter_secure_storage_ohos"
package_info_plus:
git:
url: "https://gitcode.com/CPF-Flutter/flutter_plus_plugins.git"
ref: "br_package_info_plus-v8.1.0_ohos"
path: "packages/package_info_plus/package_info_plus"
share_plus:
git:
url: "https://gitcode.com/CPF-Flutter/flutter_plus_plugins.git"
ref: "br_share_plus-v12.0.1_ohos"
path: "packages/share_plus/share_plus"
flutter:
uses-material-design: true
assets:
@@ -6,11 +6,14 @@
#include "generated_plugin_registrant.h"
#include <file_selector_windows/file_selector_windows.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <share_plus/share_plus_windows_plugin_c_api.h>
#include <url_launcher_windows/url_launcher_windows.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
SharePlusWindowsPluginCApiRegisterWithRegistrar(
@@ -3,6 +3,7 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows
flutter_secure_storage_windows
share_plus
url_launcher_windows
+1 -1
View File
@@ -22,7 +22,7 @@
"license": "ISC",
"scripts": {
"dev": "concurrently \"yarn workspace web run dev\" \"yarn workspace server run local\"",
"clean": "yarn clean:web-server && yarn clean:native",
"clean": "yarn clean:native && yarn clean:web-server",
"clean:web-server": "rimraf web/node_modules server/node_modules node_modules",
"clean:native": "rimraf native/build native/.dart_tool native/.pub native/.pub-cache native/coverage native/android/.gradle native/android/app/build native/ios/Pods native/ios/.symlinks native/ios/Flutter/.last_build_id",
"android": "cd native && flutter run -d android",
+2
View File
@@ -5,6 +5,7 @@
"nativeVersion": "native-v1.0.1",
"nativeReleaseUrl": "https://github.com/chaos-zhu/easynode/releases",
"iosReleaseUrl": "",
"ohosReleaseUrl": "",
"features": [
"原生Android、IOS移动端正式版发布,多功能更新",
"按实例鉴权,移除IP统计",
@@ -23,6 +24,7 @@
"nativeVersion": "native-v0.1.1",
"nativeReleaseUrl": "https://github.com/chaos-zhu/easynode/releases",
"iosReleaseUrl": "",
"ohosReleaseUrl": "",
"features": [
"原生Android、IOS移动端内测版发布",
"按实例鉴权,移除IP统计",