import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:image_picker/image_picker.dart'; import '../../../models/watermark.dart'; import '../../../providers/watermark_provider.dart'; import '../../../utils/toast.dart'; import 'watermark_edit_page.dart'; import 'widgets/watermark_settings_sheet.dart'; /// 水印相机模块入口:拍照 / 相册选图 → 进编辑页加水印 /// /// 刻意不做自绘相机预览(原项目也是调系统相机):省掉一整套相机权限、 /// 预览流、方向处理的复杂度,代价只是没法实时看到水印。 class WatermarkCameraPage extends ConsumerWidget { const WatermarkCameraPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final settings = ref.watch(watermarkSettingsProvider); return Scaffold( appBar: AppBar( title: const Text('水印相机'), actions: [ IconButton( tooltip: '水印设置', icon: const Icon(Icons.tune), onPressed: () => showWatermarkSettingsSheet(context), ), ], ), body: ListView( padding: const EdgeInsets.fromLTRB(16, 16, 16, 24), children: [ _introCard(), const SizedBox(height: 20), _captureButton( icon: Icons.camera_alt, label: '拍照', primary: true, onTap: () => _pick(context, ImageSource.camera), ), const SizedBox(height: 12), _captureButton( icon: Icons.photo_library_outlined, label: '从相册选', primary: false, onTap: () => _pick(context, ImageSource.gallery), ), const SizedBox(height: 24), _settingsCard(context, settings), ], ), ); } Widget _introCard() { return Container( padding: const EdgeInsets.fromLTRB(18, 18, 18, 18), decoration: BoxDecoration( gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFFFFE5CC), Color(0xFFFFD4A8)], ), borderRadius: BorderRadius.circular(16), ), child: const Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '拍一张,自动加水印', style: TextStyle( fontSize: 17, fontWeight: FontWeight.bold, color: Color(0xFF5D4037), ), ), SizedBox(height: 6), Text( '时间、地点、天气自动填好,可以拖动位置、换颜色。\n' '加好水印的照片直接存进手机相册;原图会同时备份到服务器。', style: TextStyle(fontSize: 12.5, height: 1.6, color: Color(0xFF8D6E63)), ), ], ), ); } Widget _captureButton({ required IconData icon, required String label, required bool primary, required VoidCallback onTap, }) { final style = FilledButton.styleFrom( backgroundColor: primary ? const Color(0xFFC8956E) : Colors.white, foregroundColor: primary ? Colors.white : const Color(0xFFC8956E), minimumSize: const Size.fromHeight(52), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(26), side: primary ? BorderSide.none : const BorderSide(color: Color(0xFFC8956E)), ), elevation: primary ? 0 : 0, ); return FilledButton.icon( onPressed: onTap, style: style, icon: Icon(icon, size: 20), label: Text( label, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600), ), ); } Widget _settingsCard(BuildContext context, WatermarkSettings settings) { final items = [ if (settings.showTime) '时间', if (settings.showAddress) '地点', if (settings.showWeather) '天气', if (settings.showRemark) '备注', if (settings.showBrand) '品牌角标', ]; return InkWell( onTap: () => showWatermarkSettingsSheet(context), borderRadius: BorderRadius.circular(16), child: Container( padding: const EdgeInsets.fromLTRB(18, 16, 14, 16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), ), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( '当前水印内容', style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, color: Color(0xFF5D4037), ), ), const SizedBox(height: 8), if (items.isEmpty) const Text( '全部关闭(照片不会加任何水印)', style: TextStyle(fontSize: 12, color: Color(0xFFBCAAA4)), ) else Wrap( spacing: 6, runSpacing: 6, children: items .map( (t) => Container( padding: const EdgeInsets.symmetric( horizontal: 10, vertical: 4, ), decoration: BoxDecoration( color: const Color(0xFFFFF3E6), borderRadius: BorderRadius.circular(10), ), child: Text( t, style: const TextStyle( fontSize: 12, color: Color(0xFF8D6E63), ), ), ), ) .toList(), ), ], ), ), const Icon(Icons.chevron_right, color: Color(0xFFBCAAA4)), ], ), ), ); } /// 选图。**不传 imageQuality / maxWidth** —— 要的就是原图, /// 编辑页会在合成时按最长边 1920 缩放,原始文件则原样备份到服务器。 Future _pick(BuildContext context, ImageSource source) async { try { final file = await ImagePicker().pickImage(source: source); if (file == null) return; if (!context.mounted) return; await Navigator.of(context).push( MaterialPageRoute(builder: (_) => WatermarkEditPage(imagePath: file.path)), ); } catch (e) { if (context.mounted) { showToast(context, '打开相机/相册失败:$e', error: true); } } } }