import 'dart:typed_data'; import 'dart:ui' as ui; import 'package:aesthetica_wallpaper/core/app_ads_tools.dart'; import 'package:flutter/material.dart'; import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:provider/provider.dart'; import 'package:aesthetica_wallpaper/providers/editor_provider.dart'; import 'package:aesthetica_wallpaper/screens/editor/wallpaper_painter.dart'; class SimpleSaveDialog extends StatefulWidget { final String fileName; const SimpleSaveDialog({super.key, required this.fileName}); @override State createState() => _SimpleSaveDialogState(); } class _SimpleSaveDialogState extends State { Uint8List? _previewImageBytes; bool _isGenerating = true; bool _isSaving = false; String? _errorMessage; ui.Image? _baseImage; @override void initState() { super.initState(); _generatePreview(); } Future _generatePreview() async { try { setState(() { _isGenerating = true; _errorMessage = null; }); final provider = Provider.of(context, listen: false); final recipe = provider.currentRecipe; // 加载基础图片 final ByteData data = await DefaultAssetBundle.of( context, ).load(recipe.baseImagePath); final ui.Codec codec = await ui.instantiateImageCodec( data.buffer.asUint8List(), ); final ui.FrameInfo frameInfo = await codec.getNextFrame(); _baseImage = frameInfo.image; // 创建画布并绘制 final recorder = ui.PictureRecorder(); final canvas = Canvas(recorder); // 设置画布尺寸(预览用较小尺寸) const double previewWidth = 300; const double previewHeight = 400; // 使用 WallpaperPainter 绘制 final painter = WallpaperPainter( image: _baseImage!, recipe: recipe, timeOverlay: Colors.transparent, // 简化,不使用动态效果 batterySaturation: 1.0, ); painter.paint(canvas, const Size(previewWidth, previewHeight)); // 生成图片 final picture = recorder.endRecording(); final image = await picture.toImage( previewWidth.toInt(), previewHeight.toInt(), ); final byteData = await image.toByteData(format: ui.ImageByteFormat.png); if (byteData != null) { setState(() { _previewImageBytes = byteData.buffer.asUint8List(); _isGenerating = false; }); } else { throw Exception('Failed to generate preview image'); } } catch (e) { debugPrint('Preview generation failed: $e'); setState(() { _isGenerating = false; _errorMessage = e.toString(); }); } } Future _saveImage() async { if (_baseImage == null) return; setState(() { _isSaving = true; }); try { final provider = Provider.of(context, listen: false); final recipe = provider.currentRecipe; // 创建高分辨率画布 final recorder = ui.PictureRecorder(); final canvas = Canvas(recorder); // 使用原始图片尺寸 final double saveWidth = _baseImage!.width.toDouble(); final double saveHeight = _baseImage!.height.toDouble(); // 使用 WallpaperPainter 绘制高质量版本 final painter = WallpaperPainter( image: _baseImage!, recipe: recipe, timeOverlay: Colors.transparent, batterySaturation: 1.0, ); painter.paint(canvas, Size(saveWidth, saveHeight)); // 生成高质量图片 final picture = recorder.endRecording(); final image = await picture.toImage( saveWidth.toInt(), saveHeight.toInt(), ); final byteData = await image.toByteData(format: ui.ImageByteFormat.png); if (byteData == null) { throw Exception('Failed to generate save image'); } final pngBytes = byteData.buffer.asUint8List(); // Save to gallery final result = await ImageGallerySaver.saveImage( pngBytes, name: widget.fileName, quality: 100, ); final success = result['isSuccess'] == true; if (mounted) { Navigator.of(context).pop(); // 关闭对话框 // Show result ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( success ? '✅ Wallpaper saved to gallery!' : '❌ Save failed, please try again', ), backgroundColor: success ? Colors.green : Colors.red, duration: const Duration(seconds: 3), ), ); } } catch (e) { debugPrint('Save failed: $e'); setState(() { _isSaving = false; _errorMessage = 'Save failed: $e'; }); } } @override void dispose() { _baseImage?.dispose(); super.dispose(); } @override Widget build(BuildContext context) { 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: Colors.grey[900], borderRadius: BorderRadius.circular(20), ), child: Column( children: [ // Title bar Container( padding: const EdgeInsets.all(20), decoration: const BoxDecoration( gradient: LinearGradient( colors: [Colors.pinkAccent, Colors.purpleAccent], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.only( topLeft: Radius.circular(20), topRight: Radius.circular(20), ), ), child: Row( children: [ const Icon(Icons.save_alt, color: Colors.white, size: 24), const SizedBox(width: 12), const Expanded( child: Text( 'Save Wallpaper', style: TextStyle( color: Colors.white, fontSize: 20, fontWeight: FontWeight.bold, ), ), ), IconButton( icon: const Icon(Icons.close, color: Colors.white), onPressed: ()async{ // Navigator.of(context).pop(); final bool adShown = await AppAdsTools.instance.showAd( AdPlacement.interstitial2, onAdClosed: () { Navigator.of(context).pop(); }, ); if (!adShown) { Navigator.of(context).pop(); } }, ), ], ), ), // Preview area Expanded( child: Container( margin: const EdgeInsets.all(20), decoration: BoxDecoration( color: Colors.black, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.grey[700]!, width: 2), ), child: ClipRRect( borderRadius: BorderRadius.circular(10), child: _buildPreviewContent(), ), ), ), // Button area Container( padding: const EdgeInsets.all(20), child: _buildButtons(), ), ], ), ), ); } Widget _buildPreviewContent() { if (_isGenerating) { return const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(color: Colors.pinkAccent, strokeWidth: 3), SizedBox(height: 16), Text( 'Generating preview...', style: TextStyle(color: Colors.white, fontSize: 16), ), ], ), ); } if (_errorMessage != null) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.error_outline, color: Colors.red, size: 48), const SizedBox(height: 16), const Text( 'Preview generation failed', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(height: 8), Padding( padding: const EdgeInsets.symmetric(horizontal: 20), child: Text( _errorMessage!, style: TextStyle(color: Colors.grey[400], fontSize: 14), textAlign: TextAlign.center, ), ), const SizedBox(height: 20), ElevatedButton( onPressed: _generatePreview, style: ElevatedButton.styleFrom( backgroundColor: Colors.pinkAccent, foregroundColor: Colors.white, ), child: const Text('Regenerate'), ), ], ), ); } if (_previewImageBytes != null) { return Stack( fit: StackFit.expand, children: [ Image.memory(_previewImageBytes!, fit: BoxFit.contain), if (_isSaving) Container( color: Colors.black.withValues(alpha: 0.7), child: const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator( color: Colors.pinkAccent, strokeWidth: 3, ), SizedBox(height: 16), Text( 'Saving to gallery...', style: TextStyle(color: Colors.white, fontSize: 16), ), ], ), ), ), ], ); } return const SizedBox.shrink(); } Widget _buildButtons() { if (_errorMessage != null) { return Row( mainAxisAlignment: MainAxisAlignment.center, children: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Cancel', style: TextStyle(color: Colors.grey)), ), ], ); } if (_previewImageBytes == null) { return const SizedBox.shrink(); } return Row( children: [ Expanded( child: TextButton( onPressed: _isSaving ? null : () => Navigator.of(context).pop(), child: const Text('Cancel', style: TextStyle(color: Colors.grey)), ), ), const SizedBox(width: 16), Expanded( flex: 2, child: ElevatedButton( onPressed: _isSaving ? null : _saveImage, style: ElevatedButton.styleFrom( backgroundColor: Colors.pinkAccent, foregroundColor: Colors.white, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), ), child: _isSaving ? const SizedBox( width: 20, height: 20, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : const Text( 'Save to Gallery', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), ), ), ], ); } }