Day 8-11: 底部导航与工程结构优化

在完成了基础的网络列表功能后,我们将进一步完善应用的架构,添加底部导航栏以支持多页面切换,并对项目文件进行模块化重构,使其更符合工程化标准。

一、 底部导航栏实现

我们通过引入 BottomNavigationBar 组件,实现了“备忘录”和“我的”两个功能模块的切换。

1.1 新建 Home 页 (Shell 模式)

为了管理多个页面的切换状态,我们创建了一个 HomePage 作为应用的“外壳”。

  • 文件路径: lib/pages/home_page.dart
  • 核心逻辑:
    • 使用 IndexedStack 包裹子页面列表,确保切换页面时保留原页面的滚动位置和输入状态(懒加载模式)。
    • BottomNavigationBar 管理底部 Tab 的选中状态与点击事件。
class _HomePageState extends State<HomePage> {
  int _currentIndex = 0; // 当前选中索引

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      // 使用 IndexedStack 保持页面状态
      body: IndexedStack(
        index: _currentIndex,
        children: _pages,
      ),
      bottomNavigationBar: BottomNavigationBar(
        currentIndex: _currentIndex,
        onTap: (index) {
          setState(() {
            _currentIndex = index;
          });
        },
        type: BottomNavigationBarType.fixed, // 固定模式,适合 2-4 个 Tab
        items: const [
          BottomNavigationBarItem(
            icon: Icon(Icons.note_alt_outlined), // 未选中图标
            activeIcon: Icon(Icons.note_alt),    // 选中图标
            label: '备忘录',
          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.person_outline),
            activeIcon: Icon(Icons.person),
            label: '我的',
          ),
        ],
      ),
    );
  }
}

1.2 新建个人中心页

创建一个简单的静态页面用于展示用户信息。

  • 文件路径: lib/pages/profile_page.dart
class ProfilePage extends StatelessWidget {
  const ProfilePage({super.key});

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('我的'), centerTitle: true),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const CircleAvatar(radius: 50, child: Icon(Icons.person, size: 50)),
            const SizedBox(height: 16),
            const Text('User Name', style: TextStyle(fontSize: 24)),
          ],
        ),
      ),
    );
  }
}

在这里插入图片描述
后续我们会在这个页面实现登录及新增备忘录功能

1.3 更新入口文件

修改 main.dart,将 home 属性指向新的 HomePage

// lib/main.dart
import 'pages/home_page.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  // ...
  
  Widget build(BuildContext context) {
    return MaterialApp(
      // ...
      home: const HomePage(), // 入口指向 HomePage
    );
  }
}

二、 工程结构重构

随着文件数量增加,扁平化的目录结构已不再适用。我们对 lib 目录进行了标准的模块化分层。

2.1 目录结构调整

lib/
├── models/          # 数据模型层
│   └── memo_model.dart
├── pages/           # 页面 UI 层
│   ├── home_page.dart
│   ├── memo_page.dart
│   ├── post_page.dart
│   └── profile_page.dart
└── main.dart        # 程序入口

2.2 引用路径修复

文件移动后,Dart 的 import 路径会报错,需要逐一修复。

  • main.dart:
    • import 'memo_page.dart' -> import 'pages/home_page.dart'
  • pages/memo_page.dart:
    • import 'memo_model.dart' -> import '../models/memo_model.dart' (使用相对路径访问上一级目录)

三、 遇到的问题与解决方案

3.1 编译报错:找不到构造函数 ‘HomePage’

  • 现象: 修改 main.dart 后,编译器提示 Error: Couldn't find constructor 'HomePage'
  • 原因: 虽然代码中写了 home: const HomePage(),但忘记引入 home_page.dart 文件,或者引入路径错误。
  • 解决:
    1. 检查文件头部是否包含 import 'pages/home_page.dart';
    2. 确保 HomePage 类是 public 的(类名不以 _ 开头)。

3.2 页面状态丢失

  • 现象: 在“备忘录”页面下拉刷新了一半,切换到“我的”,再切回来,刷新状态没了,或者滚动条回到了顶部。
  • 原因: 如果直接在 body 中使用条件判断切换 Widget (如 _pages[_currentIndex]),每次切换都会导致组件销毁和重建。
  • 解决: 使用 IndexedStack 组件包裹所有子页面。它会一次性构建所有子 Widget,但在视图层级上只显示当前索引的 Widget,从而保留其他页面的内存状态。

3.3 文件移动后大量爆红

  • 现象: 手动移动文件到 pages 目录后,几乎所有文件都报错。
  • 解决:
    • VS Code 技巧:在移动文件时,尽量使用 IDE 的重构功能(Refactor -> Move),它会自动更新引用路径。
    • 如果手动移动,需重点检查 import 路径,特别是相对路径 (../) 的层级是否正确。

四、 总结

通过今天的实战,我们将一个简单的单页面应用升级为了具备标准导航结构的多页面应用,并规范了代码组织结构。这为后续添加更多功能模块(如详情页、设置页)打下了良好的基础。

项目仓库:https://gitcode.com/shhzxt/flutter_OpenHarmony

欢迎加入开源鸿蒙跨平台社区: https://openharmonycrossplatform.csdn.net

Logo

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

更多推荐