Files
xianyan/lib/editor/mixins/editor_dialogs.dart
Developer 8cd2703a0b chore: 汇总2026-06-08全量功能迭代与修复
此版本包含多项功能更新与问题修复:
1. 新增iOS ShareExtension分享扩展,支持多类型内容分享
2. 修复认证流程日志提示,更新用户名检测逻辑
3. 优化会话列表UI,替换emoji为CupertinoIcons原生图标
4. 修正搜索类型与频道名称映射,新增音频类型支持
5. 调整启动页布局与多语言配置
6. 重构布局约束,修复无界布局崩溃问题
7. 迁移开发者设置到更多设置页,新增日志级别配置
8. 优化TTS健康检查与自动回退逻辑
9. 新增笔记置顶会话跳转功能
10. 更新后端配置与本地化字符串
11. 重构稍后读模块,支持音频内容处理
12. 优化编辑器功能与字体管理页面
13. 新增本地数据库置顶笔记表
14. 修复Android MANAGE_STORAGE权限配置
2026-06-08 07:55:22 +08:00

313 lines
9.4 KiB
Dart
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// 闲言APP — 编辑器对话框组件
// 创建时间: 2026-04-25
// 更新时间: 2026-04-25
// 作用: 导出选项面板等独立对话框 Widget
// 上次更新: 动态照片按钮禁用(次要色+锁定图标+即将支持提示)+_buildActionRow支持enabled参数
// ============================================================
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
class ExportConfig {
final String format;
final int quality;
final CompressFormat compressFormat;
const ExportConfig({
required this.format,
required this.quality,
required this.compressFormat,
});
static const defaults = ExportConfig(
format: 'JPEG',
quality: 92,
compressFormat: CompressFormat.jpeg,
);
}
class ExportOptionsSheet extends StatefulWidget {
final void Function(ExportConfig config) onSaveToGallery;
final void Function(ExportConfig config) onShare;
final VoidCallback onExportGif;
final VoidCallback onExportMotionPhoto;
final VoidCallback onExportXycard;
final VoidCallback onPreview;
final VoidCallback onCrop;
final VoidCallback onShowInfo;
static const _formats = ['JPEG', 'PNG', 'WebP'];
static const _qualityLabels = ['', '', '', '原始'];
static const _qualityValues = [60, 80, 92, 100];
static const _compressFormats = [
CompressFormat.jpeg,
CompressFormat.png,
CompressFormat.webp,
];
const ExportOptionsSheet({
super.key,
required this.onSaveToGallery,
required this.onShare,
required this.onExportGif,
required this.onExportMotionPhoto,
required this.onExportXycard,
required this.onPreview,
required this.onCrop,
required this.onShowInfo,
});
@override
State<ExportOptionsSheet> createState() => _ExportOptionsSheetState();
}
class _ExportOptionsSheetState extends State<ExportOptionsSheet> {
int _formatIndex = 0;
int _qualityIndex = 2;
ExportConfig get _currentConfig => ExportConfig(
format: ExportOptionsSheet._formats[_formatIndex],
quality: ExportOptionsSheet._qualityValues[_qualityIndex],
compressFormat: ExportOptionsSheet._compressFormats[_formatIndex],
);
@override
Widget build(BuildContext context) {
return Container(
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.75,
),
decoration: BoxDecoration(
color: CupertinoColors.systemBackground.resolveFrom(context),
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
),
child: SingleChildScrollView(
padding: const EdgeInsets.only(
top: 12,
left: 16,
right: 16,
bottom: 32,
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Center(
child: Container(
width: 36,
height: 4,
margin: const EdgeInsets.only(bottom: 16),
decoration: BoxDecoration(
color: CupertinoColors.separator.resolveFrom(context),
borderRadius: BorderRadius.circular(2),
),
),
),
Text(
'📤 导出卡片',
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
),
const SizedBox(height: 16),
_buildFormatSelector(),
const SizedBox(height: 12),
_buildQualitySelector(),
const SizedBox(height: 16),
const Divider(height: 1),
const SizedBox(height: 8),
_buildActionRow(
icon: '💾',
label: '保存到相册',
subtitle: _formatSubtitle,
onTap: () => widget.onSaveToGallery(_currentConfig),
),
_buildActionRow(
icon: '📤',
label: '分享图片',
subtitle: _formatSubtitle,
onTap: () => widget.onShare(_currentConfig),
),
_buildActionRow(
icon: '🎬',
label: '导出GIF',
subtitle: '动画格式',
onTap: widget.onExportGif,
),
_buildActionRow(
icon: '📸',
label: '动态照片',
subtitle: 'Live Photo / Motion Photo',
onTap: widget.onExportMotionPhoto,
enabled: false,
),
_buildActionRow(
icon: '📦',
label: '导出 .xycard',
subtitle: '源文件格式',
onTap: widget.onExportXycard,
),
const Divider(height: 1),
const SizedBox(height: 8),
_buildActionRow(
icon: '🔍',
label: '全屏预览',
subtitle: '缩放查看',
onTap: widget.onPreview,
),
_buildActionRow(
icon: '✂️',
label: '裁剪编辑',
subtitle: '旋转/翻转/裁剪',
onTap: widget.onCrop,
),
_buildActionRow(
icon: '',
label: '图片信息',
subtitle: '尺寸/格式/大小',
onTap: widget.onShowInfo,
),
],
),
),
);
}
String get _formatSubtitle {
final q = ExportOptionsSheet._qualityValues[_qualityIndex];
return '${ExportOptionsSheet._formats[_formatIndex]} · ${ExportOptionsSheet._qualityLabels[_qualityIndex]}($q%)';
}
Widget _buildFormatSelector() {
return Row(
children: [
Text(
'格式',
style: TextStyle(
fontSize: 14,
color: CupertinoColors.secondaryLabel.resolveFrom(context),
),
),
const SizedBox(width: 12),
Expanded(
child: CupertinoSegmentedControl<int>(
groupValue: _formatIndex,
children: {
for (int i = 0; i < ExportOptionsSheet._formats.length; i++)
i: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 4,
),
child: Text(
ExportOptionsSheet._formats[i],
style: const TextStyle(fontSize: 13),
),
),
},
onValueChanged: (v) => setState(() => _formatIndex = v),
),
),
],
);
}
Widget _buildQualitySelector() {
return Row(
children: [
Text(
'质量',
style: TextStyle(
fontSize: 14,
color: CupertinoColors.secondaryLabel.resolveFrom(context),
),
),
const SizedBox(width: 12),
Expanded(
child: CupertinoSegmentedControl<int>(
groupValue: _qualityIndex,
children: {
for (int i = 0; i < ExportOptionsSheet._qualityLabels.length; i++)
i: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 4,
),
child: Text(
ExportOptionsSheet._qualityLabels[i],
style: const TextStyle(fontSize: 13),
),
),
},
onValueChanged: (v) => setState(() => _qualityIndex = v),
),
),
],
);
}
Widget _buildActionRow({
required String icon,
required String label,
required String subtitle,
required VoidCallback onTap,
bool enabled = true,
}) {
// 禁用状态:次要色 + 锁定图标 + "即将支持"提示
final labelColor = enabled
? CupertinoColors.label.resolveFrom(context)
: CupertinoColors.tertiaryLabel.resolveFrom(context);
final subtitleColor = enabled
? CupertinoColors.secondaryLabel.resolveFrom(context)
: CupertinoColors.tertiaryLabel.resolveFrom(context);
final trailingIcon = enabled
? CupertinoIcons.chevron_right
: CupertinoIcons.lock_fill;
final trailingColor = enabled
? CupertinoColors.tertiaryLabel.resolveFrom(context)
: CupertinoColors.tertiaryLabel.resolveFrom(context);
final displaySubtitle = enabled
? subtitle
: '$subtitle · 即将支持';
return CupertinoButton(
padding: const EdgeInsets.symmetric(vertical: 6),
onPressed: enabled ? onTap : null,
child: Row(
children: [
Text(icon, style: TextStyle(fontSize: 20, color: labelColor)),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: labelColor,
),
),
Text(
displaySubtitle,
style: TextStyle(
fontSize: 12,
color: subtitleColor,
),
),
],
),
),
Icon(
trailingIcon,
size: enabled ? 16 : 14,
color: trailingColor,
),
],
),
);
}
}