feat: 首页UI,搜索,我的UI

This commit is contained in:
chengsiwen committed 2026-07-03 18:20:34 +08:00
1 parent 7f329aef44
commit a2b38fa1e8
22 files changed
+1382 -22

No files matched your search

+63
View File
@@ -0,0 +1,63 @@
import 'dart:convert';
import 'package:novyronst/tools/util/sp_tool.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// 本地搜索历史(SharedPreferences),key 与 [AppSp] 规则一致(`.search.history`)。
class SearchHistoryUtil {
SearchHistoryUtil._();
static const String _storageKey = '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 SpTool.getInstance();
return _readList(prefs);
}
/// 添加一条搜索;去重、去空白,并移到最前。
static Future<void> addSearch(String keyword) async {
final q = keyword.trim();
if (q.isEmpty) return;
final prefs = await SpTool.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 SpTool.getInstance();
final list = await _readList(prefs);
list.removeWhere((e) => e == keyword);
await _writeList(prefs, list);
}
/// 清空全部搜索历史。
static Future<void> clearAllSearch() async {
final prefs = await SpTool.getInstance();
await prefs.remove(_storageKey);
}
}
+57
View File
@@ -0,0 +1,57 @@
import 'package:shared_preferences/shared_preferences.dart';
class SpTool {
static SharedPreferences? _instance;
static final String _basePrefix = "app.";
static Future<SharedPreferences>? _instanceFuture;
static Future<SharedPreferences> getInstance() {
if (_instance != null) {
return Future.value(_instance);
}
_instanceFuture ??= SharedPreferences.getInstance().then((prefs) {
_instance = prefs;
return prefs;
});
return _instanceFuture!;
}
void setString(String key, String value) async {
final prefs = await getInstance();
prefs.setString("$_basePrefix$key", value);
}
Future<String> getString(String key) async {
final prefs = await getInstance();
return prefs.getString("$_basePrefix$key") ?? '';
}
void setBool(String key, bool value) async {
final prefs = await getInstance();
prefs.setBool("$_basePrefix$key", value);
}
Future<bool> getBool(String key) async {
final prefs = await getInstance();
return prefs.getBool("$_basePrefix$key") ?? false;
}
void setInt(String key, int value) async {
final prefs = await getInstance();
prefs.setInt("$_basePrefix$key", value);
}
Future<int> getInt(String key) async {
final prefs = await getInstance();
return prefs.getInt("$_basePrefix$key") ?? 0;
}
void deleteKey(String key) async {
final prefs = await getInstance();
prefs.remove("$_basePrefix$key");
}
}
+6 -2
View File
@@ -6,10 +6,11 @@ class AppBgPage extends StatelessWidget {
const AppBgPage({
super.key,
this.child,
this.typeBg = 1,
});
final Widget? child;
final int typeBg;
@override
Widget build(BuildContext context) {
@@ -19,7 +20,10 @@ class AppBgPage extends StatelessWidget {
SizedBox(
width: double.infinity,
height: double.infinity,
child: HelpImage.asset(ConstImage.pageBg, fit: BoxFit.fill),
child: HelpImage.asset(
typeBg==1 ? ConstImage.pageBg : ConstImage.mineBg,
fit: BoxFit.fill
),
),
child ?? SizedBox()
],
+79
View File
@@ -0,0 +1,79 @@
import 'package:flutter/material.dart';
import 'package:novyronst/common/help/app_nav.dart';
import 'package:novyronst/tools/widgets/novy_com_widget.dart';
import '../../common/const/const_color.dart';
class NovyAppBar extends StatelessWidget implements PreferredSizeWidget {
const NovyAppBar({
super.key,
this.title,
this.titleWidget,
this.leading,
this.actions,
this.onBack,
this.showBackButton = true,
this.centerTitle = true,
this.backgroundColor,
this.foregroundColor,
this.elevation = 0,
this.bottom,
this.toolbarHeight = kToolbarHeight,
this.titleColor,
});
final String? title;
final Color? titleColor;
final Widget? titleWidget;
final Widget? leading;
final List<Widget>? actions;
final VoidCallback? onBack;
final bool showBackButton;
final bool centerTitle;
final Color? backgroundColor;
final Color? foregroundColor;
final double elevation;
final PreferredSizeWidget? bottom;
final double toolbarHeight;
@override
Size get preferredSize =>
Size.fromHeight(toolbarHeight + (bottom?.preferredSize.height ?? 0));
@override
Widget build(BuildContext context) {
return _buildNormalAppBar(context);
}
PreferredSizeWidget _buildNormalAppBar(BuildContext context) {
final canPop = Navigator.of(context).canPop();
Widget? resolvedLeading = leading;
if (resolvedLeading == null && showBackButton && canPop) {
resolvedLeading = IconButton(
icon: Icon(Icons.arrow_back_ios, size: 18, color: Colors.white),
onPressed: onBack ?? () => AppNav.back(),
);
}
return AppBar(
title: titleWidget ?? (title != null
? NovyText(title!, fontSize: 18, fontWeight: FontWeight.bold, color: titleColor)
: null),
leading: resolvedLeading,
actions: actions,
centerTitle: centerTitle,
backgroundColor: backgroundColor ?? ConstColor.withValue('#110D08'),
foregroundColor: foregroundColor,
elevation: elevation,
scrolledUnderElevation: 0,
toolbarHeight: toolbarHeight,
);
}
}