import 'dart:convert'; import 'dart:developer'; import 'package:auto_route/auto_route.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:mnemo_cards/features/packs/pack_manager.dart'; import 'package:mnemo_cards/managers/user_state_holder.dart'; import 'package:mnemo_cards/utils/string_helper.dart'; import 'package:mnemo_cards/widgets/header.dart'; import 'package:mnemo_cards/widgets/shared_pref_button.dart'; import 'package:mnemo_cards_common/mnemo_cards_common.dart'; import 'package:shimmer/shimmer.dart'; import '../admin/add_package.dart'; import '../di/locator.dart'; import '../domain/router/app_router.dart'; import '../domain/router/app_router.gr.dart'; import '../flags.dart'; import '../managers/repository/repository.dart'; import '../theme/themes.dart'; import 'package:collection/collection.dart'; class PacksMenuWidget extends StatefulWidget { final Repository repository; final PackManager packManager; final ScrollController? scrollController; const PacksMenuWidget( this.repository, this.packManager, { this.scrollController, super.key, }); @override State createState() => _PacksMenuWidgetState(); } class _PacksMenuWidgetState extends State { @override Widget build(BuildContext context) { return LayoutBuilder(builder: (context, constraints) { final cardHeight = 130.h; final theme = Theme.of(context); return StreamBuilder( stream: widget.packManager.packsPreviewStream.asyncMap((packs) async { if (packs != null) { await _precache(packs); } return packs; }), builder: (context, snapshot) { return Column( children: [ Header( 'Темы', trail: GestureDetector( onTap: () { AppRouter.openAuthOrProfile(); }, child: Image.asset( 'icons/profile.png', width: 27.w, height: 27.h, ), ), ), Expanded( child: Padding( padding: const EdgeInsets.all(8.0), child: RefreshIndicator( onRefresh: locator.previewPackPoller.poll, backgroundColor: Colors.white, color: snapshot.data?.firstOrNull?.color?.asColor ?? Colors.black, child: (!snapshot.hasData) ? Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.all(8.0), child: Image.asset( 'images/cerdo.jpg', width: 100, ), ), Text('Загрузка...'), ], ), ) : ListView( physics: BouncingScrollPhysics( decelerationRate: ScrollDecelerationRate.fast, parent: AlwaysScrollableScrollPhysics(), ), scrollDirection: Axis.vertical, children: snapshot.requireData! .map((pack) => _PackButton4(pack, cardHeight)) .toList(), ), ), ), ), ], ); }, ); }); } Future _precache(List packs) async { for (final pack in packs) { if (pack.imageBase64 != null) { try { final bytes = await compute(base64Decode, pack.imageBase64!); final image = MemoryImage(bytes); locator.imagesHolder.setImage('pack_${pack.id}', image); await precacheImage(image, context); } catch (e) { log('${pack.id} cover not cached'); } } } } } class _PackButton4 extends StatelessWidget { final CardPackPreviewDto pack; final double cardHeight; const _PackButton4( this.pack, this.cardHeight, ); @override Widget build(BuildContext context) { final theme = Theme.of(context); return InkWell( onTap: () { AutoRouter.of(context).push( PageRouteInfo( CardPackPage.name, args: CardPackPageArgs( previewDto: pack, ), ), ); }, borderRadius: BorderRadius.circular(12.0), child: Container( margin: EdgeInsets.all(4.0.h), alignment: Alignment.center, height: cardHeight, decoration: BoxDecoration( color: Colors.white, border: Border.all( color: pack.color?.asColor?.withOpacity(0.9) ?? Colors.grey.withOpacity(0.5), width: 1, ), borderRadius: BorderRadius.circular(12.0), ), child: Row( mainAxisSize: MainAxisSize.max, children: [ Container( decoration: BoxDecoration( image: pack.imageBase64 == null ? null : DecorationImage( image: locator.imagesHolder.get('pack_${pack.id}') ?? MemoryImage(base64Decode(pack.imageBase64!)), ), ), margin: EdgeInsets.all(2.0.w), width: cardHeight, height: cardHeight, ), Expanded( child: Padding( padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ FittedBox( fit: BoxFit.scaleDown, child: Text( pack.title, style: TextStyle( fontWeight: FontWeight.w500, fontSize: 24.sp, height: 0.9, ), ), ), if (pack.subtitle != null && pack.subtitle!.isNotEmpty) FittedBox( fit: BoxFit.scaleDown, child: Text( pack.subtitle!, style: TextStyle( fontWeight: FontWeight.w300, fontSize: 16.sp, ), ), ), Spacer(), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Expanded( child: Row( children: [ Text( pack.cards.toString(), style: TextStyle( fontWeight: FontWeight.w200, fontSize: 15.sp, ), ), Image.asset( 'icons/cards.png', width: 18.w, height: 16.h, ) ], ), ), if (pack.price != null && !pack.isAvailable) Text( pack.price!, style: TextStyle( fontWeight: FontWeight.w400, fontSize: 20.sp, ), ), ], ) ], ), ), ), ], ), ), ); } }