在开发垃圾分类指南App时,搜索历史功能看似简单,实则对用户体验影响深远。这个功能让用户能够快速回顾和重复之前的查询,避免了重复输入的麻烦。我们的实现方案将搜索历史放置在个人中心模块,展示用户最近查询过的垃圾物品记录。
这个功能的核心价值在于:
我们采用Flutter框架结合GetX状态管理方案来实现这个功能。整体架构分为三层:
这种分层架构的优势在于:
dart复制class HomeController extends GetxController {
final recentSearches = <GarbageItem>[].obs;
void addRecentSearch(GarbageItem item) {
recentSearches.removeWhere((i) => i.id == item.id);
recentSearches.insert(0, item);
if (recentSearches.length > 20) {
recentSearches.removeLast();
}
_saveRecentSearches();
}
void _saveRecentSearches() {
final data = recentSearches.map((item) => item.toJson()).toList();
GetStorage().write('recentSearches', data);
}
void _loadRecentSearches() {
final data = GetStorage().read('recentSearches');
if (data != null) {
recentSearches.value = (data as List)
.map((json) => GarbageItem.fromJson(json))
.toList();
}
}
}
这段代码实现了搜索历史的核心逻辑:
我们选用GetStorage作为本地存储方案,主要考虑:
存储时机采用"修改即保存"策略,虽然IO操作稍多,但保证了数据实时性。对于搜索历史这种低频操作场景是合适的。
dart复制Scaffold(
appBar: AppBar(
title: const Text('搜索历史'),
actions: [
Obx(() => controller.recentSearches.isNotEmpty
? TextButton(
onPressed: () => _showClearDialog(controller),
child: const Text('清空'),
)
: const SizedBox()),
],
),
body: Obx(() {
if (controller.recentSearches.isEmpty) {
return _buildEmptyState();
}
return ListView.builder(
itemCount: controller.recentSearches.length,
itemBuilder: (context, index) {
return _buildHistoryCard(controller.recentSearches[index], controller);
},
);
}),
)
页面结构特点:
dart复制Widget _buildHistoryCard(GarbageItem item, HomeController controller) {
return Dismissible(
key: Key(item.id),
direction: DismissDirection.endToStart,
background: _buildDeleteBackground(),
onDismissed: (_) => _handleDelete(item, controller),
child: Card(
child: ListTile(
leading: _buildTypeIcon(item),
title: Text(item.name),
subtitle: Text(item.typeName),
trailing: const Icon(Icons.arrow_forward_ios),
onTap: () => _navigateToDetail(item),
),
),
);
}
卡片设计要点:
dart复制Widget _buildEmptyState() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.history, size: 80, color: Colors.grey[300]),
SizedBox(height: 16),
Text('暂无搜索记录', style: TextStyle(fontSize: 18, color: Colors.grey)),
SizedBox(height: 8),
Text('去搜索一些垃圾物品吧', style: TextStyle(color: Colors.grey[400])),
SizedBox(height: 24),
ElevatedButton.icon(
onPressed: () => Get.toNamed(Routes.search),
icon: Icon(Icons.search),
label: Text('去搜索'),
),
],
),
);
}
空状态设计原则:
dart复制void _handleDelete(GarbageItem item, HomeController controller) {
controller.removeFromRecentSearches(item);
Get.snackbar('提示', '已删除',
snackPosition: SnackPosition.BOTTOM,
duration: Duration(seconds: 1),
);
}
删除反馈要点:
dart复制void _showClearDialog(HomeController controller) {
Get.dialog(
AlertDialog(
title: Text('清空历史'),
content: Text('确定要清空所有搜索历史吗?'),
actions: [
TextButton(
onPressed: Get.back,
child: Text('取消'),
),
TextButton(
onPressed: () {
controller.recentSearches.clear();
Get.back();
Get.snackbar('提示', '历史已清空');
},
child: Text('清空', style: TextStyle(color: Colors.red)),
),
],
),
);
}
危险操作防护策略:
使用ListView.builder而非ListView:
历史记录管理策略:
GetStorage使用技巧:
可实现的增强功能:
dart复制Map<String, List<GarbageItem>> _groupByDate(List<GarbageItem> items) {
// 实现按今天/昨天/更早分组
}
添加搜索框实现:
dart复制TextField(
onChanged: (value) {
filteredList = controller.recentSearches
.where((item) => item.name.contains(value))
.toList();
},
)
实现多选删除:
dart复制bool isSelectMode = false;
List<String> selectedIds = [];
void _toggleSelect(String id) {
if (selectedIds.contains(id)) {
selectedIds.remove(id);
} else {
selectedIds.add(id);
}
}
数据不同步问题
性能卡顿
存储异常
状态管理选择
UI设计建议
测试要点
云端同步
智能推荐
数据分析
在实际开发中,搜索历史功能往往会被忽视,但它对提升用户留存率有着不可小觑的作用。我们的实现方案在保证功能完整性的同时,也注重了性能和用户体验的平衡。