"Modified code in multiple files, including AI chat models, info page, Hoshan drawer, and studio details, with changes to widgets, layouts, and controllers."

This commit is contained in:
OkaykOrhmn 2024-11-18 15:43:32 +03:30
parent 86c03984cd
commit 4a8c14ad09
9 changed files with 707 additions and 310 deletions

View File

@ -10,6 +10,7 @@ class ChatsModel {
String? placeholder;
String? createdAt;
String? updatedAt;
String? responseType;
BotsModel? bot;
BotAssistants? userBot;
List<Prompts>? prompts;
@ -28,6 +29,7 @@ class ChatsModel {
this.prompts,
this.isEditing = false,
this.assistantsName,
this.responseType,
this.userBot});
ChatsModel.fromJson(Map<String, dynamic> json) {
@ -38,6 +40,7 @@ class ChatsModel {
placeholder = json['placeholder'];
createdAt = json['createdAt'];
updatedAt = json['updatedAt'];
responseType = json['responseType'];
userBot = json['userBot'] != null
? BotAssistants.fromJson(json['userBot'])
: null;
@ -67,6 +70,7 @@ class ChatsModel {
data['placeholder'] = placeholder;
data['createdAt'] = createdAt;
data['updatedAt'] = updatedAt;
data['responseType'] = responseType;
if (bot != null) {
data['bot'] = bot!.toJson();
}

View File

@ -157,8 +157,9 @@ class AiChatState extends CoreProvier {
// }
final req = await AiApiService.initial(
url: '${isAssistants ? '/user' : ''}/${bot.id}/${bot.name}'
.toLowerCase(),
url:
'${isAssistants ? '/user/${bot.responseType}' : ''}/${bot.id}/${bot.name}'
.toLowerCase(),
message: message,
chatId: chatId,
file: uploadedFile,

View File

@ -51,23 +51,30 @@ class _BotAssistantsPageState extends State<BotAssistantsPage> {
(BuildContext context, BotAssistantsState state, Widget? child) =>
CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Center(
child: Padding(
padding: const EdgeInsets.only(top: 32, bottom: 24),
child: DidvanText(
'انتخاب بات‌ها',
fontSize: 20,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.checkFav,
),
),
// SliverToBoxAdapter(
// child: Center(
// child: Padding(
// padding: const EdgeInsets.only(top: 32, bottom: 24),
// child: DidvanText(
// 'انتخاب بات‌ها',
// fontSize: 20,
// fontWeight: FontWeight.bold,
// color: Theme.of(context).colorScheme.checkFav,
// ),
// ),
// ),
// ),
const SliverToBoxAdapter(
child: SizedBox(
height: 32,
),
),
if (state.appState != AppState.failed)
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.only(bottom: 12.0),
padding: const EdgeInsets.only(
bottom: 12.0,
),
child: switchAssistants(context, state),
),
),
@ -273,6 +280,7 @@ class _BotAssistantsPageState extends State<BotAssistantsPage> {
arguments: assistants.id);
},
child: const Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(
DidvanIcons.user_edit_light,
@ -348,6 +356,7 @@ class _BotAssistantsPageState extends State<BotAssistantsPage> {
Expanded(
child: InkWell(
onTap: () {
if (state.appState == AppState.busy) return;
state.isMyAssistants = true;
state.getMyAssissmant();
state.update();
@ -390,6 +399,7 @@ class _BotAssistantsPageState extends State<BotAssistantsPage> {
Expanded(
child: InkWell(
onTap: () {
if (state.appState == AppState.busy) return;
state.isMyAssistants = false;
state.getGlobalAssissmant();
state.update();

View File

@ -29,8 +29,10 @@ import 'package:didvan/views/widgets/marquee_text.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
@ -59,8 +61,6 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
@override
void initState() {
super.initState();
context.read<CreateBotAssistantsState>().getImageToolsBots();
}
void onConfirm(CreateBotAssistantsState state, int selectedBotId) async {
@ -137,7 +137,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
CreateBotAssistantsState state, Widget? child) {
int selectedBotId = state.selectedBotType == 'text'
? state.allBots.first.id!
: context.read<CreateBotAssistantsState>().imageBots.first.id!;
: state.imageBots.first.id!;
return state.loading
? Center(
child: SpinKitThreeBounce(
@ -159,7 +159,9 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
closedHeaderPadding: const EdgeInsets.all(12),
items: state.botModels,
enabled: state.assistant == null,
initialItem: state.botModels[0],
initialItem: state.selectedBotType == 'text'
? state.botModels.first
: state.botModels.last,
hideSelectedFieldWhenExpanded: false,
decoration: CustomDropdownDecoration(
listItemDecoration: ListItemDecoration(
@ -170,7 +172,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
),
closedBorder: Border.all(color: Colors.grey),
closedFillColor:
Colors.grey.shade100.withOpacity(0.1),
Theme.of(context).colorScheme.surface,
expandedFillColor:
Theme.of(context).colorScheme.surface),
// hintText: "انتخاب کنید",
@ -178,6 +180,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
final index = state.botModels.indexOf(value!);
state.selectedBotType =
index == 0 ? 'text' : 'image';
state.update();
},
),
),
@ -211,9 +214,27 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
state.image.value = null;
return;
}
state.image.value =
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) {
state.image.value = XFile(file.path);
}
}
},
),
img != null
@ -314,8 +335,8 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
key: _formDescKey,
child: DidvanTextField(
initialValue: state.desc,
// hintText:
// 'به ربات خود بگویید که چگونه رفتار کند و چگونه به پیام‌های کاربر پاسخ دهد. سعی کنید تا حد امکان واضح و مشخص باشید.',
hintText:
'توضیح دهید چه کارهایی از این ربات بر می‌آید.',
textInputType: TextInputType.multiline,
minLine: 4,
maxLine: 4,
@ -374,8 +395,6 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
),
closedBorder:
Border.all(color: Colors.grey),
closedFillColor:
Colors.grey.shade100.withOpacity(0.1),
expandedFillColor: Theme.of(context)
.colorScheme
.surface),
@ -394,7 +413,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
child: DidvanTextField(
initialValue: state.prompt,
hintText:
'به ربات خود بگویید که چگونه رفتار کند و چگونه به پیام‌های کاربر پاسخ دهد. سعی کنید تا حد امکان واضح و مشخص باشید.',
'به ربات خود بگویید که چگونه رفتار کند و چگونه به پیام‌های کاربر پاسخ دهد. سعی کنید تا حد امکان پیام واضح و مشخص باشد.',
textInputType: TextInputType.multiline,
minLine: 6,
maxLine: 6,
@ -618,6 +637,96 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
const SizedBox(
height: 24,
),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
title(
text: 'لینک وب سایت',
isRequired: false),
Row(
children: [
if (state.countOfLink.length > 1)
DidvanIconButton(
icon: CupertinoIcons
.minus_circle_fill,
onPressed: () {
state.countOfLink
.removeLast();
state.update();
},
),
if (state.countOfLink.length != 3)
DidvanIconButton(
icon: CupertinoIcons
.plus_circle_fill,
onPressed: () {
if (state.countOfLink.last
.isNotEmpty) {
state.countOfLink.add('');
state.update();
}
},
),
],
)
],
),
ListView.builder(
shrinkWrap: true,
itemCount: state.countOfLink.length,
physics:
const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => Column(
children: [
DidvanTextField(
onChanged: (value) {
state.countOfLink[index] = value;
if (state.countOfLink[index]
.isEmpty &&
state.countOfLink.length !=
1) {
state.countOfLink
.removeAt(index);
}
state.update();
},
// validator: (value) {},
hintText:
'https://www.weforum.org/agenda/2024/08',
),
const SizedBox(
height: 8,
),
],
),
),
Row(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Icon(
DidvanIcons.info_circle_light,
color: Theme.of(context)
.colorScheme
.caption,
),
const SizedBox(width: 4),
Expanded(
child: DidvanText(
'دستیار شما با استناد بر اطلاعات ارائه شده در پایگاه دانش، پیام کاربران را ارزیابی می‌کند.',
textAlign: TextAlign.right,
fontSize: 12,
color: Theme.of(context)
.colorScheme
.caption,
),
),
],
),
const SizedBox(
height: 24,
),
],
),
// title(text: 'لینک یوتیوب', isRequired: false),
@ -638,86 +747,6 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
// const SizedBox(
// height: 24,
// ),
Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
title(
text: 'لینک وب سایت', isRequired: false),
Row(
children: [
if (state.countOfLink.length > 1)
DidvanIconButton(
icon:
CupertinoIcons.minus_circle_fill,
onPressed: () {
state.countOfLink.removeLast();
state.update();
},
),
if (state.countOfLink.length != 3)
DidvanIconButton(
icon: CupertinoIcons.plus_circle_fill,
onPressed: () {
if (state
.countOfLink.last.isNotEmpty) {
state.countOfLink.add('');
state.update();
}
},
),
],
)
],
),
ListView.builder(
shrinkWrap: true,
itemCount: state.countOfLink.length,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) => Column(
children: [
DidvanTextField(
onChanged: (value) {
state.countOfLink[index] = value;
if (state.countOfLink[index].isEmpty &&
state.countOfLink.length != 1) {
state.countOfLink.removeAt(index);
}
state.update();
},
// validator: (value) {},
hintText:
'https://www.weforum.org/agenda/2024/08',
),
const SizedBox(
height: 8,
),
],
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(
DidvanIcons.info_circle_light,
color:
Theme.of(context).colorScheme.caption,
),
const SizedBox(width: 4),
Expanded(
child: DidvanText(
'دستیار شما با استناد بر اطلاعات ارائه شده در پایگاه دانش، پیام کاربران را ارزیابی می‌کند.',
textAlign: TextAlign.right,
fontSize: 12,
color:
Theme.of(context).colorScheme.caption,
),
),
],
),
const SizedBox(
height: 24,
),
],
),
Row(
@ -870,6 +899,9 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
Container botRow(BotsModel bot, BuildContext context) {
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: DesignConfig.highBorderRadius),
child: Row(
children: [
SkeletonImage(

View File

@ -37,7 +37,7 @@ class CreateBotAssistantsState extends CoreProvier {
String selectedBotType = 'text';
bool isPrivate = true;
void getImageToolsBots() async {
Future getImageToolsBots() async {
loadingImageBots = true;
final service = RequestService(
@ -91,7 +91,7 @@ class CreateBotAssistantsState extends CoreProvier {
Future getAnAssistant({required final int id}) async {
loading = true;
update();
await getImageToolsBots();
final service = RequestService(
RequestHelper.getAssistant(id),
);
@ -113,15 +113,15 @@ class CreateBotAssistantsState extends CoreProvier {
}
}
countOfLink = assistant!.websites ?? [''];
selectedBotType = assistant!.type ?? 'text';
final list = selectedBotType == 'text' ? allBots : imageBots;
final list = [...allBots, ...imageBots];
for (var bot in list) {
if (bot.id == assistant!.botId) {
initialBot = bot;
break;
}
}
selectedBotType = initialBot?.responseType ?? 'text';
}
appState = AppState.idle;
loading = false;

View File

@ -23,205 +23,214 @@ class _InfoPageState extends State<InfoPage> {
withActions: false,
onBack: () => Navigator.pop(context),
),
body: SingleChildScrollView(
child: Column(
children: [
Center(
child: Padding(
padding: const EdgeInsets.only(top: 32, bottom: 24),
child: DidvanText(
'آموزش پرامپت نویسی اصولی',
fontSize: 20,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.checkFav,
),
),
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: ClipRRect(
borderRadius: DesignConfig.lowBorderRadius,
child: ChatVideoPlayer(
src:
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
showOptions: true,
),
),
),
Padding(
padding: const EdgeInsets.all(20.0),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
DidvanText(
'آنچه در این ویدیو خواهید دید:',
fontSize: 16,
Center(
child: Padding(
padding: const EdgeInsets.only(top: 32, bottom: 24),
child: DidvanText(
'آموزش پرامپت نویسی اصولی',
fontSize: 20,
fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.checkFav,
),
],
),
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: ClipRRect(
borderRadius: DesignConfig.lowBorderRadius,
child: ChatVideoPlayer(
src:
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
showOptions: true,
),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
const Row(
children: [
Container(
margin: const EdgeInsets.only(left: 8),
decoration: const ShapeDecoration(
shape: CircleBorder(
side: BorderSide(
width: 3, color: Colors.black)),
),
),
const DidvanText(
'انتخاب کلمات کلیدی مناسب',
DidvanText(
'آنچه در این ویدیو خواهید دید:',
fontSize: 16,
fontWeight: FontWeight.bold,
),
],
),
const SizedBox(
height: 4,
),
Row(
children: [
Container(
margin: const EdgeInsets.only(left: 8),
decoration: const ShapeDecoration(
shape: CircleBorder(
side: BorderSide(
width: 3, color: Colors.black)),
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Row(
children: [
Container(
margin: const EdgeInsets.only(left: 8),
decoration: const ShapeDecoration(
shape: CircleBorder(
side: BorderSide(
width: 3, color: Colors.black)),
),
),
const DidvanText(
'انتخاب کلمات کلیدی مناسب',
fontSize: 14,
),
],
),
),
const DidvanText(
'ساختار و قالب‌بندی پرامپت‌ها',
fontSize: 16,
fontWeight: FontWeight.bold,
),
],
),
const SizedBox(
height: 4,
),
Row(
children: [
Container(
margin: const EdgeInsets.only(left: 8),
decoration: const ShapeDecoration(
shape: CircleBorder(
side: BorderSide(
width: 3, color: Colors.black)),
const SizedBox(
height: 4,
),
),
const DidvanText(
'تعیین سبک و استایل در پرامپت',
fontSize: 16,
fontWeight: FontWeight.bold,
),
],
),
const SizedBox(
height: 4,
),
Row(
children: [
Container(
margin: const EdgeInsets.only(left: 8),
decoration: const ShapeDecoration(
shape: CircleBorder(
side: BorderSide(
width: 3, color: Colors.black)),
Row(
children: [
Container(
margin: const EdgeInsets.only(left: 8),
decoration: const ShapeDecoration(
shape: CircleBorder(
side: BorderSide(
width: 3, color: Colors.black)),
),
),
const DidvanText(
'ساختار و قالب‌بندی پرامپت‌ها',
fontSize: 14,
),
],
),
),
const DidvanText(
'استفاده از جزییات و صفت‌ها',
fontSize: 16,
fontWeight: FontWeight.bold,
),
],
),
const SizedBox(
height: 4,
),
Row(
children: [
Container(
margin: const EdgeInsets.only(left: 8),
decoration: const ShapeDecoration(
shape: CircleBorder(
side: BorderSide(
width: 3, color: Colors.black)),
const SizedBox(
height: 4,
),
),
const DidvanText(
'بهینه‌سازی پرامپت‌ها و تکرار',
fontSize: 16,
fontWeight: FontWeight.bold,
),
],
),
const SizedBox(
height: 4,
),
Row(
children: [
Container(
margin: const EdgeInsets.only(left: 8),
decoration: const ShapeDecoration(
shape: CircleBorder(
side: BorderSide(
width: 3, color: Colors.black)),
Row(
children: [
Container(
margin: const EdgeInsets.only(left: 8),
decoration: const ShapeDecoration(
shape: CircleBorder(
side: BorderSide(
width: 3, color: Colors.black)),
),
),
const DidvanText(
'تعیین سبک و استایل در پرامپت',
fontSize: 14,
),
],
),
),
const DidvanText(
'اشتباهات رایج در پرامپت‌نویسی',
fontSize: 16,
fontWeight: FontWeight.bold,
),
],
),
const SizedBox(
height: 4,
),
Row(
children: [
Container(
margin: const EdgeInsets.only(left: 8),
decoration: const ShapeDecoration(
shape: CircleBorder(
side: BorderSide(
width: 3, color: Colors.black)),
),
),
const DidvanText(
'استفاده از جزییات و صفت‌ها',
fontSize: 14,
),
],
),
const SizedBox(
height: 4,
),
Row(
children: [
Container(
margin: const EdgeInsets.only(left: 8),
decoration: const ShapeDecoration(
shape: CircleBorder(
side: BorderSide(
width: 3, color: Colors.black)),
),
),
const DidvanText(
'بهینه‌سازی پرامپت‌ها و تکرار',
fontSize: 14,
),
],
),
const SizedBox(
height: 4,
),
Row(
children: [
Container(
margin: const EdgeInsets.only(left: 8),
decoration: const ShapeDecoration(
shape: CircleBorder(
side: BorderSide(
width: 3, color: Colors.black)),
),
),
const DidvanText(
'اشتباهات رایج در پرامپت‌نویسی',
fontSize: 14,
),
],
),
],
),
)
],
),
),
],
),
),
),
Column(
children: [
const Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: DidvanDivider(
verticalPadding: 12,
height: 4,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
DidvanIcons.support_solid,
size: 32,
),
const SizedBox(
width: 8,
),
const DidvanText('هنوز سوالی دارید؟'),
TextButton(
onPressed: () {
Navigator.of(context).pushNamed(
Routes.direct,
arguments: {'type': 'پشتیبانی اپلیکیشن'},
);
},
child: const DidvanText(
' پیام به پشتیبانی',
color: Color(0xff007EA7),
),
)
],
),
),
const Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0),
child: DidvanDivider(
verticalPadding: 12,
height: 4,
const SizedBox(
height: 32,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Icon(
DidvanIcons.support_solid,
size: 32,
),
const SizedBox(
width: 8,
),
const DidvanText('هنوز سوالی دارید؟'),
TextButton(
onPressed: () {
Navigator.of(context).pushNamed(
Routes.direct,
arguments: {'type': 'پشتیبانی اپلیکیشن'},
);
},
child: const DidvanText(
' پیام به پشتیبانی',
color: Color(0xff007EA7),
),
)
],
)
],
),
],
)
],
),
);
}

View File

@ -15,7 +15,6 @@ 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:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:provider/provider.dart';
@ -73,21 +72,6 @@ class _HoshanDrawerState extends State<HoshanDrawer> {
Expanded(
child: Column(
children: [
drawerBtn(
icon: Icons.handshake_rounded,
text: 'ساخت دستیار شخصی',
crossAxisAlignment: CrossAxisAlignment.start,
enable: false),
const DidvanDivider(),
drawerBtn(
icon: CupertinoIcons.doc_text_search,
text: 'جستجو در مدل‌ها',
click: () {
ActionSheetUtils(context).botsDialogSelect(
context: context, state: state);
homeScaffKey.currentState!.closeDrawer();
},
enable: false),
const DidvanDivider(),
drawerBtn(
icon: DidvanIcons.chats_regular,

View File

@ -9,6 +9,7 @@ import 'package:didvan/views/podcasts/studio_details/widgets/studio_details_widg
import 'package:didvan/views/widgets/bookmark_button.dart';
import 'package:didvan/views/widgets/didvan/app_bar.dart';
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
import 'package:didvan/views/widgets/video/primary_controls.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:didvan/routes/routes.dart';
@ -26,14 +27,7 @@ class StudioDetails extends StatefulWidget {
class _StudioDetailsState extends State<StudioDetails> {
int _currentlyPlayingId = 0;
late VideoPlayerController _videoPlayerController;
late final ChewieController _chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
autoPlay: true,
looping: true,
aspectRatio: 16 / 9,
materialProgressColors: ChewieProgressColors(
playedColor: Theme.of(context).colorScheme.title,
handleColor: Theme.of(context).colorScheme.title));
@override
void initState() {
final state = context.read<StudioDetailsState>();
@ -66,6 +60,15 @@ class _StudioDetailsState extends State<StudioDetails> {
@override
Widget build(BuildContext context) {
late final ChewieController _chewieController = ChewieController(
videoPlayerController: _videoPlayerController,
customControls: const PrimaryControls(),
autoPlay: true,
looping: true,
aspectRatio: 16 / 9,
materialProgressColors: ChewieProgressColors(
playedColor: Theme.of(context).colorScheme.title,
handleColor: Theme.of(context).colorScheme.title));
final d = MediaQuery.of(context);
return Consumer<StudioDetailsState>(
builder: (context, state, child) => StateHandler<StudioDetailsState>(
@ -151,7 +154,7 @@ class _StudioDetailsState extends State<StudioDetails> {
void dispose() {
_videoPlayerController.pause();
_videoPlayerController.dispose();
_chewieController.dispose();
// _chewieController.dispose();
super.dispose();
}
}

View File

@ -0,0 +1,354 @@
import 'dart:async';
import 'package:animated_custom_dropdown/custom_dropdown.dart';
import 'package:chewie/chewie.dart';
import 'package:didvan/config/design_config.dart';
import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/utils/date_time.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/video/play_btn_animation.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class PrimaryControls extends StatefulWidget {
const PrimaryControls({super.key});
@override
State<PrimaryControls> createState() => _PrimaryControlsState();
}
class _PrimaryControlsState extends State<PrimaryControls> {
late ChewieController chewieController;
bool isAnimating = false;
// bool isSpeedMenuOpen = false;
double opacity = 1;
Timer? _hideControlsTimer;
ValueNotifier<Duration> position = ValueNotifier(Duration.zero);
final GlobalKey<PopupMenuButtonState> _popupMenuKey = GlobalKey();
final GlobalKey<PopupMenuButtonState> _popupMenuSpeedKey = GlobalKey();
@override
void didChangeDependencies() {
super.didChangeDependencies();
chewieController = ChewieController.of(context);
chewieController.videoPlayerController.addListener(
() {
position.value = chewieController.videoPlayerController.value.position;
},
);
}
void _startHideControlsTimer() {
_hideControlsTimer?.cancel();
_hideControlsTimer = Timer(const Duration(seconds: 5), () {
setState(() {
opacity = 0;
isAnimating = false;
// if (isSpeedMenuOpen) {
// Navigator.pop(context);
// }
});
});
}
@override
void dispose() {
_hideControlsTimer?.cancel(); // Clean up the timer
super.dispose();
}
void _handlePlay() {
{
setState(() {
if (chewieController.isPlaying) {
chewieController.pause();
opacity = 1;
} else {
chewieController.play();
opacity = 0;
}
isAnimating = true;
isAnimating = true;
});
_startHideControlsTimer(); // Restart the timer on tap
}
}
@override
Widget build(BuildContext context) {
return Directionality(
textDirection: TextDirection.ltr,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 400),
opacity: opacity,
child: InkWell(
onTap: () {
if (opacity == 0) {
setState(() {
opacity = 1;
});
_startHideControlsTimer(); // Restart the timer on tap
} else {
setState(() {
opacity = 0;
});
}
},
child: IgnorePointer(
ignoring: opacity == 0,
child: Stack(
children: [
Positioned(
top: 0,
right: 0,
left: 0,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 12),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.black,
Colors.black87,
Colors.black54,
Colors.black45,
Colors.black26,
Color.fromARGB(10, 0, 0, 0)
])),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(
width: 120,
child: CustomDropdown<double>(
closedHeaderPadding: EdgeInsets.zero,
itemsListPadding: EdgeInsets.zero,
items: const [
0.25,
0.5,
0.75,
1,
1.25,
1.5,
1.75,
2
],
initialItem: 1,
listItemPadding: EdgeInsets.zero,
expandedHeaderPadding: EdgeInsets.zero,
hideSelectedFieldWhenExpanded: false,
// overlayHeight:
// chewieController.isFullScreen ? null : 54 * 8,
decoration: const CustomDropdownDecoration(
closedSuffixIcon: SizedBox(),
closedFillColor: Colors.transparent,
expandedBorderRadius:
DesignConfig.lowBorderRadius),
// hintText: "سرعت ویدیو",
listItemBuilder:
(context, item, isSelected, onItemSelect) {
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 8),
child: Column(
children: [
DidvanText('x$item'),
if (item != 2)
const DidvanDivider(
verticalPadding: 8,
)
],
));
},
headerBuilder: (context, selectedItem, enabled) =>
const Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Icon(
Icons.more_vert_rounded,
size: 32,
color: Colors.white,
),
],
),
hintBuilder: (context, hint, enabled) =>
const SizedBox(),
onChanged: (value) async {
// isSpeedMenuOpen = false;
await chewieController.videoPlayerController
.setPlaybackSpeed(value!);
_startHideControlsTimer();
},
),
),
],
),
)),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 12, vertical: 12),
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.black,
Colors.black87,
Colors.black54,
Colors.black45,
Colors.black26,
Color.fromARGB(10, 0, 0, 0)
])),
child: Row(
children: [
// _buildPlayPause(),
_buildProgressIndicator(),
_buildFullScreenToggle(),
],
),
),
),
Positioned.fill(
child: Center(
child: InkWell(
onTap: _handlePlay,
child: PlayBtnAnimation(
alwaysAnimate: false,
isAnimating: isAnimating,
onEnd: () => setState(
() => isAnimating = false,
),
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.black.withOpacity(0.4)),
child: Icon(
chewieController.isPlaying
? CupertinoIcons.pause_fill
: CupertinoIcons.play_fill,
color: Colors.white,
size: 32,
),
),
),
)))
],
),
),
),
),
);
}
Widget _buildPlayPause() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: InkWell(
onTap: _handlePlay,
child: PlayBtnAnimation(
alwaysAnimate: true,
isAnimating: isAnimating,
onEnd: () => setState(
() => isAnimating = false,
),
child: Icon(
chewieController.isPlaying
? CupertinoIcons.pause_fill
: CupertinoIcons.play_fill,
color: Colors.white,
size: 24,
),
),
),
);
}
Widget _buildProgressIndicator() {
return Expanded(
child: ValueListenableBuilder<Duration>(
valueListenable: position,
builder: (context, p, _) {
Duration duration =
chewieController.videoPlayerController.value.duration;
return Column(
children: [
SliderTheme(
data: SliderThemeData(
trackHeight: 2,
// thumbColor: Colors.transparent,
overlayShape: SliderComponentShape.noOverlay,
thumbShape: const RoundSliderThumbShape(
// elevation: 0,
// pressedElevation: 0,
enabledThumbRadius: 8)),
child: Slider(
min: 0,
max: duration.inMilliseconds.toDouble(),
value: p.inMilliseconds.toDouble(),
onChanged: (value) async {
await chewieController.pause();
position.value = Duration(milliseconds: value.round());
_startHideControlsTimer();
},
onChangeEnd: (value) async {
await chewieController
.seekTo(Duration(milliseconds: value.round()));
await chewieController.play();
setState(() {});
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DidvanText(
DateTimeUtils.normalizeTimeDuration(p),
color: Colors.white,
fontSize: 16,
),
DidvanText(
DateTimeUtils.normalizeTimeDuration(duration),
color: Colors.white,
fontSize: 16,
)
],
)
],
);
},
),
);
}
Widget _buildFullScreenToggle() {
return Padding(
padding: const EdgeInsets.fromLTRB(12, 0, 8, 12),
child: InkWell(
onTap: () => setState(() {
chewieController.toggleFullScreen();
_startHideControlsTimer(); // Restart the timer on tap
}),
child: Icon(
chewieController.isFullScreen
? Icons.fullscreen_exit
: Icons.fullscreen,
color: Colors.white,
size: 30,
),
),
);
}
}