973 lines
50 KiB
Dart
973 lines
50 KiB
Dart
// ignore_for_file: use_build_context_synchronously
|
|
|
|
import 'dart:math';
|
|
|
|
import 'package:carousel_slider/carousel_slider.dart';
|
|
import 'package:file_picker/file_picker.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.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/gen/my_flutter_app_icons.dart';
|
|
import 'package:hoshan/core/routes/route_generator.dart';
|
|
import 'package:hoshan/core/services/file_manager/download_file_services.dart';
|
|
import 'package:hoshan/data/model/ai/bots_model.dart';
|
|
import 'package:hoshan/data/model/ai/chats_history_model.dart';
|
|
import 'package:hoshan/data/model/ai/messages_model.dart';
|
|
import 'package:hoshan/data/model/ai/send_message_model.dart';
|
|
import 'package:hoshan/ui/screens/chat/bloc/messages_bloc.dart';
|
|
import 'package:hoshan/ui/screens/library/bloc/chats_history_bloc.dart';
|
|
import 'package:hoshan/ui/screens/gmedia/cubit/media_g_response_cubit.dart';
|
|
import 'package:hoshan/ui/theme/colors.dart';
|
|
import 'package:hoshan/ui/theme/cubit/theme_mode_cubit.dart';
|
|
import 'package:hoshan/ui/theme/text.dart';
|
|
import 'package:hoshan/ui/widgets/components/audio/music_player.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/dialog_handler.dart';
|
|
import 'package:hoshan/ui/widgets/components/dropdown/hint_tooltip.dart';
|
|
import 'package:hoshan/ui/widgets/components/snackbar/snackbar_manager.dart';
|
|
import 'package:share_plus/share_plus.dart';
|
|
|
|
class AudioChatPage extends StatefulWidget {
|
|
final String type;
|
|
final Bots bot;
|
|
final int? chatId;
|
|
final double maxWidth;
|
|
const AudioChatPage(
|
|
{super.key,
|
|
required this.type,
|
|
required this.bot,
|
|
this.chatId,
|
|
required this.maxWidth});
|
|
|
|
@override
|
|
State<AudioChatPage> createState() => _AudioChatPageState();
|
|
}
|
|
|
|
class _AudioChatPageState extends State<AudioChatPage> {
|
|
final FocusNode _textFieldFocus = FocusNode();
|
|
final CarouselSliderController _carouselController =
|
|
CarouselSliderController();
|
|
final ValueNotifier<int> _currentIndex = ValueNotifier(3);
|
|
final TextEditingController _query = TextEditingController();
|
|
final ValueNotifier<bool> isGhost = ValueNotifier(false);
|
|
final ValueNotifier<int> maxSize = ValueNotifier(1);
|
|
|
|
late int? chatId = widget.chatId;
|
|
late Bots bot = widget.bot;
|
|
|
|
List<List<Messages>> groupMessages(List<Messages> messages) {
|
|
return messages.fold<List<List<Messages>>>([], (acc, message) {
|
|
if (acc.isEmpty ||
|
|
(acc.last.first.fromBot != message.fromBot && acc.last.length == 2) ||
|
|
(acc.last.first.fromBot == message.fromBot && message.fromBot!) ||
|
|
(acc.last.first.fromBot == message.fromBot && !message.fromBot!)) {
|
|
acc.add([message]);
|
|
} else {
|
|
acc.last.add(message);
|
|
}
|
|
return acc;
|
|
});
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return MultiBlocProvider(
|
|
providers: [
|
|
BlocProvider<MessagesBloc>(
|
|
create: (context) {
|
|
final messagesBloc = MessagesBloc()..add(ResetMessages());
|
|
if (chatId != null) {
|
|
messagesBloc.add(GetallMessages(chatId: chatId!));
|
|
}
|
|
return messagesBloc;
|
|
},
|
|
),
|
|
],
|
|
child: Scaffold(
|
|
floatingActionButton: ValueListenableBuilder(
|
|
valueListenable: _currentIndex,
|
|
builder: (context, value, _) {
|
|
return ValueListenableBuilder(
|
|
valueListenable: maxSize,
|
|
builder: (context, size, child) {
|
|
return value < size - 1
|
|
? Padding(
|
|
padding: const EdgeInsets.only(bottom: 64.0),
|
|
child: FloatingActionButton.small(
|
|
shape: const CircleBorder(),
|
|
onPressed: () {
|
|
_carouselController.animateToPage(size - 1);
|
|
},
|
|
child: Transform.rotate(
|
|
angle: pi / 2,
|
|
child: Assets.icon.outline.arrowRight.svg(
|
|
color: Colors.white,
|
|
),
|
|
),
|
|
))
|
|
: const SizedBox.shrink();
|
|
},
|
|
);
|
|
}),
|
|
bottomSheet: Padding(
|
|
padding: const EdgeInsets.all(8.0),
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
widget.type == 'file'
|
|
? Expanded(
|
|
child: Padding(
|
|
padding: const EdgeInsets.symmetric(horizontal: 8.0),
|
|
child: LoadingButton(
|
|
width: double.infinity,
|
|
onPressed: () async {
|
|
if (context.read<MediaGResponseCubit>().state
|
|
is MediaGResponseLoading) {
|
|
return;
|
|
}
|
|
FilePickerResult? result =
|
|
await FilePicker.platform.pickFiles(
|
|
type: FileType.audio,
|
|
);
|
|
if (result != null) {
|
|
if (mounted) {
|
|
context
|
|
.read<MediaGResponseCubit>()
|
|
.request(SendMessageModel(
|
|
id: chatId,
|
|
ghost: isGhost.value,
|
|
messageId:
|
|
DateTime.now().toIso8601String(),
|
|
file: result.xFiles.single,
|
|
botId: bot.id,
|
|
));
|
|
}
|
|
}
|
|
},
|
|
backgroundColor:
|
|
Theme.of(context).colorScheme.primary,
|
|
child: Text(
|
|
'بارگذاری فایل صوتی',
|
|
style: AppTextStyles.body4
|
|
.copyWith(color: Colors.white),
|
|
)),
|
|
),
|
|
)
|
|
: Expanded(
|
|
child: Padding(
|
|
padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
|
|
child: Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: Row(
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
children: [
|
|
CircleIconBtn(
|
|
icon: Assets.icon.bold.send,
|
|
color: Theme.of(context).colorScheme.primary,
|
|
iconColor: Colors.white,
|
|
iconPadding: const EdgeInsets.all(6),
|
|
size: 26,
|
|
onTap: () {
|
|
if (context.read<MediaGResponseCubit>().state
|
|
is MediaGResponseLoading) {
|
|
return;
|
|
}
|
|
context
|
|
.read<MediaGResponseCubit>()
|
|
.request(SendMessageModel(
|
|
messageId:
|
|
DateTime.now().toIso8601String(),
|
|
id: chatId,
|
|
ghost: isGhost.value,
|
|
query: _query.text,
|
|
botId: bot.id,
|
|
));
|
|
_query.clear();
|
|
},
|
|
),
|
|
const SizedBox(
|
|
width: 8,
|
|
),
|
|
Expanded(
|
|
child: TextField(
|
|
controller: _query,
|
|
focusNode: _textFieldFocus,
|
|
onTapOutside: (event) {
|
|
_textFieldFocus.unfocus();
|
|
},
|
|
minLines: widget.type == 'music' ? 4 : 1,
|
|
maxLines: 4,
|
|
keyboardType: TextInputType.multiline,
|
|
style: AppTextStyles.body4.copyWith(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.onSurface),
|
|
decoration: InputDecoration.collapsed(
|
|
hintText:
|
|
'متن ${widget.type == 'music' ? 'ترانه ' : ''}را وارد کنید...',
|
|
hintStyle: AppTextStyles.body4.copyWith(
|
|
color: AppColors.gray[context
|
|
.read<ThemeModeCubit>()
|
|
.isDark()
|
|
? 600
|
|
: 900])),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
body: Stack(
|
|
children: [
|
|
Assets.image.audioBack.image(
|
|
width: widget.maxWidth,
|
|
height: MediaQuery.sizeOf(context).height,
|
|
// color: Theme.of(context).scaffoldBackgroundColor,
|
|
// colorBlendMode: BlendMode.multiply,
|
|
opacity: AlwaysStoppedAnimation(
|
|
context.read<ThemeModeCubit>().isDark() ? 0.4 : 0.8),
|
|
fit: BoxFit.cover,
|
|
),
|
|
Positioned.fill(child: BlocBuilder<MessagesBloc, MessagesState>(
|
|
builder: (context, mState) {
|
|
if (mState is MessagesFail) {
|
|
return const SizedBox();
|
|
}
|
|
if (mState is MessagesLoading) {
|
|
return const Center(
|
|
child: CircularProgressIndicator(),
|
|
);
|
|
}
|
|
final m = mState.messages;
|
|
List<List<Messages>> messages = groupMessages(m);
|
|
return BlocConsumer<MediaGResponseCubit, MediaGResponseState>(
|
|
listener: (context, state) async {
|
|
if (state is MediaGResponseLoading) {
|
|
await Future.delayed(const Duration(milliseconds: 600));
|
|
_carouselController.animateToPage(maxSize.value);
|
|
}
|
|
if (state is MediaGResponseFail) {
|
|
SnackBarManager(context).show(
|
|
message:
|
|
'خطا از طرف سرور لطفا لحظاتی دیگر دوباره تلاش کنید',
|
|
status: SnackBarStatus.error);
|
|
}
|
|
if (state is MediaGResponseSucess) {
|
|
context.read<MessagesBloc>().add(AddMessage(
|
|
message: Messages(
|
|
query: state.query,
|
|
file: state.file,
|
|
createdAt: DateTime.now().toIso8601String(),
|
|
error: state.response.error,
|
|
id: state.response.humanMessageId,
|
|
role: 'user')));
|
|
if (!(state.response.error ?? true)) {
|
|
context.read<MessagesBloc>().add(AddMessage(
|
|
message: Messages(
|
|
content: [
|
|
Content(
|
|
audioUrl:
|
|
FileUrl(url: state.response.content))
|
|
],
|
|
createdAt: DateTime.now().toIso8601String(),
|
|
error: state.response.error,
|
|
id: state.response.aiMessageId,
|
|
role: 'ai')));
|
|
}
|
|
if (chatId == null && !isGhost.value) {
|
|
context.read<ChatsHistoryBloc>().add(AddChat(
|
|
chats: Chats(
|
|
bot: bot,
|
|
title: state.response.chatTitle,
|
|
createdAt: DateTime.now().toIso8601String(),
|
|
id: state.response.chatId)));
|
|
}
|
|
|
|
chatId = state.response.chatId;
|
|
}
|
|
},
|
|
builder: (context, state) {
|
|
maxSize.value = messages.length +
|
|
1 +
|
|
(state is MediaGResponseLoading ? 1 : 0);
|
|
return CarouselSlider.builder(
|
|
carouselController: _carouselController,
|
|
itemCount: messages.length +
|
|
1 +
|
|
(state is MediaGResponseLoading ? 1 : 0),
|
|
options: CarouselOptions(
|
|
initialPage: 3,
|
|
viewportFraction: 1,
|
|
enlargeFactor: 0.1,
|
|
height: MediaQuery.sizeOf(context).height,
|
|
autoPlay: false,
|
|
scrollDirection: Axis.vertical,
|
|
enableInfiniteScroll: false,
|
|
onPageChanged: (index, reason) {
|
|
_currentIndex.value = index;
|
|
}),
|
|
itemBuilder: (context, index, realIndex) {
|
|
if (state is MediaGResponseLoading &&
|
|
index == messages.length + 1) {
|
|
final yourScrollController = ScrollController();
|
|
|
|
return Column(
|
|
children: [
|
|
Flexible(
|
|
flex: 1,
|
|
child: Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
widget.type == 'file'
|
|
? Container(
|
|
width: widget.maxWidth * 0.8,
|
|
padding:
|
|
const EdgeInsets.all(16),
|
|
margin:
|
|
const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: context
|
|
.read<
|
|
ThemeModeCubit>()
|
|
.isDark()
|
|
? AppColors.black[900]
|
|
: Colors.white,
|
|
borderRadius: BorderRadius
|
|
.circular(16)
|
|
.copyWith(
|
|
bottomLeft:
|
|
Radius.zero)),
|
|
child: MusicPlayer(
|
|
url: state.file!.path))
|
|
: Container(
|
|
width: widget.maxWidth * 0.8,
|
|
constraints: BoxConstraints(
|
|
maxHeight:
|
|
MediaQuery.sizeOf(
|
|
context)
|
|
.height *
|
|
0.3),
|
|
padding:
|
|
const EdgeInsets.all(16),
|
|
margin:
|
|
const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.primary,
|
|
borderRadius: BorderRadius
|
|
.circular(16)
|
|
.copyWith(
|
|
bottomRight:
|
|
Radius.zero)),
|
|
child: Directionality(
|
|
textDirection:
|
|
TextDirection.rtl,
|
|
child: textLay(
|
|
yourScrollController,
|
|
state.query ?? ''),
|
|
),
|
|
),
|
|
],
|
|
)),
|
|
Flexible(
|
|
child: Column(
|
|
children: [
|
|
loading(context),
|
|
],
|
|
))
|
|
],
|
|
);
|
|
} else if (index != 0) {
|
|
final ms = messages[index - 1];
|
|
Messages? user;
|
|
Messages? ai;
|
|
if (ms.length == 2) {
|
|
user = ms.first;
|
|
ai = ms.last;
|
|
} else if (ms.length == 1) {
|
|
if (ms.single.fromBot ?? false) {
|
|
ai = ms.single;
|
|
} else {
|
|
user = ms.single;
|
|
}
|
|
}
|
|
final yourScrollController = ScrollController();
|
|
return Column(
|
|
children: [
|
|
const SizedBox(
|
|
height: 16,
|
|
),
|
|
if (user != null)
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.end,
|
|
children: [
|
|
widget.type == 'file'
|
|
? Container(
|
|
width: widget.maxWidth * 0.8,
|
|
padding: const EdgeInsets.all(16),
|
|
margin: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
color: context
|
|
.read<
|
|
ThemeModeCubit>()
|
|
.isDark()
|
|
? AppColors.black[900]
|
|
: Colors.white,
|
|
borderRadius:
|
|
BorderRadius.circular(16)
|
|
.copyWith(
|
|
bottomLeft:
|
|
Radius.zero)),
|
|
child: MusicPlayer(
|
|
url: user.file!.path))
|
|
: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.start,
|
|
children: [
|
|
Container(
|
|
width: widget.maxWidth * 0.8,
|
|
constraints: BoxConstraints(
|
|
maxHeight:
|
|
MediaQuery.sizeOf(
|
|
context)
|
|
.height *
|
|
0.3),
|
|
padding:
|
|
const EdgeInsets.all(16),
|
|
margin:
|
|
const EdgeInsets.all(16)
|
|
.copyWith(bottom: 8),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.primary,
|
|
borderRadius: BorderRadius
|
|
.circular(16)
|
|
.copyWith(
|
|
bottomRight:
|
|
Radius.zero)),
|
|
child: Directionality(
|
|
textDirection:
|
|
TextDirection.rtl,
|
|
child: textLay(
|
|
yourScrollController,
|
|
user.query ?? ''),
|
|
),
|
|
),
|
|
Padding(
|
|
padding:
|
|
const EdgeInsets.only(
|
|
left: 18.0,
|
|
bottom: 16),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.start,
|
|
children: [
|
|
CircleIconBtn(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.primary,
|
|
iconColor: Colors.white,
|
|
icon: Assets
|
|
.icon.outline.copy,
|
|
onTap: () async {
|
|
try {
|
|
await Clipboard.setData(
|
|
ClipboardData(
|
|
text: user!
|
|
.query!));
|
|
Future.delayed(
|
|
Duration.zero,
|
|
() => SnackBarManager(
|
|
context,
|
|
id:
|
|
'Copy')
|
|
.show(
|
|
message:
|
|
'متن کپی شد 😃'));
|
|
} catch (e) {
|
|
if (kDebugMode) {
|
|
print(e);
|
|
}
|
|
}
|
|
},
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
if (user?.error ?? false)
|
|
error(
|
|
context,
|
|
() {
|
|
context.read<MessagesBloc>().add(
|
|
DeleteMessageWithId(
|
|
messageId: user!.id!));
|
|
context
|
|
.read<MediaGResponseCubit>()
|
|
.request(SendMessageModel(
|
|
id: chatId,
|
|
query: widget.type != 'file'
|
|
? null
|
|
: _query.text,
|
|
file: widget.type == 'file'
|
|
? user.file
|
|
: null,
|
|
botId: bot.id,
|
|
ghost: isGhost.value,
|
|
messageId: DateTime.now()
|
|
.toIso8601String(),
|
|
));
|
|
},
|
|
),
|
|
if (ai != null)
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
children: [
|
|
widget.type == 'file'
|
|
? Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.end,
|
|
children: [
|
|
Container(
|
|
width: widget.maxWidth * 0.8,
|
|
constraints: BoxConstraints(
|
|
maxHeight:
|
|
MediaQuery.sizeOf(
|
|
context)
|
|
.height *
|
|
0.3),
|
|
padding:
|
|
const EdgeInsets.all(16),
|
|
margin:
|
|
const EdgeInsets.all(16)
|
|
.copyWith(bottom: 8),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.primary,
|
|
borderRadius: BorderRadius
|
|
.circular(16)
|
|
.copyWith(
|
|
bottomRight:
|
|
Radius.zero)),
|
|
child: Directionality(
|
|
textDirection:
|
|
TextDirection.rtl,
|
|
child: textLay(
|
|
yourScrollController,
|
|
ai
|
|
.content
|
|
?.first
|
|
.audioUrl
|
|
?.url ??
|
|
''),
|
|
),
|
|
),
|
|
Padding(
|
|
padding:
|
|
const EdgeInsets.only(
|
|
right: 18.0,
|
|
bottom: 16),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.end,
|
|
children: [
|
|
CircleIconBtn(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.primary,
|
|
iconColor: Colors.white,
|
|
icon: Assets
|
|
.icon.outline.copy,
|
|
onTap: () async {
|
|
try {
|
|
await Clipboard.setData(
|
|
ClipboardData(
|
|
text: ai!
|
|
.content!
|
|
.first
|
|
.audioUrl!
|
|
.url!));
|
|
} catch (e) {
|
|
if (kDebugMode) {
|
|
print(e);
|
|
}
|
|
}
|
|
},
|
|
),
|
|
],
|
|
),
|
|
)
|
|
],
|
|
)
|
|
: Column(
|
|
crossAxisAlignment:
|
|
CrossAxisAlignment.end,
|
|
children: [
|
|
Container(
|
|
width: widget.maxWidth * 0.8,
|
|
padding:
|
|
const EdgeInsets.all(16),
|
|
margin:
|
|
const EdgeInsets.all(16)
|
|
.copyWith(bottom: 8),
|
|
decoration: BoxDecoration(
|
|
color: context
|
|
.read<
|
|
ThemeModeCubit>()
|
|
.isDark()
|
|
? AppColors.black[900]
|
|
: Colors.white,
|
|
borderRadius: BorderRadius
|
|
.circular(16)
|
|
.copyWith(
|
|
bottomLeft:
|
|
Radius.zero)),
|
|
child: MusicPlayer(
|
|
url: ai.content?.first
|
|
.audioUrl?.url ??
|
|
''),
|
|
),
|
|
Padding(
|
|
padding:
|
|
const EdgeInsets.only(
|
|
right: 18.0,
|
|
bottom: 16),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.end,
|
|
children: [
|
|
CircleIconBtn(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.primary,
|
|
iconColor: Colors.white,
|
|
icon: Assets.icon
|
|
.outline.download,
|
|
onTap: () {
|
|
try {
|
|
DownloadFileService.getFile(
|
|
url: ai!
|
|
.content!
|
|
.first
|
|
.audioUrl!
|
|
.url!)
|
|
.then((value) {
|
|
SnackBarManager(context).show(
|
|
message:
|
|
'فایل با موفقیت در پوشه Downloads نشست.',
|
|
status: SnackBarStatus
|
|
.success);
|
|
});
|
|
} catch (e) {
|
|
if (kDebugMode) {
|
|
print(e);
|
|
}
|
|
}
|
|
},
|
|
),
|
|
const SizedBox(
|
|
width: 8,
|
|
),
|
|
CircleIconBtn(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.primary,
|
|
iconColor: Colors.white,
|
|
icon: Assets
|
|
.icon.outline.share,
|
|
onTap: () async {
|
|
try {
|
|
await Share.share(ai!
|
|
.content!
|
|
.first
|
|
.audioUrl!
|
|
.url
|
|
.toString());
|
|
} catch (e) {
|
|
if (kDebugMode) {
|
|
print(
|
|
'Error in share Text: $e');
|
|
}
|
|
}
|
|
},
|
|
),
|
|
],
|
|
),
|
|
)
|
|
],
|
|
),
|
|
],
|
|
),
|
|
],
|
|
);
|
|
}
|
|
return Stack(
|
|
children: [
|
|
Column(
|
|
children: [
|
|
const SizedBox(
|
|
height: 16,
|
|
),
|
|
if (bot.description != null)
|
|
Container(
|
|
margin: const EdgeInsets.symmetric(
|
|
horizontal: 16),
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
borderRadius:
|
|
BorderRadius.circular(16),
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.surface),
|
|
child: Text(
|
|
bot.description!,
|
|
style: AppTextStyles.body4.copyWith(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.onSurface),
|
|
textDirection: TextDirection.rtl,
|
|
textAlign: TextAlign.justify,
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 16, vertical: 8),
|
|
child: Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
Row(
|
|
mainAxisAlignment:
|
|
MainAxisAlignment.center,
|
|
children: [
|
|
ValueListenableBuilder(
|
|
valueListenable: isGhost,
|
|
builder: (context, g, _) {
|
|
return Transform.scale(
|
|
scale: 0.8,
|
|
child: Switch.adaptive(
|
|
value: g,
|
|
thumbIcon:
|
|
WidgetStateProperty
|
|
.resolveWith<
|
|
Icon?>(
|
|
(Set<WidgetState>
|
|
states) {
|
|
if (states.contains(
|
|
WidgetState
|
|
.selected)) {
|
|
return Icon(
|
|
CustomIcons
|
|
.ghost,
|
|
color: Theme.of(
|
|
context)
|
|
.colorScheme
|
|
.onSurface);
|
|
}
|
|
return Icon(
|
|
Icons.close,
|
|
color: Theme.of(
|
|
context)
|
|
.colorScheme
|
|
.onSurface);
|
|
},
|
|
),
|
|
onChanged: (value) {
|
|
isGhost.value = value;
|
|
},
|
|
),
|
|
);
|
|
}),
|
|
const SizedBox(
|
|
width: 8,
|
|
),
|
|
Text(
|
|
'حالت ناشناس',
|
|
style: AppTextStyles.body4
|
|
.copyWith(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.onSurface,
|
|
fontWeight:
|
|
FontWeight.bold),
|
|
),
|
|
const SizedBox(
|
|
width: 8,
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.only(
|
|
bottom: 4.0),
|
|
child: HintTooltip(
|
|
hint:
|
|
'با فعال کردن این گزینه؛ چتهای شما در قسمت تاریخچه، ذخیره نمیشوند و اطلاعاتتان ناشناس باقی میماند.',
|
|
iconColor: Theme.of(context)
|
|
.colorScheme
|
|
.onSurface,
|
|
),
|
|
)
|
|
],
|
|
),
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 8, vertical: 4),
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context)
|
|
.colorScheme
|
|
.secondary,
|
|
borderRadius:
|
|
BorderRadius.circular(12)),
|
|
child: Row(
|
|
children: [
|
|
Text(
|
|
bot.cost == 0 ||
|
|
bot.cost == null
|
|
? 'رایگان'
|
|
: bot.cost.toString(),
|
|
style: AppTextStyles.body3
|
|
.copyWith(
|
|
color: Colors.white),
|
|
),
|
|
const SizedBox(
|
|
width: 4,
|
|
),
|
|
Assets.icon.outline.coin.svg(
|
|
color: Colors.white,
|
|
width: 18,
|
|
height: 18)
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(
|
|
height: 32,
|
|
),
|
|
],
|
|
),
|
|
// Positioned(
|
|
// left: 16,
|
|
// bottom: widget.type == 'music' ? 150 : 74,
|
|
// child: CircleIconBtn(
|
|
// onTap: () {
|
|
// DialogHandler(context: context)
|
|
// .onMusicCreate();
|
|
// },
|
|
// size: 32,
|
|
// icon: Assets.icon.outline.idea))
|
|
],
|
|
);
|
|
});
|
|
},
|
|
);
|
|
},
|
|
))
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Scrollbar textLay(ScrollController yourScrollController, String text) {
|
|
return Scrollbar(
|
|
thumbVisibility: true,
|
|
trackVisibility: true,
|
|
interactive: true,
|
|
controller: yourScrollController,
|
|
radius: const Radius.circular(16),
|
|
child: SingleChildScrollView(
|
|
controller: yourScrollController,
|
|
physics: const BouncingScrollPhysics(),
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(left: 8.0),
|
|
child: SelectableText(
|
|
text,
|
|
|
|
style: AppTextStyles.body4.copyWith(
|
|
color: Colors.white,
|
|
),
|
|
// overflow: TextOverflow.ellipsis,
|
|
// textAlign: TextAlign.justify,
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Widget error(BuildContext context, Function()? onRetry) {
|
|
return Column(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(8),
|
|
color: AppColors.red.defaultShade),
|
|
child: Center(
|
|
child: Text(
|
|
'خطا لطفا مجددا تلاش کنید',
|
|
style: AppTextStyles.body5
|
|
.copyWith(color: Colors.white, fontWeight: FontWeight.bold),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(
|
|
height: 8,
|
|
),
|
|
// LoadingButton(
|
|
// color: AppColors.red.defaultShade,
|
|
// onPressed:(){
|
|
// context.go(Routes.purchase);
|
|
// },
|
|
// child: Text(
|
|
// 'افزایش اعتبار',
|
|
// style: AppTextStyles.body4.copyWith(color: Colors.white),
|
|
// ),
|
|
// )
|
|
],
|
|
);
|
|
}
|
|
|
|
Container loading(BuildContext context) {
|
|
return Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
|
padding: const EdgeInsets.all(16),
|
|
decoration: BoxDecoration(
|
|
borderRadius: BorderRadius.circular(8),
|
|
color: context.read<ThemeModeCubit>().isDark()
|
|
? AppColors.black[900]
|
|
: AppColors.secondryColor[50]),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: SpinKitThreeBounce(
|
|
size: 32,
|
|
color: Theme.of(context).colorScheme.secondary,
|
|
)),
|
|
Text(
|
|
'این کار ممکن است کمی طول بکشد',
|
|
style: AppTextStyles.body5
|
|
.copyWith(color: Theme.of(context).colorScheme.onSurface),
|
|
)
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|