import 'dart:async'; import 'dart:ui' as ui; import 'package:flutter/material.dart'; import 'package:flutter/services.dart' show rootBundle, ByteData; import 'package:provider/provider.dart'; import 'package:battery_plus/battery_plus.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:aesthetica_wallpaper/models/recipe.dart'; import 'package:aesthetica_wallpaper/providers/editor_provider.dart'; import 'wallpaper_painter.dart'; // ------------------------------------- // --- 2. 编辑器预览 (EditorPreview) --- // ------------------------------------- class EditorPreview extends StatefulWidget { final GlobalKey? repaintBoundaryKey; final GlobalKey? pureImageKey; // 新增:纯净图片的key const EditorPreview({super.key, this.repaintBoundaryKey, this.pureImageKey}); @override State createState() => _EditorPreviewState(); } class _EditorPreviewState extends State { // State 变量,用于管理异步资源和流 ui.Image? _loadedImage; bool _isLoading = true; String _currentImagePath = ''; // 动态效果的当前值 Color _timeOverlayColor = Colors.transparent; double _batterySaturationMod = 1.0; // 1.0 = 正常, 0.0 = 黑白 // 流订阅 StreamSubscription? _timeSubscription; StreamSubscription? _batterySubscription; // 异步加载 ui.Image Future _loadImage(String path) async { if (mounted) { setState(() { _isLoading = true; }); } try { final ByteData data = await rootBundle.load(path); final ui.Codec codec = await ui.instantiateImageCodec( data.buffer.asUint8List(), ); final ui.FrameInfo fi = await codec.getNextFrame(); if (mounted) { setState(() { _loadedImage = fi.image; _isLoading = false; }); } } catch (e) { if (mounted) { setState(() { _isLoading = false; }); } debugPrint("Error loading image: $e"); } } // 在 didChangeDependencies 中加载图片和订阅流 @override void didChangeDependencies() { super.didChangeDependencies(); final provider = Provider.of(context); final recipe = provider.currentRecipe; // 检查图片路径是否已更改 if (recipe.baseImagePath != _currentImagePath && recipe.baseImagePath.isNotEmpty) { setState(() { _isLoading = true; _currentImagePath = recipe.baseImagePath; }); _loadImage(_currentImagePath); } // 订阅或取消订阅流 _subscribeToStreams(provider); } // 管理流订阅 void _subscribeToStreams(EditorProvider provider) { // ---- 时间感知 ---- _timeSubscription?.cancel(); if (provider.isTimeAware) { _timeSubscription = Stream.periodic( const Duration(minutes: 1), (_) => DateTime.now(), ).listen((time) { if (mounted) { setState(() => _timeOverlayColor = _getTimeAwareOverlay(time)); } }); // 立即设置一次 _timeOverlayColor = _getTimeAwareOverlay(DateTime.now()); } else { _timeOverlayColor = Colors.transparent; } // ---- 电量感知 ---- _batterySubscription?.cancel(); if (provider.isBatteryAware) { final battery = Battery(); _batterySubscription = battery.onBatteryStateChanged.listen((_) async { _updateBatteryEffect(battery); }); // 立即设置一次 _updateBatteryEffect(battery); } else { _batterySaturationMod = 1.0; } } // (已修复) Future _updateBatteryEffect(Battery battery) async { try { // 错误 1 修复: // 'getBatteryLevel()' 不是一个方法。 // 正确的属性是 '.batteryLevel',它是一个 Future。 final level = await battery.batteryLevel; final newState = (level < 20) ? 0.0 : 1.0; // 低于20%则变为黑白 if (mounted && newState != _batterySaturationMod) { setState(() => _batterySaturationMod = newState); } } catch (e) { debugPrint("Error getting battery level: $e"); } } // 在 widget 销毁时取消所有订阅 @override void dispose() { _timeSubscription?.cancel(); _batterySubscription?.cancel(); _loadedImage?.dispose(); super.dispose(); } // 帮助函数: 根据时间获取动态蒙版颜色 Color _getTimeAwareOverlay(DateTime time) { final hour = time.hour; if (hour < 5 || hour > 20) { // 夜晚 (8 PM - 5 AM) return Colors.blue.withValues(alpha: 0.3); } else if (hour < 10) { // 早晨 (5 AM - 10 AM) return Colors.yellow.withValues(alpha: 0.15); } return Colors.transparent; // 白天 } @override Widget build(BuildContext context) { // 监听 EditorProvider 的变化以触发重建 final provider = context.watch(); final recipe = provider.currentRecipe; return Stack( children: [ // 纯净图片渲染区域(用于保存,使用Offstage隐藏) Offstage( offstage: true, // 隐藏但仍然渲染 child: RepaintBoundary( key: widget.pureImageKey, child: (_isLoading || _loadedImage == null) ? const SizedBox(width: 100, height: 100) // 占位符 : _buildPureImageRenderer(recipe), ), ), // 模拟手机屏幕预览 _buildPhonePreview(recipe), ], ); } // 构建纯净的图片渲染器(用于保存) Widget _buildPureImageRenderer(Recipe recipe) { // 获取原始图片尺寸 final imageWidth = _loadedImage!.width.toDouble(); final imageHeight = _loadedImage!.height.toDouble(); Widget canvasWidget = CustomPaint( size: Size(imageWidth, imageHeight), painter: WallpaperPainter( image: _loadedImage!, recipe: recipe, timeOverlay: _timeOverlayColor, batterySaturation: _batterySaturationMod, ), ); // 应用像素化效果 if (recipe.pixelate > 1.0) { final Matrix4 pixelMatrix = Matrix4.identity(); final double scale = 1.0 / recipe.pixelate; pixelMatrix.scaleByDouble(scale, scale, 1.0, 1.0); return ImageFiltered( imageFilter: ui.ImageFilter.matrix( pixelMatrix.storage, filterQuality: ui.FilterQuality.none, ), child: canvasWidget, ); } return canvasWidget; } // 构建手机预览界面 Widget _buildPhonePreview(Recipe recipe) { return Container( margin: const EdgeInsets.all(24), decoration: BoxDecoration( color: Colors.black, border: Border.all(color: Colors.grey[700]!, width: 4), borderRadius: BorderRadius.circular(40), ), child: ClipRRect( borderRadius: BorderRadius.circular(36), child: RepaintBoundary( key: widget.repaintBoundaryKey, child: Stack( fit: StackFit.expand, children: [ // --- 核心渲染区 --- (_isLoading || _loadedImage == null) ? const Center(child: CircularProgressIndicator()) : _buildCanvasRenderer(recipe), // --- 模拟手机 UI (保持在顶部) --- _buildMockUI(context), ], ), ), ), ); } // (已修复) Widget _buildCanvasRenderer(Recipe recipe) { // 我们的 "像素化" 效果是一个特例 // 它通过 ImageFiltered hack 实现,所以我们把它放在 CustomPaint 的 *外部* Widget canvasWidget = CustomPaint( // 错误 1, 2, 3 修复: // 确保所有参数都在 CustomPaint 构造函数内部 painter: WallpaperPainter( image: _loadedImage!, recipe: recipe, timeOverlay: _timeOverlayColor, batterySaturation: _batterySaturationMod, ), // 必须有一个 child 才能让 CustomPaint 获得大小 child: const SizedBox.expand(), ); // 应用像素化 Hack if (recipe.pixelate > 1.0) { // 错误 4, 5, 6 修复: // 确保 `Matrix4` 逻辑在 CustomPaint *外部* final Matrix4 pixelMatrix = Matrix4.identity(); final double scale = 1.0 / recipe.pixelate; pixelMatrix.scaleByDouble(scale, scale, 1.0, 1.0); return ImageFiltered( imageFilter: ui.ImageFilter.matrix( pixelMatrix.storage, // 明确传递 .storage (一个 Float64List) filterQuality: ui.FilterQuality.none, // 关键:使用最近邻插值 ), child: canvasWidget, ); } return canvasWidget; } // 模拟手机UI Widget _buildMockUI(BuildContext context) { return Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ // 模拟状态栏 Padding( padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '9:41', style: GoogleFonts.lato( color: Colors.white, fontWeight: FontWeight.bold, ), ), const Row( children: [ Icon( Icons.signal_cellular_alt, color: Colors.white, size: 16, ), SizedBox(width: 4), Icon(Icons.wifi, color: Colors.white, size: 16), SizedBox(width: 4), Icon(Icons.battery_full, color: Colors.white, size: 16), ], ), ], ), ), // 模拟 DOCK 栏 Container( padding: const EdgeInsets.all(12), margin: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.black.withValues(alpha: 0.3), borderRadius: BorderRadius.circular(24), ), child: const Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Icon(Icons.phone, color: Colors.white, size: 32), Icon(Icons.message, color: Colors.white, size: 32), Icon(Icons.camera_alt, color: Colors.white, size: 32), Icon(Icons.music_note, color: Colors.white, size: 32), ], ), ), ], ); } }