import 'dart:io'; import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:intl/intl.dart'; import 'package:path_provider/path_provider.dart'; import 'package:chicken_farm/core/utils/logger.dart'; class WatermarkedCamera extends StatefulWidget { final Function(File)? onImageCaptured; final String? watermarkText; const WatermarkedCamera({ super.key, this.onImageCaptured, this.watermarkText, }); @override State createState() => _WatermarkedCameraState(); } class _WatermarkedCameraState extends State { XFile? _capturedImage; File? _watermarkedImage; bool _isProcessing = false; Future _takePicture() async { try { final ImagePicker picker = ImagePicker(); final XFile? photo = await picker.pickImage( source: ImageSource.camera, imageQuality: 80, ); if (photo != null) { setState(() { _capturedImage = photo; _isProcessing = true; }); // 添加水印并处理图像 await _addWatermark(photo); } } catch (e) { if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('拍照失败: $e'))); } logger.e('拍照失败: $e'); } } Future _addWatermark(XFile imageFile) async { try { // 获取水印文本 String watermark = widget.watermarkText ?? DateFormat('yyyy-MM-dd HH:mm:ss').format(DateTime.now()); // 将图片转换为字节以便处理 final Uint8List imageBytes = await imageFile.readAsBytes(); // 创建图片对象 final ui.Codec codec = await ui.instantiateImageCodec(imageBytes); final ui.FrameInfo frameInfo = await codec.getNextFrame(); final ui.Image image = frameInfo.image; // 创建画布 final ui.PictureRecorder recorder = ui.PictureRecorder(); final ui.Canvas canvas = ui.Canvas(recorder); // 绘制原始图片 canvas.drawImage(image, Offset.zero, ui.Paint()); // 添加水印文字 final ui.ParagraphBuilder paragraphBuilder = ui.ParagraphBuilder( ui.ParagraphStyle( textAlign: TextAlign.left, fontSize: 24.0, textDirection: ui.TextDirection.ltr, ), ); paragraphBuilder.pushStyle( ui.TextStyle( color: const ui.Color(0x80FFFFFF), // 半透明白色 fontSize: 24.0, ), ); paragraphBuilder.addText(watermark); final ui.Paragraph paragraph = paragraphBuilder.build() ..layout(ui.ParagraphConstraints(width: image.width.toDouble())); // 在右下角绘制水印 canvas.drawParagraph( paragraph, Offset( image.width.toDouble() - paragraph.width - 20, image.height.toDouble() - 40, ), ); // 完成绘制 final ui.Picture picture = recorder.endRecording(); final ui.Image watermarkedImage = await picture.toImage( image.width, image.height, ); // 转换为字节数据 final ByteData? byteData = await watermarkedImage.toByteData( format: ui.ImageByteFormat.png, ); if (byteData != null) { // 保存到临时文件 final tempDir = await getTemporaryDirectory(); final file = File( '${tempDir.path}/watermarked_${DateTime.now().millisecondsSinceEpoch}.png', ); await file.writeAsBytes(byteData.buffer.asUint8List()); setState(() { _watermarkedImage = file; _isProcessing = false; }); } else { setState(() { _isProcessing = false; }); if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(const SnackBar(content: Text('处理图片失败'))); } logger.e('处理图片失败'); } } catch (e) { setState(() { _isProcessing = false; }); if (mounted) { ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text('添加水印失败: $e'))); } logger.e('添加水印失败: $e'); } } void _confirmImage() { if (_watermarkedImage != null && widget.onImageCaptured != null) { widget.onImageCaptured!(_watermarkedImage!); } Navigator.of(context).pop(); } void _retakePicture() { setState(() { _capturedImage = null; _watermarkedImage = null; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('拍照签到'), leading: IconButton( icon: const Icon(Icons.close), onPressed: () => Navigator.of(context).pop(), ), ), body: Column( children: [ Expanded( child: _capturedImage == null ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon( Icons.camera_alt, size: 100, color: Colors.grey, ), const SizedBox(height: 20), const Text('点击按钮拍照', style: TextStyle(fontSize: 18)), ], ), ) : _isProcessing ? const Center(child: CircularProgressIndicator()) : Center( child: _watermarkedImage != null ? Image.file(_watermarkedImage!) : Image.file(File(_capturedImage!.path)), ), ), Padding( padding: const EdgeInsets.all(16.0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ if (_capturedImage == null) FloatingActionButton( onPressed: _takePicture, child: const Icon(Icons.camera), ) else if (!_isProcessing) Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: _retakePicture, style: ElevatedButton.styleFrom( backgroundColor: Colors.grey, foregroundColor: Colors.white, ), child: const Text('重新拍照'), ), ElevatedButton( onPressed: _confirmImage, style: ElevatedButton.styleFrom( backgroundColor: Colors.green, foregroundColor: Colors.white, ), child: const Text('确认提交'), ), ], ), ], ), ), ], ), ); } }