这份 Day 8-11 的教程标志着你的项目从“Demo”迈向了“工程化”。底部导航与目录重构是大型 App 的地基。为了让这篇教程更具实战参考价值,我建议在状态持久化、鸿蒙原生动效适配、以及模块化深度上进行补充优化。

🎯 核心目标

本阶段我们将告别“单页面”模式,构建一个具备多页切换能力的商业化 App 雏形:

  1. 多端导航:使用 IndexedStack 实现丝滑的 Tab 切换,保留页面滚动状态。

  2. 结构重构:按照 MVC/MVVM 思想分层,解决代码耦合问题。

  3. 状态保持:深度解析鸿蒙环境下页面生命周期的管理。


一、 底部导航栏:构建应用“骨架”

1.1 HomePage (Shell) 的设计细节

在鸿蒙系统中,底部导航的交互感非常重要。我们不仅要实现切换,还要确保状态不丢失

// lib/pages/home_page.dart
class _HomePageState extends State<HomePage> {
  int _currentIndex = 0;

  // 定义页面列表
  final List<Widget> _pages = [
    const MemoPage(),    // 备忘录列表
    const ProfilePage(), // 个人中心
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // ✨ 关键点:IndexedStack 能够保持子页面的滚动位置,避免重复加载网络数据
      body: IndexedStack(
        index: _currentIndex,
        children: _pages,
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: (index) => setState(() => _currentIndex = index),
        // 适配鸿蒙风格:使用固定类型,避免 Tab 抖动
        type: BottomNavigationBarType.fixed,
        selectedItemColor: Theme.of(context).primaryColor,
        unselectedItemColor: Colors.grey,
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.description_outlined),
            activeIcon: Icon(Icons.description),
            label: '备忘录',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.person_outline),
            activeIcon: Icon(Icons.person),
            label: '我的',
          ),
        ],
      ),
    );
  }
}

二、 工程重构:从“扁平”到“层级”

随着业务逻辑增加,如果所有文件都在 lib 根目录,维护将变成灾难。

2.1 标准分层规范

推荐采用以下分层结构,这在华为 HarmonyOS 原生开发中也是被推崇的目录逻辑:

目录 职责
lib/models/ 数据模型:负责 JSON 解析与实体类定义
lib/pages/ 视图层:所有的 UI 页面文件
lib/widgets/ 通用组件:可复用的卡片、按钮、输入框
lib/services/ 服务层:封装 HTTP 请求、数据库操作
lib/utils/ 工具类:日期转换、日志工具、常量定义

三、 避坑与进阶指南

3.1 页面切换时的生命周期问题

现象:在使用 IndexedStack 后,虽然页面不再重新销毁(Dispose),但切换 Tab 时,initState 不会再次触发。

解决:如果需要在每次切回页面时刷新数据,可以配合 RouteAware 或者在 onTap 切换时通过全局状态(如 Provider)通知子页面。

3.2 鸿蒙特有的“返回键”处理

在鸿蒙真机上,用户可能会点击硬件/手势返回键。如果不处理,可能会直接退出应用。

优化建议:在 HomePage 外层包裹 PopScope(原 WillPopScope),实现“连续点击两次退出应用”的逻辑。

Dart

return PopScope(
  canPop: false,
  onPopInvoked: (didPop) {
    // 处理双击退出的逻辑
  },
  child: Scaffold(...),
);

3.3 自动修复 Import 路径

在 VS Code 中移动文件后,如果出现大面积报错:

  1. 全局搜索替换:利用正则或简单的文本替换。

  2. Dart Fix:在终端运行 dart fix --apply,它可以自动修复部分简单的引用错误。


四、 总结

Day 8-11 的工作虽然看起来只是“搬家”和“加个底座”,但这是项目从 Demo 走向产品的必经之路。规范的目录结构能让你的代码在 AtomGit 社区中获得更高的专业评价。

五:目标

本阶段我们将告别“单页面”模式,构建一个具备多页切换能力的商业化 App 雏形:

  1. 多端导航:使用 IndexedStack 实现丝滑的 Tab 切换,保留页面滚动状态。

  2. 结构重构:按照 MVC/MVVM 思想分层,解决代码耦合问题。

  3. 状态保持:深度解析鸿蒙环境下页面生命周期的管理。


一、 底部导航栏:构建应用“骨架”

1.1 HomePage (Shell) 的设计细节

在鸿蒙系统中,底部导航的交互感非常重要。我们不仅要实现切换,还要确保状态不丢失

Dart

// lib/pages/home_page.dart
class _HomePageState extends State<HomePage> {
  int _currentIndex = 0;

  // 定义页面列表
  final List<Widget> _pages = [
    const MemoPage(),    // 备忘录列表
    const ProfilePage(), // 个人中心
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      // ✨ 关键点:IndexedStack 能够保持子页面的滚动位置,避免重复加载网络数据
      body: IndexedStack(
        index: _currentIndex,
        children: _pages,
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: (index) => setState(() => _currentIndex = index),
        // 适配鸿蒙风格:使用固定类型,避免 Tab 抖动
        type: BottomNavigationBarType.fixed,
        selectedItemColor: Theme.of(context).primaryColor,
        unselectedItemColor: Colors.grey,
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.description_outlined),
            activeIcon: Icon(Icons.description),
            label: '备忘录',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.person_outline),
            activeIcon: Icon(Icons.person),
            label: '我的',
          ),
        ],
      ),
    );
  }
}

二、 工程重构:从“扁平”到“层级”

随着业务逻辑增加,如果所有文件都在 lib 根目录,维护将变成灾难。

2.1 标准分层规范

推荐采用以下分层结构,这在华为 HarmonyOS 原生开发中也是被推崇的目录逻辑:

目录 职责
lib/models/ 数据模型:负责 JSON 解析与实体类定义
lib/pages/ 视图层:所有的 UI 页面文件
lib/widgets/ 通用组件:可复用的卡片、按钮、输入框
lib/services/ 服务层:封装 HTTP 请求、数据库操作
lib/utils/ 工具类:日期转换、日志工具、常量定义

三、 避坑与进阶指南

3.1 页面切换时的生命周期问题

现象:在使用 IndexedStack 后,虽然页面不再重新销毁(Dispose),但切换 Tab 时,initState 不会再次触发。

解决:如果需要在每次切回页面时刷新数据,可以配合 RouteAware 或者在 onTap 切换时通过全局状态(如 Provider)通知子页面。

3.2 鸿蒙特有的“返回键”处理

在鸿蒙真机上,用户可能会点击硬件/手势返回键。如果不处理,可能会直接退出应用。

优化建议:在 HomePage 外层包裹 PopScope(原 WillPopScope),实现“连续点击两次退出应用”的逻辑。

Dart

return PopScope(
  canPop: false,
  onPopInvoked: (didPop) {
    // 处理双击退出的逻辑
  },
  child: Scaffold(...),
);

3.3 自动修复 Import 路径

在 VS Code 中移动文件后,如果出现大面积报错:

  1. 全局搜索替换:利用正则或简单的文本替换。

  2. Dart Fix:在终端运行 dart fix --apply,它可以自动修复部分简单的引用错误。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐