Flutter 开发鸿蒙实战:Windows 环境下从 HAP 构建到四 Tab 页面运行
很多初学者第一次用 Flutter 开发鸿蒙时,最容易卡在两个地方:一是看到项目里生成了 ohos 目录,就忍不住直接改鸿蒙工程;二是构建时不知道应该先启动 DevEco Studio 模拟器,还是先执行 Flutter 命令。结果往往是 HAP 包能构建一半,运行时找不到设备,或者页面代码写好了但没有真正装到鸿蒙模拟器里。

本文按真实操作顺序,把 Windows 环境下 Flutter 开发鸿蒙应用的流程串起来:先确认工程结构,再启动模拟器,然后执行 flutter build hap --debug,最后用 flutter run 把一个四 Tab Demo 跑到鸿蒙模拟器上。


1. 先理解 Flutter 鸿蒙工程的目录边界
Flutter 适配鸿蒙后,项目里会出现 ohos 目录。这个目录很关键,但不建议一上来就直接修改里面的代码。对大多数 Flutter 页面开发来说,真正的业务入口仍然在 lib 目录,鸿蒙侧工程更多承担构建、打包和平台承载的职责。
| 目录 | 作用 | 建议 |
|---|---|---|
lib/ |
Flutter 业务代码 | 日常页面、路由、状态主要写这里 |
lib/main.dart |
应用入口 | 挂载 MaterialApp、首页和导航结构 |
lib/pages/ |
页面分层 | 按业务模块拆页面 |
ohos/ |
鸿蒙平台工程 | 初学阶段不要随意改生成代码 |
build/ |
构建产物 | 可清理,不要手工维护 |
更稳的思路是:页面先在 Flutter 层跑通,平台侧只在确实需要权限、插件、原生能力时再进入 ohos 目录处理。
2. 启动 DevEco Studio 模拟器
运行鸿蒙目标前,先确保模拟器已经启动。可以打开 DevEco Studio,用任意项目进入设备管理,只要把鸿蒙模拟器启动起来即可。这里不要求一定用当前 Flutter 项目打开 DevEco Studio,重点是让系统里出现可用的 HarmonyOS 运行设备。
启动后建议先做三项检查:
- 模拟器已经完全进入桌面,不停留在启动动画。
- 命令行能识别到设备。
- Flutter 工程没有停留在依赖下载或环境错误状态。
powershell
flutter doctor
flutter devices
代码解释:
flutter doctor用来检查 Flutter、Dart、设备链路是否有明显问题。flutter devices用来确认当前是否能发现鸿蒙模拟器或真机。- 如果设备列表为空,先处理模拟器和连接问题,不要急着构建 HAP。
3. 构建鸿蒙 HAP 包
设备准备好后,再进入 Flutter 项目根目录执行构建命令。原文里使用的是 debug 构建,这个适合开发阶段快速验证。
powershell
flutter build hap --debug
代码解释:
build hap表示构建鸿蒙应用包。--debug适合开发调试,构建速度和调试便利性更好。- 如果这里失败,优先看 Flutter 环境、鸿蒙 SDK、依赖下载和项目配置。
构建阶段常见问题如下:
| 现象 | 高概率原因 | 处理方式 |
|---|---|---|
找不到 hap 构建目标 |
Flutter 鸿蒙适配环境没配好 | 重新检查 Flutter HarmonyOS 版本 |
| 构建中断 | 依赖或 SDK 缺失 | 先跑 flutter doctor |
| 构建成功但无法运行 | 设备没启动或未识别 | 检查 flutter devices |
修改 ohos 后报错 |
误改生成工程 | 回退平台工程改动,从 lib 开始调试 |
4. 运行项目并选择鸿蒙模拟器
HAP 构建完成后,可以继续执行运行命令:
powershell
flutter run
如果命令行出现多个设备选项,根据列表选择鸿蒙模拟器对应的编号。原文里选择了 1,实际项目中编号可能变化,不要固定记编号,要看设备名称。
更稳的习惯是先看设备:
powershell
flutter devices
flutter run -d <deviceId>
代码解释:
flutter run会构建、安装并启动应用。-d <deviceId>可以指定设备,避免选错模拟器或浏览器目标。- 如果运行卡住,先确认模拟器没有锁屏、没有断开连接。
5. 设计一个四 Tab 页面结构
跑通空项目以后,可以做一个最小 Demo 验证页面开发链路。这里按电影类应用做四个 Tab:Home、Cinema、Film、Mine。
推荐目录结构如下:
text
lib/
main.dart
pages/
Home/
index.dart
Cinema/
index.dart
Film/
index.dart
Mine/
index.dart
这套结构的好处是清晰:每个页面有自己的目录,后续加组件、接口、状态管理时不会全部堆在 main.dart。
6. 编写四个页面组件
先写最小页面,每个页面只展示一个标题。这样可以先验证导航是否正确,再逐步补业务。
dart
import 'package:flutter/material.dart';
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text('首页', style: TextStyle(fontSize: 24)),
);
}
}
dart
import 'package:flutter/material.dart';
class CinemaPage extends StatelessWidget {
const CinemaPage({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text('影院', style: TextStyle(fontSize: 24)),
);
}
}
dart
import 'package:flutter/material.dart';
class FilmPage extends StatelessWidget {
const FilmPage({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text('电影', style: TextStyle(fontSize: 24)),
);
}
}
dart
import 'package:flutter/material.dart';
class MinePage extends StatelessWidget {
const MinePage({super.key});
@override
Widget build(BuildContext context) {
return const Center(
child: Text('我的', style: TextStyle(fontSize: 24)),
);
}
}
代码解释:
- 四个页面都使用
StatelessWidget,因为当前没有内部状态。 - 每个页面先只保留核心展示,降低第一次运行的排错难度。
- 页面文件拆开以后,
main.dart只负责组装,不负责承载全部页面内容。
7. 在 main.dart 中组装底部导航
四个页面准备好后,在 main.dart 中维护当前选中的 Tab,并用 BottomNavigationBar 切换页面。
dart
import 'package:flutter/material.dart';
import 'package:my_app01/pages/Cinema/index.dart';
import 'package:my_app01/pages/Film/index.dart';
import 'package:my_app01/pages/Home/index.dart';
import 'package:my_app01/pages/Mine/index.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Harmony Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const MainScreen(),
);
}
}
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int _currentIndex = 0;
final List<Widget> _pages = const [
HomePage(),
CinemaPage(),
FilmPage(),
MinePage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(_title)),
body: IndexedStack(
index: _currentIndex,
children: _pages,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
type: BottomNavigationBarType.fixed,
selectedItemColor: Colors.blue,
unselectedItemColor: Colors.grey,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home_outlined), label: '首页'),
BottomNavigationBarItem(icon: Icon(Icons.local_movies_outlined), label: '影院'),
BottomNavigationBarItem(icon: Icon(Icons.movie_outlined), label: '电影'),
BottomNavigationBarItem(icon: Icon(Icons.person_outline), label: '我的'),
],
),
);
}
String get _title {
switch (_currentIndex) {
case 0:
return '首页';
case 1:
return '影院';
case 2:
return '电影';
case 3:
return '我的';
default:
return 'Flutter Harmony Demo';
}
}
}
代码解释:
IndexedStack可以保留各 Tab 的页面状态,比直接_pages[_currentIndex]更适合后续扩展。BottomNavigationBarType.fixed适合四个及以上 Tab,避免底部导航样式异常。debugShowCheckedModeBanner: false能让截图和演示效果更干净。- Tab 的 label、页面数组、标题顺序必须一致,否则会出现点击"电影"却显示其他页面的问题。
8. 运行前做一次最小检查
页面写完以后,不要直接改很多功能再运行。建议先做一次最小检查:
powershell
flutter analyze
flutter build hap --debug
flutter run
代码解释:
flutter analyze可以提前发现导入路径、语法和静态问题。- 构建 HAP 用来验证鸿蒙目标是否可生成。
flutter run用来确认应用能安装到模拟器并正常打开。
如果你只改了 Dart 页面,大多数问题应该在 analyze 或 run 阶段暴露;如果你改了 ohos 目录,排查成本会明显上升。
9. 常见问题排查表
| 问题 | 可能原因 | 处理方式 |
|---|---|---|
flutter run 看不到鸿蒙设备 |
模拟器未启动或未连接 | 先打开 DevEco Studio 模拟器,再跑 flutter devices |
flutter build hap 失败 |
Flutter 鸿蒙环境或 SDK 配置异常 | 回到环境检查,不要先改业务代码 |
| 页面切换后状态丢失 | 直接替换 body 页面 | 使用 IndexedStack 保留 Tab 状态 |
| 底部导航文字和页面不一致 | _pages、items、标题 switch 顺序不一致 |
三处顺序保持一致 |
改了 ohos 后项目跑不起来 |
误改平台工程 | 先回退,业务优先放在 lib |
10. 小结
Windows 下用 Flutter 开发鸿蒙应用,关键不是背命令,而是把顺序和边界搞清楚:先启动鸿蒙模拟器,再构建 HAP,然后运行到设备;业务页面优先写在 lib,不要一开始就乱改 ohos。当四 Tab Demo 能稳定跑起来后,再继续接接口、状态管理和平台能力,排查成本会低很多。
版权声明 :本文基于 CSDN 博主「大雷神」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:原文链接