collection_screen.dart 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import 'dart:async';
  2. import 'package:flutter/material.dart';
  3. import 'package:puzzleweave/audio/jc_audio_controller.dart';
  4. import 'package:puzzleweave/collection/grid_item.dart';
  5. import 'package:puzzleweave/config/device.dart';
  6. import 'package:puzzleweave/l10n/app_localizations.dart';
  7. import 'package:puzzleweave/models/cached_request.dart';
  8. import 'package:puzzleweave/models/data.dart';
  9. import 'package:puzzleweave/models/items.dart';
  10. import 'package:logging/logging.dart';
  11. import 'package:lottie/lottie.dart';
  12. import 'package:provider/provider.dart';
  13. import 'package:puzzleweave/skin/skin.dart';
  14. final Logger _log = Logger('collection_screen');
  15. class CollectionScreen extends StatefulWidget {
  16. const CollectionScreen({super.key});
  17. @override
  18. State<StatefulWidget> createState() => _CollectionScreen();
  19. static PageRouteBuilder buildRoute() {
  20. return PageRouteBuilder(
  21. pageBuilder: (context, animation, secondaryAnimation) {
  22. return CollectionScreen();
  23. },
  24. transitionsBuilder: (context, animation, secondaryAnimation, child) {
  25. // return FadeTransition(opacity: animation, child: child);
  26. return SlideTransition(
  27. position: Tween(begin: const Offset(1, 0), end: Offset.zero).animate(animation),
  28. child: child,
  29. );
  30. },
  31. );
  32. }
  33. }
  34. class _CollectionScreen extends State<CollectionScreen> {
  35. late JcAudioController audio;
  36. late Data data;
  37. List<ListItem>? collection;
  38. late CachedRequest collectionCachedRequest;
  39. late StreamSubscription? collectionSubscription;
  40. @override
  41. void initState() {
  42. super.initState();
  43. audio = context.read<JcAudioController>();
  44. data = context.read<Data>();
  45. collectionCachedRequest = data.collection;
  46. // 主动获取缓存数据(关键)
  47. final collectionCachedData = collectionCachedRequest.cachedData;
  48. if (collectionCachedData != null) {
  49. _onCollectionDataUpdate(collectionCachedData);
  50. }
  51. collectionSubscription = collectionCachedRequest.stream.listen(_onCollectionDataUpdate, onError: _onCollectionDataError);
  52. }
  53. @override
  54. void dispose() {
  55. collectionSubscription?.cancel();
  56. super.dispose();
  57. }
  58. _onCollectionDataUpdate(data) async {
  59. _log.info('_onCollectionDataUpdate.... ');
  60. if (data != null) {
  61. collection = data as List<ListItem>;
  62. setState(() {});
  63. }
  64. }
  65. _onCollectionDataError(error) {
  66. _log.info('_onCollectionDataError.... $error');
  67. }
  68. Future<void> refresh() async {
  69. _log.info('refresh...');
  70. await collectionCachedRequest.refresh();
  71. }
  72. @override
  73. Widget build(BuildContext context) {
  74. final device = context.read<Device>();
  75. final isTablet = device.isTablet;
  76. return Scaffold(
  77. backgroundColor: SkinHelper.colorWhite,
  78. appBar: AppBar(
  79. backgroundColor: SkinHelper.colorWhite,
  80. // elevation: 1,
  81. centerTitle: true,
  82. leading: IconButton(
  83. onPressed: () {
  84. audio.playSfx(SfxType.click);
  85. Navigator.pop(context);
  86. },
  87. icon: const Icon(Icons.arrow_back_outlined, color: Colors.black87),
  88. ),
  89. title: Text(
  90. AppLocalizations.of(context)!.collection,
  91. style: TextStyle(color: Colors.black87, fontWeight: FontWeight.bold, fontSize: 24),
  92. ),
  93. ),
  94. body: collection == null
  95. ? scrollableDummy
  96. : RefreshIndicator(
  97. onRefresh: refresh,
  98. child: CustomScrollView(
  99. slivers: <Widget>[
  100. // header
  101. SliverPadding(
  102. sliver: SliverGrid(
  103. gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(maxCrossAxisExtent: isTablet ? 300 : 210, childAspectRatio: 2 / 3),
  104. delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
  105. return _buildItem(context, index);
  106. }, childCount: collection!.length),
  107. ),
  108. padding: const EdgeInsets.only(left: 10.0, right: 10.0),
  109. ),
  110. ],
  111. ),
  112. ),
  113. );
  114. }
  115. // Widget get scrollableDummy => LayoutBuilder(
  116. // builder: (p0, p1) {
  117. // return SingleChildScrollView(
  118. // physics: const AlwaysScrollableScrollPhysics(),
  119. // child: SizedBox(
  120. // height: p1.maxHeight,
  121. // child: Center(
  122. // child: ListView(
  123. // shrinkWrap: true,
  124. // children: [
  125. // Lottie.asset('assets/lottie/loading.json', height: 100),
  126. // const Center(child: Text("loading...")),
  127. // ],
  128. // ),
  129. // ),
  130. // ),
  131. // );
  132. // },
  133. // );
  134. Widget get scrollableDummy => Scaffold(
  135. backgroundColor: SkinHelper.colorWhite,
  136. body: Center(
  137. child: Column(
  138. mainAxisAlignment: MainAxisAlignment.center,
  139. children: [
  140. // 替换 Lottie 为原生进度条
  141. SizedBox(width: 40, height: 40, child: CircularProgressIndicator(strokeWidth: 3, valueColor: AlwaysStoppedAnimation<Color>(SkinHelper.coreBgColor))),
  142. const SizedBox(height: 16),
  143. Text(
  144. "Loading...",
  145. style: TextStyle(color: SkinHelper.slotBorderColor.withOpacity(0.7), fontSize: 14, fontWeight: FontWeight.w500),
  146. ),
  147. ],
  148. ),
  149. ),
  150. );
  151. Widget _buildItem(context, index) {
  152. ListItem item = collection![index];
  153. final bool isLocked = index >= data.currentCollectionIndex; // 假设 currentCollectionIndex 之前是解锁的
  154. return Padding(
  155. padding: const EdgeInsets.all(10.0),
  156. child: CollectionGridItem(item: item, lock: isLocked, index: index),
  157. );
  158. }
  159. }