在这里插入图片描述

Tab 是移动端最高效的信息组织方式之一。本文详解 DiaryPage 的三 Tab 架构设计与数据共享。

一、Tab 配置

// diary_page.dart
class _DiaryPageState extends State<DiaryPage>
    with SingleTickerProviderStateMixin {
  late TabController _tabController;

  
  void initState() {
    super.initState();
    _tabController = TabController(length: 3, vsync: this);
    widget.moodStorage.addListener(_loadMoods);
    _loadMoods();
  }
}

SingleTickerProviderStateMixin 提供 Ticker(帧回调),TabController 需要它来驱动标签页的切换动画。

二、TabBar + TabBarView


Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: const Text('情绪日记'),
      bottom: TabBar(
        controller: _tabController,
        tabs: const [
          Tab(text: '记录'),    // Tab 0
          Tab(text: '时间线'),  // Tab 1
          Tab(text: '统计'),    // Tab 2
        ],
      ),
    ),
    body: TabBarView(
      controller: _tabController,
      children: [
        _buildRecordTab(),
        _buildTimelineTab(),
        _buildStatsTab(),
      ],
    ),
  );
}

★ Insight ─────────────────────────────────────
将 TabBar 放在 AppBar 的 bottom 属性中,使标签页成为导航栏的一部分——这是 Material Design 的标准模式。用户在切换标签时,标签栏始终可见,不会随内容滚动消失。
─────────────────────────────────────────────────

三、三 Tab 的数据流

MoodStorage (单例)
  ↓ addListener(_loadMoods)
_loadMoods() {
  _allMoods = storage.getAll();    ← 所有 Tab 共用
  _weekMoods = storage.getByWeek(); ← 统计 Tab 专用
}
  ↓ setState()
Tab 0: _selectedMood, _noteController (本地状态)
Tab 1: _allMoods (共享数据)
Tab 2: _weekMoods (共享数据)

关键设计:数据在 _loadMoods() 中统一加载,然后所有 Tab 都能访问。

四、Tab 0:记录页

Widget _buildRecordTab() {
  return SingleChildScrollView(
    padding: const EdgeInsets.all(20),
    child: Column(
      children: [
        // 日期选择器
        Row(
          children: [
            IconButton(icon: Icons.chevron_left, onPressed: goToPrevDay),
            Text(_formatDate(_selectedDate)),
            IconButton(icon: Icons.chevron_right, onPressed: goToNextDay),
          ],
        ),
        // 情绪选择器
        MoodPicker(selected: _selectedMood, onChanged: (m) => setState(...)),
        // 备注输入
        TextField(controller: _noteController, maxLength: 500, maxLines: 3),
        // 保存按钮
        ElevatedButton(onPressed: _saveMood),
      ],
    ),
  );
}

五、Tab 间联动:编辑功能

void _editMood(MoodEntry mood) {
  if (mood.id == null) return;
  _noteController.text = mood.note ?? '';
  setState(() {
    _selectedMood = mood.moodType;
    _selectedDate = mood.createdAt;
    _editingId = mood.id;
  });
  _tabController.animateTo(0);  // 自动切换到"记录"Tab
}

从时间线(Tab 1)底部弹窗点击"编辑"→ 自动跳转到记录 Tab(Tab 0)并回填数据。TabController 的 animateTo(0) 提供了平滑的切换动画。

六、Tab 2:统计页

Widget _buildStatsTab() {
  return SingleChildScrollView(
    padding: const EdgeInsets.all(20),
    child: Column(
      children: [
        const Text('本周心情'),
        const SizedBox(height: 16),
        MoodChart(weekMoods: _weekMoods),
        const SizedBox(height: 16),
        Text(MoodChart.summaryText(_weekMoods)),
      ],
    ),
  );
}

简洁的布局:标题 + 图表 + AI 式总结文字。

七、生命周期清理


void dispose() {
  widget.moodStorage.removeListener(_loadMoods);
  _tabController.dispose();
  _noteController.dispose();
  super.dispose();
}

三个资源都要清理:数据监听器、Tab 控制器、文本控制器。

小结

三 Tab 架构是移动端信息密集型页面的标准解法。E-Brufen 的实现体现了三个关键原则:统一数据加载(一个 _loadMoods 服务所有 Tab)、Tab 间联动(编辑跳转回记录)、资源清理(三个 dispose)。


作者简介:E-Brufen Dev,Flutter & 鸿蒙开发者,专注于跨平台移动应用开发与心理健康数字化,项目地址:AtomGit - E-Brufen

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐