1. 项目背景与核心需求
Flutter作为Google推出的跨平台开发框架,在OpenHarmony生态中的应用正逐渐成为开发者关注的热点。这次我们要实现的是一个美食烹饪助手App的核心功能——收藏菜谱模块。这个功能看似简单,但在OpenHarmony环境下使用Flutter开发,会遇到一些特有的技术挑战。
在实际开发中,我发现很多教程只演示基础功能实现,却忽略了真实项目中的关键细节。比如:
- 如何设计既符合OpenHarmony设计规范又保持Flutter跨平台特性的UI?
- 本地存储方案选型时,如何平衡性能和开发效率?
- 在多设备同步场景下,数据一致性如何保证?
2. 开发环境准备与项目初始化
2.1 环境配置要点
在Windows+Ubuntu双系统下搭建Flutter for OpenHarmony环境时,有几个容易踩坑的地方:
-
Flutter SDK版本选择:
bash复制
flutter channel stable flutter upgrade必须使用3.0以上版本才能良好支持OpenHarmony,我实测3.7.3版本最稳定。
-
OpenHarmony工具链配置:
- 需要安装DevEco Studio 3.1 Beta1及以上版本
- 配置ohpm包管理器时,注意设置国内镜像源:
bash复制ohpm config set registry https://repo.harmonyos.com/ohpm/
-
环境变量配置:
在~/.bashrc中添加:bash复制export OHOS_HOME=/path/to/openharmony export PATH=$PATH:$OHOS_HOME/build-tools/clang/bin
提示:如果遇到"you are applying flutter's main gradle plugin imperatively"警告,需要在android/build.gradle中移除apply plugin: 'com.android.application'这行。
2.2 项目创建与基础架构
使用以下命令创建项目:
bash复制flutter create --platforms=ohos recipe_app
项目目录结构调整建议:
code复制lib/
├── models/ # 数据模型
│ └── recipe.dart
├── services/ # 服务层
│ └── storage_service.dart
├── widgets/ # 自定义组件
│ └── recipe_card.dart
└── main.dart # 入口文件
3. 收藏功能的数据层实现
3.1 数据模型设计
在lib/models/recipe.dart中定义菜谱模型:
dart复制class Recipe {
final String id;
final String title;
final String imageUrl;
final int cookingTime;
final List<String> ingredients;
bool isFavorite;
Recipe({
required this.id,
required this.title,
// 其他参数...
this.isFavorite = false,
});
// 添加fromJson/toJson方法便于序列化
}
3.2 本地存储方案选型
在OpenHarmony环境下,我们有几种存储方案可选:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| shared_preferences | 简单易用 | 只支持基础数据类型 | 小量简单数据 |
| hive | 高性能 | 需要模型适配 | 结构化数据存储 |
| sqlite | 功能强大 | 配置复杂 | 复杂查询需求 |
经过实测,我推荐使用hive方案:
bash复制flutter pub add hive hive_flutter
ohpm install @ohos/hive
配置lib/services/storage_service.dart:
dart复制class StorageService {
static final StorageService _instance = StorageService._internal();
late Box<Recipe> _recipeBox;
Future<void> init() async {
await Hive.initFlutter();
Hive.registerAdapter(RecipeAdapter());
_recipeBox = await Hive.openBox<Recipe>('recipes');
}
Future<void> toggleFavorite(String recipeId) async {
final recipe = _recipeBox.get(recipeId);
if (recipe != null) {
recipe.isFavorite = !recipe.isFavorite;
await _recipeBox.put(recipeId, recipe);
}
}
}
4. UI实现与交互设计
4.1 收藏按钮组件
创建lib/widgets/favorite_button.dart:
dart复制class FavoriteButton extends StatelessWidget {
final bool isFavorite;
final VoidCallback onPressed;
const FavoriteButton({
super.key,
required this.isFavorite,
required this.onPressed,
});
@override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(
isFavorite ? Icons.favorite : Icons.favorite_border,
color: isFavorite ? Colors.red : Colors.grey,
),
onPressed: onPressed,
);
}
}
4.2 菜谱卡片集成
在菜谱卡片中使用收藏按钮:
dart复制Widget buildRecipeCard(Recipe recipe) {
return Card(
child: Column(
children: [
// 其他内容...
FavoriteButton(
isFavorite: recipe.isFavorite,
onPressed: () async {
await StorageService.instance.toggleFavorite(recipe.id);
setState(() {});
},
),
],
),
);
}
5. 性能优化与调试技巧
5.1 列表性能优化
当收藏列表变长时,需要使用ListView.builder:
dart复制ListView.builder(
itemCount: recipes.length,
itemBuilder: (context, index) {
return Dismissible(
key: Key(recipes[index].id),
onDismissed: (direction) {
// 处理删除逻辑
},
child: buildRecipeCard(recipes[index]),
);
},
)
5.2 状态管理方案对比
对于更复杂的场景,可以考虑以下状态管理方案:
| 方案 | 复杂度 | 学习曲线 | 适用规模 |
|---|---|---|---|
| setState | 低 | 低 | 小型组件 |
| Provider | 中 | 中 | 中型应用 |
| Riverpod | 高 | 高 | 大型应用 |
我建议中小型项目使用Provider:
dart复制final favoriteRecipesProvider = FutureProvider<List<Recipe>>((ref) async {
final box = await Hive.openBox<Recipe>('recipes');
return box.values.where((r) => r.isFavorite).toList();
});
6. 多设备同步方案探索
6.1 云端同步实现
如果需要实现跨设备收藏同步,可以集成华为云服务:
dart复制import 'package:agconnect_database/agconnect_database.dart';
class CloudSyncService {
final AGConnectCloudDB _cloudDB = AGConnectCloudDB.instance;
Future<void> syncFavorites() async {
// 实现云端同步逻辑
}
}
6.2 冲突解决策略
当本地和云端数据冲突时,可以采用以下策略:
- 时间戳优先:最后修改的版本生效
- 用户选择:出现冲突时提示用户
- 自动合并:智能合并非冲突字段
我推荐方案1的实现:
dart复制DateTime localModified = localRecipe.modifiedAt;
DateTime cloudModified = cloudRecipe.modifiedAt;
if (localModified.isAfter(cloudModified)) {
// 使用本地版本
} else {
// 使用云端版本
}
7. 测试与发布准备
7.1 单元测试要点
测试收藏功能的核心用例:
dart复制void main() {
test('toggle favorite status', () async {
final recipe = Recipe(id: '1', title: 'Test');
await StorageService.instance.init();
await StorageService.instance.saveRecipe(recipe);
// 初始状态
expect(recipe.isFavorite, false);
// 第一次点击
await StorageService.instance.toggleFavorite(recipe.id);
var updated = await StorageService.instance.getRecipe(recipe.id);
expect(updated?.isFavorite, true);
// 第二次点击
await StorageService.instance.toggleFavorite(recipe.id);
updated = await StorageService.instance.getRecipe(recipe.id);
expect(updated?.isFavorite, false);
});
}
7.2 OpenHarmony应用发布
发布前需要配置config.json:
json复制{
"app": {
"bundleName": "com.example.recipeapp",
"version": {
"code": 1,
"name": "1.0.0"
}
}
}
使用以下命令构建HAP包:
bash复制flutter build ohos --release
在实际项目中,我发现收藏功能的用户体验很大程度上取决于动画细节。这里分享一个我优化过的收藏动画实现:
dart复制AnimatedSwitcher(
duration: Duration(milliseconds: 300),
transitionBuilder: (child, animation) {
return ScaleTransition(
scale: animation,
child: child,
);
},
child: Icon(
isFavorite ? Icons.favorite : Icons.favorite_border,
key: ValueKey(isFavorite),
color: isFavorite ? Colors.red : Colors.grey,
),
)
这个实现会让图标在切换时有轻微的缩放效果,比直接切换更符合用户预期。同时,对于网络同步场景,建议添加同步状态指示器:
dart复制StreamBuilder<SyncStatus>(
stream: CloudSyncService.instance.syncStatus,
builder: (context, snapshot) {
if (snapshot.data == SyncStatus.syncing) {
return CircularProgressIndicator(size: 16);
}
return SizedBox.shrink();
},
)
