feat: UI pages

This commit is contained in:
chengsiwen committed 2026-06-28 17:16:31 +08:00
1 parent fbcd33ea2b
commit 197c3e9b88
41 files changed
+1270 -274

No files matched your search

+63
View File
@@ -0,0 +1,63 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../utils/tools/app_sp.dart';
/// 本地搜索历史(SharedPreferences),key 与 [AppSp] 规则一致(`.search.history`)。
class SearchUtil {
SearchUtil._();
static const String _storageKey = 'sky.search.history';
static const int _maxHistory = 20;
static Future<List<String>> _readList(SharedPreferences prefs) async {
final raw = prefs.getString(_storageKey);
if (raw == null || raw.isEmpty) return [];
try {
final decoded = jsonDecode(raw);
if (decoded is! List) return [];
return decoded.map((e) => e.toString()).where((s) => s.isNotEmpty).toList();
} catch (_) {
return [];
}
}
static Future<void> _writeList(SharedPreferences prefs, List<String> list) async {
await prefs.setString(_storageKey, jsonEncode(list));
}
/// 查询搜索历史列表(新记录在前)。
static Future<List<String>> getSearchList() async {
final prefs = await AppSp.getInstance();
return _readList(prefs);
}
/// 添加一条搜索;去重、去空白,并移到最前。
static Future<void> addSearch(String keyword) async {
final q = keyword.trim();
if (q.isEmpty) return;
final prefs = await AppSp.getInstance();
final list = await _readList(prefs);
list.removeWhere((e) => e == q);
list.insert(0, q);
if (list.length > _maxHistory) {
list.removeRange(_maxHistory, list.length);
}
await _writeList(prefs, list);
}
/// 删除单条搜索记录。
static Future<void> deleteSearch(String keyword) async {
final prefs = await AppSp.getInstance();
final list = await _readList(prefs);
list.removeWhere((e) => e == keyword);
await _writeList(prefs, list);
}
/// 清空全部搜索历史。
static Future<void> clearAllSearch() async {
final prefs = await AppSp.getInstance();
await prefs.remove(_storageKey);
}
}
+89
View File
@@ -0,0 +1,89 @@
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:skywood/global/const/common_color.dart';
import 'package:skywood/main/lib_file.dart';
import 'package:skywood/utils/tools/widgets/sky_common_widget.dart';
class SearchView extends StatelessWidget {
const SearchView({super.key,
required this.searchHistory,
this.onTapHistory,
this.onTapClear,
});
final List<String> searchHistory;
final ValueChanged<String>? onTapHistory;
final Function()? onTapClear;
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 15.w),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Expanded(child: SkyText(text: 'Search History',
fontSize: 14,
color: CommonColor.black,
fontWeight: FontWeight.bold,
lineHeight: 1,
)),
IconButton(onPressed: () {
onTapClear?.call();
}, icon: HelpImg.asset(ConstImg.delete, width: 16.w, height: 16.w)),
],
),
Wrap(
spacing: 16.w,
runSpacing: 14.h,
children: searchHistory
.map(
(item) => _HistoryChip(
text: item,
onTap: () => onTapHistory?.call(item),
),
)
.toList(),
),
],
),
);
}
}
class _HistoryChip extends StatelessWidget {
const _HistoryChip({
required this.text,
this.onTap,
});
final String text;
final VoidCallback? onTap;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Container(
padding: EdgeInsets.symmetric(horizontal: 12.w, vertical: 8.h),
decoration: BoxDecoration(
color: const Color(0x0D000000),
borderRadius: BorderRadius.circular(20.r),
),
child: Text(
text,
style: TextStyle(
color: const Color(0x80000000),
fontSize: 12.sp,
fontWeight: FontWeight.w500,
height: 1,
),
),
),
);
}
}