collection_screen.dart 5.6 KB

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