【Flutter For OpenHarmony第三方库】Flutter+三方库实战:课程表App开发与鸿蒙化移植全流程实践
在跨平台开发领域,Flutter 凭借高效渲染与多端适配能力成为主流;鸿蒙化三方库大幅降低开发成本;开源鸿蒙(OpenHarmony)提供稳定的国产系统运行环境。本文将带你零基础开发一款课程表App,使用已完成鸿蒙适配的稳定三方库,实现添加课程、周视图展示、本地存储等功能,并完成鸿蒙平台移植,全程可一步步跟着操作。本文通过Flutter + 鸿蒙化三方库完成了课程表App开发,实现了多端兼容与鸿蒙
·
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
前言
在跨平台开发领域,Flutter 凭借高效渲染与多端适配能力成为主流;鸿蒙化三方库大幅降低开发成本;开源鸿蒙(OpenHarmony)提供稳定的国产系统运行环境。
本文将带你零基础开发一款课程表App,使用已完成鸿蒙适配的稳定三方库,实现添加课程、周视图展示、本地存储等功能,并完成鸿蒙平台移植,全程可一步步跟着操作。
一、项目功能
- 课程表一周视图展示
- 添加课程(名称、教室、星期、节次)
- 本地持久化存储(重启不丢失)
- 界面简洁、适配鸿蒙
- 无白屏、无崩溃、可正常运行
二、技术栈(全部鸿蒙化,无违规库)
| 库名 | 功能 | 鸿蒙适配 |
|---|---|---|
| flutter | 主框架 | ✅ |
| provider | 状态管理 | ✅ 鸿蒙化 |
| shared_preferences | 本地存储 | ✅ 鸿蒙化 |
| intl | 时间工具 | ✅ 鸿蒙化 |
三、开发环境
- Flutter 3.10+
- VS Code / Android Studio
- DevEco Studio(鸿蒙运行)
- 项目路径纯英文、无中文、无空格
四、第一步:创建项目
flutter create timetable
cd timetable
五、第二步:添加依赖(pubspec.yaml)
dependencies:
flutter:
sdk: flutter
provider: ^6.1.2
shared_preferences: ^2.2.3
intl: ^0.19.0
执行安装:
flutter pub get
六、第三步:编写数据模型(lib/course_model.dart)
class Course {
final String id;
final String name; // 课程名
final String room; // 教室
final int weekDay; // 星期 1-7
final int timeIndex; // 节次 1、2、3...
Course({
required this.id,
required this.name,
required this.room,
required this.weekDay,
required this.timeIndex,
});
Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'room': room,
'weekDay': weekDay,
'timeIndex': timeIndex,
};
}
static Course fromJson(Map<String, dynamic> json) {
return Course(
id: json['id'],
name: json['name'],
room: json['room'],
weekDay: json['weekDay'],
timeIndex: json['timeIndex'],
);
}
}
七、第四步:状态管理 + 存储(lib/course_provider.dart)
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import 'course_model.dart';
class CourseProvider extends ChangeNotifier {
List<Course> _courses = [];
SharedPreferences? _prefs;
List<Course> get courses => _courses;
Future<void> initProvider() async {
try {
_prefs = await SharedPreferences.getInstance();
_loadCourses();
} catch (e) {
print("存储初始化异常:$e");
}
}
void _loadCourses() {
if (_prefs == null) return;
final data = _prefs!.getString("courses");
if (data != null) {
try {
final list = jsonDecode(data) as List;
_courses = list.map((e) => Course.fromJson(e)).toList();
} catch (e) {
_courses = [];
}
}
notifyListeners();
}
Future<void> _save() async {
if (_prefs == null) return;
final jsonList = _courses.map((e) => e.toJson()).toList();
await _prefs!.setString("courses", jsonEncode(jsonList));
notifyListeners();
}
Future<void> addCourse(Course course) async {
_courses.add(course);
await _save();
}
Future<void> deleteCourse(String id) async {
_courses.removeWhere((c) => c.id == id);
await _save();
}
List<Course> getCoursesByWeekDay(int weekday) {
return _courses.where((c) => c.weekDay == weekday).toList();
}
}
八、第五步:主页面 + 完整UI(lib/main.dart)
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'course_model.dart';
import 'course_provider.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (ctx) => CourseProvider(),
child: MaterialApp(
title: "课程表",
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.blue),
home: const TimetablePage(),
),
);
}
}
class TimetablePage extends StatefulWidget {
const TimetablePage({super.key});
State<TimetablePage> createState() => _TimetablePageState();
}
class _TimetablePageState extends State<TimetablePage> {
bool _loading = true;
final _nameController = TextEditingController();
final _roomController = TextEditingController();
int _selectedWeekday = 1;
int _selectedTime = 1;
void initState() {
super.initState();
_init();
}
Future<void> _init() async {
final provider = Provider.of<CourseProvider>(context, listen: false);
await provider.initProvider();
if (mounted) {
setState(() => _loading = false);
}
}
void _showAddDialog() {
_nameController.clear();
_roomController.clear();
_selectedWeekday = 1;
_selectedTime = 1;
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: const Text("添加课程"),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: _nameController,
decoration: const InputDecoration(labelText: "课程名称"),
),
TextField(
controller: _roomController,
decoration: const InputDecoration(labelText: "教室"),
),
const SizedBox(height: 10),
DropdownButton<int>(
value: _selectedWeekday,
items: List.generate(7, (i) => i + 1)
.map((e) => DropdownMenuItem(
value: e,
child: Text("星期$e"),
))
.toList(),
onChanged: (v) {
if (v != null) setState(() => _selectedWeekday = v);
},
),
DropdownButton<int>(
value: _selectedTime,
items: List.generate(8, (i) => i + 1)
.map((e) => DropdownMenuItem(
value: e,
child: Text("第$e节"),
))
.toList(),
onChanged: (v) {
if (v != null) setState(() => _selectedTime = v);
},
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text("取消"),
),
TextButton(
onPressed: () async {
final provider = Provider.of<CourseProvider>(context, listen: false);
if (_nameController.text.isNotEmpty) {
await provider.addCourse(Course(
id: DateTime.now().millisecondsSinceEpoch.toString(),
name: _nameController.text,
room: _roomController.text,
weekDay: _selectedWeekday,
timeIndex: _selectedTime,
));
}
Navigator.pop(ctx);
},
child: const Text("确定"),
),
],
),
);
}
Widget build(BuildContext context) {
final provider = Provider.of<CourseProvider>(context);
return Scaffold(
appBar: AppBar(
title: const Text("课程表"),
actions: [
IconButton(
icon: const Icon(Icons.add),
onPressed: _showAddDialog,
),
],
),
body: _loading
? const Center(child: CircularProgressIndicator())
: ListView(
padding: const EdgeInsets.all(8),
children: List.generate(7, (weekIndex) {
final day = weekIndex + 1;
final list = provider.getCoursesByWeekDay(day);
return Card(
margin: const EdgeInsets.only(bottom: 10),
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"星期$day",
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold),
),
const Divider(),
list.isEmpty
? const Text(" 无课程")
: Column(
children: list.map((c) {
return ListTile(
title: Text(c.name),
subtitle: Text("教室:${c.room} | 第${c.timeIndex}节"),
trailing: IconButton(
icon: const Icon(Icons.delete, color: Colors.red),
onPressed: () async {
await provider.deleteCourse(c.id);
},
),
);
}).toList(),
),
],
),
),
);
}),
),
);
}
}
九、第六步:运行
flutter clean
flutter pub get
flutter run
功能正常:
- 打开不白屏
- 点击右上角 + 添加课程
- 选择星期、节次、教室
- 课程自动展示
- 长按删除(点击垃圾桶)
- 重启App数据不丢失
十、第七步:鸿蒙移植(解决 hvigor 报错)
1. 确保路径纯英文
2. 执行
flutter create --platforms=harmony .
flutter run
3. 鸿蒙运行成功
所有三方库均已鸿蒙化,无编译错误。

十一、常见问题
1. 白屏
解决:异步初始化放到页面加载完成后,使用 Loading 状态。
2. 添加课程没反应
解决:按钮必须使用 await,provider 必须刷新。
3. 鸿蒙编译报错 hvigor
解决:路径纯英文 + 清理缓存 + 重新生成鸿蒙工程。
十二、总结
本文通过 Flutter + 鸿蒙化三方库 完成了课程表App开发,实现了多端兼容与鸿蒙平台运行,展示了跨平台开发在鸿蒙生态中的高效实践。
欢迎大家加入开源鸿蒙跨平台开发者社区
更多推荐




所有评论(0)