数据备份

This commit is contained in:
DelLevin-Home
2026-03-07 11:55:27 +08:00
parent 082145ff79
commit 874e21708a
21 changed files with 73 additions and 512 deletions

View File

@@ -1,251 +0,0 @@
# MookNote 快速入门指南
## 第一步:安装 Flutter 开发环境
### Windows 系统详细步骤
#### 1. 下载 Flutter SDK
访问https://docs.flutter.dev/get-started/install/windows
- 点击 "Download Flutter SDK"
- 下载完成后解压到 `C:\src\flutter`
#### 2. 添加环境变量
```
1. 右键"此电脑" → "属性" → "高级系统设置"
2. 点击"环境变量"
3. 在"系统变量"中找到 "Path",点击"编辑"
4. 点击"新建"添加C:\src\flutter\bin
5. 确定保存
```
#### 3. 验证安装
打开命令提示符cmd或 PowerShell
```bash
flutter --version
flutter doctor
```
#### 4. 安装 Android Studio
- 下载地址https://developer.android.com/studio
- 安装完成后打开 Android Studio
- 配置 Android SDK
- File → Settings → Appearance & Behavior → System Settings → Android SDK
- 选择 "SDK Platforms" 标签
- 勾选 "Android 13.0 (API 33)" 或更高版本
- 选择 "SDK Tools" 标签
- 勾选 "Android SDK Build-Tools" 和 "Android Emulator"
- 点击 Apply 开始安装
#### 5. 接受 Android 许可证
```bash
flutter doctor --android-licenses
```
按提示输入 `y` 接受所有许可证
#### 6. 再次运行 flutter doctor
```bash
flutter doctor
```
确保看到类似以下输出:
```
[✓] Flutter (Channel stable, 3.x.x, on Microsoft Windows)
[✓] Android toolchain - develop for Android devices
[✓] Android Studio
[✓] Connected device
```
## 第二步:运行 MookNote 项目
### 1. 进入项目目录
```bash
cd d:\UserData\Desktop\my_proj\mooknote
```
### 2. 安装依赖包
```bash
flutter pub get
```
### 3. 准备设备
**选项 A使用真机**
```
1. 手机开启"开发者选项"
- 设置 → 关于手机 → 连续点击"版本号"7 次
2. 开启"USB 调试"
- 设置 → 开发者选项 → USB 调试
3. 用 USB 连接电脑
4. 手机上允许 USB 调试授权
```
**选项 B使用模拟器**
```
1. Android Studio → Tools → Device Manager
2. Click "Create device"
3. 选择设备型号(如 Pixel 6
4. 下载并选择系统镜像(推荐 API 33
5. 完成创建后点击启动按钮
```
### 4. 运行应用
```bash
flutter run
```
或者在 Android Studio 中:
- File → Open → 选择 mooknote 文件夹
- 点击顶部工具栏的运行按钮(绿色三角形)
## 第三步:理解代码结构
### 核心文件说明
#### 1. 入口文件 - `lib/main.dart`
```dart
void main() {
runApp(const MyApp());
}
```
应用的起点,配置主题和路由
#### 2. 主页面 - `lib/pages/home_page.dart`
包含:
- 顶部 AppBar菜单按钮 + 标题)
- 三个标签页切换(观影/阅读/笔记)
- 主体内容区域
#### 3. 状态管理 - `lib/providers/app_provider.dart`
管理全局状态:
- 当前选中的标签页
- 当前选中的状态筛选
- 侧边菜单开关
#### 4. 数据模型 - `lib/models/data_models.dart`
定义数据结构:
- `Movie` - 电影数据
- `Book` - 书籍数据
- `Note` - 笔记数据
### Widget 组件层次
```
MyApp
└── HomePage (主页面)
├── CustomDrawer (侧边菜单)
│ └── 热力图组件
├── AppBar (顶部栏)
├── TabBar (标签栏)
│ ├── 观影
│ ├── 阅读
│ └── 笔记
├── TabContent (标签内容)
│ ├── MovieTabPage
│ │ ├── MovieStatusBar (状态栏)
│ │ └── MovieListItem (列表项)
│ ├── BookTabPage
│ │ ├── BookStatusBar (状态栏)
│ │ └── BookListItem (列表项)
│ └── NoteTabPage
│ └── NoteListItem (列表项)
└── CustomBottomNavBar (底部导航)
```
## 第四步:修改和定制
### 修改应用名称
编辑 `pubspec.yaml`:
```yaml
name: mooknote # 包名
```
编辑 `android/app/src/main/AndroidManifest.xml`:
```xml
android:label="MookNote" <!-- 显示名称 -->
```
### 修改主题颜色
编辑 `lib/utils/app_theme.dart`:
```dart
static const Color primaryColor = Color(0xFF6200EE); // 主色调
```
### 添加示例数据
编辑对应的 page 文件,例如 `lib/pages/movie_tab_page.dart`:
```dart
List<Movie> _getSampleMovies(int statusIndex) {
// 在这里添加你的电影数据
}
```
## 第五步:构建 APK
### 开发版本
```bash
flutter build apk --debug
```
### 发布版本
```bash
flutter build apk --release
```
生成的 APK 位置:
```
build/app/outputs/flutter-apk/app-release.apk
```
## 常见问题解决
### Q1: flutter pub get 失败
```bash
# 清除缓存
flutter clean
flutter pub cache repair
flutter pub get
```
### Q2: 无法识别设备
```bash
# 查看设备列表
flutter devices
# 如果没有设备,检查:
# 1. USB 线是否连接好
# 2. 手机 USB 调试是否开启
# 3. 是否安装了手机驱动
```
### Q3: 热重载不工作
`R` 进行热重载,按 `r` 完全重启
如果都不行,停止应用重新运行
### Q4: 中文乱码
确保所有文件使用 UTF-8 编码
## 下一步学习建议
### 1. 学习 Dart 基础语法1-2 天)
- 变量和类型
- 函数
- 类和对象
- 异步编程async/await
### 2. 学习 Flutter 基础3-5 天)
- Widget 概念
- StatelessWidget vs StatefulWidget
- 布局组件Row, Column, Container 等)
- 列表ListView
### 3. 实践项目功能
- 实现添加功能表单
- 集成 SQLite 数据库
- 实现搜索功能
- 完善详情页
### 4. 参考资源
- [Dart 语言中文文档](https://dart.cn/)
- [Flutter 实战电子书](https://book.flutterchina.club/)
- [B 站 Flutter 教程](https://search.bilibili.com/all?keyword=flutter 教程)
---
祝你开发顺利!有问题随时询问。

106
README.md
View File

@@ -74,18 +74,6 @@ MookNote 是一款用于记录观影、阅读和笔记的 Android 应用,使
flutter doctor --android-licenses flutter doctor --android-licenses
``` ```
```bash
$env:HTTP_PROXY="http://127.0.0.1:10808"
$env:HTTPS_PROXY="http://127.0.0.1:10808"
flutter create --platforms android .
```
## 运行项目 ## 运行项目
### 1. 克隆/复制项目后,进入项目目录 ### 1. 克隆/复制项目后,进入项目目录
@@ -97,9 +85,13 @@ cd mooknote
```bash ```bash
flutter pub get flutter pub get
# 如果失败可以先添加代理再进行get
$env:HTTP_PROXY="http://127.0.0.1:10808"
$env:HTTPS_PROXY="http://127.0.0.1:10808"
``` ```
### 3. 连接设备或启动模拟器 ### 3. 连接设备或启动模拟器
- 连接 Android 真机(需开启 USB 调试) - 连接 Android 真机(需开启 USB 调试)
- 或启动 Android 模拟器 - 或启动 Android 模拟器
@@ -112,43 +104,6 @@ flutter run
- 打开项目 - 打开项目
- 点击运行按钮 (Run) - 点击运行按钮 (Run)
## 项目结构
```
mooknote/
├── android/ # Android 平台配置
│ └── app/
│ └── src/main/
│ ├── AndroidManifest.xml
│ └── ...
├── lib/ # Dart 源代码
│ ├── main.dart # 应用入口
│ ├── pages/ # 页面组件
│ │ ├── home_page.dart # 主页面
│ │ ├── movie_tab_page.dart # 观影标签页
│ │ ├── book_tab_page.dart # 阅读标签页
│ │ └── note_tab_page.dart # 笔记标签页
│ ├── widgets/ # 可复用组件
│ │ ├── custom_drawer.dart # 侧边菜单
│ │ ├── bottom_nav_bar.dart # 底部导航
│ │ ├── movie_status_bar.dart # 观影状态栏
│ │ ├── movie_list_item.dart # 观影列表项
│ │ ├── book_status_bar.dart # 书籍状态栏
│ │ ├── book_list_item.dart # 书籍列表项
│ │ └── note_list_item.dart # 笔记列表项
│ ├── models/ # 数据模型
│ │ └── data_models.dart # Movie, Book, Note 模型
│ ├── providers/ # 状态管理
│ │ └── app_provider.dart # 全局状态管理
│ └── utils/ # 工具类
│ └── app_theme.dart # 主题配置
├── assets/ # 资源文件
│ └── images/ # 图片资源
├── fonts/ # 字体文件
├── pubspec.yaml # 项目配置文件
└── README.md # 本文件
```
## 技术栈 ## 技术栈
- **Flutter**: 跨平台移动应用框架 - **Flutter**: 跨平台移动应用框架
@@ -162,58 +117,17 @@ mooknote/
- [x] 集成 SQLite 数据库sqflite 包) - [x] 集成 SQLite 数据库sqflite 包)
- [x] 实现本地数据持久化 - [x] 实现本地数据持久化
- [ ] 数据备份与恢复 - [x] 数据备份与恢复
### 功能增强 ### 功能增强
- [x] 添加/编辑表单页面 - [x] 添加/编辑表单页面
- [ ] 详情页完整实现 - [x] 详情页完整实现
- [ ] 搜索功能 - [x] 搜索功能
- [ ] 统计图表页面 - [x] 统计图表页面
- [ ] 回收站功能 - [x] 回收站功能
- [ ] 设置页面
### 用户体验
- [ ] 加载动画
- [ ] 下拉刷新
- [ ] 无限滚动加载
- [ ] 深色模式优化
### 高级功能 ### 高级功能
- [ ] 导出功能JSON/CSV - [x] 导出功能JSON
- [ ] 云同步 - [ ] 云同步
- [ ] 桌面小组件
## 常见问题
### Q: flutter doctor 显示 Android license status unknown
A: 运行 `flutter doctor --android-licenses` 接受许可证
### Q: 模拟器无法启动
A: 确保在 BIOS 中启用了虚拟化技术VT-x/AMD-V
### Q: 热重载不生效
A: 某些代码更改需要完全重启应用(如 main() 函数修改)
## 开发建议
### 对于有 Vue 经验的你
Flutter 的很多概念与 Vue 相似:
- **Widget** ≈ Vue 组件
- **StatefulWidget** ≈ 带数据的 Vue 组件
- **setState()** ≈ this.$forceUpdate() + 数据更新
- **Provider** ≈ Vuex/Pinia
- **pubspec.yaml** ≈ package.json
### 学习资源
- [Flutter 官方文档](https://docs.flutter.dev/)
- [Dart 语言指南](https://dart.dev/guides)
- [Flutter 中文社区](https://flutterchina.club/)
## 许可证
MIT License
---
**作者**: 你的昵称
**创建时间**: 2024-03-04

View File

@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../utils/backup_service.dart'; import '../utils/backup_service.dart';
import '../utils/toast_util.dart';
/// 数据备份页面 /// 数据备份页面
class BackupPage extends StatefulWidget { class BackupPage extends StatefulWidget {
@@ -185,9 +186,7 @@ class _BackupPageState extends State<BackupPage> {
if (!mounted) return; if (!mounted) return;
if (result.cancelled) { if (result.cancelled) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '已取消导出');
const SnackBar(content: Text('已取消导出')),
);
} else if (result.success) { } else if (result.success) {
// 显示导出成功信息 // 显示导出成功信息
showDialog( showDialog(
@@ -214,15 +213,11 @@ class _BackupPageState extends State<BackupPage> {
), ),
); );
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, result.errorMessage ?? '导出失败');
SnackBar(content: Text(result.errorMessage ?? '导出失败')),
);
} }
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '导出失败: $e');
SnackBar(content: Text('导出失败: $e')),
);
} }
} finally { } finally {
if (mounted) { if (mounted) {
@@ -267,9 +262,7 @@ class _BackupPageState extends State<BackupPage> {
if (!mounted) return; if (!mounted) return;
if (result.cancelled) { if (result.cancelled) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '已取消导入');
const SnackBar(content: Text('已取消导入')),
);
} else if (result.success) { } else if (result.success) {
// 刷新数据 // 刷新数据
await context.read<AppProvider>().loadMovies(); await context.read<AppProvider>().loadMovies();
@@ -293,15 +286,11 @@ class _BackupPageState extends State<BackupPage> {
), ),
); );
} else { } else {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, result.errorMessage ?? '导入失败');
SnackBar(content: Text(result.errorMessage ?? '导入失败')),
);
} }
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '导入失败: $e');
SnackBar(content: Text('导入失败: $e')),
);
} }
} finally { } finally {
if (mounted) { if (mounted) {

View File

@@ -5,6 +5,7 @@ import 'package:share_plus/share_plus.dart';
import 'package:cross_file/cross_file.dart'; import 'package:cross_file/cross_file.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
import 'book_reviews_page.dart'; import 'book_reviews_page.dart';
import 'book_excerpts_page.dart'; import 'book_excerpts_page.dart';
@@ -657,9 +658,7 @@ class _BookDetailPageState extends State<BookDetailPage> {
if (!mounted) return; if (!mounted) return;
Navigator.pop(context); Navigator.pop(context);
Navigator.pop(context); Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '已删除');
const SnackBar(content: Text('已删除')),
);
}, },
child: const Text('删除', style: TextStyle(color: Colors.red)), child: const Text('删除', style: TextStyle(color: Colors.red)),
), ),
@@ -692,9 +691,7 @@ class _BookDetailPageState extends State<BookDetailPage> {
); );
await context.read<AppProvider>().updateBook(updatedBook); await context.read<AppProvider>().updateBook(updatedBook);
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '封面已清空');
const SnackBar(content: Text('封面已清空')),
);
} }
}, },
child: const Text('清空', style: TextStyle(color: Colors.red)), child: const Text('清空', style: TextStyle(color: Colors.red)),
@@ -707,18 +704,14 @@ class _BookDetailPageState extends State<BookDetailPage> {
/// 下载封面到本地 /// 下载封面到本地
Future<void> _downloadCover(Book book) async { Future<void> _downloadCover(Book book) async {
if (book.coverPath == null || book.coverPath!.isEmpty) { if (book.coverPath == null || book.coverPath!.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '没有可下载的封面');
const SnackBar(content: Text('没有可下载的封面')),
);
return; return;
} }
try { try {
final sourceFile = File(book.coverPath!); final sourceFile = File(book.coverPath!);
if (!await sourceFile.exists()) { if (!await sourceFile.exists()) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '封面文件不存在');
const SnackBar(content: Text('封面文件不存在')),
);
return; return;
} }
@@ -738,9 +731,7 @@ class _BookDetailPageState extends State<BookDetailPage> {
text: '下载自 MookNote', text: '下载自 MookNote',
); );
} catch (e) { } catch (e) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '下载失败: $e');
SnackBar(content: Text('下载失败: $e')),
);
} }
} }
} }

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
/// 摘抄表单页面 - 新增/编辑摘抄 /// 摘抄表单页面 - 新增/编辑摘抄
@@ -171,15 +172,11 @@ class _BookExcerptFormPageState extends State<BookExcerptFormPage> {
if (mounted) { if (mounted) {
Navigator.pop(context); Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, _isEditing ? '摘抄已更新' : '摘抄已添加');
SnackBar(content: Text(_isEditing ? '摘抄已更新' : '摘抄已添加')),
);
} }
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '保存失败: $e');
SnackBar(content: Text('保存失败: $e')),
);
} }
} finally { } finally {
if (mounted) { if (mounted) {

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
import 'book_excerpt_form_page.dart'; import 'book_excerpt_form_page.dart';
/// 书籍摘抄列表页面 /// 书籍摘抄列表页面
@@ -34,9 +35,7 @@ class _BookExcerptsPageState extends State<BookExcerptsPage> {
}); });
} catch (e) { } catch (e) {
setState(() => _isLoading = false); setState(() => _isLoading = false);
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '加载失败: $e');
SnackBar(content: Text('加载失败: $e')),
);
} }
} }
@@ -287,9 +286,7 @@ class _BookExcerptsPageState extends State<BookExcerptsPage> {
await context.read<AppProvider>().removeBookExcerpt(excerpt.id); await context.read<AppProvider>().removeBookExcerpt(excerpt.id);
Navigator.pop(context); Navigator.pop(context);
_loadExcerpts(); _loadExcerpts();
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '已删除');
const SnackBar(content: Text('已删除')),
);
}, },
child: const Text('删除', style: TextStyle(color: Colors.red)), child: const Text('删除', style: TextStyle(color: Colors.red)),
), ),

View File

@@ -6,6 +6,7 @@ import 'package:path/path.dart' as path;
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
/// 添加/编辑书籍页面 - 紧凑双行布局设计 /// 添加/编辑书籍页面 - 紧凑双行布局设计
class BookFormPage extends StatefulWidget { class BookFormPage extends StatefulWidget {
@@ -663,9 +664,7 @@ class _BookFormPageState extends State<BookFormPage> {
} }
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '选择封面失败: $e');
SnackBar(content: Text('选择封面失败: $e')),
);
} }
} }
} }
@@ -718,12 +717,7 @@ class _BookFormPageState extends State<BookFormPage> {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, widget.book == null ? '添加成功' : '更新成功');
SnackBar(
content: Text(widget.book == null ? '添加成功' : '更新成功'),
behavior: SnackBarBehavior.floating,
),
);
Navigator.pop(context); Navigator.pop(context);
} }

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
/// 添加/编辑书评页面 - 极简设计 /// 添加/编辑书评页面 - 极简设计
@@ -253,12 +254,7 @@ class _BookReviewFormPageState extends State<BookReviewFormPage> {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, widget.review == null ? '添加成功' : '更新成功');
SnackBar(
content: Text(widget.review == null ? '添加成功' : '更新成功'),
behavior: SnackBarBehavior.floating,
),
);
Navigator.pop(context); Navigator.pop(context);
} }

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
import 'book_review_form_page.dart'; import 'book_review_form_page.dart';
/// 书籍书评列表页面 /// 书籍书评列表页面
@@ -34,9 +35,7 @@ class _BookReviewsPageState extends State<BookReviewsPage> {
}); });
} catch (e) { } catch (e) {
setState(() => _isLoading = false); setState(() => _isLoading = false);
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '加载失败: $e');
SnackBar(content: Text('加载失败: $e')),
);
} }
} }
@@ -257,9 +256,7 @@ class _BookReviewsPageState extends State<BookReviewsPage> {
await context.read<AppProvider>().removeBookReview(review.id); await context.read<AppProvider>().removeBookReview(review.id);
Navigator.pop(context); Navigator.pop(context);
_loadReviews(); _loadReviews();
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '已删除');
const SnackBar(content: Text('已删除')),
);
}, },
child: const Text('删除', style: TextStyle(color: Colors.red)), child: const Text('删除', style: TextStyle(color: Colors.red)),
), ),

View File

@@ -8,6 +8,7 @@ import 'package:cross_file/cross_file.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
import 'movie_reviews_page.dart'; import 'movie_reviews_page.dart';
import 'movie_posters_page.dart'; import 'movie_posters_page.dart';
@@ -212,9 +213,7 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
); );
await context.read<AppProvider>().updateMovie(updatedMovie); await context.read<AppProvider>().updateMovie(updatedMovie);
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '海报已清空');
const SnackBar(content: Text('海报已清空')),
);
} }
}, },
child: const Text('清空', style: TextStyle(color: Colors.red)), child: const Text('清空', style: TextStyle(color: Colors.red)),
@@ -768,9 +767,7 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
if (!mounted) return; if (!mounted) return;
Navigator.pop(context); Navigator.pop(context);
Navigator.pop(context); Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '已删除');
const SnackBar(content: Text('已删除')),
);
}, },
child: const Text('删除', style: TextStyle(color: Colors.red)), child: const Text('删除', style: TextStyle(color: Colors.red)),
), ),
@@ -782,18 +779,14 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
/// 下载海报到本地 /// 下载海报到本地
Future<void> _downloadPoster(Movie movie) async { Future<void> _downloadPoster(Movie movie) async {
if (movie.posterPath == null || movie.posterPath!.isEmpty) { if (movie.posterPath == null || movie.posterPath!.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '没有可下载的海报');
const SnackBar(content: Text('没有可下载的海报')),
);
return; return;
} }
try { try {
final sourceFile = File(movie.posterPath!); final sourceFile = File(movie.posterPath!);
if (!await sourceFile.exists()) { if (!await sourceFile.exists()) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '海报文件不存在');
const SnackBar(content: Text('海报文件不存在')),
);
return; return;
} }
@@ -813,9 +806,7 @@ class _MovieDetailPageState extends State<MovieDetailPage> {
text: '下载自 MookNote', text: '下载自 MookNote',
); );
} catch (e) { } catch (e) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '下载失败: $e');
SnackBar(content: Text('下载失败: $e')),
);
} }
} }

View File

@@ -6,6 +6,7 @@ import 'package:path/path.dart' as path;
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
/// 添加/编辑影视页面 - 紧凑双行布局设计 /// 添加/编辑影视页面 - 紧凑双行布局设计
class MovieFormPage extends StatefulWidget { class MovieFormPage extends StatefulWidget {
@@ -705,9 +706,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
} }
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '选择海报失败: $e');
SnackBar(content: Text('选择海报失败: $e')),
);
} }
} }
} }
@@ -788,12 +787,7 @@ class _MovieFormPageState extends State<MovieFormPage> {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, widget.movie == null ? '添加成功' : '更新成功');
SnackBar(
content: Text(widget.movie == null ? '添加成功' : '更新成功'),
behavior: SnackBarBehavior.floating,
),
);
Navigator.pop(context); Navigator.pop(context);
} }

View File

@@ -6,6 +6,7 @@ import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
/// 添加/编辑影视记录页面Typecho 风格) /// 添加/编辑影视记录页面Typecho 风格)
class MovieFormPage extends StatefulWidget { class MovieFormPage extends StatefulWidget {
@@ -527,23 +528,12 @@ class _MovieFormPageState extends State<MovieFormPage> {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, widget.movie == null ? '添加成功' : '更新成功');
SnackBar(
content: Text(widget.movie == null ? '添加成功' : '更新成功'),
behavior: SnackBarBehavior.floating,
),
);
Navigator.pop(context); Navigator.pop(context);
} catch (e) { } catch (e) {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '保存失败:$e');
SnackBar(
content: Text('保存失败:$e'),
behavior: SnackBarBehavior.floating,
backgroundColor: Colors.red,
),
);
} finally { } finally {
if (mounted) { if (mounted) {
setState(() { setState(() {

View File

@@ -248,9 +248,7 @@ class _MoviePostersPageState extends State<MoviePostersPage> {
} }
} catch (e) { } catch (e) {
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '添加海报失败: $e');
SnackBar(content: Text('添加海报失败: $e')),
);
} }
} }
} }

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
/// 添加/编辑影评页面 - 极简设计 /// 添加/编辑影评页面 - 极简设计
class MovieReviewFormPage extends StatefulWidget { class MovieReviewFormPage extends StatefulWidget {
@@ -251,12 +252,7 @@ class _MovieReviewFormPageState extends State<MovieReviewFormPage> {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, widget.review == null ? '添加成功' : '更新成功');
SnackBar(
content: Text(widget.review == null ? '添加成功' : '更新成功'),
behavior: SnackBarBehavior.floating,
),
);
Navigator.pop(context); Navigator.pop(context);
} }

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
import 'movie_review_form_page.dart'; import 'movie_review_form_page.dart';
/// 影视影评列表页面 /// 影视影评列表页面
@@ -250,9 +251,7 @@ class _MovieReviewsPageState extends State<MovieReviewsPage> {
await context.read<AppProvider>().removeMovieReview(review.id); await context.read<AppProvider>().removeMovieReview(review.id);
Navigator.pop(context); Navigator.pop(context);
_loadReviews(); _loadReviews();
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '已删除');
const SnackBar(content: Text('已删除')),
);
}, },
child: const Text('删除', style: TextStyle(color: Colors.red)), child: const Text('删除', style: TextStyle(color: Colors.red)),
), ),

View File

@@ -3,6 +3,7 @@ import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
/// 笔记详情页 - 极简主义设计 /// 笔记详情页 - 极简主义设计
class NoteDetailPage extends StatefulWidget { class NoteDetailPage extends StatefulWidget {
@@ -255,9 +256,7 @@ class _NoteDetailPageState extends State<NoteDetailPage> {
if (!mounted) return; if (!mounted) return;
Navigator.pop(context); Navigator.pop(context);
Navigator.pop(context); Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '已删除');
const SnackBar(content: Text('已删除')),
);
}, },
child: const Text('删除', style: TextStyle(color: Colors.red)), child: const Text('删除', style: TextStyle(color: Colors.red)),
), ),

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
/// 添加/编辑笔记页面 - 极简书写界面 /// 添加/编辑笔记页面 - 极简书写界面
class NoteFormPage extends StatefulWidget { class NoteFormPage extends StatefulWidget {
@@ -331,9 +332,7 @@ class _NoteFormPageState extends State<NoteFormPage> {
final content = _contentController.text.trim(); final content = _contentController.text.trim();
if (content.isEmpty) { if (content.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '笔记内容不能为空');
const SnackBar(content: Text('笔记内容不能为空')),
);
return; return;
} }
@@ -363,12 +362,7 @@ class _NoteFormPageState extends State<NoteFormPage> {
if (!mounted) return; if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, _isEditing ? '保存成功' : '添加成功');
SnackBar(
content: Text(_isEditing ? '保存成功' : '添加成功'),
behavior: SnackBarBehavior.floating,
),
);
Navigator.pop(context); Navigator.pop(context);
} }

View File

@@ -6,6 +6,7 @@ import 'package:path/path.dart' as path;
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../utils/user_prefs.dart'; import '../utils/user_prefs.dart';
import '../utils/toast_util.dart';
import 'recycle_bin_page.dart'; import 'recycle_bin_page.dart';
import 'backup_page.dart'; import 'backup_page.dart';
import 'statistics_page.dart'; import 'statistics_page.dart';
@@ -446,13 +447,7 @@ class _ProfilePageState extends State<ProfilePage> {
/// 显示提示 /// 显示提示
void _showToast(String message) { void _showToast(String message) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, message);
SnackBar(
content: Text(message),
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 2),
),
);
} }
/// 选择头像 /// 选择头像

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
/// 回收站页面 /// 回收站页面
class RecycleBinPage extends StatefulWidget { class RecycleBinPage extends StatefulWidget {
@@ -246,9 +247,7 @@ class _RecycleBinPageState extends State<RecycleBinPage> with SingleTickerProvid
await context.read<AppProvider>().restoreMovie(movie.id); await context.read<AppProvider>().restoreMovie(movie.id);
_loadDeletedItems(); _loadDeletedItems();
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '影视已恢复');
const SnackBar(content: Text('影视已恢复')),
);
} }
} }
@@ -259,9 +258,7 @@ class _RecycleBinPageState extends State<RecycleBinPage> with SingleTickerProvid
await context.read<AppProvider>().permanentDeleteMovie(movie.id); await context.read<AppProvider>().permanentDeleteMovie(movie.id);
_loadDeletedItems(); _loadDeletedItems();
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '已彻底删除');
const SnackBar(content: Text('已彻底删除')),
);
} }
} }
} }
@@ -271,9 +268,7 @@ class _RecycleBinPageState extends State<RecycleBinPage> with SingleTickerProvid
await context.read<AppProvider>().restoreBook(book.id); await context.read<AppProvider>().restoreBook(book.id);
_loadDeletedItems(); _loadDeletedItems();
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '书籍已恢复');
const SnackBar(content: Text('书籍已恢复')),
);
} }
} }
@@ -284,9 +279,7 @@ class _RecycleBinPageState extends State<RecycleBinPage> with SingleTickerProvid
await context.read<AppProvider>().permanentDeleteBook(book.id); await context.read<AppProvider>().permanentDeleteBook(book.id);
_loadDeletedItems(); _loadDeletedItems();
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '已彻底删除');
const SnackBar(content: Text('已彻底删除')),
);
} }
} }
} }
@@ -296,9 +289,7 @@ class _RecycleBinPageState extends State<RecycleBinPage> with SingleTickerProvid
await context.read<AppProvider>().restoreNote(note.id); await context.read<AppProvider>().restoreNote(note.id);
_loadDeletedItems(); _loadDeletedItems();
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '笔记已恢复');
const SnackBar(content: Text('笔记已恢复')),
);
} }
} }
@@ -309,9 +300,7 @@ class _RecycleBinPageState extends State<RecycleBinPage> with SingleTickerProvid
await context.read<AppProvider>().permanentDeleteNote(note.id); await context.read<AppProvider>().permanentDeleteNote(note.id);
_loadDeletedItems(); _loadDeletedItems();
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '已彻底删除');
const SnackBar(content: Text('已彻底删除')),
);
} }
} }
} }
@@ -362,9 +351,7 @@ class _RecycleBinPageState extends State<RecycleBinPage> with SingleTickerProvid
await context.read<AppProvider>().clearRecycleBin(); await context.read<AppProvider>().clearRecycleBin();
_loadDeletedItems(); _loadDeletedItems();
if (mounted) { if (mounted) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '回收站已清空');
const SnackBar(content: Text('回收站已清空')),
);
} }
}, },
child: const Text('清空', style: TextStyle(color: Colors.red)), child: const Text('清空', style: TextStyle(color: Colors.red)),

View File

@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../models/data_models.dart'; import '../models/data_models.dart';
import '../utils/toast_util.dart';
import 'movie_detail_page.dart'; import 'movie_detail_page.dart';
import 'book_detail_page.dart'; import 'book_detail_page.dart';
import 'note_detail_page.dart'; import 'note_detail_page.dart';
@@ -65,9 +66,7 @@ class _SearchPageState extends State<SearchPage> {
}); });
} catch (e) { } catch (e) {
setState(() => _isSearching = false); setState(() => _isSearching = false);
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, '搜索失败: $e');
SnackBar(content: Text('搜索失败: $e')),
);
} }
} }

View File

@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../providers/app_provider.dart'; import '../providers/app_provider.dart';
import '../utils/user_prefs.dart'; import '../utils/user_prefs.dart';
import '../utils/toast_util.dart';
/// 自定义左侧弹出菜单 - 极简主义设计 /// 自定义左侧弹出菜单 - 极简主义设计
class CustomDrawer extends StatelessWidget { class CustomDrawer extends StatelessWidget {
@@ -188,12 +189,6 @@ class CustomDrawer extends StatelessWidget {
/// 显示提示 /// 显示提示
void _showToast(BuildContext context, String message) { void _showToast(BuildContext context, String message) {
ScaffoldMessenger.of(context).showSnackBar( ToastUtil.show(context, message);
SnackBar(
content: Text(message),
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 2),
),
);
} }
} }