feat(order info): added order info screen

This commit is contained in:
Username1548
2025-04-04 20:21:31 +07:00
parent 9715f4cac9
commit 748484cf8f
387 changed files with 16171 additions and 18199 deletions

View File

@@ -5,12 +5,12 @@ import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
class AboutOrderButton extends StatelessWidget {
const AboutOrderButton({super.key});
const AboutOrderButton({super.key, required this.onTap});
final void Function() onTap;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {},
onTap: onTap,
child: Container(
height: 44,
decoration: BoxDecoration(
@@ -20,13 +20,13 @@ class AboutOrderButton extends StatelessWidget {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox.shrink(),
const SizedBox.shrink(),
Text(
S.current.more_about_order,
style: TextStyles.textSMedium.copyWith(color: appTheme.black),
),
Container(
margin: EdgeInsets.only(right: 4),
margin: const EdgeInsets.only(right: 4),
width: 36,
height: 36,
decoration: BoxDecoration(

View File

@@ -27,7 +27,7 @@ class ChatBubbleWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final size = MediaQuery.sizeOf(context);
return Container(
return SizedBox(
width: size.width,
child: Padding(
padding: EdgeInsets.fromLTRB(isCurrentUser ? 64 : 16, 10, isCurrentUser ? 16 : 64, 4),
@@ -41,7 +41,7 @@ class ChatBubbleWidget extends StatelessWidget {
padding: const EdgeInsets.only(right: 4, bottom: 4),
child: Text(
sendTime.format('hh:mm'),
style: TextStyle(
style: const TextStyle(
color: Color(0xFF7B8794),
height: 1,
fontSize: 10,
@@ -52,16 +52,16 @@ class ChatBubbleWidget extends StatelessWidget {
constraints: BoxConstraints(maxWidth: size.width * 0.7),
decoration: BoxDecoration(
color: isCurrentUser ? appTheme.gray50 : appTheme.gray60004,
borderRadius: mediaData.length > 0
borderRadius: mediaData.isNotEmpty
? BorderRadius.circular(24)
: BorderRadius.horizontal(
left: isCurrentUser ? Radius.circular(32) : Radius.circular(16),
right: isCurrentUser ? Radius.circular(16) : Radius.circular(24),
left: isCurrentUser ? const Radius.circular(32) : const Radius.circular(16),
right: isCurrentUser ? const Radius.circular(16) : const Radius.circular(24),
),
),
child: Padding(
padding:
mediaData.isNotEmpty ? EdgeInsets.all(8) : EdgeInsets.only(top: 8, bottom: 8, left: isCurrentUser ? 16 : 8, right: isCurrentUser ? 8 : 16),
mediaData.isNotEmpty ? const EdgeInsets.all(8) : EdgeInsets.only(top: 8, bottom: 8, left: isCurrentUser ? 16 : 8, right: isCurrentUser ? 8 : 16),
child: Column(
crossAxisAlignment: isCurrentUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
children: [
@@ -70,7 +70,7 @@ class ChatBubbleWidget extends StatelessWidget {
width: size.width * 0.7,
child: CustomScrollView(
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
physics: const NeverScrollableScrollPhysics(),
slivers: [
SliverList.separated(
itemBuilder: (BuildContext context, int index) {
@@ -78,25 +78,25 @@ class ChatBubbleWidget extends StatelessWidget {
!mediaData[index].toString().contains('.jpg') &&
!mediaData[index].toString().contains('.jpeg')) {
return ListTile(
contentPadding: EdgeInsets.all(4),
contentPadding: const EdgeInsets.all(4),
horizontalTitleGap: 8,
onTap: () async {
if (!await launchUrl(Uri.parse(baseUrl + '/storage/' + mediaData[index].toString()))) {
if (!await launchUrl(Uri.parse('$baseUrl/storage/${mediaData[index]}'))) {
throw Exception('Could not launch ${mediaData[index].toString()}');
}
},
leading: Icon(
leading: const Icon(
Icons.file_copy_outlined,
color: Colors.white,
),
trailing: Icon(
trailing: const Icon(
Icons.download_outlined,
color: Colors.white,
),
title: Text(
mediaData[index].toString().replaceAll('chat_files/', ''),
maxLines: 2,
style: TextStyle(
style: const TextStyle(
color: Colors.white,
fontSize: 12,
overflow: TextOverflow.ellipsis,
@@ -108,18 +108,18 @@ class ChatBubbleWidget extends StatelessWidget {
onTap: () {
context.pushTransparentRoute(
PhotoViewScreen(
imageUrls: mediaData.map((e) => baseUrl + '/storage/' + e.toString()).toList(),
imageUrls: mediaData.map((e) => '$baseUrl/storage/$e').toList(),
index: index,
),
);
},
child: Hero(
tag: baseUrl + '/storage/' + '${mediaData[index]}',
tag: '$baseUrl/storage/${mediaData[index]}',
transitionOnUserGestures: true,
child: ClipRRect(
borderRadius: BorderRadius.circular(16),
child: CachedNetworkImage(
imageUrl: baseUrl + '/storage/' + '${mediaData[index]}',
imageUrl: '$baseUrl/storage/${mediaData[index]}',
),
),
),
@@ -131,7 +131,7 @@ class ChatBubbleWidget extends StatelessWidget {
],
),
),
if (mediaData.isNotEmpty && text.isNotEmpty) SizedBox(height: 8),
if (mediaData.isNotEmpty && text.isNotEmpty) const SizedBox(height: 8),
if (text.isNotEmpty)
Text(
text,
@@ -146,7 +146,7 @@ class ChatBubbleWidget extends StatelessWidget {
padding: const EdgeInsets.only(left: 4, bottom: 4),
child: Text(
sendTime.format('hh:mm'),
style: TextStyle(
style: const TextStyle(
color: Color(0xFF7B8794),
height: 1,
fontSize: 10,

View File

@@ -8,7 +8,7 @@ import 'package:go_router/go_router.dart';
class ChatEntryWidget extends StatefulWidget {
final ChatRoom chatRoom;
ChatEntryWidget({
const ChatEntryWidget({
required this.chatRoom,
super.key,
});
@@ -29,10 +29,10 @@ class _ChatEntryWidgetState extends State<ChatEntryWidget> {
},
),
child: Ink(
padding: EdgeInsets.all(12),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16),
color: Color(0xFFF6FAFF),
color: const Color(0xFFF6FAFF),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
@@ -52,10 +52,10 @@ class _ChatEntryWidgetState extends State<ChatEntryWidget> {
Text(
widget.chatRoom.users.toChatUser.name.length <= 20
? widget.chatRoom.users.toChatUser.name
: widget.chatRoom.users.toChatUser.name.substring(0, 20) + '...',
: '${widget.chatRoom.users.toChatUser.name.substring(0, 20)}...',
style: theme.textTheme.titleMedium,
),
SizedBox(height: 8),
const SizedBox(height: 8),
if (widget.chatRoom.lastMessage != null)
SizedBox(
width: MediaQuery.sizeOf(context).width * 0.5,
@@ -68,22 +68,22 @@ class _ChatEntryWidgetState extends State<ChatEntryWidget> {
),
],
),
Spacer(),
const Spacer(),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (widget.chatRoom.lastMessage != null)
Padding(
padding: EdgeInsets.only(left: 8),
padding: const EdgeInsets.only(left: 8),
child: Text(
widget.chatRoom.lastMessage!.updatedAt.format('dd MMM', 'ru'),
style: theme.textTheme.bodyMedium,
),
),
SizedBox(height: 4),
const SizedBox(height: 4),
if (widget.chatRoom.unreadMessagesCount > 0)
Container(
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 6),
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: appTheme.blueA400,

View File

@@ -3,8 +3,8 @@ import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:flutter/material.dart';
class ChatSearchBarWidget extends StatelessWidget {
ChatSearchBarWidget({
Key? key,
const ChatSearchBarWidget({
super.key,
this.alignment,
this.width,
this.scrollPadding,
@@ -27,7 +27,7 @@ class ChatSearchBarWidget extends StatelessWidget {
this.validator,
this.onChanged,
required this.onSuffixIconTap,
}) : super(key: key);
});
final Alignment? alignment;
@@ -107,7 +107,7 @@ class ChatSearchBarWidget extends StatelessWidget {
hintText: hintText ?? "",
hintStyle: hintStyle ?? CustomTextStyles.bodySmallMulishBluegray800,
prefixIcon: prefix ??
Padding(
const Padding(
padding: EdgeInsets.only(left: 12),
child: Icon(
Icons.search,
@@ -126,7 +126,7 @@ class ChatSearchBarWidget extends StatelessWidget {
),
),
isDense: true,
contentPadding: EdgeInsets.all(16),
contentPadding: const EdgeInsets.all(16),
fillColor: fillColor ?? appTheme.gray100,
filled: filled,
border: borderDecoration ??

View File

@@ -0,0 +1,90 @@
import 'package:eatvio_app/core/utils/toasts.dart';
import 'package:eatvio_app/core/widgets/bottom_sheet_handler_bar.dart';
import 'package:eatvio_app/core/widgets/custom_divider.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_state.dart';
import 'package:eatvio_app/features/diets/presentation/widgets/calendar_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/core/widgets/custom_outlined_button.dart';
import 'package:flutter/material.dart';
class ChosePlanDateWidget extends StatefulWidget {
const ChosePlanDateWidget({super.key, required this.chatCubit});
final ChatRoomCubit chatCubit;
@override
State<ChosePlanDateWidget> createState() => _ChosePlanDateWidget();
}
class _ChosePlanDateWidget extends State<ChosePlanDateWidget> {
final startDate = [DateTime.now()];
@override
Widget build(BuildContext context) {
final s = S.of(context);
String? planName;
if (widget.chatCubit.state is ChatRoomStateLoaded) {
planName = (widget.chatCubit.state as ChatRoomStateLoaded).chatRoom.chosenPlan?.plan.name;
}
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const SizedBox(height: 4),
const BottomSheetHandlerBar(),
const SizedBox(height: 12),
Text(
planName ?? '',
style: TextStyles.headingXLSemibold.copyWith(
color: appTheme.black,
),
),
const SizedBox(height: 12),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 25.0),
child: Text(
s.chose_plan_start_date_2,
textAlign: TextAlign.center,
style: TextStyles.textMMedium.copyWith(
color: appTheme.black,
),
),
),
const SizedBox(height: 12),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
child: CustomDivider(),
),
CalendarWidget(
startDate: startDate,
),
Padding(
padding: const EdgeInsets.only(left: 16, right: 16, top: 16, bottom: 28),
child: CustomOutlinedButton(
onPressed: () async {
final result = await widget.chatCubit.choseStartPlanDate(startDate[0]);
if (result) {
showSuccessToast('Дата установлена');
if (!context.mounted) return;
Navigator.pop(context);
} else {
showErrorToast('Не удалось установить дату');
if (!context.mounted) return;
Navigator.pop(context);
}
},
height: 60,
text: s.confirm,
buttonTextStyle: TextStyles.textLSemibold.copyWith(
color: appTheme.whiteA700,
),
buttonStyle: ButtonStyle(
backgroundColor: WidgetStatePropertyAll(appTheme.blueA400),
side: const WidgetStatePropertyAll(BorderSide.none),
),
),
),
],
);
}
}

View File

@@ -1,8 +1,13 @@
import 'package:eatvio_app/core/theme/custom_text_style.dart';
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/chat/presentation/bloc/chat_room_cubit/chat_room_cubit.dart';
import 'package:eatvio_app/features/chat/presentation/pages/payment_screen.dart';
import 'package:eatvio_app/features/chat/presentation/widgets/chose_plan_date_widget.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
class NotificationBanner extends StatelessWidget {
const NotificationBanner({super.key, required this.bannerType});
@@ -32,38 +37,52 @@ class NotificationBanner extends StatelessWidget {
BannerType.date => [
SizedBox(
width: size.width * 0.6,
child: Text(s.can_not_choose_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
child: Text(s.choose_plan_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
),
SizedBox(height: 12),
const SizedBox(height: 12),
GestureDetector(
onTap: () {},
child: Text(s.add_card, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
onTap: () {
final cubit = context.read<ChatRoomCubit>();
showModalBottomSheet(
context: context,
builder: (context) => ChosePlanDateWidget(
chatCubit: cubit,
),
);
},
child: Text(s.choose_date, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
),
SizedBox(height: 16),
const SizedBox(height: 16),
],
BannerType.card => [
SizedBox(
width: size.width * 0.6,
child: Text(s.can_not_choose_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
),
SizedBox(height: 12),
const SizedBox(height: 12),
GestureDetector(
onTap: () {},
child: Text(s.add_card, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
),
SizedBox(height: 16),
const SizedBox(height: 16),
],
BannerType.payment => [
SizedBox(
width: size.width * 0.6,
child: Text(s.can_not_choose_start_date, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
child: Text(s.tap_on_link_to_pay, style: TextStyles.textMMedium.copyWith(color: appTheme.black)),
),
SizedBox(height: 12),
const SizedBox(height: 12),
GestureDetector(
onTap: () {},
child: Text(s.add_card, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
onTap: () async {
final paymentLink = await context.read<ChatRoomCubit>().getPaymentLink();
if (paymentLink != null) {
if (!context.mounted) return;
GoRouter.of(context).push(PaymentScreen.path, extra: paymentLink);
}
},
child: Text(s.pay, style: TextStyles.textLSemibold.copyWith(color: appTheme.blueA400)),
),
SizedBox(height: 16),
const SizedBox(height: 16),
],
},
),

View File

@@ -33,8 +33,8 @@ class _OrderCardState extends State<OrderCard> {
};
final s = S.of(context);
return Container(
margin: EdgeInsets.symmetric(horizontal: 16),
padding: EdgeInsets.only(top: 8, bottom: 12, left: 8, right: 8),
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.only(top: 8, bottom: 12, left: 8, right: 8),
decoration: BoxDecoration(borderRadius: BorderRadius.circular(12), color: appTheme.gray60004),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
@@ -47,7 +47,7 @@ class _OrderCardState extends State<OrderCard> {
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
baseUrl + '/storage/' + widget.plan.image,
'$baseUrl/storage/${widget.plan.image}',
width: double.infinity,
fit: BoxFit.cover,
),
@@ -59,7 +59,7 @@ class _OrderCardState extends State<OrderCard> {
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: appTheme.blueA400,
borderRadius: BorderRadius.only(
borderRadius: const BorderRadius.only(
topRight: Radius.circular(12),
bottomRight: Radius.circular(12),
),
@@ -77,7 +77,7 @@ class _OrderCardState extends State<OrderCard> {
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: appTheme.greenA700,
borderRadius: BorderRadius.only(
borderRadius: const BorderRadius.only(
topRight: Radius.circular(12),
bottomRight: Radius.circular(12),
),
@@ -91,17 +91,17 @@ class _OrderCardState extends State<OrderCard> {
],
),
),
SizedBox(height: 12),
const SizedBox(height: 12),
Text(
'${widget.plan.name}',
widget.plan.name,
style: TextStyles.headingXLSemibold.copyWith(color: appTheme.black),
),
SizedBox(height: 12),
const SizedBox(height: 12),
Text(
'${s.option}: ${option?.short ?? ''}',
style: TextStyles.textMSemibold.copyWith(color: appTheme.black),
),
SizedBox(height: 8),
const SizedBox(height: 8),
if (option != null)
...option.items
.map(
@@ -114,11 +114,11 @@ class _OrderCardState extends State<OrderCard> {
),
),
)
.toList(),
,
if (widget.isCustomerView) ...[
SizedBox(height: 8),
const SizedBox(height: 8),
isLoading
? Loader()
? const Loader()
: GestureDetector(
onTap: () async {
setState(() => isLoading = true);
@@ -126,7 +126,7 @@ class _OrderCardState extends State<OrderCard> {
setState(() => isLoading = false);
},
child: Text(
'${s.cancel_order}',
s.cancel_order,
style: TextStyles.textSMedium.copyWith(color: appTheme.strongRed),
),
),