import 'dart:convert'; import 'dart:math'; import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:gymlink_module_web/components/app_bar.dart'; import 'package:gymlink_module_web/components/heading.dart'; import 'package:gymlink_module_web/interfaces/items.dart'; import 'package:gymlink_module_web/pages/basket.dart'; import 'package:gymlink_module_web/providers/cart.dart'; import 'package:gymlink_module_web/providers/main.dart'; import 'package:gymlink_module_web/tools/items.dart'; import 'package:gymlink_module_web/tools/prefs.dart'; import 'package:gymlink_module_web/tools/routes.dart'; import 'package:gymlink_module_web/tools/text.dart'; import 'package:http/http.dart' as http; import 'package:provider/provider.dart'; class DetailPage extends StatefulWidget { final String id; const DetailPage({ super.key, required this.id, }); @override State createState() => _DetailPageState(); } class _DetailPageState extends State { bool isInCart = false; int quantity = 0; GymItem? item; String? categoryName; final CarouselController _carouselController = CarouselController(); int _currentImage = 0; @override void initState() { getCart().then((value) { setState(() { isInCart = value.any((element) => element['id'] == widget.id); if (isInCart) { quantity = value .firstWhere((element) => element['id'] == widget.id)['count']; } }); }); _getItem(); super.initState(); } Future _getItem() async { final Uri url = Uri.https('gymlink.freemyip.com', 'api/product/get/${widget.id}'); final response = await http.get(url, headers: { 'Authorization': 'Bearer ${context.read().token}', }); if (response.statusCode == 200) { final data = GymItem.fromJson(jsonDecode(utf8.decode(response.bodyBytes))); setState(() { item = data; }); WidgetsBinding.instance.addPostFrameCallback((_) { for (var element in item!.images) { precacheImage(NetworkImage(element.url), context); } }); if (mounted) { getCategories(context).then((value) { setState(() { categoryName = value .firstWhere( (element) => element.id == (item!.categoryId), orElse: () => GymCategory(id: item!.categoryId, name: ''), ) .name; }); }); } } } Widget _buildRowOrCol( {required List children, required BuildContext context, MainAxisAlignment mainAxisAlignment = MainAxisAlignment.spaceAround, CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center}) { return Column( mainAxisAlignment: mainAxisAlignment, crossAxisAlignment: crossAxisAlignment, children: children); } Widget _buildButton() { if (!isInCart) { return ElevatedButton( onPressed: () async { await addItemToCart(widget.id); setState(() { isInCart = true; quantity = 1; }); if (mounted) { context.read().updateCartLength(); } }, style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(50)), ), foregroundColor: Colors.white, padding: const EdgeInsetsDirectional.fromSTEB(34, 10, 34, 10)), child: const Text('Добавить в корзину'), ); } else { return Column( children: [ Row( mainAxisSize: MainAxisSize.min, children: [ IconButton( icon: const Icon(Icons.remove), onPressed: () async { await removeItemFromCart(widget.id); setState(() { if (quantity > 1) { quantity--; } else { isInCart = false; quantity = 0; } }); if (mounted) { context.read().updateCartLength(); } }, ), const SizedBox(width: 10), Text('$quantity'), const SizedBox(width: 10), IconButton( icon: const Icon(Icons.add), onPressed: () async { if (item!.count > quantity) { await addItemToCart(widget.id); setState(() { quantity++; }); } }, ), ], ), Padding( padding: const EdgeInsets.only(top: 10), child: ElevatedButton( onPressed: () { Navigator.pushReplacement(context, CustomPageRoute(builder: (context) => const BasketPage())); }, style: ElevatedButton.styleFrom( backgroundColor: Theme.of(context).primaryColor, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(50)), ), foregroundColor: Colors.white, ), child: const Text('Открыть корзину'), ), ) ], ); } } double _getAspectRatio() { double width = MediaQuery.sizeOf(context).width; double height = MediaQuery.sizeOf(context).height; return max(width, height) / min(width, height); } @override Widget build(BuildContext context) { return Scaffold( appBar: const GymLinkAppBar(), body: item != null ? Column(mainAxisAlignment: MainAxisAlignment.start, children: [ GymLinkHeader(title: shortString(item!.title, length: 20)), Expanded( child: SingleChildScrollView( child: Padding( padding: const EdgeInsets.all(20), child: SizedBox( width: MediaQuery.sizeOf(context).width, child: _buildRowOrCol( context: context, mainAxisAlignment: MainAxisAlignment.spaceAround, crossAxisAlignment: CrossAxisAlignment.center, children: [ item!.images.length > 1 ? Column(children: [ CarouselSlider.builder( itemCount: item!.images.length, itemBuilder: (context, index, realIdx) { return Center( child: Image.network( item!.images[index].url, width: min( 550, MediaQuery.sizeOf(context) .width)), ); }, carouselController: _carouselController, options: CarouselOptions( enlargeCenterPage: true, height: min( MediaQuery.sizeOf(context).height - 100, 400), enableInfiniteScroll: false, onPageChanged: (index, reason) { setState(() { _currentImage = index; }); }), ), Row( mainAxisAlignment: MainAxisAlignment.center, children: item!.images .asMap() .entries .map((entry) { return GestureDetector( onTap: () => _carouselController .animateToPage(entry.key), child: Container( width: 12.0, height: 12.0, margin: const EdgeInsets.symmetric( vertical: 8.0, horizontal: 4.0), decoration: BoxDecoration( shape: BoxShape.circle, color: (Theme.of(context) .brightness == Brightness.dark ? Colors.white : Colors.black) .withOpacity( _currentImage == entry.key ? 0.9 : 0.4)), ), ); }).toList(), ), ]) : Image.network( item!.images[0].url, height: 400, ), Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Text( item!.title, style: Theme.of(context).textTheme.titleLarge, textAlign: TextAlign.center, ), ), Center( child: Padding( padding: const EdgeInsets.symmetric(vertical: 10), child: Chip( label: Text(categoryName != null ? (categoryName == "" ? "Без категории" : categoryName!) : ''), backgroundColor: Colors.white, labelStyle: const TextStyle(color: Colors.black), ), ), ), Center( child: MarkdownBody( data: '### Остаток: _${item!.count}_', )), item!.description != '' ? Padding( padding: const EdgeInsetsDirectional.all(30), child: ConstrainedBox( constraints: const BoxConstraints( minWidth: 340, maxWidth: 340, maxHeight: 600, ), child: Card( elevation: 4, color: Theme.of(context) .scaffoldBackgroundColor, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), child: SingleChildScrollView( child: Padding( padding: const EdgeInsetsDirectional.all( 15), child: ConstrainedBox( constraints: const BoxConstraints( minHeight: 100, ), child: Text( item!.description, style: Theme.of(context) .textTheme .bodyMedium, ), ), ), ), ), ), ) : const SizedBox.shrink(), Align( alignment: const AlignmentDirectional(0, -1), child: Padding( padding: const EdgeInsetsDirectional.fromSTEB( 0, 30, 0, 0), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text( 'Стоимость ${item!.price.toStringAsFixed(2)}руб.', style: Theme.of(context).textTheme.bodyLarge, ), _buildButton() ], ), ), ), ], ), ), ), ), ), ]) : const Center( child: CircularProgressIndicator(), ), ); } }