| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164 |
- import 'dart:ui' as ui;
- import 'package:flutter/material.dart';
- import 'package:flutter/services.dart';
- import 'package:logging/logging.dart';
- import 'package:puzzleweave/config/device.dart';
- import 'package:puzzleweave/models/download.dart';
- import 'package:puzzleweave/models/items.dart';
- final Logger _log = Logger('home_board');
- enum HomeBoardStatus { loading, dealing, playing, done, unlocking }
- class HomeBoard {
- final Device device;
- // 原合集图
- ui.Image? image;
- // 纸牌背面图
- ui.Image? cardImage;
- // board 的canvas绘制区域尺寸
- final double canvasWidth;
- final double canvasHeight;
- /// 一张合集分为几宫格,固定5x5
- final int rows = 5;
- final int cols = 5;
- int get count => rows * cols;
- // 每个piece的逻辑尺寸
- double get pieceLogicalWidth => canvasWidth / cols;
- double get pieceLogicalHeight => canvasHeight / rows;
- // 用于触发重绘的通知器
- final ValueNotifier<int> boardNotifier = ValueNotifier<int>(1);
- // 用于通知外部资源已准备就绪
- final ValueNotifier<bool> isReadyNotifier = ValueNotifier<bool>(false);
- HomeBoardStatus status = HomeBoardStatus.loading;
- ListItem? _currentCollectionItem;
- // 2. 优化:记录正在加载的 ID,解决异步竞态问题
- String? _loadingImageUrl;
- ListItem? get currentCollectionItem => _currentCollectionItem;
- set currentCollectionItem(ListItem? item) {
- if (item == null) return;
- if (_currentCollectionItem?.id != item.id) {
- _currentCollectionItem = item;
- _loadImage();
- }
- }
- Offset _unlockTargetOffset = Offset.zero;
- double _unlockTargetScale = 1.0;
- Offset get unlockTargetOffset => _unlockTargetOffset;
- double get unlockTargetScale => _unlockTargetScale;
- void setUnlockAnimationTarget({required Offset targetOffset, required double targetScale}) {
- _unlockTargetOffset = targetOffset;
- _unlockTargetScale = targetScale;
- }
- HomeBoard({required this.canvasWidth, required this.canvasHeight, required this.device}) {
- _loadCardImage();
- }
- void invalidate() {
- boardNotifier.value++;
- }
- // 3. 核心改进:带竞态检查和资源释放的图片加载
- Future<void> _loadImage() async {
- if (_currentCollectionItem == null) return;
- final String currentId = _currentCollectionItem!.id;
- _loadingImageUrl = currentId; // 记录当前请求的 ID
- // 如果需要切换时立即白屏,可以取消下面注释:
- // _clearImage();
- try {
- double dpr = device.effectivePixelRatio;
- ItemLoader itemLoader = ItemLoader.load(_currentCollectionItem!, device.suggestedQuality);
- // 异步获取图片
- ui.Image? loadedImage = await itemLoader.getImageBySize((canvasWidth * dpr).round(), (canvasHeight * dpr).round());
- // --- 关键判断:竞态条件处理 ---
- // 如果图片回来时,用户已经切换到了下一个合集,则丢弃当前图片并释放内存
- if (_loadingImageUrl != currentId) {
- _log.info('丢弃已过时的图片加载结果: $currentId');
- loadedImage.dispose();
- return;
- }
- // 4. 修改:在赋值新图前,先安全释放旧图内存
- if (image != null) {
- image!.dispose();
- }
- image = loadedImage;
- isReadyNotifier.value = true;
- invalidate();
- _log.info('成功加载合集图片: $currentId');
- } catch (e) {
- _log.severe('加载合集图片失败: $e');
- isReadyNotifier.value = false;
- }
- }
- // 5. 修改:安全加载卡片背面图
- Future<void> _loadCardImage() async {
- try {
- double dpr = device.realPixelRatio;
- final Size bestCardSize = Size(pieceLogicalWidth * dpr, pieceLogicalHeight * dpr);
- final ByteData cardData = await rootBundle.load('assets/images/backcard_green.png');
- final ui.Codec cardCodec = await ui.instantiateImageCodec(
- cardData.buffer.asUint8List(),
- targetWidth: bestCardSize.width.round(),
- targetHeight: bestCardSize.height.round(),
- );
- final ui.FrameInfo cardFrameInfo = await cardCodec.getNextFrame();
- cardImage = cardFrameInfo.image;
- invalidate();
- } catch (e) {
- _log.severe('加载卡片背面图失败: $e');
- }
- }
- // 6. 新增:彻底释放所有图片内存,防止 OOM
- void dispose() {
- _clearImage();
- if (cardImage != null) {
- cardImage!.dispose();
- cardImage = null;
- }
- boardNotifier.dispose();
- isReadyNotifier.dispose();
- }
- void _clearImage() {
- if (image != null) {
- image!.dispose();
- image = null;
- }
- isReadyNotifier.value = false;
- }
- void switchToNextCollection(ListItem newItem) {
- _log.info('切换到新的合集: ${newItem.id}');
- // 切换状态,让 UI 进入 loading
- status = HomeBoardStatus.loading;
- currentCollectionItem = newItem;
- }
- }
|