generated from dellevin/template
数据备份
This commit is contained in:
251
QUICKSTART.md
251
QUICKSTART.md
@@ -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
106
README.md
@@ -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
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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')),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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)),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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')),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(() {
|
||||||
|
|||||||
@@ -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')),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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)),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 选择头像
|
/// 选择头像
|
||||||
|
|||||||
@@ -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)),
|
||||||
|
|||||||
@@ -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')),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user