// ignore_for_file: deprecated_member_use_from_same_package, use_build_context_synchronously import 'dart:math'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_rating_bar/flutter_rating_bar.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:go_router/go_router.dart'; import 'package:hoshan/core/gen/assets.gen.dart'; import 'package:hoshan/core/routes/route_generator.dart'; import 'package:hoshan/core/services/api/dio_service.dart'; import 'package:hoshan/core/utils/date_time.dart'; import 'package:hoshan/core/utils/strings.dart'; import 'package:hoshan/data/model/ai/bots_model.dart'; import 'package:hoshan/data/model/assistant_comments_model.dart'; import 'package:hoshan/data/model/chat_args.dart'; import 'package:hoshan/data/model/edittext_state_model.dart'; import 'package:hoshan/data/repository/bot_repository.dart'; import 'package:hoshan/ui/screens/assistant/bloc/assistant_info_bloc.dart'; import 'package:hoshan/ui/screens/assistant/bloc/same_assistants_bloc.dart'; import 'package:hoshan/ui/screens/assistant/cubit/assistant_comments_cubit.dart'; import 'package:hoshan/ui/screens/splash/cubit/user_info_cubit.dart'; import 'package:hoshan/ui/theme/colors.dart'; import 'package:hoshan/ui/theme/cubit/theme_mode_cubit.dart'; import 'package:hoshan/ui/theme/responsive.dart'; import 'package:hoshan/ui/theme/text.dart'; import 'package:hoshan/ui/widgets/components/bot/bot_grid_card.dart'; import 'package:hoshan/ui/widgets/components/bot/bot_grid_card_placeholder.dart'; import 'package:hoshan/ui/widgets/components/button/circle_icon_btn.dart'; import 'package:hoshan/ui/widgets/components/button/loading_button.dart'; import 'package:hoshan/ui/widgets/components/dialog/bottom_sheets.dart'; import 'package:hoshan/ui/widgets/components/image/network_image.dart'; import 'package:hoshan/ui/widgets/components/snackbar/snackbar_manager.dart'; import 'package:hoshan/ui/widgets/components/text/credit_cost.dart'; import 'package:hoshan/ui/widgets/components/text/labeled_text_field.dart'; import 'package:hoshan/ui/widgets/sections/header/reversible_appbar.dart'; import 'package:hoshan/ui/widgets/sections/loading/default_placeholder.dart'; import 'package:persian_number_utility/persian_number_utility.dart'; class AssistantPage extends StatefulWidget { const AssistantPage({super.key}); @override State createState() => _AssistantPageState(); } class _AssistantPageState extends State { EdittextStateModel commentState = EdittextStateModel( label: 'گوشمون به شماست!', hintText: 'از نظر شما این دستیار چقدر عالی کار کرده؟', ); double score = 0; ValueNotifier loadingAddComment = ValueNotifier(false); ValueNotifier errorAddComment = ValueNotifier(null); ValueNotifier maxLines = ValueNotifier(5); @override Widget build(BuildContext context) { return Scaffold( appBar: ReversibleAppbar( context, title: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ Padding( padding: const EdgeInsets.only(top: 4.0), child: CreditCost( call: false, loadingColor: Theme.of(context).colorScheme.secondary, ), ), const SizedBox( width: 8, ), CircleIconBtn( icon: Assets.icon.outline.coin, color: AppColors.secondryColor[50], iconColor: AppColors.secondryColor.defaultShade, ), ], ), ), body: BlocConsumer( listener: (context, state) { if (state is AssistantInfoSuccess) { context.read().add(GetSameAssistants( id: state.assistantInfo.category!.id!, botId: state.assistantInfo.id)); context.read().loadComments( id: state.assistantInfo.id!, ); } else if (state is AssistantInfoFail) { context.pop(); SnackBarManager(context, id: 'GetAssistantError').show( status: SnackBarStatus.error, message: 'خطا در بارگذاری دستیار'); } }, builder: (context, state) { if (state is AssistantInfoSuccess) { final info = state.assistantInfo; ValueNotifier comments = ValueNotifier(info.comments ?? 0); ValueNotifier scoreOfInfo = ValueNotifier(info.score ?? 0); UserComment? userComment = info.userComment; // commentState.formController.text = info.userComment?.text ?? ''; // if (info.userComment != null) { // score = info.userComment!.score ?? score; // } return Responsive(context).maxWidthInDesktop( child: (contxet, maxWidth) => SingleChildScrollView( physics: const BouncingScrollPhysics(), child: Directionality( textDirection: TextDirection.rtl, child: Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), child: Column( children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( flex: 3, child: Row( children: [ ImageNetwork( width: 62, height: 62, radius: 360, baseUrl: DioService.baseURL, url: info.image), const SizedBox( width: 8, ), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( info.name ?? '', style: AppTextStyles.headline6 .copyWith( color: Theme.of(context) .colorScheme .onSurface), maxLines: 1, overflow: TextOverflow.ellipsis, ), Row( children: [ Assets .icon.outline.elementPlus .svg( color: Theme.of(context) .colorScheme .onSurface), const SizedBox( width: 8, ), Expanded( child: Text( 'دسته‌بندی: ${info.category?.name ?? ''}', style: AppTextStyles.body4 .copyWith( color: Theme.of( context) .colorScheme .onSurface), maxLines: 1, overflow: TextOverflow.ellipsis, ), ) ], ) ], ), ) ], ), ), const SizedBox( width: 8, ), Expanded( child: ClipRRect( borderRadius: BorderRadius.circular(8), child: Column( children: [ Container( width: double.infinity, padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 8), decoration: BoxDecoration( color: AppColors.green.defaultShade, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Padding( padding: const EdgeInsets.only( top: 4.0), child: ValueListenableBuilder( valueListenable: scoreOfInfo, builder: (context, sc, _) { return Text( sc .toStringAsFixed( 1) .padRight(1, '0'), style: AppTextStyles .body4 .copyWith( fontWeight: FontWeight .bold, color: Colors .white), ); }), ), const Icon( Icons.star_rate_rounded, color: Colors.white, ), ], ), ), Container( width: double.infinity, padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: Theme.of(context) .colorScheme .surface, ), child: Center( child: ValueListenableBuilder( valueListenable: comments, builder: (context, cms, _) { return Text( '$cms نظر', style: AppTextStyles.body4 .copyWith( fontWeight: FontWeight .bold, color: Theme.of( context) .colorScheme .onSurface), ); }), ), ), ], ), ), ), ], ), const SizedBox( height: 12, ), Row( children: [ Expanded( child: infoContainer( title: 'کاربران این دستیار', description: '${info.users ?? 10} کاربر'), ), Expanded( child: infoContainer( title: 'تعداد پیام‌ها', description: '${info.messages ?? 20} پیام'), ), ], ), Row( children: [ Expanded( child: infoContainer( title: 'سازنده دستیار', description: info.user?.username ?? 'هوشان'), ), Expanded( child: infoContainer( title: 'تاریخ ایجاد', description: DateTimeUtils .convertStringIsoToDate( info.createdAt!) .toPersianDate()), ), ], ), if (info.description != null) Padding( padding: const EdgeInsets.only(bottom: 16.0), child: LayoutBuilder( builder: (context, constraints) { return ValueListenableBuilder( valueListenable: maxLines, builder: (context, lines, _) { final span = TextSpan( text: info.description, style: AppTextStyles.body4.copyWith( color: AppColors.gray[context .read< ThemeModeCubit>() .isDark() ? 600 : 900])); final tp = TextPainter( text: span, textDirection: TextDirection.ltr); tp.layout( maxWidth: constraints.maxWidth); final numLines = tp.computeLineMetrics().length; return Padding( padding: const EdgeInsets.fromLTRB( 4, 12, 4, 4), child: Stack( children: [ Column( children: [ Text( info.description!, style: AppTextStyles.body4 .copyWith( color: AppColors .gray[context .read< ThemeModeCubit>() .isDark() ? 600 : 900]), maxLines: (lines), textAlign: TextAlign.justify, ), if (lines == null && numLines >= 5) Transform.rotate( angle: -pi / 2, child: CircleIconBtn( onTap: () { if (maxLines .value == null) { maxLines .value = 5; return; } maxLines.value = null; }, size: 46, color: Theme.of( context) .colorScheme .primary, iconColor: Colors.white, icon: Assets .icon .outline .arrowRight)), ], ), if (lines != null && numLines > lines) Positioned( bottom: 0, left: 0, right: 0, child: Container( height: 64, decoration: BoxDecoration( gradient: LinearGradient( colors: [ Theme.of(context) .scaffoldBackgroundColor, Theme.of(context) .scaffoldBackgroundColor .withAlpha( 140) ], begin: Alignment .bottomCenter, end: Alignment .topCenter, ), ), alignment: Alignment .bottomCenter, child: SizedBox( child: Transform.rotate( angle: pi / 2, child: CircleIconBtn( onTap: () { if (maxLines .value == null) { maxLines .value = 5; return; } maxLines.value = null; }, size: 46, color: Theme.of(context).colorScheme.primary, iconColor: Colors.white, icon: Assets.icon.outline.arrowRight)), ), )) ], ), ); }); }), ), const SizedBox( height: 12, ), LoadingButton( onPressed: () { contxet.go(Routes.chatFromAssistant, extra: ChatArgs(bot: state.assistantInfo)); }, color: AppColors.primaryColor.defaultShade, radius: 16, width: double.infinity, height: 48, child: Text( 'استفاده از دستیار', style: AppTextStyles.body3 .copyWith(color: Colors.white), )), const SizedBox( height: 12, ), LoadingButton( color: Theme.of(context).colorScheme.secondary, isOutlined: true, height: 48, backgroundColor: Theme.of(context).scaffoldBackgroundColor, width: double.infinity, onPressed: () { BottomSheetHandler(context) .showReportOptions(); }, child: Text( 'گزارش اشکال', style: AppTextStyles.body4.copyWith( fontWeight: FontWeight.bold, color: Theme.of(context) .colorScheme .secondary), ), ), const SizedBox( height: 12, ), ValueListenableBuilder( valueListenable: loadingAddComment, builder: (context, loading, _) { return Container( decoration: BoxDecoration( color: Theme.of(context) .colorScheme .surface, borderRadius: BorderRadius.circular(8)), padding: const EdgeInsets.all(16), child: Stack( children: [ IgnorePointer( ignoring: loading, child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Center( child: Directionality( textDirection: TextDirection.ltr, child: RatingBar( initialRating: 1, direction: Axis.horizontal, allowHalfRating: true, itemCount: 5, itemSize: 46, minRating: 1, maxRating: 5, glow: false, ratingWidget: RatingWidget( full: Icon( Icons .star_rate_rounded, color: AppColors.green .defaultShade, ), half: Icon( Icons .star_half_rounded, color: AppColors.green .defaultShade, ), empty: Icon( Icons .star_outline_rounded, color: AppColors.gray .defaultShade, ), ), itemPadding: EdgeInsets.zero, onRatingUpdate: (rating) { score = rating; }, ), ), ), const SizedBox( height: 16, ), LabeledTextField( stateController: commentState, onValid: (value) { if (value != null && value.isEmpty) { return 'لطفا فیلد نظر را پر کنید!'; } return null; }, ), const SizedBox( height: 12, ), TextButton( onPressed: () async { if (!commentState .formState .currentState! .validate()) { return; } else if (score < 1) { SnackBarManager(context, id: 'score-fail') .show( message: 'حداقل امتیاز وازد شده باید 1 باشد', status: SnackBarStatus .error); } loadingAddComment.value = true; errorAddComment.value = null; try { AssistantComments comment = AssistantComments( createdAt: DateTimeUtils .getNow() .toIso8601String(), score: score, text: commentState .formController .text, user: AssistantCommentsUser( image: UserInfoCubit .userInfoModel .image, username: UserInfoCubit .userInfoModel .username)); await BotRepository .addCommentInAssistant( state .assistantInfo .id!, comment); loadingAddComment .value = false; context .read< AssistantCommentsCubit>() .removeComment( username: UserInfoCubit .userInfoModel .username!); context .read< AssistantCommentsCubit>() .addComment( comment: comment); SnackBarManager(context, id: 'addComment') .show( status: SnackBarStatus .success, message: 'نظر شما با موفقیت ارسال شد'); if (state.assistantInfo .userComment != null) { scoreOfInfo .value = (scoreOfInfo .value * comments .value + (score - (userComment ?.score ?? 0))) / comments.value; } else { scoreOfInfo .value = ((scoreOfInfo .value * comments .value) + score) / (comments.value + 1); comments.value = comments.value + 1; } userComment = UserComment( score: score, text: commentState .formController .text); commentState .formController .clear(); } on DioException catch (e) { try { errorAddComment .value = e.response?.data[ 'detail']; } catch (e) { errorAddComment .value = 'مشکلی پیش آمده لحظاتی دیگر دوباره تلاش کنید'; } if (kDebugMode) { print( 'Dio Error is: $e'); } } loadingAddComment.value = false; }, child: Padding( padding: const EdgeInsets.all( 8.0), child: Text( state.assistantInfo .userComment != null ? 'تغییر نظر' : 'ثبت نظر', style: AppTextStyles .body3 .copyWith( color: Theme.of( context) .colorScheme .primary), ), )) ], ), ), if (loading) Positioned.fill( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), color: Theme.of(context) .colorScheme .surface .withValues(alpha: 0.5)), child: Center( child: SpinKitThreeBounce( color: AppColors.primaryColor .defaultShade, size: 32, ), ), )) ], ), ); }), const SizedBox( height: 12, ), BlocBuilder( builder: (context, commentState) { if (commentState is AssistantCommentsInitial) { return Column( children: [ ListTile( contentPadding: EdgeInsets.zero, title: Text( 'نظرات کاربران', style: AppTextStyles.body3.copyWith( fontWeight: FontWeight.bold), ), ), const SizedBox( height: 12, ), ListView.builder( physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemCount: 3, itemBuilder: (context, index) { return commentContainerPlaceholder(); }, ), ], ); } if (commentState.comments.isNotEmpty) { return Column( children: [ ListTile( contentPadding: EdgeInsets.zero, title: Text( 'نظرات کاربران', style: AppTextStyles.body3.copyWith( fontWeight: FontWeight.bold), ), ), const SizedBox( height: 12, ), ListView.builder( physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, itemCount: commentState.comments.length, itemBuilder: (context, index) { final comment = commentState.comments[index]; return commentContainer(comment); }, ), const SizedBox( height: 12, ), if (commentState is AssistantCommentsLoading) Padding( padding: const EdgeInsets.symmetric( vertical: 12.0), child: LinearProgressIndicator( color: AppColors .primaryColor.defaultShade, borderRadius: BorderRadius.circular(8), ), ), if ((commentState is AssistantCommentsSuccess || commentState is AssistantCommentsFail) && !(commentState.lastPage != null && commentState.page > commentState.lastPage!)) GestureDetector( onTap: () { if (context .read< AssistantCommentsCubit>() .state is AssistantCommentsLoading || context .read< AssistantCommentsCubit>() .state is AssistantCommentsInitial) { return; } context .read< AssistantCommentsCubit>() .loadComments( id: state .assistantInfo.id!); }, child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'مشاهده بیشتر', style: AppTextStyles.body3 .copyWith( fontWeight: FontWeight.bold), ), const SizedBox( width: 8, ), Transform.rotate( angle: pi / 2, child: Assets .icon.outline.arrowRight .svg( color: AppColors .secondryColor .defaultShade)) ], ), ), ], ); } return const SizedBox.shrink(); }, ), const SizedBox( height: 24, ), ValueListenableBuilder( valueListenable: errorAddComment, builder: (context, message, child) { if (message != null && message.isNotEmpty) { return Container( decoration: BoxDecoration( color: AppColors.red[50], borderRadius: BorderRadius.circular(16)), padding: const EdgeInsets.all(16), child: Row( crossAxisAlignment: CrossAxisAlignment.center, children: [ Icon( Icons.warning_rounded, color: AppColors.red.defaultShade, size: 32, ), const SizedBox( width: 8, ), Expanded( child: Text( message, style: AppTextStyles.body4.copyWith( color: AppColors.gray[context .read< ThemeModeCubit>() .isDark() ? 600 : 900]), ), ), ], ), ); } return const SizedBox.shrink(); }, ) ], ), ), const SizedBox( height: 12, ), BlocBuilder( builder: (context, state) { if (state is SameAssistantsFail) { return const SizedBox.shrink(); } if (state is SameAssistantsSuccess) { if (state.bots.isEmpty) { return const SizedBox.shrink(); } return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric( horizontal: 16.0, ), child: ListTile( contentPadding: EdgeInsets.zero, title: Text( 'دستیارهای مشابه', style: AppTextStyles.body3.copyWith( fontWeight: FontWeight.bold), ), ), ), SizedBox( height: 170, child: ListView.builder( shrinkWrap: true, itemCount: state.bots.length, padding: const EdgeInsets.symmetric( horizontal: 8), physics: const BouncingScrollPhysics(), scrollDirection: Axis.horizontal, itemBuilder: (context, index) { return SizedBox( width: (Responsive(context) .isDesktop() ? 300 : MediaQuery.sizeOf(context) .width) * 0.5, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 4.0, vertical: 8), child: GestureDetector( onTap: () { contxet.push(Routes.assistant, extra: state.bots[index].id); }, child: BotGridCard( bot: state.bots[index], ), ), )); }, ), ), ], ); } return Column( children: [ Padding( padding: const EdgeInsets.symmetric( horizontal: 16.0, ), child: DefaultPlaceHolder( child: ListTile( contentPadding: EdgeInsets.zero, title: Text( 'دستیارهای مشابه', style: AppTextStyles.body3.copyWith( fontWeight: FontWeight.bold), ), ), ), ), SizedBox( height: 170, child: ListView.builder( shrinkWrap: true, itemCount: 10, padding: const EdgeInsets.symmetric( horizontal: 16), physics: const NeverScrollableScrollPhysics(), scrollDirection: Axis.horizontal, itemBuilder: (context, index) { return SizedBox( width: (Responsive(context) .isDesktop() ? 300 : MediaQuery.sizeOf(context) .width) * 0.5, child: Padding( padding: const EdgeInsets.symmetric( horizontal: 4.0, vertical: 8), child: GestureDetector( onTap: () {}, child: BotGridCardPlaceholder( index: index, ), ), )); }, ), ), ], ); }, ), const SizedBox( height: 12, ), ], ), ), ), ), ); } return Center( child: SpinKitThreeBounce( size: 32, color: AppColors.primaryColor.defaultShade, ), ); }, ), ); } Container commentContainerPlaceholder() { return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(24), border: Border.all(color: AppColors.gray[600]), color: Theme.of(context).colorScheme.surface), padding: const EdgeInsetsDirectional.all(16), margin: const EdgeInsets.symmetric(vertical: 4), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ DefaultPlaceHolder( child: Container( width: 52, height: 52, margin: const EdgeInsets.all(8), decoration: const BoxDecoration( shape: BoxShape.circle, color: Colors.white), ), ), DefaultPlaceHolder( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Colors.white), child: Text( 'کدباز 2025', style: AppTextStyles.body4 .copyWith(fontWeight: FontWeight.bold), ), ), ) ], ), DefaultPlaceHolder( child: Directionality( textDirection: TextDirection.ltr, child: RatingBar( initialRating: 3, direction: Axis.horizontal, allowHalfRating: true, itemCount: 5, itemSize: 24, ignoreGestures: true, ratingWidget: RatingWidget( full: Icon( Icons.star_rate_rounded, color: AppColors.green.defaultShade, ), half: Icon( Icons.star_half_rounded, color: AppColors.green.defaultShade, ), empty: Icon( Icons.star_outline_rounded, color: AppColors.green.defaultShade, ), ), itemPadding: EdgeInsets.zero, onRatingUpdate: (rating) {}, ), ), ) ], ), DefaultPlaceHolder( child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), color: Colors.white), child: Padding( padding: const EdgeInsets.all(8.0), child: Text( '"این دستیار برنامه‌نویسی خیلی به کارم اومد! باگ‌هایی رو که کلی وقت می‌گرفتن، سریع پیدا می‌کنه و حتی راه‌حل‌های پیشنهادی می‌ده".👌🤖', style: AppTextStyles.body4.copyWith(fontWeight: FontWeight.w500), ), ), ), ) ], ), ); } Container commentContainer(final AssistantComments comment) { final daysAgo = DateTimeUtils.getDaysBetweenNowAnd(comment.createdAt!); return Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(24), border: Border.all(color: AppColors.gray[600]), color: Theme.of(context).colorScheme.surface), padding: const EdgeInsetsDirectional.all(8), margin: const EdgeInsets.symmetric(vertical: 4), child: Column( children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ ImageNetwork( url: comment.user?.image ?? '', width: 52, height: 52, radius: 360, ), const SizedBox( width: 8, ), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( comment.user?.username ?? '', style: AppTextStyles.body4.copyWith( fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.onSurface), ), Text( daysAgo == 0 ? 'امروز' : '$daysAgo روز قبل', style: AppTextStyles.body6 .copyWith(color: AppColors.gray[700]), ) ], ) ], ), Directionality( textDirection: TextDirection.ltr, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Opacity( opacity: (comment.score ?? 0) == 0 ? 0 : 1, child: RatingBar( initialRating: comment.score ?? 0, direction: Axis.horizontal, allowHalfRating: true, itemCount: 5, itemSize: 24, ignoreGestures: true, ratingWidget: RatingWidget( full: Icon( Icons.star_rate_rounded, color: AppColors.green.defaultShade, ), half: Icon( Icons.star_half_rounded, color: AppColors.green.defaultShade, ), empty: Icon( Icons.star_outline_rounded, color: AppColors.green.defaultShade, ), ), itemPadding: EdgeInsets.zero, onRatingUpdate: (rating) {}, ), ), const SizedBox( height: 4, ), InkWell( onTap: BottomSheetHandler(context).showReportOptions, child: Row( mainAxisSize: MainAxisSize.min, children: [ Assets.icon.outline.flag2 .svg(color: AppColors.gray[700]), const SizedBox( width: 4, ), Text( 'گزارش', style: AppTextStyles.body4 .copyWith(color: AppColors.gray[700]), ), ], ), ) ], ), ) ], ), const SizedBox( height: 8, ), Padding( padding: const EdgeInsets.all(8.0), child: SizedBox( width: double.infinity, child: Text( comment.text ?? '', style: AppTextStyles.body4.copyWith( fontWeight: FontWeight.w500, color: Theme.of(context).colorScheme.onSurface), textDirection: comment.text != null && comment.text!.startsWithEnglish() ? TextDirection.ltr : TextDirection.rtl, ), ), ) ], ), ); } Container infoContainer( {required final String title, required final String description}) { return Container( margin: const EdgeInsets.all(4), decoration: BoxDecoration( color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(8)), padding: const EdgeInsets.all(8), child: Column( children: [ Text( title, style: AppTextStyles.body4.copyWith( fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.primary), maxLines: 1, overflow: TextOverflow.ellipsis, ), const SizedBox( height: 8, ), Text( description, style: AppTextStyles.body5 .copyWith(color: Theme.of(context).colorScheme.primary), maxLines: 1, overflow: TextOverflow.ellipsis, ) ], ), ); } }