深色模式、首页设置页面和功能优化
This commit is contained in:
@@ -2,10 +2,13 @@ import 'dart:async';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../constants/app_constants.dart';
|
||||
import '../../config/app_config.dart';
|
||||
import 'collect_notes.dart';
|
||||
import '../../controllers/history_controller.dart';
|
||||
import '../../services/network_listener_service.dart';
|
||||
import '../../services/get/theme_controller.dart';
|
||||
|
||||
/// 时间: 2026-03-26
|
||||
/// 功能: 本地笔记列表组件
|
||||
@@ -23,6 +26,7 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
List<Map<String, dynamic>> _notes = [];
|
||||
bool _isLoadingNotes = false;
|
||||
StreamSubscription<NetworkEvent>? _networkSubscription;
|
||||
final ThemeController _themeController = Get.find<ThemeController>();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -72,71 +76,107 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_isLoadingNotes && _notes.isEmpty) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return Obx(() {
|
||||
final isDark = _themeController.isDarkMode;
|
||||
|
||||
if (_notes.isEmpty) {
|
||||
return _buildEmptyNotes();
|
||||
}
|
||||
if (_isLoadingNotes && _notes.isEmpty) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: isDark ? Colors.white : AppConstants.primaryColor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: _loadNotes,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 80),
|
||||
itemCount: _notes.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == _notes.length) {
|
||||
return _buildBottomIndicator();
|
||||
}
|
||||
final note = _notes[index];
|
||||
return _buildNoteCard(note);
|
||||
},
|
||||
),
|
||||
);
|
||||
if (_notes.isEmpty) {
|
||||
return _buildEmptyNotes(isDark);
|
||||
}
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: _loadNotes,
|
||||
child: ListView.builder(
|
||||
// 添加底部内边距,让内容延伸到导航栏下方,实现玻璃效果
|
||||
padding: EdgeInsets.fromLTRB(
|
||||
16,
|
||||
16,
|
||||
16,
|
||||
AppConfig.liquidGlassTotalHeight + 16,
|
||||
),
|
||||
itemCount: _notes.length + 1,
|
||||
itemBuilder: (context, index) {
|
||||
if (index == _notes.length) {
|
||||
return _buildBottomIndicator(isDark);
|
||||
}
|
||||
final note = _notes[index];
|
||||
return _buildNoteCard(note, isDark);
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildBottomIndicator() {
|
||||
Widget _buildBottomIndicator(bool isDark) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 24),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(width: 40, height: 1, color: Colors.grey[300]),
|
||||
Container(
|
||||
width: 40,
|
||||
height: 1,
|
||||
color: isDark ? Colors.grey[700] : Colors.grey[300],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: Text(
|
||||
'到底了',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey[400]),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: isDark ? Colors.grey[500] : Colors.grey[400],
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(width: 40, height: 1, color: Colors.grey[300]),
|
||||
Container(
|
||||
width: 40,
|
||||
height: 1,
|
||||
color: isDark ? Colors.grey[700] : Colors.grey[300],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 构建空笔记状态
|
||||
Widget _buildEmptyNotes() {
|
||||
Widget _buildEmptyNotes(bool isDark) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.note_add_outlined, size: 64, color: Colors.grey[400]),
|
||||
Icon(
|
||||
Icons.note_add_outlined,
|
||||
size: 64,
|
||||
color: isDark ? Colors.grey[600] : Colors.grey[400],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text('暂无笔记', style: TextStyle(fontSize: 16, color: Colors.grey[600])),
|
||||
Text(
|
||||
'暂无笔记',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: isDark ? Colors.grey[400] : Colors.grey[600],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'点击右下角按钮创建新笔记',
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey[500]),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: isDark ? Colors.grey[500] : Colors.grey[500],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 构建笔记卡片
|
||||
Widget _buildNoteCard(Map<String, dynamic> note) {
|
||||
Widget _buildNoteCard(Map<String, dynamic> note, bool isDark) {
|
||||
final title = note['title'] as String? ?? '';
|
||||
final content = note['content'] as String? ?? '';
|
||||
final timeStr = note['time'] as String? ?? '';
|
||||
@@ -145,7 +185,6 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
final isPinned = note['isPinned'] == true;
|
||||
final isLocked = note['isLocked'] == true;
|
||||
|
||||
// 显示逻辑:有标题显示标题,无标题有分类显示分类,否则显示内容
|
||||
String displayText;
|
||||
bool hasTitle = title.isNotEmpty;
|
||||
bool hasCategory = category.isNotEmpty && category != '未分类';
|
||||
@@ -161,16 +200,20 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
color: isDark ? const Color(0xFF2A2A2A) : Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.08),
|
||||
color: isDark
|
||||
? Colors.black.withValues(alpha: 0.3)
|
||||
: Colors.black.withValues(alpha: 0.08),
|
||||
blurRadius: 12,
|
||||
offset: const Offset(0, 4),
|
||||
),
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.04),
|
||||
color: isDark
|
||||
? Colors.black.withValues(alpha: 0.2)
|
||||
: Colors.black.withValues(alpha: 0.04),
|
||||
blurRadius: 6,
|
||||
offset: const Offset(0, 2),
|
||||
),
|
||||
@@ -180,7 +223,6 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Stack(
|
||||
children: [
|
||||
// 原始内容
|
||||
InkWell(
|
||||
onTap: () => _handleNoteTap(note, isLocked),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
@@ -189,42 +231,40 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// 顶部:创建时间、保存时间和置顶/锁定按钮
|
||||
Row(
|
||||
children: [
|
||||
// 创建时间
|
||||
if (createTimeStr.isNotEmpty) ...[
|
||||
Icon(
|
||||
Icons.add_circle_outline,
|
||||
size: 12,
|
||||
color: Colors.grey[400],
|
||||
color: isDark ? Colors.grey[500] : Colors.grey[400],
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
Text(
|
||||
_formatDate(createTimeStr),
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.grey[400],
|
||||
color: isDark
|
||||
? Colors.grey[500]
|
||||
: Colors.grey[400],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
// 保存时间
|
||||
Icon(
|
||||
Icons.access_time,
|
||||
size: 12,
|
||||
color: Colors.grey[400],
|
||||
color: isDark ? Colors.grey[500] : Colors.grey[400],
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
Text(
|
||||
_formatDateTime(timeStr),
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.grey[400],
|
||||
color: isDark ? Colors.grey[500] : Colors.grey[400],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
// 分类标签
|
||||
if (hasCategory)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
@@ -246,7 +286,6 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
),
|
||||
),
|
||||
if (hasCategory) const SizedBox(width: 8),
|
||||
// 锁定图标
|
||||
if (isLocked)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
@@ -256,7 +295,6 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
color: AppConstants.primaryColor,
|
||||
),
|
||||
),
|
||||
// 置顶按钮
|
||||
GestureDetector(
|
||||
onTap: () => _togglePin(note['id'] as String?),
|
||||
child: Container(
|
||||
@@ -268,14 +306,15 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
size: 16,
|
||||
color: isPinned
|
||||
? AppConstants.primaryColor
|
||||
: Colors.grey[400],
|
||||
: (isDark
|
||||
? Colors.grey[500]
|
||||
: Colors.grey[400]),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
// 标题或内容
|
||||
Text(
|
||||
displayText,
|
||||
style: TextStyle(
|
||||
@@ -283,13 +322,12 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
fontWeight: hasTitle
|
||||
? FontWeight.w600
|
||||
: FontWeight.normal,
|
||||
color: Colors.black87,
|
||||
color: isDark ? Colors.white : Colors.black87,
|
||||
height: 1.5,
|
||||
),
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
// 底部:字数和删除按钮
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
@@ -297,11 +335,10 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
'${note['charCount'] ?? displayText.length} 字',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[400],
|
||||
color: isDark ? Colors.grey[500] : Colors.grey[400],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
// 删除按钮
|
||||
GestureDetector(
|
||||
onTap: () =>
|
||||
_showDeleteNoteDialog(note['id'] as String?),
|
||||
@@ -310,7 +347,9 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
child: Icon(
|
||||
Icons.delete_outline,
|
||||
size: 18,
|
||||
color: Colors.grey[400],
|
||||
color: isDark
|
||||
? Colors.grey[500]
|
||||
: Colors.grey[400],
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -320,7 +359,6 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
),
|
||||
),
|
||||
),
|
||||
// 锁定时的毛玻璃遮罩
|
||||
if (isLocked)
|
||||
Positioned.fill(
|
||||
child: GestureDetector(
|
||||
@@ -330,29 +368,32 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
child: BackdropFilter(
|
||||
filter: ImageFilter.blur(sigmaX: 8, sigmaY: 8),
|
||||
child: Container(
|
||||
color: Colors.white.withValues(alpha: 0.3),
|
||||
color: (isDark ? Colors.black : Colors.white)
|
||||
.withValues(alpha: 0.3),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
// 顶部时间信息
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.access_time,
|
||||
size: 12,
|
||||
color: Colors.grey[600],
|
||||
color: isDark
|
||||
? Colors.grey[400]
|
||||
: Colors.grey[600],
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
_formatDateTime(timeStr),
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.grey[600],
|
||||
color: isDark
|
||||
? Colors.grey[400]
|
||||
: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
// 删除按钮
|
||||
GestureDetector(
|
||||
onTap: () => _showDeleteNoteDialog(
|
||||
note['id'] as String?,
|
||||
@@ -362,14 +403,15 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
child: Icon(
|
||||
Icons.delete_outline,
|
||||
size: 16,
|
||||
color: Colors.grey[600],
|
||||
color: isDark
|
||||
? Colors.grey[400]
|
||||
: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
// 中间锁定图标(左右结构)
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@@ -397,7 +439,9 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
'点击输入密码访问',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[600],
|
||||
color: isDark
|
||||
? Colors.grey[400]
|
||||
: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -405,14 +449,15 @@ class _LocalNotesListState extends State<LocalNotesList> {
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
// 底部字数
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'${note['charCount'] ?? displayText.length} 字',
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Colors.grey[600],
|
||||
color: isDark
|
||||
? Colors.grey[400]
|
||||
: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user