import 'dart:async'; import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:image_puzzle/audio/audio_controller.dart'; import 'package:image_puzzle/collection/collection_screen.dart'; import 'package:image_puzzle/config/device.dart'; import 'package:image_puzzle/homepage/home_board.dart'; import 'package:image_puzzle/homepage/home_board_play.dart'; import 'package:image_puzzle/models/cached_request.dart'; import 'package:image_puzzle/models/data.dart'; import 'package:image_puzzle/models/items.dart'; import 'package:image_puzzle/play/board_play.dart'; import 'package:image_puzzle/settings/settings_dialog.dart'; import 'package:image_puzzle/skin/skin.dart'; import 'package:image_puzzle/utils/mybutton.dart'; import 'package:logging/logging.dart'; import 'package:lottie/lottie.dart'; import 'package:provider/provider.dart'; final Logger _log = Logger('home_screen'); class HomeScreen extends StatefulWidget { const HomeScreen({super.key}); @override State createState() => _HomeScreen(); } class _HomeScreen extends State { late AudioController audio; late Data data; List? latest; late CachedRequest latestCachedRequest; late StreamSubscription? latestSubscription; // 自定义画布控制器(可选,用于控制画布绘制逻辑) final _canvasKey = GlobalKey(); // !!! 新增:用于定位 Collection 按钮的 GlobalKey final GlobalKey _collectionKey = GlobalKey(); bool get isLoading => currentItem == null; @override void initState() { super.initState(); audio = context.read(); data = context.read(); latestCachedRequest = data.latest; // 主动获取缓存数据(关键) final cachedData = latestCachedRequest.cachedData; if (cachedData != null) { _onLatestDataUpdate(cachedData); } latestSubscription = latestCachedRequest.stream.listen(_onLatestDataUpdate, onError: _onLatestDataError); } @override void dispose() { latestSubscription?.cancel(); super.dispose(); } _onLatestDataUpdate(data) { _log.info('_onLatestDataUpdate.... '); if (data != null) { latest = data as List; setState(() {}); if (data.length >= 20) { // 远程latest列表已加载,说明网络已通,这个时候再来初始化Admod,ATT, UMP这些东西 initThird(); } } } _onLatestDataError(error) { _log.info('_onLatestDataError.... $error'); if (latest == null || latest!.isEmpty || latest!.length < 20) { // 列表数据如果少于20,说明只是内置图,仍然刷新远程请求 _log.warning("_onLatestDataError, retry again"); // refresh(); Future.delayed(Duration(seconds: 3), () => refresh()); } } Future refresh() async { _log.info('refresh...'); await latestCachedRequest.refresh(); } void initThird() async {} ListItem? get currentItem { if (latest != null && latest!.isNotEmpty) { return latest![data.currentLevel]; } return null; } @override Widget build(BuildContext context) { if (isLoading) return scrollableDummy; Device device = context.read(); // 2. 计算画布尺寸(宽=屏幕宽-60,高=宽×3/2) final canvasWidth = device.screenSize.width - 30 * 2; // 左右各30px final canvasHeight = canvasWidth * 3 / 2; return Scaffold( appBar: AppBar( backgroundColor: Colors.white, elevation: 1, centerTitle: true, leading: RepaintBoundary( // !!! 改造点 1: 包裹 RepaintBoundary key: _collectionKey, // !!! 改造点 2: 关联 GlobalKey child: IconButton( onPressed: () { audio.playSfx(SfxType.tap); Navigator.push(context, CollectionScreen.buildRoute()); }, icon: const Icon(Icons.collections, color: Colors.black54), ), ), title: const Text( 'PuzzleWeave', style: TextStyle(color: Colors.black54, fontFamily: 'Arial Black', fontWeight: FontWeight.bold, fontSize: 24), ), actions: [ IconButton( onPressed: () { audio.playSfx(SfxType.tap); Navigator.push(context, SettingsDialog.buildRoute()); }, icon: const Icon(Icons.settings, color: Colors.black54), ), ], ), body: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ // 2. 画布区域(固定尺寸) Padding( padding: const EdgeInsets.symmetric(horizontal: 30), // 左右30px child: SizedBox( width: canvasWidth, height: canvasHeight, child: ValueListenableBuilder( valueListenable: data.completedWorks, builder: (context, value, child) { return HomeBoardPlay( key: _canvasKey, canvasWidth: canvasWidth, canvasHeight: canvasHeight, collectionKey: _collectionKey, onCollectionDone: () { // 可选:在这里处理合集解锁后的其他逻辑 }, ); }, ), ), ), playButton, ], ), ), Container( height: device.bannerHeight, width: double.infinity, color: Colors.green.shade100, child: const Center(child: Text('Banner 广告区域', style: TextStyle(fontSize: 12))), ), ], ), ); } Widget get playButton { return MyElevatedButton( width: 200, height: 70, borderRadius: BorderRadius.circular(20), gradient: LinearGradient(colors: [SkinHelper.coreBgColor, SkinHelper.slotBorderColor]), onPressed: () async { audio.playSfx(SfxType.tap); _canvasKey.currentState?.startFlipAnimation(); // if (currentItem != null) { // PageRouteBuilder? pageRouteBuilder = BoardPlay.buildRoute(currentItem!); // final result = await Navigator.push(context, pageRouteBuilder); // if (result == true) { // _canvasKey.currentState?.startFlipAnimation(); // } // } else { // Fluttertoast.showToast( // msg: "更多图片敬请期待...", // toastLength: Toast.LENGTH_SHORT, // gravity: ToastGravity.CENTER, // timeInSecForIosWeb: 1, // backgroundColor: SkinHelper.slotBorderColor, // textColor: Colors.white, // fontSize: 16.0, // ); // } }, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Text( '玩', style: TextStyle(color: Colors.white, fontFamily: 'Arial Black', fontSize: 24, fontWeight: FontWeight.bold), ), ValueListenableBuilder>( valueListenable: data.completedWorks, builder: (context, isSoundOn, child) { return Text('关卡 ${data.currentLevel + 1}', style: const TextStyle(color: Colors.white, fontSize: 16)); }, ), ], ), ); } Widget get scrollableDummy => Scaffold( body: LayoutBuilder( builder: (p0, p1) { return SingleChildScrollView( physics: const AlwaysScrollableScrollPhysics(), child: SizedBox( height: p1.maxHeight, child: Center(child: ListView(shrinkWrap: true, children: [Lottie.asset('assets/lottie/loading.json', height: 100)])), ), ); }, ), ); }