This commit is contained in:
OkaykOrhmn 2024-11-20 08:50:12 +03:30
parent 7bec995377
commit f28592c421
9 changed files with 324 additions and 535 deletions

View File

@ -16,6 +16,7 @@ import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class ActionSheetUtils {
final BuildContext context;
@ -286,118 +287,83 @@ class ActionSheetUtils {
);
}
Future<void> botsDialogSelect(
{required final BuildContext context,
required final HistoryAiChatState state}) async {
Future<void> botsDialogSelect({
required final BuildContext context,
}) async {
ActionSheetUtils(context).openDialog(
data: ActionSheetData(
hasConfirmButton: false,
hasDismissButton: false,
content: Column(
children: [
// Row(
// mainAxisAlignment: MainAxisAlignment.end,
// children: [
// Padding(
// padding: const EdgeInsets.symmetric(vertical: 8.0),
// child: InkWell(
// onTap: () {
// ActionSheetUtils.pop();
// },
// child: const Icon(DidvanIcons.close_solid)),
// )
// ],
// ),
// SearchField(
// title: 'هوش مصنوعی',
// value: state.search,
// onChanged: (value) {
// state.search = value;
// if (value.isEmpty) {
// state.getBots();
// return;
// }
// state.timer?.cancel();
// state.timer = Timer(const Duration(seconds: 1), () {
// state.getSearchBots(value);
// });
// },
// focusNode: FocusNode()),
// const SizedBox(
// height: 12,
// ),
SizedBox(
width: double.infinity,
height: MediaQuery.sizeOf(context).height / 3,
child: ValueListenableBuilder<bool>(
valueListenable: state.loadingBots,
builder: (context, value, child) => value
? Center(
child: Image.asset(
Assets.loadingAnimation,
width: 60,
height: 60,
content: SizedBox(
width: double.infinity,
height: MediaQuery.sizeOf(context).height / 3,
child: Consumer<HistoryAiChatState>(
builder: (context, state, child) {
return state.loadingBots
? Center(
child: Image.asset(
Assets.loadingAnimation,
width: 60,
height: 60,
),
)
: state.bots.isEmpty
? Padding(
padding:
const EdgeInsets.symmetric(horizontal: 12.0),
child: EmptyState(
asset: Assets.emptyResult,
title: 'نتیجه‌ای پیدا نشد',
height: 120,
),
)
: state.bots.isEmpty
? Padding(
padding: const EdgeInsets.symmetric(
horizontal: 12.0),
child: EmptyState(
asset: Assets.emptyResult,
title: 'نتیجه‌ای پیدا نشد',
height: 120,
),
)
: ListView.builder(
itemCount: state.bots.length,
physics: const BouncingScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, index) {
final bot = state.bots[index];
return InkWell(
onTap: () {
ActionSheetUtils(context).pop();
state.bot = bot;
state.update();
},
child: Container(
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(
vertical: 8),
decoration: BoxDecoration(
border: index == state.bots.length - 1
? null
: Border(
bottom: BorderSide(
color: Theme.of(context)
.colorScheme
.border,
width: 1))),
child: Row(
children: [
SkeletonImage(
imageUrl: bot.image.toString(),
width: 42,
height: 42,
borderRadius:
BorderRadius.circular(360),
),
const SizedBox(width: 12),
Expanded(
child: DidvanText(
bot.name.toString(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
))
],
: ListView.builder(
itemCount: state.bots.length,
physics: const BouncingScrollPhysics(),
shrinkWrap: true,
itemBuilder: (context, index) {
final bot = state.bots[index];
return InkWell(
onTap: () {
ActionSheetUtils(context).pop();
state.bot = bot;
state.update();
},
child: Container(
alignment: Alignment.center,
padding:
const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
border: index == state.bots.length - 1
? null
: Border(
bottom: BorderSide(
color: Theme.of(context)
.colorScheme
.border,
width: 1))),
child: Row(
children: [
SkeletonImage(
imageUrl: bot.image.toString(),
width: 42,
height: 42,
borderRadius:
BorderRadius.circular(360),
),
),
);
}),
),
)
],
const SizedBox(width: 12),
Expanded(
child: DidvanText(
bot.name.toString(),
maxLines: 1,
overflow: TextOverflow.ellipsis,
))
],
),
),
);
});
}),
)));
}

View File

@ -34,9 +34,9 @@ class _AiState extends State<Ai> {
Future.delayed(
Duration.zero,
() {
if (context.read<HistoryAiChatState>().refresh) {
context.read<HistoryAiChatState>().getChats();
context.read<HistoryAiChatState>().refresh = false;
if (state.refresh) {
state.getBots();
state.refresh = false;
}
state.getBots();
},
@ -85,8 +85,7 @@ class _AiState extends State<Ai> {
),
InkWell(
onTap: () => ActionSheetUtils(context)
.botsDialogSelect(
context: context, state: state),
.botsDialogSelect(context: context),
child: Container(
decoration: BoxDecoration(
borderRadius:
@ -287,26 +286,29 @@ class _AiState extends State<Ai> {
}
},
),
Positioned(
top: 32,
right: 0,
child: InkWell(
onTap: () => homeScaffKey.currentState!.openDrawer(),
child: Container(
width: 46,
height: 46,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
bottomLeft: Radius.circular(12)),
boxShadow: DesignConfig.defaultShadow),
child: Icon(
DidvanIcons.angle_left_light,
color: Theme.of(context).colorScheme.title,
),
)),
)
Consumer<HistoryAiChatState>(builder: (context, state, child) {
if (state.bots.isEmpty) return const SizedBox();
return Positioned(
top: 32,
right: 0,
child: InkWell(
onTap: () => homeScaffKey.currentState!.openDrawer(),
child: Container(
width: 46,
height: 46,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(12),
bottomLeft: Radius.circular(12)),
boxShadow: DesignConfig.defaultShadow),
child: Icon(
DidvanIcons.angle_left_light,
color: Theme.of(context).colorScheme.title,
),
)),
);
})
],
);
}

View File

@ -63,7 +63,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
super.initState();
}
void onConfirm(CreateBotAssistantsState state, int selectedBotId) async {
void onConfirm(CreateBotAssistantsState state) async {
bool isValid = true;
// if (!_formYouTubeKey.currentState!.validate()) {
// isValid = false;
@ -95,7 +95,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
type: state.selectedBotType,
name: state.name,
description: state.desc,
botId: selectedBotId,
botId: state.initialBot!.id!,
prompt: state.prompt,
webLinks:
state.countOfLink.first.isEmpty ? null : state.countOfLink,
@ -135,9 +135,6 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
),
body: Consumer<CreateBotAssistantsState>(builder: (BuildContext context,
CreateBotAssistantsState state, Widget? child) {
int selectedBotId = state.selectedBotType == 'text'
? state.allBots.first.id!
: state.imageBots.first.id!;
return state.loading
? Center(
child: SpinKitThreeBounce(
@ -400,7 +397,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
.surface),
// hintText: "انتخاب کنید",
onChanged: (value) {
selectedBotId = value!.id!;
state.initialBot = value;
},
),
),
@ -804,7 +801,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
? ' '
: 'ذخیره تغییرات',
onPressed: () async =>
onConfirm(state, selectedBotId),
onConfirm(state),
),
if (state.loadingCreate)
const Positioned.fill(
@ -871,8 +868,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
DidvanButton(
title:
state.loadingCreate ? ' ' : 'ذخیره',
onPressed: () async =>
onConfirm(state, selectedBotId)),
onPressed: () async => onConfirm(state)),
if (state.loadingCreate)
const Positioned.fill(
child: Center(

View File

@ -29,7 +29,7 @@ class CreateBotAssistantsState extends CoreProvier {
navigatorKey.currentContext!.read<HistoryAiChatState>().bots;
final List<String> botModels = ['مدل زبانی', 'مدل تصویری'];
BotsModel? initialBot;
late BotsModel? initialBot = allBots.first;
List<String> countOfLink = [''];
List<FileCreateAssistantsModel> files = [];
@ -123,6 +123,9 @@ class CreateBotAssistantsState extends CoreProvier {
}
selectedBotType = initialBot?.responseType ?? 'text';
}
if (initialBot == null) {
selectedBotType == 'text' ? allBots.first : imageBots.first;
}
appState = AppState.idle;
loading = false;
update();

View File

@ -9,7 +9,6 @@ import 'package:didvan/providers/core.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:flutter/cupertino.dart';
class HistoryAiChatState extends CoreProvier {
final List<ChatsModel> chats = [];
@ -17,15 +16,17 @@ class HistoryAiChatState extends CoreProvier {
// final List<int> chatsToDelete = [];
final List<BotsModel> bots = [];
BotsModel? bot;
ValueNotifier<bool> loadingBots = ValueNotifier(false);
bool loadingBots = false;
bool loadingchangeTitle = false;
bool loadingdeleteAll = false;
bool loadinggetAll = false;
bool refresh = false;
Timer? timer;
String search = '';
Future<void> getChats({final bool archived = false}) async {
appState = AppState.busy;
loadinggetAll = true;
update();
final service = RequestService(
archived ? RequestHelper.aiArchived() : RequestHelper.aiChats(),
@ -39,10 +40,13 @@ class HistoryAiChatState extends CoreProvier {
.add(ChatsModel.fromJson(messages[i]));
}
appState = AppState.idle;
loadinggetAll = false;
update();
return;
}
appState = AppState.failed;
loadinggetAll = false;
update();
}
@ -72,7 +76,9 @@ class HistoryAiChatState extends CoreProvier {
}
Future<void> getBots() async {
loadingBots.value = true;
appState = AppState.busy;
loadingBots = true;
update();
final service = RequestService(
RequestHelper.aiBots(),
);
@ -83,19 +89,23 @@ class HistoryAiChatState extends CoreProvier {
for (var i = 0; i < messages.length; i++) {
bots.add(BotsModel.fromJson(messages[i]));
}
appState = AppState.idle;
loadingBots.value = false;
bot = bots.first;
appState = AppState.idle;
loadingBots = false;
update();
getChats();
return;
}
appState = AppState.failed;
loadingBots.value = false;
loadingBots = false;
update();
}
Future<void> getSearchBots(String q) async {
loadingBots.value = true;
appState = AppState.busy;
loadingBots = true;
final service = RequestService(
RequestHelper.aiSearchBots(q),
);
@ -107,13 +117,13 @@ class HistoryAiChatState extends CoreProvier {
bots.add(BotsModel.fromJson(messages[i]));
}
appState = AppState.idle;
loadingBots.value = false;
loadingBots = false;
update();
return;
}
appState = AppState.failed;
loadingBots.value = false;
loadingBots = false;
update();
}

View File

@ -17,7 +17,6 @@ import 'package:didvan/services/media/media.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/ai/ai_chat_state.dart';
import 'package:didvan/views/ai/history_ai_chat_state.dart';
import 'package:didvan/views/ai/widgets/audio_wave.dart';
import 'package:didvan/views/ai/widgets/message_bar_btn.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
@ -66,7 +65,6 @@ class _AiMessageBarState extends State<AiMessageBar> {
Timer? _timer;
final theSource = AudioSource.microphone;
final ValueNotifier<Duration> _countTimer = ValueNotifier(Duration.zero);
late HistoryAiChatState historyState = context.read<HistoryAiChatState>();
@override
void initState() {
@ -256,7 +254,7 @@ class _AiMessageBarState extends State<AiMessageBar> {
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
recorderAndSendButton(state, historyState),
recorderAndSendButton(state),
if (!(_mRecorder!.isStopped))
ValueListenableBuilder(
valueListenable: _countTimer,
@ -453,13 +451,13 @@ class _AiMessageBarState extends State<AiMessageBar> {
);
}
recorderAndSendButton(AiChatState state, HistoryAiChatState historyState) {
recorderAndSendButton(AiChatState state) {
return ValueListenableBuilder(
valueListenable: state.message,
builder: (context, message, child) => Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 12, 8),
child: message.text.isEmpty &&
historyState.bot!.attachmentType!.contains('audio') &&
widget.bot.attachmentType!.contains('audio') &&
state.file == null &&
widget.bot.attachment != 0
? MessageBarBtn(
@ -531,150 +529,6 @@ class _AiMessageBarState extends State<AiMessageBar> {
);
}
// AnimatedVisibility attachmentLayout(AiChatState state,
// HistoryAiChatState historyState, BuildContext context) {
// return AnimatedVisibility(
// isVisible: openAttach,
// duration: DesignConfig.lowAnimationDuration,
// child: Container(
// padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 12),
// child: state.file != null && !(state.file!.isRecorded)
// ? fileContainer()
// : Row(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// if (historyState.bot!.attachmentType!.contains('pdf'))
// attachBtn(
// title: "PDF",
// icon: CupertinoIcons.doc_fill,
// color: Colors.redAccent,
// click: () async {
// MediaService.onLoadingPickFile(context);
// FilePickerResult? result =
// await MediaService.pickPdfFile();
// if (result != null) {
// if (kIsWeb) {
// Uint8List? bytes = result.files.first
// .bytes; // Access the bytes property
// String? name = result.files.first.name;
// // Store bytes and file name directly in your state or model
// state.file = FilesModel(
// '', // No need for a file path on web
// name: name,
// bytes: bytes,
// audio: false,
// image: false,
// );
// } else {
// state.file = FilesModel(result.files.single.path!,
// audio: false, image: false);
// }
// }
// Future.delayed(
// Duration.zero,
// () => ActionSheetUtils(context).pop(),
// );
// },
// ),
// if (historyState.bot!.attachmentType!.contains('image'))
// attachBtn(
// title: "تصویر",
// icon: CupertinoIcons.photo,
// color: Colors.deepOrangeAccent,
// click: () async {
// MediaService.onLoadingPickFile(context);
// final pickedFile = await MediaService.pickImage(
// source: ImageSource.gallery);
// File? file;
// if (pickedFile != null && !kIsWeb) {
// file = await ImageCropper().cropImage(
// sourcePath: pickedFile.path,
// androidUiSettings: const AndroidUiSettings(
// toolbarTitle: 'برش تصویر'),
// iosUiSettings: const IOSUiSettings(
// title: 'برش تصویر',
// doneButtonTitle: 'تایید',
// cancelButtonTitle: 'بازگشت',
// ),
// compressQuality: 30,
// );
// if (file == null) {
// await Future.delayed(
// Duration.zero,
// () => ActionSheetUtils(context).pop(),
// );
// return;
// }
// }
// if (pickedFile == null) {
// await Future.delayed(
// Duration.zero,
// () => ActionSheetUtils(context).pop(),
// );
// return;
// }
// state.file = kIsWeb
// ? FilesModel(pickedFile.path,
// name: pickedFile.name,
// image: true,
// audio: false)
// : FilesModel(file!.path,
// image: true, audio: false);
// await Future.delayed(
// Duration.zero,
// () => ActionSheetUtils(context).pop(),
// );
// },
// ),
// // if (!kIsWeb && !Platform.isIOS)
// if (historyState.bot!.attachmentType!.contains('audio'))
// attachBtn(
// title: "صوت",
// icon: CupertinoIcons.music_note_2,
// color: Colors.indigoAccent,
// click: () async {
// MediaService.onLoadingPickFile(context);
// FilePickerResult? result =
// await MediaService.pickAudioFile();
// if (result != null) {
// if (kIsWeb) {
// Uint8List? bytes = result.files.first
// .bytes; // Access the bytes property
// String? name = result.files.first.name;
// // final blob = html.Blob([bytes]);
// // final blobUrl =
// // html.Url.createObjectUrlFromBlob(blob);
// state.file = FilesModel(
// "", // No need for a file path on web
// name: name,
// bytes: bytes,
// audio: true,
// image: false,
// );
// } else {
// state.file = FilesModel(result.files.single.path!,
// audio: true, image: false);
// }
// }
// await Future.delayed(
// Duration.zero,
// () => ActionSheetUtils(context).pop(),
// );
// },
// )
// ],
// ),
// ));
// }
AnimatedVisibility attachmentLayout(AiChatState state, BuildContext context) {
return AnimatedVisibility(
isVisible: openAttach,
@ -683,45 +537,48 @@ class _AiMessageBarState extends State<AiMessageBar> {
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (historyState.bot!.attachmentType!.contains('pdf'))
MessageBarBtn(
enable: true,
icon: CupertinoIcons.doc_fill,
click: () async {
MediaService.onLoadingPickFile(context);
FilePickerResult? result = await MediaService.pickPdfFile();
if (result != null) {
String? name = result.files.single.name;
if (kIsWeb) {
Uint8List? bytes =
result.files.first.bytes; // Access the bytes property
// Store bytes and file name directly in your state or model
state.file = FilesModel(
'', // No need for a file path on web
name: name,
bytes: bytes,
audio: false,
image: false,
);
} else {
state.file = FilesModel(result.files.single.path!,
audio: false, image: false, name: name);
}
}
Future.delayed(
Duration.zero,
() => ActionSheetUtils(context).pop(),
);
openAttach = !openAttach;
state.update();
},
),
if (historyState.bot!.attachmentType!.contains('image'))
if (widget.bot.attachmentType!.contains('pdf'))
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
padding: const EdgeInsets.only(right: 8.0),
child: MessageBarBtn(
enable: true,
icon: CupertinoIcons.doc_fill,
click: () async {
MediaService.onLoadingPickFile(context);
FilePickerResult? result = await MediaService.pickPdfFile();
if (result != null) {
String? name = result.files.single.name;
if (kIsWeb) {
Uint8List? bytes = result
.files.first.bytes; // Access the bytes property
// Store bytes and file name directly in your state or model
state.file = FilesModel(
'', // No need for a file path on web
name: name,
bytes: bytes,
audio: false,
image: false,
);
} else {
state.file = FilesModel(result.files.single.path!,
audio: false, image: false, name: name);
}
}
Future.delayed(
Duration.zero,
() => ActionSheetUtils(context).pop(),
);
openAttach = !openAttach;
state.update();
},
),
),
if (widget.bot.attachmentType!.contains('image'))
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: MessageBarBtn(
enable: true,
icon: CupertinoIcons.photo,
@ -777,86 +634,53 @@ class _AiMessageBarState extends State<AiMessageBar> {
),
),
// if (!kIsWeb && !Platform.isIOS)
if (historyState.bot!.attachmentType!.contains('audio'))
MessageBarBtn(
enable: true,
icon: CupertinoIcons.music_note_2,
click: () async {
MediaService.onLoadingPickFile(context);
if (widget.bot.attachmentType!.contains('audio'))
Padding(
padding: const EdgeInsets.only(right: 8.0),
child: MessageBarBtn(
enable: true,
icon: CupertinoIcons.music_note_2,
click: () async {
MediaService.onLoadingPickFile(context);
FilePickerResult? result = await MediaService.pickAudioFile();
if (result != null) {
String? name = result.files.first.name;
FilePickerResult? result =
await MediaService.pickAudioFile();
if (result != null) {
String? name = result.files.first.name;
if (kIsWeb) {
Uint8List? bytes =
result.files.first.bytes; // Access the bytes property
if (kIsWeb) {
Uint8List? bytes = result
.files.first.bytes; // Access the bytes property
final blob = html.Blob([bytes]);
final blobUrl = html.Url.createObjectUrlFromBlob(blob);
final blob = html.Blob([bytes]);
final blobUrl = html.Url.createObjectUrlFromBlob(blob);
state.file = FilesModel(
blobUrl, // No need for a file path on web
name: name,
bytes: bytes,
audio: true,
image: false,
);
} else {
state.file = FilesModel(result.files.single.path!,
name: name, audio: true, image: false);
state.file = FilesModel(
blobUrl, // No need for a file path on web
name: name,
bytes: bytes,
audio: true,
image: false,
);
} else {
state.file = FilesModel(result.files.single.path!,
name: name, audio: true, image: false);
}
}
}
await Future.delayed(
Duration.zero,
() => ActionSheetUtils(context).pop(),
);
openAttach = !openAttach;
await Future.delayed(
Duration.zero,
() => ActionSheetUtils(context).pop(),
);
openAttach = !openAttach;
state.update();
},
state.update();
},
),
)
],
));
}
// InkWell attachBtn(
// {required final String title,
// required final IconData icon,
// final Color? color,
// final Function()? click}) {
// final state = context.read<AiChatState>();
// return InkWell(
// onTap: () async {
// await click?.call();
// state.update();
// },
// child: Column(
// children: [
// Container(
// width: 40,
// height: 40,
// decoration: BoxDecoration(shape: BoxShape.circle, color: color),
// padding: const EdgeInsets.all(0.8),
// margin: const EdgeInsets.symmetric(horizontal: 12),
// child: Icon(
// icon,
// size: 20,
// color: Theme.of(context).colorScheme.white,
// )),
// const SizedBox(
// height: 4,
// ),
// DidvanText(
// title,
// fontSize: 12,
// )
// ],
// ),
// );
// }
Widget audioContainer() {
final state = context.watch<AiChatState>();

View File

@ -1,6 +1,5 @@
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/constants/assets.dart';
import 'package:didvan/main.dart';
import 'package:didvan/models/ai/ai_chat_args.dart';
import 'package:didvan/models/ai/chats_model.dart';
@ -15,8 +14,9 @@ import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:didvan/views/widgets/state_handlers/empty_list.dart';
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:provider/provider.dart';
class HoshanDrawer extends StatefulWidget {
@ -27,6 +27,11 @@ class HoshanDrawer extends StatefulWidget {
}
class _HoshanDrawerState extends State<HoshanDrawer> {
@override
initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Drawer(
@ -139,88 +144,28 @@ class _HoshanDrawerState extends State<HoshanDrawer> {
// height: 12,
// ),
Expanded(
child: state.loadingdeleteAll ||
state.appState == AppState.busy
? ListView.builder(
shrinkWrap: true,
itemCount: 10,
padding: const EdgeInsets.symmetric(
horizontal: 12),
physics:
const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return const Padding(
padding: EdgeInsets.symmetric(
vertical: 12.0),
child: Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
ClipOval(
child: ShimmerPlaceholder(
height: 24,
width: 24,
),
),
SizedBox(width: 12),
Expanded(
child: ShimmerPlaceholder(
height: 24,
),
),
SizedBox(width: 12),
ShimmerPlaceholder(
height: 24,
width: 24,
),
SizedBox(width: 8),
ShimmerPlaceholder(
height: 24,
width: 12,
),
],
),
);
},
)
: state.chats.isEmpty
? Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
SvgPicture.asset(
Assets.emptyResult,
height:
MediaQuery.sizeOf(context)
.height /
10,
),
const DidvanText(
'لیست خالی است',
fontSize: 14,
fontWeight: FontWeight.bold,
)
],
),
)
: ListView.builder(
shrinkWrap: true,
itemCount: state.chats.length,
padding: const EdgeInsets.symmetric(
horizontal: 12),
physics:
const BouncingScrollPhysics(),
itemBuilder: (context, index) {
final chat = state.chats[index];
TextEditingController title =
TextEditingController(
text: chat.title);
child: CustomScrollView(
slivers: [
SliverStateHandler(
state: state,
centerEmptyState: true,
emptyState: const EmptyList(),
// enableEmptyState: state.chats.isEmpty,
placeholder: chatRowPlaceholder(),
placeholderCount: 10,
builder: (context, state, index) {
final chat = state.chats[index];
TextEditingController title =
TextEditingController(
text: chat.title);
return chatRow(chat, title, state, index);
},
childCount: state.chats.length,
onRetry: () => state.getChats())
],
)),
return chatRow(
chat, title, state, index);
},
),
),
// SizedBox(
// height: 12,
// ),
@ -268,6 +213,39 @@ class _HoshanDrawerState extends State<HoshanDrawer> {
);
}
Padding chatRowPlaceholder() {
return const Padding(
padding: EdgeInsets.symmetric(vertical: 12.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipOval(
child: ShimmerPlaceholder(
height: 24,
width: 24,
),
),
SizedBox(width: 12),
Expanded(
child: ShimmerPlaceholder(
height: 24,
),
),
SizedBox(width: 12),
ShimmerPlaceholder(
height: 24,
width: 24,
),
SizedBox(width: 8),
ShimmerPlaceholder(
height: 24,
width: 12,
),
],
),
);
}
Widget drawerBtn(
{final CrossAxisAlignment? crossAxisAlignment,
required final IconData icon,

View File

@ -142,7 +142,6 @@ class _HomeState extends State<Home>
if (_tabController.index == 2) {
homeScaffKey.currentState!.closeDrawer();
context.read<HistoryAiChatState>().getChats();
context.read<HistoryAiChatState>().getBots();
}
});

View File

@ -1,18 +1,18 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/utils/extension.dart';
import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:persian_number_utility/persian_number_utility.dart';
class DidvanTextField extends StatefulWidget {
final void Function(String value)? onChanged;
final void Function(String value)? onSubmitted;
final bool enabled;
final bool autoFocus;
final TextAlign textAlign;
final TextAlign? textAlign;
final String? title;
final String? hintText;
final dynamic initialValue;
@ -37,7 +37,7 @@ class DidvanTextField extends StatefulWidget {
this.initialValue,
this.validator,
this.textInputType,
this.textAlign = TextAlign.right,
this.textAlign,
this.obsecureText = false,
this.autoFocus = false,
this.onSubmitted,
@ -69,7 +69,7 @@ class _DidvanTextFieldState extends State<DidvanTextField> {
}
_hideContent = widget.obsecureText;
_focusNode.addListener(() {
setState(() {});
// setState(() {});
});
super.initState();
}
@ -98,47 +98,58 @@ class _DidvanTextFieldState extends State<DidvanTextField> {
? null
: Border.all(color: _borderColor()),
),
child: TextFormField(
inputFormatters: <TextInputFormatter>[
if (!widget.acceptSpace)
FilteringTextInputFormatter.allow(RegExp("[0-9a-zA-Z]")),
],
autofocus: widget.autoFocus,
obscureText: _hideContent,
textAlign: widget.textAlign,
keyboardType: widget.textInputType,
focusNode: _focusNode,
controller: _controller,
onFieldSubmitted: widget.onSubmitted,
onChanged: _onChanged,
validator: _validator,
maxLines: widget.maxLine,
minLines: widget.minLine,
maxLength: widget.maxLength,
obscuringCharacter: '*',
buildCounter: widget.showLen
? null
: (context,
{required currentLength,
required isFocused,
required maxLength}) =>
const SizedBox(),
style: (widget.isSmall
? Theme.of(context).textTheme.bodySmall!
: Theme.of(context).textTheme.bodyMedium!)
.copyWith(fontFamily: DesignConfig.fontFamily.padRight(3)),
decoration: InputDecoration(
suffixIcon: _suffixBuilder(),
enabled: widget.enabled,
border: InputBorder.none,
hintText: widget.hintText,
errorStyle: const TextStyle(height: 0.01),
hintStyle: (widget.isSmall
? Theme.of(context).textTheme.bodySmall!
: Theme.of(context).textTheme.bodyMedium!)
.copyWith(color: Theme.of(context).colorScheme.hint),
),
),
child: ValueListenableBuilder(
valueListenable: _controller,
builder: (context, val, _) {
return Directionality(
textDirection: val.text.startsWithEnglish()
? TextDirection.ltr
: TextDirection.rtl,
child: TextFormField(
inputFormatters: <TextInputFormatter>[
if (!widget.acceptSpace)
FilteringTextInputFormatter.allow(
RegExp("[0-9a-zA-Z]")),
],
autofocus: widget.autoFocus,
obscureText: _hideContent,
textAlign: widget.textAlign ?? TextAlign.start,
keyboardType: widget.textInputType,
focusNode: _focusNode,
controller: _controller,
onFieldSubmitted: widget.onSubmitted,
onChanged: _onChanged,
validator: _validator,
maxLines: widget.maxLine,
minLines: widget.minLine,
maxLength: widget.maxLength,
obscuringCharacter: '*',
buildCounter: widget.showLen
? null
: (context,
{required currentLength,
required isFocused,
required maxLength}) =>
const SizedBox(),
style: (widget.isSmall
? Theme.of(context).textTheme.bodySmall!
: Theme.of(context).textTheme.bodyMedium!)
.copyWith(
fontFamily: DesignConfig.fontFamily.padRight(3)),
decoration: InputDecoration(
suffixIcon: _suffixBuilder(),
enabled: widget.enabled,
border: InputBorder.none,
hintText: widget.hintText,
errorStyle: const TextStyle(height: 0.01),
hintStyle: (widget.isSmall
? Theme.of(context).textTheme.bodySmall!
: Theme.of(context).textTheme.bodyMedium!)
.copyWith(color: Theme.of(context).colorScheme.hint),
),
),
);
}),
),
AnimatedVisibility(
isVisible: _error != null,
@ -224,7 +235,7 @@ class _DidvanTextFieldState extends State<DidvanTextField> {
setState(() {
_error = null;
});
value = value.toEnglishDigit();
// value = value.toEnglishDigit();
widget.onChanged?.call(value);
}