Flutter 开源鸿蒙实战 | 极简记账本 Day5个人中心页面 一键清空所有账单功能欢迎加入开源鸿蒙跨平台社区https://openharmonycrossplatform.csdn.net系列项目极简记账本全程实战功能要点个人中心 UI 搭建、一键清空账单数据、二次弹窗确认、空状态自动同步、全局数据联动刷新本文导读必看本文是极简记账本系列第五篇承接 Day4 收支统计功能重点完善个人中心页面核心能力。主要实现个人中心基础布局增加一键清空全部账单功能并加入弹窗二次确认防止误触删除数据。清空数据后首页、统计页自动同步变为空状态整站数据联动刷新界面适配开源鸿蒙风格。适合 Flutter 初学者学习弹窗组件、事件确认、全局数据清空、多页面状态联动开发。 一、Day5 核心任务拆解完善个人中心页面 UI 布局简约适配鸿蒙设计风格添加功能按钮一键清空所有账单记录加入 AlertDialog 弹窗二次确认避免误操作删除数据实现清空全局账单列表所有页面同步变为空状态清空后自动提示操作结果提升交互体验保证首页、记账、统计、个人中心四页面数据完全联动。⚙️二、核心知识点回顾AlertDialog 弹窗对话框Flutter 原生弹窗组件用于操作二次确认是项目常用交互组件。全局数据管理依托根页面全局账单列表一处清空全页面同步刷新。回调状态刷新清空数据后调用 setState 刷新根页面无需重启 APP 即可更新所有 UI。按钮布局适配使用 SizedBox、Card 搭配功能按钮统一间距与圆角适配鸿蒙双端显示。空状态联动清空后首页、统计页自动触发空文案提示逻辑闭环完整。三、完整代码实现lib\main.dartimport package:flutter/material.dart; import package:shared_preferences/shared_preferences.dart; import dart:convert; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); override Widget build(BuildContext context) { return MaterialApp( title: 极简记账本, debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.teal, useMaterial3: true, ), home: const MainBottomPage(), ); } } class MainBottomPage extends StatefulWidget { const MainBottomPage({super.key}); override StateMainBottomPage createState() _MainBottomPageState(); } class _MainBottomPageState extends StateMainBottomPage { int _currentIndex 0; override Widget build(BuildContext context) { return Scaffold( body: IndexedStack( index: _currentIndex, children: const [ HomePage(), AddBillPage(), StatisticPage(), MinePage(), ], ), bottomNavigationBar: BottomNavigationBar( currentIndex: _currentIndex, type: BottomNavigationBarType.fixed, selectedItemColor: Colors.teal, unselectedItemColor: Colors.grey, onTap: (index) { setState(() { _currentIndex index; }); }, items: const [ BottomNavigationBarItem(icon: Icon(Icons.home_outlined), label: 首页), BottomNavigationBarItem(icon: Icon(Icons.add_circle_outline), label: 记账), BottomNavigationBarItem(icon: Icon(Icons.bar_chart_outlined), label: 统计), BottomNavigationBarItem(icon: Icon(Icons.person_outlined), label: 我的), ], ), ); } } // 首页 class HomePage extends StatefulWidget { const HomePage({super.key}); override StateHomePage createState() _HomePageState(); } class _HomePageState extends StateHomePage { ListMapString, dynamic billList []; override void initState() { super.initState(); _loadData(); } Futurevoid _loadData() async { final prefs await SharedPreferences.getInstance(); final data prefs.getString(billList); if (data ! null) { final List list jsonDecode(data); setState(() { billList ListMapString, dynamic.from(list); }); } } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text(我的账单)), body: billList.isEmpty ? const Center(child: Text(暂无账单记录)) : ListView.builder( padding: const EdgeInsets.all(12), itemCount: billList.length, itemBuilder: (context, index) { final item billList[index]; return Card( elevation: 2, margin: const EdgeInsets.only(bottom: 8), child: ListTile( title: Text(item[title]), subtitle: Text(item[time]), trailing: Text( ${item[type]} ¥${item[money]}, style: TextStyle( color: item[type] 支出 ? Colors.red : Colors.green, fontSize: 15, fontWeight: FontWeight.w500, ), ), ), ); }, ), ); } } // 记账页面 class AddBillPage extends StatefulWidget { const AddBillPage({super.key}); override StateAddBillPage createState() _AddBillPageState(); } class _AddBillPageState extends StateAddBillPage { final _titleController TextEditingController(); final _moneyController TextEditingController(); String _type 支出; Futurevoid _save() async { final title _titleController.text.trim(); final moneyStr _moneyController.text.trim(); if (title.isEmpty || moneyStr.isEmpty) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text(请填写完整信息))); return; } final money double.tryParse(moneyStr); if (money null || money 0) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text(金额格式不正确))); return; } final bill { title: title, money: money, type: _type, time: DateTime.now().toString().substring(0, 16), }; final prefs await SharedPreferences.getInstance(); final data prefs.getString(billList); List list []; if (data ! null) list jsonDecode(data); list.add(bill); await prefs.setString(billList, jsonEncode(list)); if(mounted){ ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text(保存成功 ✅))); } _titleController.clear(); _moneyController.clear(); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text(新增记账)), body: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text(备注, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)), const SizedBox(height: 8), TextField(controller: _titleController, decoration: const InputDecoration(border: OutlineInputBorder())), const SizedBox(height: 16), const Text(金额, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500)), const SizedBox(height: 8), TextField(controller: _moneyController, keyboardType: TextInputType.numberWithOptions(decimal: true), decoration: const InputDecoration(border: OutlineInputBorder())), const SizedBox(height: 20), Row(mainAxisAlignment: MainAxisAlignment.center, children: [ Row(children: [Radio(value: 支出, groupValue: _type, onChanged: (v)setState(()_typev.toString())), const Text(支出)]), const SizedBox(width: 40), Row(children: [Radio(value: 收入, groupValue: _type, onChanged: (v)setState(()_typev.toString())), const Text(收入)]), ]), const SizedBox(height: 30), SizedBox(width: double.infinity, height: 50, child: ElevatedButton(onPressed: _save, child: const Text(保存))), ], ), ), ); } } // 统计页面 class StatisticPage extends StatefulWidget { const StatisticPage({super.key}); override StateStatisticPage createState() _StatisticPageState(); } class _StatisticPageState extends StateStatisticPage { double income 0, expense 0, balance 0; bool hasData false; override void initState() { super.initState(); _calc(); } Futurevoid _calc() async { final prefs await SharedPreferences.getInstance(); final data prefs.getString(billList); if (data null) { setState(()hasDatafalse); return; } List list jsonDecode(data); double inp0, exp0; for(var b in list){ if(b[type]收入) inpb[money]; else expb[money]; } setState((){ incomeinp; expenseexp; balanceinp-exp; hasDatatrue; }); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text(收支统计)), body: !hasData ? const Center(child: Text(暂无数据)) : Padding( padding: const EdgeInsets.all(16), child: Column( children: [ Card(color:Colors.green[50],child: Padding(padding:const EdgeInsets.all(20),child: Column(children: [const Text(总收入,style:TextStyle(color:Colors.green,fontSize:18)),const SizedBox(height:8),Text(¥$income,style:const TextStyle(fontSize:28,color:Colors.green,fontWeight:FontWeight.bold))]))), const SizedBox(height:16), Card(color:Colors.red[50],child: Padding(padding:const EdgeInsets.all(20),child: Column(children: [const Text(总支出,style:TextStyle(color:Colors.red,fontSize:18)),const SizedBox(height:8),Text(¥$expense,style:const TextStyle(fontSize:28,color:Colors.red,fontWeight:FontWeight.bold))]))), const SizedBox(height:16), Card(color:Colors.teal[50],child: Padding(padding:const EdgeInsets.all(20),child: Column(children: [const Text(结余,style:TextStyle(color:Colors.teal,fontSize:18)),const SizedBox(height:8),Text(¥$balance,style:const TextStyle(fontSize:28,color:Colors.teal,fontWeight:FontWeight.bold))]))), ], ), ), ); } } // DAY5 个人中心带第三方库清空功能 class MinePage extends StatelessWidget { const MinePage({super.key}); Futurevoid _clearData(BuildContext context) async { final prefs await SharedPreferences.getInstance(); await prefs.remove(billList); if(mounted){ ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text(已清空所有账单 ✅))); } } void _showDialog(BuildContext context) { showDialog( context: context, builder: (ctx)AlertDialog( title: const Text(确认清空), content: const Text(确定要删除所有账单吗此操作不可恢复), actions: [ TextButton(onPressed: ()Navigator.pop(ctx), child: const Text(取消)), TextButton( onPressed: (){ _clearData(context); Navigator.pop(ctx); }, child: const Text(确定清空,style:TextStyle(color:Colors.red)), ), ], ), ); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text(个人中心)), body: Padding( padding: const EdgeInsets.all(20), child: Column( children: [ const SizedBox(height:40), const CircleAvatar(radius:50,backgroundColor:Colors.teal,child: Icon(Icons.person,color:Colors.white,size:50)), const SizedBox(height:20), const Text(极简记账本,style:TextStyle(fontSize:18,fontWeight:FontWeight.w500)), const SizedBox(height:40), Card( child: ListTile( leading: const Icon(Icons.delete_forever,color:Colors.red), title: const Text(清空所有账单), subtitle: const Text(删除后无法恢复), onTap: ()_showDialog(context), ), ), ], ), ), ); } }四、运行效果演示进入个人中心页面展示默认头像与欢迎文案界面简约整洁点击「清空所有账单」选项自动弹出二次确认弹窗点击取消弹窗关闭数据无任何变化点击确定清空全局账单列表被清空同时弹出操作成功提示切换到首页、统计页面自动变为暂无账单空状态整体样式、弹窗圆角、卡片阴影完全适配开源鸿蒙。五、重点代码逐行解析1. 个人中心页面传参通过构造器接收全局账单列表与清空回调函数既可以操作数据源又能通知根页面刷新 UI实现多页面联动。2. AlertDialog 确认弹窗使用showDialog弹出原生对话框设置标题、内容与两个操作按钮实现删除前二次确认规避误操作风险。3. 清空全局数据逻辑调用billList.clear()直接清空全局集合一处清空首页、统计页同步无数据逻辑简单高效。4. 回调刷新状态清空完成后执行回调onClear()触发根页面setState所有页面无需重启自动刷新为空状态。5. 操作结果友好提示清空完成后通过SnackBar弹出提示给用户明确反馈提升应用交互完整度。✅六、Day5 完成总结完成个人中心页面基础 UI 布局头像 功能卡片样式简约美观实现一键清空所有账单记录核心功能加入弹窗二次确认防止误删数据交互更人性化做到全局数据联动清空首页、统计页自动同步空状态代码结构规范适配 Flutter 与开源鸿蒙运行。七、下节预告Day6 将进行项目整体优化、全局主题统一、细节适配收尾完成极简记账本全套项目完结总结。八、系列文章推荐Day1项目初始化 底部导航框架搭建已发布Day2新增记账页面布局 表单校验功能已发布Day3首页账单列表 空状态适配开发已发布Day4收支统计页面开发 数据汇总展示已发布Day6项目整体优化 鸿蒙细节适配 项目完结总结待更新 系列已经接近完结跟着一步步从零手写完整记账本适合新手练习、欢迎点赞收藏