549 lines
15 KiB
Dart
549 lines
15 KiB
Dart
import 'dart:io';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:receive_sharing_intent/receive_sharing_intent.dart';
|
|
import '../providers/share_provider.dart';
|
|
import 'custom_button.dart';
|
|
import 'image_grid_preview.dart';
|
|
import 'folder_selector.dart';
|
|
import 'tag_selector.dart';
|
|
|
|
/// 保存对话框 - 处理分享图片的保存界面
|
|
/// 支持批量保存、文件夹选择、标签添加等功能
|
|
class SaveDialog extends ConsumerStatefulWidget {
|
|
/// 分享接收的媒体文件列表
|
|
final List<SharedMediaFile> sharedFiles;
|
|
|
|
/// 对话框关闭回调
|
|
final VoidCallback onClose;
|
|
|
|
const SaveDialog({
|
|
Key? key,
|
|
required this.sharedFiles,
|
|
required this.onClose,
|
|
}) : super(key: key);
|
|
|
|
@override
|
|
ConsumerState<SaveDialog> createState() => _SaveDialogState();
|
|
}
|
|
|
|
class _SaveDialogState extends ConsumerState<SaveDialog> {
|
|
/// 当前选中的文件夹ID
|
|
String? selectedFolderId;
|
|
|
|
/// 当前输入的标签列表
|
|
final List<String> tags = [];
|
|
|
|
/// 标签输入控制器
|
|
final TextEditingController tagController = TextEditingController();
|
|
|
|
/// 备注输入控制器
|
|
final TextEditingController noteController = TextEditingController();
|
|
|
|
/// 是否为批量保存模式
|
|
bool isBatchMode = true;
|
|
|
|
/// 当前编辑的图片索引
|
|
int currentEditingIndex = 0;
|
|
|
|
@override
|
|
void dispose() {
|
|
tagController.dispose();
|
|
noteController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
/// 切换批量/单张编辑模式
|
|
void toggleEditMode() {
|
|
setState(() {
|
|
isBatchMode = !isBatchMode;
|
|
currentEditingIndex = 0;
|
|
});
|
|
}
|
|
|
|
/// 切换到下一张图片编辑
|
|
void nextImage() {
|
|
if (currentEditingIndex < widget.sharedFiles.length - 1) {
|
|
setState(() {
|
|
currentEditingIndex++;
|
|
});
|
|
}
|
|
}
|
|
|
|
/// 切换到上一张图片编辑
|
|
void previousImage() {
|
|
if (currentEditingIndex > 0) {
|
|
setState(() {
|
|
currentEditingIndex--;
|
|
});
|
|
}
|
|
}
|
|
|
|
/// 添加标签
|
|
void addTag(String tag) {
|
|
if (tag.isNotEmpty && !tags.contains(tag)) {
|
|
setState(() {
|
|
tags.add(tag);
|
|
});
|
|
tagController.clear();
|
|
}
|
|
}
|
|
|
|
/// 移除标签
|
|
void removeTag(String tag) {
|
|
setState(() {
|
|
tags.remove(tag);
|
|
});
|
|
}
|
|
|
|
/// 执行保存操作
|
|
Future<void> performSave() async {
|
|
if (selectedFolderId == null) {
|
|
_showErrorSnackBar('请选择保存文件夹');
|
|
return;
|
|
}
|
|
|
|
// 显示保存进度
|
|
showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (context) => const Dialog(
|
|
child: Padding(
|
|
padding: EdgeInsets.all(24.0),
|
|
child: Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
CircularProgressIndicator(),
|
|
SizedBox(height: 16),
|
|
Text('正在保存图片...'),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
|
|
try {
|
|
if (isBatchMode) {
|
|
// 批量保存模式
|
|
await ref.read(shareProvider.notifier).saveBatchImages(
|
|
sharedFiles: widget.sharedFiles,
|
|
folderId: selectedFolderId!,
|
|
tags: tags,
|
|
note: noteController.text.trim(),
|
|
);
|
|
} else {
|
|
// 单张保存模式
|
|
await ref.read(shareProvider.notifier).saveSingleImage(
|
|
sharedFile: widget.sharedFiles[currentEditingIndex],
|
|
folderId: selectedFolderId!,
|
|
tags: tags,
|
|
note: noteController.text.trim(),
|
|
);
|
|
}
|
|
|
|
// 关闭进度对话框
|
|
Navigator.of(context).pop();
|
|
|
|
// 显示成功消息
|
|
_showSuccessSnackBar('图片保存成功!');
|
|
|
|
// 关闭保存对话框
|
|
widget.onClose();
|
|
} catch (e) {
|
|
// 关闭进度对话框
|
|
Navigator.of(context).pop();
|
|
|
|
// 显示错误消息
|
|
_showErrorSnackBar('保存失败:${e.toString()}');
|
|
}
|
|
}
|
|
|
|
/// 显示错误提示
|
|
void _showErrorSnackBar(String message) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(message),
|
|
backgroundColor: Theme.of(context).colorScheme.error,
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 显示成功提示
|
|
void _showSuccessSnackBar(String message) {
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(
|
|
content: Text(message),
|
|
backgroundColor: Theme.of(context).colorScheme.primary,
|
|
),
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
final colorScheme = theme.colorScheme;
|
|
|
|
return Dialog(
|
|
backgroundColor: Colors.transparent,
|
|
child: Container(
|
|
width: MediaQuery.of(context).size.width * 0.9,
|
|
height: MediaQuery.of(context).size.height * 0.8,
|
|
decoration: BoxDecoration(
|
|
color: theme.scaffoldBackgroundColor.withOpacity(0.95),
|
|
borderRadius: BorderRadius.circular(16),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.3),
|
|
blurRadius: 20,
|
|
offset: const Offset(0, 8),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
children: [
|
|
// 标题栏
|
|
_buildHeader(colorScheme),
|
|
|
|
// 内容区域
|
|
Expanded(
|
|
child: Row(
|
|
children: [
|
|
// 左侧图片预览区域
|
|
Expanded(
|
|
flex: 2,
|
|
child: _buildImagePreviewArea(),
|
|
),
|
|
|
|
// 分隔线
|
|
VerticalDivider(
|
|
color: colorScheme.outline.withOpacity(0.2),
|
|
thickness: 1,
|
|
),
|
|
|
|
// 右侧设置区域
|
|
Expanded(
|
|
flex: 1,
|
|
child: _buildSettingsArea(colorScheme),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// 底部操作栏
|
|
_buildFooter(colorScheme),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 构建标题栏
|
|
Widget _buildHeader(ColorScheme colorScheme) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surface,
|
|
borderRadius: const BorderRadius.only(
|
|
topLeft: Radius.circular(16),
|
|
topRight: Radius.circular(16),
|
|
),
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
'保存灵感',
|
|
style: Theme.of(context).textTheme.headlineSmall?.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const Spacer(),
|
|
|
|
// 批量/单张切换按钮
|
|
if (widget.sharedFiles.length > 1)
|
|
Padding(
|
|
padding: const EdgeInsets.only(right: 8),
|
|
child: Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
FilterChip(
|
|
label: const Text('批量'),
|
|
selected: isBatchMode,
|
|
onSelected: (selected) {
|
|
if (selected) toggleEditMode();
|
|
},
|
|
avatar: const Icon(Icons.photo_library),
|
|
),
|
|
const SizedBox(width: 8),
|
|
FilterChip(
|
|
label: const Text('单张'),
|
|
selected: !isBatchMode,
|
|
onSelected: (selected) {
|
|
if (selected) toggleEditMode();
|
|
},
|
|
avatar: const Icon(Icons.photo),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
|
|
// 关闭按钮
|
|
IconButton(
|
|
onPressed: widget.onClose,
|
|
icon: const Icon(Icons.close),
|
|
tooltip: '关闭',
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 构建图片预览区域
|
|
Widget _buildImagePreviewArea() {
|
|
if (isBatchMode) {
|
|
// 批量模式:使用图片网格预览组件
|
|
return ImageGridPreview(
|
|
images: widget.sharedFiles,
|
|
selectedIndices: [], // 批量模式下显示所有图片
|
|
onSelectionChanged: (index, selected) {
|
|
// 批量模式下不处理选择
|
|
},
|
|
showSelection: false, // 批量模式下不显示选择框
|
|
crossAxisCount: 2,
|
|
spacing: 8.0,
|
|
padding: const EdgeInsets.all(16),
|
|
);
|
|
} else {
|
|
// 单张模式:显示单张图片和切换控制
|
|
return Column(
|
|
children: [
|
|
// 图片显示区域
|
|
Expanded(
|
|
child: Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: ClipRRect(
|
|
borderRadius: BorderRadius.circular(8),
|
|
child: Image.file(
|
|
File(widget.sharedFiles[currentEditingIndex].path),
|
|
fit: BoxFit.contain,
|
|
errorBuilder: (context, error, stackTrace) {
|
|
return Container(
|
|
color: Colors.grey[300],
|
|
child: const Icon(
|
|
Icons.broken_image,
|
|
color: Colors.grey,
|
|
size: 64,
|
|
),
|
|
);
|
|
},
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// 图片切换控制
|
|
if (widget.sharedFiles.length > 1)
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
IconButton(
|
|
onPressed: currentEditingIndex > 0 ? previousImage : null,
|
|
icon: const Icon(Icons.keyboard_arrow_left),
|
|
),
|
|
Text(
|
|
'${currentEditingIndex + 1} / ${widget.sharedFiles.length}',
|
|
style: Theme.of(context).textTheme.bodyMedium,
|
|
),
|
|
IconButton(
|
|
onPressed: currentEditingIndex < widget.sharedFiles.length - 1
|
|
? nextImage
|
|
: null,
|
|
icon: const Icon(Icons.keyboard_arrow_right),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 构建设置区域
|
|
Widget _buildSettingsArea(ColorScheme colorScheme) {
|
|
return Padding(
|
|
padding: const EdgeInsets.all(16),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// 文件夹选择
|
|
_buildFolderSelector(colorScheme),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// 标签输入
|
|
_buildTagInput(colorScheme),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// 备注输入
|
|
_buildNoteInput(colorScheme),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 构建文件夹选择器
|
|
Widget _buildFolderSelector(ColorScheme colorScheme) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'选择文件夹',
|
|
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
GestureDetector(
|
|
onTap: _showFolderSelector,
|
|
child: Container(
|
|
width: double.infinity,
|
|
padding: const EdgeInsets.all(12),
|
|
decoration: BoxDecoration(
|
|
border: Border.all(color: colorScheme.outline.withOpacity(0.3)),
|
|
borderRadius: BorderRadius.circular(8),
|
|
color: colorScheme.surface,
|
|
),
|
|
child: Row(
|
|
children: [
|
|
Icon(
|
|
selectedFolderId != null ? Icons.folder : Icons.folder_outlined,
|
|
color: colorScheme.primary,
|
|
),
|
|
const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
selectedFolderId != null ? '已选择文件夹' : '点击选择文件夹',
|
|
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
|
color: selectedFolderId != null
|
|
? colorScheme.onSurface
|
|
: colorScheme.onSurface.withOpacity(0.6),
|
|
),
|
|
),
|
|
),
|
|
const Icon(Icons.arrow_drop_down),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
/// 构建标签输入
|
|
Widget _buildTagInput(ColorScheme colorScheme) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'添加标签',
|
|
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
TagSelector(
|
|
selectedTags: tags,
|
|
onTagsChanged: (newTags) {
|
|
setState(() {
|
|
tags.clear();
|
|
tags.addAll(newTags);
|
|
});
|
|
},
|
|
showCreateButton: true,
|
|
maxTags: 10,
|
|
hintText: '输入标签或从已有标签中选择',
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
/// 构建备注输入
|
|
Widget _buildNoteInput(ColorScheme colorScheme) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
'添加备注',
|
|
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
|
fontWeight: FontWeight.bold,
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
TextField(
|
|
controller: noteController,
|
|
decoration: const InputDecoration(
|
|
hintText: '输入备注信息(可选)',
|
|
border: OutlineInputBorder(),
|
|
prefixIcon: Icon(Icons.note_add),
|
|
),
|
|
maxLines: 3,
|
|
minLines: 1,
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
/// 构建底部操作栏
|
|
Widget _buildFooter(ColorScheme colorScheme) {
|
|
return Container(
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surface,
|
|
borderRadius: const BorderRadius.only(
|
|
bottomLeft: Radius.circular(16),
|
|
bottomRight: Radius.circular(16),
|
|
),
|
|
),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
// 取消按钮
|
|
CustomButton(
|
|
text: '取消',
|
|
onPressed: widget.onClose,
|
|
buttonType: ButtonType.text,
|
|
),
|
|
const SizedBox(width: 16),
|
|
|
|
// 保存按钮
|
|
CustomButton(
|
|
text: isBatchMode
|
|
? '保存 ${widget.sharedFiles.length} 张图片'
|
|
: '保存图片',
|
|
onPressed: performSave,
|
|
buttonType: ButtonType.primary,
|
|
isLoading: false,
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 显示文件夹选择器
|
|
void _showFolderSelector() {
|
|
showDialog(
|
|
context: context,
|
|
builder: (context) => FolderSelectorDialog(
|
|
onFolderSelected: (folderId) {
|
|
setState(() {
|
|
selectedFolderId = folderId;
|
|
});
|
|
Navigator.of(context).pop();
|
|
},
|
|
currentFolderId: selectedFolderId,
|
|
),
|
|
);
|
|
}
|
|
} |