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

View File

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

View File

@ -51,23 +51,30 @@ class _BotAssistantsPageState extends State<BotAssistantsPage> {
(BuildContext context, BotAssistantsState state, Widget? child) => (BuildContext context, BotAssistantsState state, Widget? child) =>
CustomScrollView( CustomScrollView(
slivers: [ slivers: [
SliverToBoxAdapter( // SliverToBoxAdapter(
child: Center( // child: Center(
child: Padding( // child: Padding(
padding: const EdgeInsets.only(top: 32, bottom: 24), // padding: const EdgeInsets.only(top: 32, bottom: 24),
child: DidvanText( // child: DidvanText(
'انتخاب بات‌ها', // 'انتخاب بات‌ها',
fontSize: 20, // fontSize: 20,
fontWeight: FontWeight.bold, // fontWeight: FontWeight.bold,
color: Theme.of(context).colorScheme.checkFav, // color: Theme.of(context).colorScheme.checkFav,
), // ),
), // ),
// ),
// ),
const SliverToBoxAdapter(
child: SizedBox(
height: 32,
), ),
), ),
if (state.appState != AppState.failed) if (state.appState != AppState.failed)
SliverToBoxAdapter( SliverToBoxAdapter(
child: Padding( child: Padding(
padding: const EdgeInsets.only(bottom: 12.0), padding: const EdgeInsets.only(
bottom: 12.0,
),
child: switchAssistants(context, state), child: switchAssistants(context, state),
), ),
), ),
@ -273,6 +280,7 @@ class _BotAssistantsPageState extends State<BotAssistantsPage> {
arguments: assistants.id); arguments: assistants.id);
}, },
child: const Row( child: const Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
Icon( Icon(
DidvanIcons.user_edit_light, DidvanIcons.user_edit_light,
@ -348,6 +356,7 @@ class _BotAssistantsPageState extends State<BotAssistantsPage> {
Expanded( Expanded(
child: InkWell( child: InkWell(
onTap: () { onTap: () {
if (state.appState == AppState.busy) return;
state.isMyAssistants = true; state.isMyAssistants = true;
state.getMyAssissmant(); state.getMyAssissmant();
state.update(); state.update();
@ -390,6 +399,7 @@ class _BotAssistantsPageState extends State<BotAssistantsPage> {
Expanded( Expanded(
child: InkWell( child: InkWell(
onTap: () { onTap: () {
if (state.appState == AppState.busy) return;
state.isMyAssistants = false; state.isMyAssistants = false;
state.getGlobalAssissmant(); state.getGlobalAssissmant();
state.update(); 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/shimmer_placeholder.dart';
import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart'; import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -59,8 +61,6 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
context.read<CreateBotAssistantsState>().getImageToolsBots();
} }
void onConfirm(CreateBotAssistantsState state, int selectedBotId) async { void onConfirm(CreateBotAssistantsState state, int selectedBotId) async {
@ -137,7 +137,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
CreateBotAssistantsState state, Widget? child) { CreateBotAssistantsState state, Widget? child) {
int selectedBotId = state.selectedBotType == 'text' int selectedBotId = state.selectedBotType == 'text'
? state.allBots.first.id! ? state.allBots.first.id!
: context.read<CreateBotAssistantsState>().imageBots.first.id!; : state.imageBots.first.id!;
return state.loading return state.loading
? Center( ? Center(
child: SpinKitThreeBounce( child: SpinKitThreeBounce(
@ -159,7 +159,9 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
closedHeaderPadding: const EdgeInsets.all(12), closedHeaderPadding: const EdgeInsets.all(12),
items: state.botModels, items: state.botModels,
enabled: state.assistant == null, enabled: state.assistant == null,
initialItem: state.botModels[0], initialItem: state.selectedBotType == 'text'
? state.botModels.first
: state.botModels.last,
hideSelectedFieldWhenExpanded: false, hideSelectedFieldWhenExpanded: false,
decoration: CustomDropdownDecoration( decoration: CustomDropdownDecoration(
listItemDecoration: ListItemDecoration( listItemDecoration: ListItemDecoration(
@ -170,7 +172,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
), ),
closedBorder: Border.all(color: Colors.grey), closedBorder: Border.all(color: Colors.grey),
closedFillColor: closedFillColor:
Colors.grey.shade100.withOpacity(0.1), Theme.of(context).colorScheme.surface,
expandedFillColor: expandedFillColor:
Theme.of(context).colorScheme.surface), Theme.of(context).colorScheme.surface),
// hintText: "انتخاب کنید", // hintText: "انتخاب کنید",
@ -178,6 +180,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
final index = state.botModels.indexOf(value!); final index = state.botModels.indexOf(value!);
state.selectedBotType = state.selectedBotType =
index == 0 ? 'text' : 'image'; index == 0 ? 'text' : 'image';
state.update();
}, },
), ),
), ),
@ -211,9 +214,27 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
state.image.value = null; state.image.value = null;
return; return;
} }
state.image.value = final pickedFile =
await MediaService.pickImage( await MediaService.pickImage(
source: ImageSource.gallery); 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 img != null
@ -314,8 +335,8 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
key: _formDescKey, key: _formDescKey,
child: DidvanTextField( child: DidvanTextField(
initialValue: state.desc, initialValue: state.desc,
// hintText: hintText:
// 'به ربات خود بگویید که چگونه رفتار کند و چگونه به پیام‌های کاربر پاسخ دهد. سعی کنید تا حد امکان واضح و مشخص باشید.', 'توضیح دهید چه کارهایی از این ربات بر می‌آید.',
textInputType: TextInputType.multiline, textInputType: TextInputType.multiline,
minLine: 4, minLine: 4,
maxLine: 4, maxLine: 4,
@ -374,8 +395,6 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
), ),
closedBorder: closedBorder:
Border.all(color: Colors.grey), Border.all(color: Colors.grey),
closedFillColor:
Colors.grey.shade100.withOpacity(0.1),
expandedFillColor: Theme.of(context) expandedFillColor: Theme.of(context)
.colorScheme .colorScheme
.surface), .surface),
@ -394,7 +413,7 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
child: DidvanTextField( child: DidvanTextField(
initialValue: state.prompt, initialValue: state.prompt,
hintText: hintText:
'به ربات خود بگویید که چگونه رفتار کند و چگونه به پیام‌های کاربر پاسخ دهد. سعی کنید تا حد امکان واضح و مشخص باشید.', 'به ربات خود بگویید که چگونه رفتار کند و چگونه به پیام‌های کاربر پاسخ دهد. سعی کنید تا حد امکان پیام واضح و مشخص باشد.',
textInputType: TextInputType.multiline, textInputType: TextInputType.multiline,
minLine: 6, minLine: 6,
maxLine: 6, maxLine: 6,
@ -618,6 +637,96 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
const SizedBox( const SizedBox(
height: 24, 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), // title(text: 'لینک یوتیوب', isRequired: false),
@ -638,86 +747,6 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
// const SizedBox( // const SizedBox(
// height: 24, // 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( Row(
@ -870,6 +899,9 @@ class _CreateBotAssistantsPageState extends State<CreateBotAssistantsPage> {
Container botRow(BotsModel bot, BuildContext context) { Container botRow(BotsModel bot, BuildContext context) {
return Container( return Container(
alignment: Alignment.center, alignment: Alignment.center,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: DesignConfig.highBorderRadius),
child: Row( child: Row(
children: [ children: [
SkeletonImage( SkeletonImage(

View File

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

View File

@ -23,7 +23,12 @@ class _InfoPageState extends State<InfoPage> {
withActions: false, withActions: false,
onBack: () => Navigator.pop(context), onBack: () => Navigator.pop(context),
), ),
body: SingleChildScrollView( body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: SingleChildScrollView(
physics: const BouncingScrollPhysics(),
child: Column( child: Column(
children: [ children: [
Center( Center(
@ -77,8 +82,7 @@ class _InfoPageState extends State<InfoPage> {
), ),
const DidvanText( const DidvanText(
'انتخاب کلمات کلیدی مناسب', 'انتخاب کلمات کلیدی مناسب',
fontSize: 16, fontSize: 14,
fontWeight: FontWeight.bold,
), ),
], ],
), ),
@ -97,8 +101,7 @@ class _InfoPageState extends State<InfoPage> {
), ),
const DidvanText( const DidvanText(
'ساختار و قالب‌بندی پرامپت‌ها', 'ساختار و قالب‌بندی پرامپت‌ها',
fontSize: 16, fontSize: 14,
fontWeight: FontWeight.bold,
), ),
], ],
), ),
@ -117,8 +120,7 @@ class _InfoPageState extends State<InfoPage> {
), ),
const DidvanText( const DidvanText(
'تعیین سبک و استایل در پرامپت', 'تعیین سبک و استایل در پرامپت',
fontSize: 16, fontSize: 14,
fontWeight: FontWeight.bold,
), ),
], ],
), ),
@ -137,8 +139,7 @@ class _InfoPageState extends State<InfoPage> {
), ),
const DidvanText( const DidvanText(
'استفاده از جزییات و صفت‌ها', 'استفاده از جزییات و صفت‌ها',
fontSize: 16, fontSize: 14,
fontWeight: FontWeight.bold,
), ),
], ],
), ),
@ -157,8 +158,7 @@ class _InfoPageState extends State<InfoPage> {
), ),
const DidvanText( const DidvanText(
'بهینه‌سازی پرامپت‌ها و تکرار', 'بهینه‌سازی پرامپت‌ها و تکرار',
fontSize: 16, fontSize: 14,
fontWeight: FontWeight.bold,
), ),
], ],
), ),
@ -177,8 +177,7 @@ class _InfoPageState extends State<InfoPage> {
), ),
const DidvanText( const DidvanText(
'اشتباهات رایج در پرامپت‌نویسی', 'اشتباهات رایج در پرامپت‌نویسی',
fontSize: 16, fontSize: 14,
fontWeight: FontWeight.bold,
), ),
], ],
), ),
@ -188,6 +187,12 @@ class _InfoPageState extends State<InfoPage> {
], ],
), ),
), ),
],
),
),
),
Column(
children: [
const Padding( const Padding(
padding: EdgeInsets.symmetric(horizontal: 20.0), padding: EdgeInsets.symmetric(horizontal: 20.0),
child: DidvanDivider( child: DidvanDivider(
@ -219,10 +224,14 @@ class _InfoPageState extends State<InfoPage> {
), ),
) )
], ],
),
const SizedBox(
height: 32,
),
],
) )
], ],
), ),
),
); );
} }
} }

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/didvan/text.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:didvan/views/widgets/skeleton_image.dart'; import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart'; import 'package:flutter_svg/svg.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -73,21 +72,6 @@ class _HoshanDrawerState extends State<HoshanDrawer> {
Expanded( Expanded(
child: Column( child: Column(
children: [ 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(), const DidvanDivider(),
drawerBtn( drawerBtn(
icon: DidvanIcons.chats_regular, 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/bookmark_button.dart';
import 'package:didvan/views/widgets/didvan/app_bar.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/state_handlers/state_handler.dart';
import 'package:didvan/views/widgets/video/primary_controls.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:didvan/routes/routes.dart'; import 'package:didvan/routes/routes.dart';
@ -26,14 +27,7 @@ class StudioDetails extends StatefulWidget {
class _StudioDetailsState extends State<StudioDetails> { class _StudioDetailsState extends State<StudioDetails> {
int _currentlyPlayingId = 0; int _currentlyPlayingId = 0;
late VideoPlayerController _videoPlayerController; 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 @override
void initState() { void initState() {
final state = context.read<StudioDetailsState>(); final state = context.read<StudioDetailsState>();
@ -66,6 +60,15 @@ class _StudioDetailsState extends State<StudioDetails> {
@override @override
Widget build(BuildContext context) { 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); final d = MediaQuery.of(context);
return Consumer<StudioDetailsState>( return Consumer<StudioDetailsState>(
builder: (context, state, child) => StateHandler<StudioDetailsState>( builder: (context, state, child) => StateHandler<StudioDetailsState>(
@ -151,7 +154,7 @@ class _StudioDetailsState extends State<StudioDetails> {
void dispose() { void dispose() {
_videoPlayerController.pause(); _videoPlayerController.pause();
_videoPlayerController.dispose(); _videoPlayerController.dispose();
_chewieController.dispose(); // _chewieController.dispose();
super.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,
),
),
);
}
}