import 'dart:ui'; import 'package:carousel_slider/carousel_options.dart'; import 'package:carousel_slider/carousel_slider.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:get/get.dart'; import 'package:taxglide/consts/app_asstes.dart'; import 'package:taxglide/consts/app_style.dart'; import 'package:taxglide/consts/app_colors.dart'; import 'package:taxglide/consts/comman_serivce.dart'; import 'package:taxglide/consts/home_page_headers.dart'; import 'package:taxglide/consts/local_store.dart'; import 'package:taxglide/consts/notification_webscoket.dart'; import 'package:taxglide/consts/responsive_helper.dart'; import 'package:taxglide/controller/api_contoller.dart'; import 'package:taxglide/model/serivce_list_model.dart'; import 'package:taxglide/view/Main_controller/main_controller.dart'; import 'package:taxglide/view/screens/history/detail_screen.dart'; class HomeScreen extends ConsumerStatefulWidget { const HomeScreen({super.key}); @override ConsumerState createState() => _HomeScreenState(); } class _HomeScreenState extends ConsumerState { final CarouselSliderController _carouselController = CarouselSliderController(); int _currentIndex = 0; @override void initState() { super.initState(); _initializeWebSocket(); } Future _initializeWebSocket() async { final userId = await LocalStore().getUserId(); if (userId == null) { print("❌ No User ID found. Cannot connect WS"); return; } NotificationWebSocket().connect( userId: userId, container: ProviderScope.containerOf(context), ); } @override Widget build(BuildContext context) { final dashboardAsync = ref.watch(dashboardProvider); final serviceAsync = ref.watch(serviceListProvider); final r = ResponsiveUtils(context); return Scaffold( backgroundColor: const Color.fromARGB(255, 230, 229, 229), body: SafeArea( child: dashboardAsync.when( data: (dashboard) => _buildHomeContent( context, dashboard, dashboard.companyName.toString(), serviceAsync, r, ), loading: () => const Center(child: CircularProgressIndicator()), error: (err, _) => Center(child: Text('Error: $err')), ), ), ); } Widget _buildHomeContent( BuildContext context, dynamic dashboard, String userName, AsyncValue> serviceAsync, ResponsiveUtils r, ) { final List list = dashboard.serviceRequestLists.data; // Responsive values final hPadding = r.spacing(mobile: 16, tablet: 20, desktop: 28); final sectionTitleSize = r.fontSize(mobile: 18, tablet: 18, desktop: 20); final bookingTitleSize = r.fontSize(mobile: 20, tablet: 20, desktop: 22); final seeAllSize = r.fontSize(mobile: 15, tablet: 15, desktop: 16); final labelSize = r.fontSize(mobile: 13, tablet: 14, desktop: 15); final valueSize = r.fontSize(mobile: 13, tablet: 14, desktop: 15); final fileIdSize = r.fontSize(mobile: 14, tablet: 15, desktop: 16); final amountSize = r.fontSize(mobile: 17, tablet: 18, desktop: 20); final buttonTextSize = r.fontSize(mobile: 14, tablet: 16, desktop: 17); final buttonHeight = r.getValue( mobile: 44, tablet: 50, desktop: 54, ); final cardPadding = r.spacing(mobile: 12, tablet: 14, desktop: 16); final cardSpacing = r.spacing(mobile: 10, tablet: 12, desktop: 14); final gridCrossAxisCount = r.getValue( mobile: 3, tablet: 4, desktop: 5, ); final carouselHeight = r.getValue( mobile: 160, tablet: 180, desktop: 200, ); final carouselSectionHeight = r.getValue( mobile: 200, tablet: 220, desktop: 240, ); final reviewCardWidth = r.getValue( mobile: 160, tablet: 180, desktop: 200, ); final reviewCardHeight = r.getValue( mobile: 180, tablet: 192, desktop: 210, ); final reviewTitleSize = r.fontSize(mobile: 20, tablet: 20, desktop: 22); final kycTitleSize = r.fontSize(mobile: 16, tablet: 18, desktop: 20); final kycBodySize = r.fontSize(mobile: 10, tablet: 10, desktop: 11); // My Task Status Button responsive values final taskStatusGlowWidth = r.getValue( mobile: 200, tablet: 220, desktop: 240, ); final taskStatusGlowHeight = r.getValue( mobile: 45, tablet: 50, desktop: 55, ); final taskStatusButtonWidth = r.getValue( mobile: 197.45, tablet: 215, desktop: 235, ); final taskStatusButtonHeight = r.getValue( mobile: 41.38, tablet: 46, desktop: 50, ); final taskStatusFontSize = r.fontSize(mobile: 18, tablet: 20, desktop: 22); return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ HomePageHeader(userName: userName), const SizedBox(height: 60), Padding( padding: EdgeInsets.symmetric(horizontal: hPadding), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // KYC Banner if (dashboard.isKycCompleted == true)...[ Container( width: double.infinity, padding: EdgeInsets.all(cardPadding), decoration: const BoxDecoration( color: AppColors.homepagekycdetails, borderRadius: BorderRadius.only( topLeft: Radius.circular(10), topRight: Radius.circular(100), bottomRight: Radius.circular(100), bottomLeft: Radius.circular(10), ), ), child: Row( children: [ Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "KYC Details", style: AppTextStyles.semiBold.copyWith( fontSize: kycTitleSize, color: Colors.white, ), ), const SizedBox(height: 8), Text( "Your KYC verification couldn't be completed. Please complete your KYC details to continue exploring more features.", style: AppTextStyles.regular.copyWith( fontSize: kycBodySize, color: const Color(0xFFFFFFCC), ), ), ], ), ), Stack( alignment: Alignment.center, children: [ Container( width: 79, height: 79, decoration: const BoxDecoration( shape: BoxShape.circle, color: Color(0x9640ED8E), ), ), SizedBox( width: 90, height: 90, child: CircularProgressIndicator( value: 0.6, strokeWidth: 6, backgroundColor: Colors.white, valueColor: AlwaysStoppedAnimation( AppColors.homepagekycdetailsorange, ), ), ), Icon( Icons.person, size: 35.25, color: AppColors.homepagekycdetailsorange, ), ], ), ], ), ), SizedBox( height: r.spacing(mobile: 26, tablet: 28, desktop: 32), ),], Center( child: Stack( alignment: Alignment.center, children: [ /// 🌈 Gradient + Blur Glow ClipRRect( borderRadius: BorderRadius.circular(40), child: BackdropFilter( filter: ImageFilter.blur( sigmaX: 16.54, sigmaY: 16.54, ), child: Container( width: taskStatusGlowWidth, height: taskStatusGlowHeight, decoration: BoxDecoration( borderRadius: BorderRadius.circular(40), gradient: const LinearGradient( colors: [ Color(0xFF7BFAFA), Color(0xFF8BF0FD), Color(0xFF8EFFD5), Color(0xFFC8F895), Color(0xFFE7F392), Color(0xFFFBE28D), Color(0xFFFCDAB1), Color(0xFFFEDCAA), ], ), ), ), ), ), /// 🔲 Black Button GestureDetector( onTap: () { Get.offAll( () => const MainController( initialIndex: 2, sourceTabIndex: 0, ), ); }, child: Container( width: taskStatusButtonWidth, height: taskStatusButtonHeight, alignment: Alignment.center, decoration: BoxDecoration( color: const Color(0xFF000000), borderRadius: BorderRadius.circular(39.02), ), child: Text( "My Task Status", textAlign: TextAlign.center, style: AppTextStyles.semiBold.copyWith( fontSize: taskStatusFontSize, height: 1.4, letterSpacing: 0.03 * taskStatusFontSize, color: Colors.white, ), ), ), ), ], ), ), SizedBox( height: r.spacing(mobile: 26, tablet: 28, desktop: 32), ), Text( "List of Services Offered", style: AppTextStyles.bold.copyWith( fontSize: sectionTitleSize, height: 1.3, letterSpacing: 0.72, ), ), SizedBox( height: r.spacing(mobile: 26, tablet: 28, desktop: 32), ), // Service Grid serviceAsync.when( data: (services) { final searchText = ref.watch(serviceSearchProvider); final filteredServices = services.where((service) { return service.service.toLowerCase().contains( searchText.toLowerCase(), ); }).toList(); if (filteredServices.isEmpty) { return const Padding( padding: EdgeInsets.only(top: 20), child: Center(child: Text("No Services Found")), ); } return GridView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), itemCount: services.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: gridCrossAxisCount, mainAxisSpacing: 16, crossAxisSpacing: 12, childAspectRatio: 0.75, ), itemBuilder: (context, index) { return CommonServiceItem( service: services[index], sourceTabIndex: 0, ); }, ); }, loading: () => const Center(child: CircularProgressIndicator()), error: (err, _) => Center(child: Text('Error: $err')), ), ], ), ), SizedBox(height: r.spacing(mobile: 26, tablet: 28, desktop: 32)), // Booking List Section if (list.isNotEmpty) Container( width: double.infinity, color: const Color(0xFFFFFDF0), child: Padding( padding: EdgeInsets.symmetric( vertical: r.spacing(mobile: 38, tablet: 40, desktop: 44), horizontal: hPadding, ), child: Column( children: [ // Header Row Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Booking List", style: AppTextStyles.bold.copyWith( fontSize: bookingTitleSize, height: 1.4, letterSpacing: 0.6, ), ), GestureDetector( onTap: () { Get.offAll( () => const MainController( initialIndex: 2, sourceTabIndex: 0, ), ); }, child: Text( "See All", style: AppTextStyles.semiBold.copyWith( fontSize: seeAllSize, height: 1.4, letterSpacing: 0.45, color: const Color(0xFF000000), ), ), ), ], ), // Booking Cards ListView.separated( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), padding: EdgeInsets.symmetric(vertical: cardSpacing), itemCount: list.length, separatorBuilder: (_, __) => SizedBox(height: cardSpacing), itemBuilder: (context, index) { final item = list[index]; return Container( width: double.infinity, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(10), border: Border.all( color: const Color(0xFFE1E1E1), width: 1, ), boxShadow: [ BoxShadow( color: const Color.fromARGB(117, 192, 192, 189), blurRadius: 15, offset: const Offset(0, 1), ), ], ), padding: EdgeInsets.all(cardPadding), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // File ID Row Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ RichText( text: TextSpan( children: [ TextSpan( text: 'File ID : ', style: AppTextStyles.semiBold.copyWith( fontSize: fileIdSize, color: const Color(0xFF111827), ), ), TextSpan( text: '${item.id}', style: AppTextStyles.medium.copyWith( fontSize: fileIdSize, color: const Color(0xFF111827), ), ), ], ), ), // Status Badge if (item.status == 'Waiting for Admin' || item.status == 'Payment Pending' || item.status == 'Cancelled By Admin') Container( width: r.getValue( mobile: 110, tablet: 120, desktop: 130, ), height: r.getValue( mobile: 30, tablet: 34, desktop: 36, ), decoration: BoxDecoration( color: const Color(0xFFFFE8E8), borderRadius: BorderRadius.circular( 5.52, ), border: Border.all( color: const Color(0xFFFFD7D7), width: 1.84, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity( 0.25, ), blurRadius: 7.17, offset: const Offset(0, 3.68), ), ], ), child: Center( child: Text( item.status.toString(), style: AppTextStyles.regular.copyWith( fontSize: r.fontSize( mobile: 10, tablet: 11, desktop: 12, ), color: const Color(0xFFFF0F0F), ), ), ), ), if (item.status == 'In Progress') Container( width: r.getValue( mobile: 88, tablet: 98, desktop: 108, ), height: r.getValue( mobile: 30, tablet: 34.9, desktop: 38, ), decoration: BoxDecoration( color: const Color(0xFFEAFAE6), borderRadius: BorderRadius.circular( 6.21, ), border: Border.all( color: const Color(0xFFDDFFDD), width: 2.07, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity( 0.25, ), blurRadius: 8.08, offset: const Offset(0, 4.14), ), ], ), child: Center( child: Text( 'In Progress', style: AppTextStyles.regular.copyWith( fontSize: r.fontSize( mobile: 11, tablet: 12.43, desktop: 13, ), letterSpacing: 0.03, color: const Color(0xFF12800C), ), ), ), ), if (item.status == 'Completed') Container( width: r.getValue( mobile: 78, tablet: 87, desktop: 96, ), height: r.getValue( mobile: 28, tablet: 31, desktop: 34, ), decoration: BoxDecoration( color: const Color(0xFFFAF7E6), borderRadius: BorderRadius.circular( 5.52, ), border: Border.all( color: const Color(0xFFFFE9DD), width: 1.84, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity( 0.25, ), blurRadius: 7.17, offset: const Offset(0, 3.68), ), ], ), child: Center( child: Text( 'Completed', style: AppTextStyles.regular.copyWith( fontSize: r.fontSize( mobile: 10, tablet: 11.03, desktop: 12, ), letterSpacing: 0.03, color: const Color(0xFFFF630F), ), ), ), ), ], ), SizedBox(height: cardSpacing), // Request Type RichText( text: TextSpan( children: [ TextSpan( text: 'Request Type : ', style: AppTextStyles.semiBold.copyWith( fontSize: labelSize, color: const Color(0xFF111827), ), ), TextSpan( text: item.service, style: AppTextStyles.medium.copyWith( fontSize: valueSize, color: const Color(0xFF111827), ), ), ], ), ), SizedBox(height: cardSpacing), // Date and Time Row Row( children: [ RichText( text: TextSpan( children: [ TextSpan( text: 'Date : ', style: AppTextStyles.semiBold.copyWith( fontSize: labelSize, color: const Color(0xFF111827), ), ), TextSpan( text: item.createdDate, style: AppTextStyles.medium.copyWith( fontSize: valueSize, color: const Color(0xFF111827), ), ), ], ), ), SizedBox( width: r.spacing( mobile: 16, tablet: 20, desktop: 24, ), ), RichText( text: TextSpan( children: [ TextSpan( text: 'Time : ', style: AppTextStyles.semiBold.copyWith( fontSize: labelSize, color: const Color(0xFF111827), ), ), TextSpan( text: item.createdTime, style: AppTextStyles.medium.copyWith( fontSize: valueSize, color: const Color(0xFF111827), ), ), ], ), ), ], ), SizedBox(height: cardSpacing), // Message RichText( text: TextSpan( children: [ TextSpan( text: 'Message : ', style: AppTextStyles.semiBold.copyWith( fontSize: labelSize, color: const Color(0xFF111827), ), ), TextSpan( text: item.message, style: AppTextStyles.medium.copyWith( fontSize: valueSize, color: const Color(0xFF111827), ), ), ], ), ), SizedBox(height: cardSpacing), const Divider(), SizedBox(height: cardSpacing), // Payment Actions if (item.paymentStatus == "Unpaid") ...[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( "Total Amount", style: AppTextStyles.semiBold.copyWith( fontSize: labelSize, color: const Color(0xFF111827), ), ), Text( '₹ ${item.paymentAmount.toString()}', style: AppTextStyles.semiBold.copyWith( fontFamily: 'Roboto', fontSize: amountSize, color: const Color(0xFF111827), ), ), ], ), SizedBox(height: cardSpacing), Row( children: [ Expanded( child: GestureDetector( onTap: () {}, child: Container( height: buttonHeight, decoration: BoxDecoration( color: const Color(0xFFF7E9FF), borderRadius: BorderRadius.circular( 6, ), border: Border.all( color: const Color(0xFFD39FEA), width: 1, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity( 0.25, ), blurRadius: 7.8, offset: const Offset(0, 4), ), ], ), child: Center( child: Text( "Pay Now", style: AppTextStyles.semiBold.copyWith( fontSize: buttonTextSize, color: const Color(0xFF61277A), ), ), ), ), ), ), SizedBox( width: r.spacing( mobile: 10, tablet: 12, desktop: 14, ), ), Expanded( child: GestureDetector( onTap: () { ref.invalidate(serviceDetailProvider); Get.offAll( () => MainController( initialIndex: 0, child: DetailScreen( id: int.parse( item.id.toString(), ), sourceTabIndex: 0, ), ), ); }, child: Container( height: buttonHeight, decoration: BoxDecoration( color: const Color(0xFF5F297B), borderRadius: BorderRadius.circular( 6, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity( 0.25, ), blurRadius: 7.8, offset: const Offset(0, 4), ), ], ), child: Center( child: Text( "View Details", style: AppTextStyles.semiBold.copyWith( fontSize: buttonTextSize, color: Colors.white, ), ), ), ), ), ), ], ), ] else if (item.paymentStatus == "Paid") ...[ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Container( width: r.getValue( mobile: 140, tablet: 149, desktop: 160, ), height: r.getValue( mobile: 30, tablet: 33, desktop: 36, ), decoration: BoxDecoration( color: const Color(0xFF04690B), borderRadius: BorderRadius.circular( 4.53, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity( 0.25, ), blurRadius: 5.89, offset: const Offset(0, 3.02), ), ], ), alignment: Alignment.center, child: Text( "Payment Status: Paid", textAlign: TextAlign.center, style: AppTextStyles.regular.copyWith( fontSize: r.fontSize( mobile: 11, tablet: 12.08, desktop: 13, ), letterSpacing: 0.04, height: 1.3, color: Colors.white, ), ), ), GestureDetector( onTap: () { Get.offAll( () => MainController( initialIndex: 0, child: DetailScreen( id: int.parse(item.id.toString()), sourceTabIndex: 0, ), ), ); }, child: Container( height: buttonHeight, width: r.getValue( mobile: 130, tablet: 140, desktop: 150, ), decoration: BoxDecoration( color: const Color(0xFF5F297B), borderRadius: BorderRadius.circular( 6, ), boxShadow: [ BoxShadow( color: Colors.black.withOpacity( 0.25, ), blurRadius: 7.8, offset: const Offset(0, 4), ), ], ), child: Center( child: Text( "View Details", style: AppTextStyles.semiBold.copyWith( fontSize: buttonTextSize, color: Colors.white, ), ), ), ), ), ], ), ] else if (item.paymentStatus == "Waiting") ...[ Align( alignment: Alignment.centerRight, child: GestureDetector( onTap: () { Get.offAll( () => MainController( initialIndex: 0, child: DetailScreen( id: int.parse(item.id.toString()), sourceTabIndex: 0, ), ), ); }, child: Container( height: buttonHeight, width: r.getValue( mobile: 130, tablet: 140, desktop: 150, ), decoration: BoxDecoration( color: const Color(0xFF5F297B), borderRadius: BorderRadius.circular(6), boxShadow: [ BoxShadow( color: Colors.black.withOpacity( 0.25, ), blurRadius: 7.8, offset: const Offset(0, 4), ), ], ), child: Center( child: Text( "View Details", style: AppTextStyles.semiBold.copyWith( fontSize: buttonTextSize, color: Colors.white, ), ), ), ), ), ), ], SizedBox(height: cardSpacing), ], ), ); }, ), ], ), ), ), SizedBox(height: r.spacing(mobile: 16, tablet: 18, desktop: 20)), // Carousel Slider dashboard.imageSlider.isEmpty ? const SizedBox.shrink() : SizedBox( height: carouselSectionHeight, child: Column( children: [ CarouselSlider.builder( carouselController: _carouselController, itemCount: dashboard.imageSlider.length, itemBuilder: (context, index, realIndex) { final slider = dashboard.imageSlider[index]; return Container( margin: const EdgeInsets.symmetric(horizontal: 8), decoration: BoxDecoration( borderRadius: BorderRadius.circular(12), image: DecorationImage( image: NetworkImage(slider.image), fit: BoxFit.cover, ), ), ); }, options: CarouselOptions( height: carouselHeight, autoPlay: true, enlargeCenterPage: true, viewportFraction: 0.9, onPageChanged: (index, reason) { setState(() => _currentIndex = index); }, ), ), const SizedBox(height: 10), Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate( dashboard.imageSlider.length, (index) => GestureDetector( onTap: () => _carouselController.animateToPage(index), child: AnimatedContainer( duration: const Duration(milliseconds: 300), width: _currentIndex == index ? 18 : 8, height: 8, margin: const EdgeInsets.symmetric(horizontal: 4), decoration: BoxDecoration( borderRadius: BorderRadius.circular(10), color: _currentIndex == index ? AppColors.commanbutton : Colors.grey.shade400, ), ), ), ), ), ], ), ), SizedBox(height: r.spacing(mobile: 16, tablet: 18, desktop: 20)), // Customer Review Section Stack( clipBehavior: Clip.none, children: [ Image.asset( AppAssets.back, width: double.infinity, fit: BoxFit.cover, ), Container( width: double.infinity, height: r.getValue( mobile: 240, tablet: 260, desktop: 280, ), color: Colors.black.withOpacity(0.45), ), Positioned.fill( child: Padding( padding: EdgeInsets.only( top: r.spacing(mobile: 60, tablet: 70, desktop: 80), ), child: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ Text( "What Our Customers Say", style: AppTextStyles.semiBold.copyWith( fontSize: reviewTitleSize, color: Colors.white, ), ), ], ), ), ), Transform.translate( offset: Offset( 0, r.getValue(mobile: 140, tablet: 150, desktop: 160), ), child: SizedBox( height: reviewCardHeight + 30, child: ListView.builder( scrollDirection: Axis.horizontal, padding: EdgeInsets.symmetric(horizontal: hPadding), itemCount: dashboard.reviews.length, itemBuilder: (context, index) { final review = dashboard.reviews[index]; return Row( children: [ _testimonialCard( name: review.name, position: review.about, content: review.content, rating: review.rating, iconColor: Colors.blueAccent, cardWidth: reviewCardWidth, cardHeight: reviewCardHeight, r: r, ), const SizedBox(width: 16), ], ); }, ), ), ), ], ), SizedBox(height: r.spacing(mobile: 120, tablet: 140, desktop: 160)), ], ), ); } Widget _testimonialCard({ required String name, required String position, required String content, required int rating, required Color iconColor, required double cardWidth, required double cardHeight, required ResponsiveUtils r, }) { return Stack( clipBehavior: Clip.none, children: [ Container( width: cardWidth, height: cardHeight, padding: EdgeInsets.all( r.spacing(mobile: 14, tablet: 16, desktop: 18), ), margin: EdgeInsets.only( left: r.spacing(mobile: 26, tablet: 30, desktop: 34), ), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(24), boxShadow: const [ BoxShadow( color: Colors.black12, blurRadius: 10, offset: Offset(0, 4), ), ], ), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Text( name, style: AppTextStyles.bold.copyWith( fontSize: r.fontSize(mobile: 13, tablet: 15, desktop: 16), color: Colors.black87, ), ), Text( position, style: AppTextStyles.regular.copyWith( fontSize: r.fontSize(mobile: 10, tablet: 11, desktop: 12), color: Colors.grey, ), ), const SizedBox(height: 6), Row( mainAxisAlignment: MainAxisAlignment.center, children: List.generate( rating, (index) => Icon( Icons.star, size: r.getValue( mobile: 12, tablet: 14, desktop: 16, ), color: Colors.amber, ), ), ), const SizedBox(height: 8), Text( content, maxLines: 4, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, style: AppTextStyles.regular.copyWith( fontSize: r.fontSize(mobile: 11, tablet: 12, desktop: 13), height: 1.3, color: Colors.black87, ), ), ], ), ), Positioned( left: 0, top: cardHeight / 2 - 20, child: Container( width: 40, height: 40, decoration: BoxDecoration(color: iconColor, shape: BoxShape.circle), child: Center( child: Text( "❝", style: AppTextStyles.bold.copyWith( color: Colors.white, fontSize: 22, ), ), ), ), ), ], ); } }