【maaath】 Flutter for OpenHarmony 翻译词典应用的开发实践
随着 OpenHarmony 生态的蓬勃发展,Flutter for OpenHarmony 作为跨平台开发框架,为开发者提供了使用 Dart 语言同时覆盖多平台的能力。本文将通过一个完整的翻译词典应用实例,展示如何使用 Flutter for OpenHarmony 开发高质量的鸿蒙应用,并分享实际开发中的经验与踩坑心得。本文将基于一个翻译词典应用,详细讲解 Flutter for OpenHa
Flutter for OpenHarmony 翻译词典应用的开发实践
作者:maaath
欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net
前言
随着 OpenHarmony 生态的蓬勃发展,Flutter for OpenHarmony 作为跨平台开发框架,为开发者提供了使用 Dart 语言同时覆盖多平台的能力。本文将通过一个完整的翻译词典应用实例,展示如何使用 Flutter for OpenHarmony 开发高质量的鸿蒙应用,并分享实际开发中的经验与踩坑心得。
项目概述
本文将基于一个翻译词典应用,详细讲解 Flutter for OpenHarmony 的开发流程。该应用具备以下核心功能:
- 多语言翻译支持(中英互译等)
- 语音合成朗读
- 收藏夹管理
- 历史记录
- 学习模式
通过这个实战项目,读者将掌握 Flutter for OpenHarmony 的核心开发模式,包括状态管理、页面导航、数据持久化等关键技能。
项目架构
首先创建项目的目录结构,遵循 Flutter 的最佳实践:
lib/
├── main.dart # 应用入口
├── models/
│ ├── translation_result.dart # 翻译结果模型
│ └── language.dart # 语言模型
├── services/
│ ├── translation_service.dart # 翻译服务
│ ├── storage_service.dart # 存储服务
│ └── tts_service.dart # 语音合成服务
├── viewmodels/
│ └── translation_viewmodel.dart # 视图模型
├── pages/
│ ├── main_page.dart # 主页面
│ ├── translate_page.dart # 翻译页面
│ ├── favorites_page.dart # 收藏页面
│ ├── history_page.dart # 历史页面
│ └── settings_page.dart # 设置页面
└── widgets/
├── language_selector.dart # 语言选择器
└── word_card.dart # 单词卡片组件
核心代码实现
1. 数据模型定义
良好的数据模型是应用架构的基础。我们定义翻译结果和语言相关的模型:
/// 语言枚举
enum Language {
english('en', '英语', 'English', '🇬🇧'),
chinese('zh', '中文', '中文', '🇨🇳'),
japanese('ja', '日语', '日本語', '🇯🇵'),
korean('ko', '韩语', '한국어', '🇰🇷'),
french('fr', '法语', 'Français', '🇫🇷'),
german('de', '德语', 'Deutsch', '🇩🇪');
const Language(this.code, this.name, this.nativeName, this.flag);
final String code;
final String name;
final String nativeName;
final String flag;
}
/// 翻译结果模型
class TranslationResult {
final String id;
final String sourceText;
final String targetText;
final Language sourceLanguage;
final Language targetLanguage;
final String? pronunciation;
final String? targetPronunciation;
final DateTime timestamp;
bool isFavorite;
TranslationResult({
required this.id,
required this.sourceText,
required this.targetText,
required this.sourceLanguage,
required this.targetLanguage,
this.pronunciation,
this.targetPronunciation,
required this.timestamp,
this.isFavorite = false,
});
Map<String, dynamic> toJson() => {
'id': id,
'sourceText': sourceText,
'targetText': targetText,
'sourceLanguage': sourceLanguage.code,
'targetLanguage': targetLanguage.code,
'pronunciation': pronunciation,
'targetPronunciation': targetPronunciation,
'timestamp': timestamp.toIso8601String(),
'isFavorite': isFavorite,
};
factory TranslationResult.fromJson(Map<String, dynamic> json) {
return TranslationResult(
id: json['id'],
sourceText: json['sourceText'],
targetText: json['targetText'],
sourceLanguage: Language.values.firstWhere(
(l) => l.code == json['sourceLanguage'],
orElse: () => Language.english,
),
targetLanguage: Language.values.firstWhere(
(l) => l.code == json['targetLanguage'],
orElse: () => Language.chinese,
),
pronunciation: json['pronunciation'],
targetPronunciation: json['targetPronunciation'],
timestamp: DateTime.parse(json['timestamp']),
isFavorite: json['isFavorite'] ?? false,
);
}
}
2. 翻译服务实现
翻译服务是应用的核心模块,负责与翻译引擎交互:
import 'package:translation_dictionary/models/translation_result.dart';
import 'package:translation_dictionary/models/language.dart';
/// 翻译状态枚举
enum TranslationState {
idle,
loading,
success,
error,
}
/// 翻译服务类
class TranslationService {
// 模拟词典数据,实际项目中应连接真实翻译API
static final Map<String, Map<String, String>> _dictionary = {
'hello': {'zh': '你好', 'pronunciation': '/həˈloʊ/'},
'world': {'zh': '世界', 'pronunciation': '/wɜːrld/'},
'thank': {'zh': '谢谢', 'pronunciation': '/θæŋk/'},
'you': {'zh': '你', 'pronunciation': '/juː/'},
'good': {'zh': '好', 'pronunciation': '/ɡʊd/'},
'morning': {'zh': '早上', 'pronunciation': '/ˈmɔːrnɪŋ/'},
'abundant': {'zh': '丰富的', 'pronunciation': '/əˈbʌndənt/'},
'benevolent': {'zh': '仁慈的', 'pronunciation': '/bɪˈnevələnt/'},
'candid': {'zh': '坦白的', 'pronunciation': '/ˈkændɪd/'},
};
/// 执行翻译
Future<TranslationResult> translate({
required String text,
required Language sourceLang,
required Language targetLang,
}) async {
// 模拟网络延迟
await Future.delayed(const Duration(milliseconds: 500));
final lowerText = text.toLowerCase().trim();
String targetText;
String? pronunciation;
String? targetPronunciation;
if (sourceLang == Language.english && targetLang == Language.chinese) {
// 英译中
final entry = _dictionary[lowerText];
if (entry != null) {
targetText = entry['zh']!;
pronunciation = entry['pronunciation'];
} else {
targetText = '[翻译] $text';
}
} else if (sourceLang == Language.chinese && targetLang == Language.english) {
// 中译英 - 简化处理
targetText = '[Translation] $text';
} else {
targetText = '[翻译] $text';
}
return TranslationResult(
id: DateTime.now().millisecondsSinceEpoch.toString(),
sourceText: text,
targetText: targetText,
sourceLanguage: sourceLang,
targetLanguage: targetLang,
pronunciation: pronunciation,
targetPronunciation: targetPronunciation,
timestamp: DateTime.now(),
);
}
}
3. 存储服务实现
使用 OpenHarmony 的轻量化存储能力保存用户数据:
import 'dart:convert';
import 'package:translation_dictionary/models/translation_result.dart';
/// 存储服务 - 处理收藏夹和历史记录
class StorageService {
// 模拟本地存储
final List<TranslationResult> _favorites = [];
final List<TranslationResult> _history = [];
/// 添加到收藏
Future<void> addToFavorites(TranslationResult result) async {
final favoriteResult = TranslationResult(
id: result.id,
sourceText: result.sourceText,
targetText: result.targetText,
sourceLanguage: result.sourceLanguage,
targetLanguage: result.targetLanguage,
pronunciation: result.pronunciation,
targetPronunciation: result.targetPronunciation,
timestamp: result.timestamp,
isFavorite: true,
);
_favorites.add(favoriteResult);
}
/// 从收藏移除
Future<void> removeFromFavorites(String id) async {
_favorites.removeWhere((r) => r.id == id);
}
/// 获取收藏列表
Future<List<TranslationResult>> getFavorites() async {
return List.from(_favorites);
}
/// 添加到历史
Future<void> addToHistory(TranslationResult result) async {
_history.insert(0, result);
// 限制历史记录数量
if (_history.length > 100) {
_history.removeLast();
}
}
/// 获取历史记录
Future<List<TranslationResult>> getHistory() async {
return List.from(_history);
}
/// 清空历史
Future<void> clearHistory() async {
_history.clear();
}
}
4. ViewModel 模式实现
采用 MVVM 架构,使用 ChangeNotifier 管理状态:
import 'package:flutter/foundation.dart';
import 'package:translation_dictionary/models/translation_result.dart';
import 'package:translation_dictionary/models/language.dart';
import 'package:translation_dictionary/services/translation_service.dart';
import 'package:translation_dictionary/services/storage_service.dart';
/// 翻译视图模型
class TranslationViewModel extends ChangeNotifier {
final TranslationService _translationService = TranslationService();
final StorageService _storageService = StorageService();
// 状态
Language _sourceLanguage = Language.english;
Language _targetLanguage = Language.chinese;
String _sourceText = '';
String _targetText = '';
TranslationState _state = TranslationState.idle;
TranslationResult? _currentResult;
List<TranslationResult> _favorites = [];
List<TranslationResult> _history = [];
bool _isRecording = false;
// Getters
Language get sourceLanguage => _sourceLanguage;
Language get targetLanguage => _targetLanguage;
String get sourceText => _sourceText;
String get targetText => _targetText;
TranslationState get translationState => _state;
TranslationResult? get currentResult => _currentResult;
List<TranslationResult> get favorites => _favorites;
List<TranslationResult> get history => _history;
bool get isRecording => _isRecording;
/// 初始化
Future<void> initialize() async {
_favorites = await _storageService.getFavorites();
_history = await _storageService.getHistory();
notifyListeners();
}
/// 设置源语言
void setSourceLanguage(Language language) {
_sourceLanguage = language;
notifyListeners();
}
/// 设置目标语言
void setTargetLanguage(Language language) {
_targetLanguage = language;
notifyListeners();
}
/// 交换语言
void swapLanguages() {
final temp = _sourceLanguage;
_sourceLanguage = _targetLanguage;
_targetLanguage = temp;
final tempText = _sourceText;
_sourceText = _targetText;
_targetText = tempText;
notifyListeners();
}
/// 清空输入
void clearInput() {
_sourceText = '';
_targetText = '';
_currentResult = null;
notifyListeners();
}
/// 执行翻译
Future<void> doTranslate() async {
if (_sourceText.trim().isEmpty) return;
_state = TranslationState.loading;
notifyListeners();
try {
_currentResult = await _translationService.translate(
text: _sourceText,
sourceLang: _sourceLanguage,
targetLang: _targetLanguage,
);
_targetText = _currentResult!.targetText;
_state = TranslationState.success;
// 保存到历史
await _storageService.addToHistory(_currentResult!);
_history = await _storageService.getHistory();
} catch (e) {
_state = TranslationState.error;
}
notifyListeners();
}
/// 添加到收藏
Future<void> addToFavorites() async {
if (_currentResult == null) return;
await _storageService.addToFavorites(_currentResult!);
_currentResult!.isFavorite = true;
_favorites = await _storageService.getFavorites();
notifyListeners();
}
/// 从收藏移除
Future<void> removeFromFavorites(String id) async {
await _storageService.removeFromFavorites(id);
_favorites = await _storageService.getFavorites();
if (_currentResult?.id == id) {
_currentResult!.isFavorite = false;
}
notifyListeners();
}
/// 开始语音输入
Future<void> startVoiceInput() async {
_isRecording = true;
notifyListeners();
// 模拟语音识别
await Future.delayed(const Duration(seconds: 2));
_sourceText = 'Hello';
_isRecording = false;
notifyListeners();
}
/// 停止语音输入
void stopVoiceInput() {
_isRecording = false;
notifyListeners();
}
/// 播放源语言发音
void playSourcePronunciation() {
// 调用TTS服务播放发音
}
/// 播放目标语言发音
void playTargetPronunciation() {
// 调用TTS服务播放发音
}
}
5. 翻译页面实现
主翻译界面,包含语言选择、输入、翻译结果展示:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:translation_dictionary/models/language.dart';
import 'package:translation_dictionary/viewmodels/translation_viewmodel.dart';
import 'package:translation_dictionary/widgets/language_selector.dart';
class TranslatePage extends StatelessWidget {
const TranslatePage({super.key});
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => TranslationViewModel()..initialize(),
child: Scaffold(
backgroundColor: const Color(0xFFF5F5F5),
body: SafeArea(
child: Consumer<TranslationViewModel>(
builder: (context, vm, _) {
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
_buildHeader(context),
const SizedBox(height: 16),
_buildLanguageBar(context, vm),
const SizedBox(height: 16),
_buildInputArea(context, vm),
const SizedBox(height: 16),
_buildTranslateButton(context, vm),
if (vm.targetText.isNotEmpty) ...[
const SizedBox(height: 16),
_buildResultArea(context, vm),
],
const SizedBox(height: 16),
_buildActionBar(context, vm),
],
),
);
},
),
),
),
);
}
Widget _buildHeader(BuildContext context) {
return Row(
children: [
const Text(
'翻译词典',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Color(0xFF333333),
),
),
const Spacer(),
IconButton(
icon: const Text('⚙️', style: TextStyle(fontSize: 24)),
onPressed: () {
// 导航到设置页
},
),
],
);
}
Widget _buildLanguageBar(BuildContext context, TranslationViewModel vm) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
GestureDetector(
onTap: () => _showLanguageSelector(context, vm, true),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Text(vm.sourceLanguage.flag, style: const TextStyle(fontSize: 20)),
const SizedBox(width: 4),
Text(vm.sourceLanguage.name, style: const TextStyle(fontSize: 14)),
const Icon(Icons.arrow_drop_down, size: 16, color: Color(0xFF666666)),
],
),
),
),
IconButton(
icon: const Text('⇄', style: TextStyle(fontSize: 24, color: Color(0xFF4A90D9))),
onPressed: vm.swapLanguages,
),
GestureDetector(
onTap: () => _showLanguageSelector(context, vm, false),
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8),
),
child: Row(
children: [
Text(vm.targetLanguage.flag, style: const TextStyle(fontSize: 20)),
const SizedBox(width: 4),
Text(vm.targetLanguage.name, style: const TextStyle(fontSize: 14)),
const Icon(Icons.arrow_drop_down, size: 16, color: Color(0xFF666666)),
],
),
),
),
],
);
}
Widget _buildInputArea(BuildContext context, TranslationViewModel vm) {
return Column(
children: [
Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: TextField(
maxLines: 4,
decoration: const InputDecoration(
hintText: '请输入要翻译的文本',
hintStyle: TextStyle(color: Color(0xFF999999)),
border: InputBorder.none,
contentPadding: EdgeInsets.all(16),
),
onChanged: (value) {
vm.sourceText = value;
},
),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
icon: Text(
vm.isRecording ? '🔴' : '🎤',
style: const TextStyle(fontSize: 24),
),
onPressed: vm.isRecording ? vm.stopVoiceInput : vm.startVoiceInput,
),
IconButton(
icon: const Text('✕', style: TextStyle(fontSize: 24, color: Color(0xFF999999))),
onPressed: vm.clearInput,
),
if (vm.sourceText.isNotEmpty)
IconButton(
icon: const Text('▶️', style: TextStyle(fontSize: 24)),
onPressed: vm.playSourcePronunciation,
),
],
),
],
);
}
Widget _buildTranslateButton(BuildContext context, TranslationViewModel vm) {
return SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF4A90D9),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
),
onPressed: vm.translationState == TranslationState.loading
? null
: () => vm.doTranslate(),
child: vm.translationState == TranslationState.loading
? const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
),
)
: const Text('翻译', style: TextStyle(fontSize: 16)),
),
);
}
Widget _buildResultArea(BuildContext context, TranslationViewModel vm) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (vm.currentResult?.pronunciation != null)
Padding(
padding: const EdgeInsets.only(bottom: 8),
child: Row(
children: [
Text(
vm.currentResult!.pronunciation!,
style: const TextStyle(color: Color(0xFF666666), fontSize: 14),
),
const SizedBox(width: 8),
GestureDetector(
onTap: vm.playSourcePronunciation,
child: const Text('▶️', style: TextStyle(fontSize: 16)),
),
],
),
),
Text(
vm.targetText,
style: const TextStyle(fontSize: 18, color: Color(0xFF333333)),
),
],
),
);
}
Widget _buildActionBar(BuildContext context, TranslationViewModel vm) {
return Container(
height: 80,
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
_buildActionItem(
vm.currentResult?.isFavorite == true ? '❤️' : '🤍',
'收藏',
() {
if (vm.currentResult?.isFavorite == true) {
vm.removeFromFavorites(vm.currentResult!.id);
} else {
vm.addToFavorites();
}
},
),
_buildActionItem('📜', '历史', () {
// 导航到历史页面
}),
_buildActionItem('📚', '收藏列表', () {
// 导航到收藏列表
}),
_buildActionItem('📋', '复制', () {
// 复制到剪贴板
}),
_buildActionItem('📤', '分享', () {
// 分享功能
}),
],
),
);
}
Widget _buildActionItem(String emoji, String label, VoidCallback onTap) {
return GestureDetector(
onTap: onTap,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(emoji, style: const TextStyle(fontSize: 24)),
const SizedBox(height: 4),
Text(
label,
style: const TextStyle(fontSize: 12, color: Color(0xFF666666)),
),
],
),
);
}
void _showLanguageSelector(
BuildContext context,
TranslationViewModel vm,
bool isSource,
) {
showModalBottomSheet(
context: context,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
),
builder: (context) => LanguageSelector(
selectedLanguage: isSource ? vm.sourceLanguage : vm.targetLanguage,
onSelect: (lang) {
if (isSource) {
vm.setSourceLanguage(lang);
} else {
vm.setTargetLanguage(lang);
}
Navigator.pop(context);
},
),
);
}
}
6. 应用入口
import 'package:flutter/material.dart';
import 'package:translation_dictionary/pages/translate_page.dart';
import 'package:translation_dictionary/pages/favorites_page.dart';
import 'package:translation_dictionary/pages/history_page.dart';
void main() {
runApp(const TranslationDictApp());
}
class TranslationDictApp extends StatelessWidget {
const TranslationDictApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: '翻译词典',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
scaffoldBackgroundColor: const Color(0xFFF5F5F5),
fontFamily: 'HarmonyOS Sans',
),
home: const MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({super.key});
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
int _currentIndex = 0;
final List<Widget> _pages = const [
TranslatePage(),
LearnContent(),
FavoritesPage(),
SettingsPage(),
];
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _currentIndex,
children: _pages,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
selectedItemColor: const Color(0xFF4A90D9),
unselectedItemColor: const Color(0xFF999999),
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(
icon: Text('🔤', style: TextStyle(fontSize: 24)),
label: '翻译',
),
BottomNavigationBarItem(
icon: Text('📖', style: TextStyle(fontSize: 24)),
label: '学习',
),
BottomNavigationBarItem(
icon: Text('❤️', style: TextStyle(fontSize: 24)),
label: '收藏',
),
BottomNavigationBarItem(
icon: Text('👤', style: TextStyle(fontSize: 24)),
label: '我的',
),
],
),
);
}
}
// 占位组件
class LearnContent extends StatelessWidget {
const LearnContent({super.key});
Widget build(BuildContext context) {
return const Center(child: Text('学习中心'));
}
}
class FavoritesPage extends StatelessWidget {
const FavoritesPage({super.key});
Widget build(BuildContext context) {
return const Center(child: Text('收藏列表'));
}
}
class SettingsPage extends StatelessWidget {
const SettingsPage({super.key});
Widget build(BuildContext context) {
return const Center(child: Text('设置'));
}
}
截图运行验证
以下是翻译词典应用在鸿蒙设备上的运行截图:
图1:应用启动页面

图2:翻译功能演示
![![翻译页面截图 - 待添加]](https://i-blog.csdnimg.cn/direct/f817a2e6939e4109865c2d636c2d0d14.png)
图3:学习中心界面
![![学习中心截图 - 待添加]](https://i-blog.csdnimg.cn/direct/67a41414ff7c4011b834c348e765a02e.png)
注意:以上截图需要在实际鸿蒙设备或模拟器上运行后补充。建议开发者使用华为 DevEcho Studio 或连接真实鸿蒙设备进行调试和截图。
开发经验总结
1. 状态管理选择
在 Flutter for OpenHarmony 项目中,推荐使用 provider 或 riverpod 进行状态管理。本项目采用 ChangeNotifier 模式,代码结构清晰,易于维护。
2. 平台适配注意事项
- OpenHarmony 的 Flutter 实现与标准 Flutter 存在部分 API 差异,需要注意测试
- 语音合成(TTS)服务需要使用
@ohos.multimedia.text-to-speech相关 API - 文件存储推荐使用 OpenHarmony 提供的轻量级存储 API
3. 性能优化建议
- 使用
const构造函数减少不必要的重建 - 对列表组件使用
ListView.builder进行懒加载 - 合理使用
RepaintBoundary隔离重绘区域
4. 调试技巧
- 使用
flutter doctor -v检查环境配置 - 通过
hilog输出日志便于追踪问题 - 善用 DevEcho Studio 的热重载功能加速开发
代码仓库
本项目的完整代码已托管至 AtomGit 平台:
仓库地址:https://atomgit.com/maaath/translation-dictionary-flutter
结语
通过本文的实战演练,我们完整实现了一个 Flutter for OpenHarmony 翻译词典应用。可以看到,Flutter 的跨平台能力在 OpenHarmony 上得到了良好支持,开发者可以复用 Dart 生态的丰富组件,快速构建高质量的鸿蒙应用。随着 OpenHarmony 生态的持续发展,Flutter for OpenHarmony 将成为越来越多开发者的选择。
欢迎各位开发者交流探讨,共同推动开源鸿蒙跨平台技术的发展!
参考链接:
- Flutter for OpenHarmony 官方文档:https://gitee.com/openharmony-sig/flutter
- AtomGit 代码托管:https://atomgit.com
- OpenHarmony 跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)