Flutter for OpenHarmony生活助手App实战:购物清单功能实现
摘要:本文介绍了一个生活助手App中购物清单功能的设计与实现。该功能解决了传统购物中容易遗漏、冲动消费等问题,通过清单记录商品信息(名称、数量、分类),并提供进度展示、快速勾选/删除、分类标签等交互设计。技术上采用Flutter框架实现,包括进度统计、商品卡片UI、添加商品对话框等组件,优化了用户的购物体验,提高了购物效率和准确性。

说起买东西,我以前总是凭记忆,结果到了超市就忘了要买什么。有时候买了一堆不需要的,真正需要的反而忘了买。后来我开始用购物清单,情况就好多了。所以在做这个生活助手App时,购物清单是必不可少的功能。
为什么需要购物清单
很多人觉得购物清单没必要,记在脑子里就行。但实际使用后你会发现:
不会遗漏
想买的东西提前列好,到了超市照着清单买,不会漏掉任何东西。
避免冲动消费
有了清单,就不会看到什么买什么。只买清单上的东西,能省不少钱。
节省时间
不用在超市里想"还要买什么",按清单买完就走,效率很高。
家人共享
家里谁发现缺什么,就加到清单里。其他人去买的时候,一次性买齐。
功能设计思路
在设计这个功能时,我主要考虑了几个方面:
清晰的进度展示
顶部显示已购买和总数,让用户知道还有多少东西没买。
分类标签
每个商品都有分类标签,比如水果、蔬菜、日用品等,方便查找。
快速操作
点击复选框标记已购买,长按或滑动删除商品。
进度展示区域
页面顶部显示购买进度:
Container(
padding: EdgeInsets.all(20.w),
color: Colors.orange[50],
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'已购买 $checkedCount / ${items.length} 项',
style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold),
),
],
),
)
为什么用橙色?
橙色给人一种温暖、活力的感觉,很适合购物这个场景。而且橙色在视觉上比较醒目,但又不会太刺眼。
进度的表达方式
"已购买 3 / 10 项"这种表达方式很直观,用户一眼就能看出还有多少东西没买。比单纯的百分比更容易理解。
商品卡片的实现
每个商品用一个卡片展示:
Container(
margin: EdgeInsets.only(bottom: 12.h),
padding: EdgeInsets.all(16.w),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12.r),
),
child: Row(
children: [
Checkbox(
value: item['checked'],
onChanged: (value) {
setState(() {
items[index]['checked'] = value;
});
},
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item['name'],
style: TextStyle(
fontSize: 16.sp,
fontWeight: FontWeight.bold,
decoration: item['checked'] ? TextDecoration.lineThrough : null,
),
),
SizedBox(height: 4.h),
Text(
'${item['quantity']} · ${item['category']}',
style: TextStyle(fontSize: 12.sp, color: Colors.grey),
),
],
),
),
IconButton(
icon: const Icon(Icons.delete_outline, color: Colors.red),
onPressed: () {
setState(() {
items.removeAt(index);
});
},
),
],
),
)
商品信息的组织
每个商品包含三个信息:
- 名称:商品的名字,用粗体显示
- 数量:要买多少,比如"2斤"、“3瓶”
- 分类:属于哪个类别,比如"水果"、“蔬菜”
数量和分类用小号灰色字体显示在名称下方,用"·"分隔。这种排版方式既清晰又不占空间。
删除线的使用
当商品被标记为已购买时,名称会显示删除线:
decoration: item['checked'] ? TextDecoration.lineThrough : null
这是一个很经典的设计,让用户能快速识别哪些商品已经买了。
删除按钮的位置
删除按钮放在右侧,用红色的删除图标。Icons.delete_outline是一个轮廓样式的删除图标,比实心的更柔和一些。
点击删除按钮后,直接从列表中移除:
setState(() {
items.removeAt(index);
});
这里没有二次确认,因为购物清单的数据不是特别重要,误删了可以重新添加。如果要更谨慎,可以加一个确认对话框。
添加商品的功能
用户需要能够添加新的商品,我在AppBar右上角加了一个加号按钮:
appBar: AppBar(
title: const Text('购物清单'),
actions: [
IconButton(
icon: const Icon(Icons.add),
onPressed: _showAddItemDialog,
),
],
)
点击后弹出一个对话框:
void _showAddItemDialog() {
final nameController = TextEditingController();
final quantityController = TextEditingController();
String selectedCategory = '水果';
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('添加商品'),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(
controller: nameController,
decoration: const InputDecoration(
labelText: '商品名称',
border: OutlineInputBorder(),
),
),
SizedBox(height: 12.h),
TextField(
controller: quantityController,
decoration: const InputDecoration(
labelText: '数量',
hintText: '例如:2斤、3瓶',
border: OutlineInputBorder(),
),
),
SizedBox(height: 12.h),
DropdownButtonFormField<String>(
value: selectedCategory,
decoration: const InputDecoration(
labelText: '分类',
border: OutlineInputBorder(),
),
items: ['水果', '蔬菜', '肉类', '日用品', '其他']
.map((cat) => DropdownMenuItem(value: cat, child: Text(cat)))
.toList(),
onChanged: (value) {
selectedCategory = value!;
},
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
if (nameController.text.isNotEmpty) {
// 添加商品到列表
Navigator.pop(context);
}
},
child: const Text('添加'),
),
],
),
);
}
三个输入字段
添加商品需要三个信息:
- 商品名称:必填,不能为空
- 数量:选填,可以写"2斤"、"3瓶"这样的描述
- 分类:从预定义的分类中选择
下拉选择器的好处
分类用下拉选择器而不是文本输入,有两个好处:
- 避免输入错误,比如有人写"水果",有人写"水果类"
- 方便后续的分类筛选和统计
输入验证
保存前检查商品名称是否为空:
if (nameController.text.isNotEmpty) {
// 添加商品
}
如果名称为空,不执行添加操作。这是最基本的输入验证。
数据模型设计
购物清单需要一个清晰的数据模型:
class ShoppingItem {
final String id;
final String name;
final String quantity;
final String category;
final bool checked;
ShoppingItem({
required this.id,
required this.name,
required this.quantity,
required this.category,
this.checked = false,
});
Map<String, dynamic> toJson() => {
'id': id,
'name': name,
'quantity': quantity,
'category': category,
'checked': checked,
};
factory ShoppingItem.fromJson(Map<String, dynamic> json) => ShoppingItem(
id: json['id'],
name: json['name'],
quantity: json['quantity'],
category: json['category'],
checked: json['checked'],
);
}
这个模型包含了商品的所有必要信息,还提供了JSON序列化方法。
数据持久化
购物清单需要保存到本地:
class ShoppingListService {
static const String _keyItems = 'shopping_items';
static Future<void> saveItems(List<ShoppingItem> items) async {
final prefs = await SharedPreferences.getInstance();
final jsonList = items.map((i) => i.toJson()).toList();
await prefs.setString(_keyItems, jsonEncode(jsonList));
}
static Future<List<ShoppingItem>> getItems() async {
final prefs = await SharedPreferences.getInstance();
final jsonString = prefs.getString(_keyItems);
if (jsonString == null) return [];
final jsonList = jsonDecode(jsonString) as List;
return jsonList.map((json) => ShoppingItem.fromJson(json)).toList();
}
static Future<void> clearCheckedItems() async {
final items = await getItems();
final uncheckedItems = items.where((i) => !i.checked).toList();
await saveItems(uncheckedItems);
}
}
清除已购买商品
clearCheckedItems方法可以一键清除所有已购买的商品,这在购物完成后很有用。
实际使用体验
我自己用这个功能管理购物清单已经两个月了,发现了一些有趣的规律:
提前规划很重要
周末去超市前,我会提前一天列好清单。这样到了超市就不会手忙脚乱。
分类很有用
超市里商品是按区域摆放的,有了分类,可以按区域依次购买,不用来回跑。
避免了很多冲动消费
以前去超市总是买一堆零食,现在有了清单,只买需要的东西,每个月能省不少钱。
可以改进的地方
如果要做得更完善,可以考虑:
常用商品
保存常用商品列表,下次添加时可以直接选择,不用重新输入。
语音输入
支持语音添加商品,比如说"添加苹果2斤",系统自动识别并添加。
价格记录
记录每个商品的价格,可以统计总花费,还能对比不同超市的价格。
购物历史
保存每次购物的记录,可以查看过去买了什么,方便下次参考。
共享功能
家人之间可以共享购物清单,谁发现缺什么就加到清单里,其他人去买的时候能看到。
超市地图
集成超市的平面图,根据清单规划最优购物路线。
小结
购物清单功能通过简单的交互,帮助用户更好地管理购物需求。复选框标记、分类标签、删除按钮,这些设计都是为了让购物变得更有条理。
在实现过程中,我特别注重实用性。添加商品只需要几秒钟,标记已购买只需要点一下,这种低门槛的设计让用户更愿意使用。
从我自己的使用体验来看,有了这个工具后,购物变得更有计划了。不再是想到什么买什么,而是有目的地购买需要的东西。希望这个功能也能帮助你更好地管理购物清单。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐
所有评论(0)