fixed some bugs

This commit is contained in:
Mr.Jebelli 2025-11-17 16:20:22 +03:30
parent 762444e16e
commit 1871b8dd7e
37 changed files with 842 additions and 389 deletions

View File

@ -165,9 +165,9 @@ class AppInitializer {
if (brightness != null) { if (brightness != null) {
final themeMode = final themeMode =
brightness == 'dark' ? ThemeMode.dark : ThemeMode.light; brightness == 'dark' ? ThemeMode.dark : ThemeMode.light;
final fontFamily = await StorageService.getValue(key: 'fontFamily'); final fontFamily = await StorageService.getValue(key: 'fontFamily') ?? 'IranYekan';
final fontScale = double.parse( final fontScale = double.parse(
await StorageService.getValue(key: 'fontSizeScale'), await StorageService.getValue(key: 'fontSizeScale') ?? '1',
); );
return SettingsData( return SettingsData(
fontFamily: fontFamily, fontFamily: fontFamily,

View File

@ -160,7 +160,7 @@ class _HistoryAiChatPageState extends State<HistoryAiChatPage> {
centerEmptyState: false, centerEmptyState: false,
emptyState: EmptyState( emptyState: EmptyState(
asset: Assets.emptyResult, asset: Assets.emptyResult,
height: 550, height: 400,
title: 'لیست خالی است', title: 'لیست خالی است',
titleColor: const Color.fromARGB(255, 2, 126, 167), titleColor: const Color.fromARGB(255, 2, 126, 167),
subtitle: subtitle:
@ -433,32 +433,34 @@ class _HistoryAiChatPageState extends State<HistoryAiChatPage> {
MainAxisAlignment MainAxisAlignment
.spaceBetween, .spaceBetween,
children: [ children: [
Column( Expanded(
mainAxisAlignment: child: Column(
MainAxisAlignment mainAxisAlignment:
.start, MainAxisAlignment
crossAxisAlignment: .start,
CrossAxisAlignment crossAxisAlignment:
.start, CrossAxisAlignment
children: [ .start,
DidvanText( children: [
chat.title DidvanText(
.toString(), chat.title
maxLines: 1, .toString(),
overflow: maxLines: 1,
TextOverflow overflow:
.ellipsis, TextOverflow
), .ellipsis,
DidvanText( ),
DateTime.parse(chat DidvanText(
.updatedAt DateTime.parse(chat
.toString()) .updatedAt
.toPersianDateStr(), .toString())
style: .toPersianDateStr(),
const TextStyle( style:
fontSize: const TextStyle(
12)), fontSize:
], 12)),
],
),
), ),
const SizedBox( const SizedBox(
width: 10, width: 10,

View File

@ -254,7 +254,7 @@ class _AiSectionPageState extends State<AiSectionPage>
child: FadeTransition( child: FadeTransition(
opacity: _fadeAnimation, opacity: _fadeAnimation,
child: SizedBox( child: SizedBox(
height: 500, height: 400,
child: _buildAiGrid(context, aiState), child: _buildAiGrid(context, aiState),
), ),
), ),

View File

@ -46,12 +46,14 @@ class _PasswordInputState extends State<PasswordInput> {
const SizedBox( const SizedBox(
height: 15, height: 15,
), ),
GestureDetector( Center(
onTap: () => state.currentPageIndex++, child: GestureDetector(
child: DidvanText( onTap: () => state.currentPageIndex++,
'فراموشی رمز عبور', child: DidvanText(
style: Theme.of(context).textTheme.titleSmall, 'فراموشی رمز عبور',
color: Theme.of(context).colorScheme.primary, style: Theme.of(context).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.normal),
color: Theme.of(context).colorScheme.primary,
),
), ),
), ),
const SizedBox( const SizedBox(
@ -68,7 +70,7 @@ class _PasswordInputState extends State<PasswordInput> {
height: 20, height: 20,
), ),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 60), padding: const EdgeInsets.symmetric(horizontal: 20),
child: RichText( child: RichText(
textAlign: TextAlign.center, textAlign: TextAlign.center,
text: TextSpan( text: TextSpan(

View File

@ -72,7 +72,7 @@ class _UsernameInputState extends State<UsernameInput> {
height: 20, height: 20,
), ),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 60), padding: const EdgeInsets.symmetric(horizontal: 20),
child: RichText( child: RichText(
textAlign: TextAlign.center, textAlign: TextAlign.center,
text: TextSpan( text: TextSpan(

View File

@ -239,7 +239,7 @@ class _VerificationState extends State<Verification> {
height: 24, height: 24,
), ),
Padding( Padding(
padding: const EdgeInsets.symmetric(horizontal: 60), padding: const EdgeInsets.symmetric(horizontal: 20),
child: RichText( child: RichText(
textAlign: TextAlign.center, textAlign: TextAlign.center,
text: TextSpan( text: TextSpan(

View File

@ -46,11 +46,11 @@ class AuthenticationLayout extends StatelessWidget {
const SizedBox(height: 20), const SizedBox(height: 20),
SvgPicture.asset( SvgPicture.asset(
'lib/assets/images/logos/logo-horizontal-light.svg', 'lib/assets/images/logos/logo-horizontal-light.svg',
height: 90, height: 70,
), ),
Image.asset( Image.asset(
'lib/assets/icons/login_img.png', 'lib/assets/icons/login_img.png',
height: 450, height: 300,
width: double.infinity, width: double.infinity,
fit: BoxFit.fill, fit: BoxFit.fill,
) )
@ -59,14 +59,10 @@ class AuthenticationLayout extends StatelessWidget {
), ),
SliverPadding( SliverPadding(
padding: const EdgeInsets.symmetric(horizontal: 20), padding: const EdgeInsets.symmetric(horizontal: 20),
sliver: SliverFillRemaining( sliver: SliverList(
hasScrollBody: true, delegate: SliverChildListDelegate([
fillOverscroll: true, for (var i = 0; i < children.length; i++) children[i],
child: Column( ]),
children: [
for (var i = 0; i < children.length; i++) children[i],
],
),
), ),
), ),
], ],

View File

@ -50,17 +50,17 @@ class _CommentsState extends State<Comments> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final bottomViewInset = MediaQuery.of(context).viewInsets.bottom; final bottomViewInset = MediaQuery.of(context).viewInsets.bottom;
if (bottomViewInset == 0) { if (bottomViewInset == 10) {
if (_bottomPadding != 0) { if (_bottomPadding != 10) {
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
_bottomPadding = 0; _bottomPadding = 10;
} }
} }
_bottomPadding = bottomViewInset; _bottomPadding = bottomViewInset;
final scaffold = DidvanScaffold( final scaffold = DidvanScaffold(
hidePlayer: true, hidePlayer: true,
physics: const BouncingScrollPhysics(), // physics: const BouncingScrollPhysics(),
backgroundColor: Theme.of(context).colorScheme.surface, backgroundColor: Theme.of(context).colorScheme.surface,
appBarData: _isPage appBarData: _isPage
? AppBarData( ? AppBarData(
@ -69,20 +69,23 @@ class _CommentsState extends State<Comments> {
subtitle: widget.pageData['title'], subtitle: widget.pageData['title'],
) )
: null, : null,
padding: _isPage ? const EdgeInsets.only(bottom: 92) : EdgeInsets.zero, padding: _isPage ? const EdgeInsets.only(bottom: 100) : EdgeInsets.zero,
slivers: [ slivers: [
Consumer<CommentsState>( Consumer<CommentsState>(
builder: (context, state, child) => SliverStateHandler<CommentsState>( builder: (context, state, child) => SliverStateHandler<CommentsState>(
onRetry: state.getComments, onRetry: state.getComments,
state: state, state: state,
itemPadding: const EdgeInsets.only(top: 16, bottom: 16), itemPadding: const EdgeInsets.only(top: 0, bottom: 20),
childCount: state.comments.length, childCount: state.comments.length,
placeholder: const _CommentPlaceholder(), placeholder: const _CommentPlaceholder(),
centerEmptyState: _isPage, centerEmptyState: false,
enableEmptyState: state.comments.isEmpty, enableEmptyState: state.comments.isEmpty,
paddingEmptyState: 0,
emptyState: EmptyState( emptyState: EmptyState(
asset: Assets.emptyChat, asset: Assets.emptyChat,
title: 'اولین نظر را بنویسید...', title: 'لیست خالی است',
titleColor: const Color.fromARGB(255, 0, 126, 167),
subtitle: 'در حال حاضر آیتمی در این بخش ثبت نشده است. هر زمان مورد جدیدی اضافه شود، در اینجا نمایش داده می‌شود.',
), ),
builder: (context, state, index) => Comment( builder: (context, state, index) => Comment(
key: ValueKey( key: ValueKey(
@ -106,7 +109,7 @@ class _CommentsState extends State<Comments> {
Positioned( Positioned(
left: 20, left: 20,
right: 20, right: 20,
bottom: 20, top: 0,
child: CommentMessageBox(focusNode: _focusNode), child: CommentMessageBox(focusNode: _focusNode),
), ),
], ],

View File

@ -255,7 +255,10 @@ class _StaggeredEntryState extends State<StaggeredEntry>
class SwotSection extends StatelessWidget { class SwotSection extends StatelessWidget {
final List<SwotItem> swotItems; final List<SwotItem> swotItems;
const SwotSection({super.key, required this.swotItems}); final double? headerSize;
final double? moreSize;
const SwotSection({super.key, required this.swotItems, this.headerSize, this.moreSize});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -282,7 +285,11 @@ class SwotSection extends StatelessWidget {
const SizedBox(width: 5), const SizedBox(width: 5),
DidvanText( DidvanText(
"ماژول بایدها و نبایدها", "ماژول بایدها و نبایدها",
style: Theme.of(context).textTheme.titleSmall, style: headerSize == null
? Theme.of(context).textTheme.titleSmall
: TextStyle(
fontSize: headerSize,
fontWeight: FontWeight.bold),
color: DesignConfig.isDark color: DesignConfig.isDark
? const Color.fromARGB(255, 0, 125, 166) ? const Color.fromARGB(255, 0, 125, 166)
: const Color.fromARGB(255, 0, 89, 119), : const Color.fromARGB(255, 0, 89, 119),
@ -303,8 +310,11 @@ class SwotSection extends StatelessWidget {
children: [ children: [
DidvanText( DidvanText(
"مشاهده همه", "مشاهده همه",
style: moreSize == null
? Theme.of(context).textTheme.titleSmall
: TextStyle(fontSize: moreSize),
color: Theme.of(context).colorScheme.primary, color: Theme.of(context).colorScheme.primary,
fontWeight: FontWeight.bold, fontWeight: moreSize == null ? FontWeight.bold : FontWeight.normal,
), ),
], ],
), ),
@ -376,7 +386,7 @@ class MainPageSection extends StatelessWidget {
case 'technology': case 'technology':
return 'lib/assets/icons/Technology.svg'; return 'lib/assets/icons/Technology.svg';
case 'risk': case 'risk':
return 'lib/assets/icons/RiskRadar.svg'; //TODO:FIX ICON return 'lib/assets/icons/RiskRadar.svg';
case 'startup': case 'startup':
return 'lib/assets/icons/Startup.svg'; return 'lib/assets/icons/Startup.svg';
case 'survey': case 'survey':
@ -389,7 +399,7 @@ class MainPageSection extends StatelessWidget {
double _calculateSliderHeight() { double _calculateSliderHeight() {
switch (list.type) { switch (list.type) {
case 'monthly': case 'monthly':
return 225; return 150;
case 'news': case 'news':
return 225; return 225;
case 'radar': case 'radar':
@ -415,7 +425,7 @@ class MainPageSection extends StatelessWidget {
switch (list.type) { switch (list.type) {
case 'monthly': case 'monthly':
targetWidth = 350.0; targetWidth = 320.0;
break; break;
case 'news': case 'news':
targetWidth = 265.0; targetWidth = 265.0;
@ -424,7 +434,7 @@ class MainPageSection extends StatelessWidget {
targetWidth = 310.0; targetWidth = 310.0;
break; break;
case 'trend': case 'trend':
targetWidth = 350.0; targetWidth = 320.0;
break; break;
case 'technology': case 'technology':
targetWidth = 250.0; targetWidth = 250.0;
@ -433,7 +443,7 @@ class MainPageSection extends StatelessWidget {
targetWidth = 310.0; targetWidth = 310.0;
break; break;
case 'startup': case 'startup':
targetWidth = 360.0; targetWidth = 330.0;
break; break;
case 'delphi': case 'delphi':
targetWidth = 250.0; targetWidth = 250.0;
@ -579,8 +589,6 @@ class MainPageSection extends StatelessWidget {
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.all(8.0),
child: Container( child: Container(
height: 200,
width: 350,
margin: margin:
const EdgeInsets.symmetric(vertical: 3, horizontal: 5), const EdgeInsets.symmetric(vertical: 3, horizontal: 5),
decoration: BoxDecoration( decoration: BoxDecoration(
@ -622,10 +630,9 @@ class MainPageSection extends StatelessWidget {
: Colors.black, : Colors.black,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 15), fontSize: 15),
maxLines: 3, maxLines: 1,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
const SizedBox(height: 4),
Row( Row(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
children: [ children: [
@ -655,7 +662,7 @@ class MainPageSection extends StatelessWidget {
const SizedBox(width: 6), const SizedBox(width: 6),
Expanded( Expanded(
child: Text( child: Text(
'${item.subtitles.first} صفحه', '${item.subtitles.first.toPersianDigit()} صفحه',
style: const TextStyle( style: const TextStyle(
color: Color.fromARGB( color: Color.fromARGB(
255, 102, 102, 102), 255, 102, 102, 102),
@ -870,76 +877,79 @@ class MainPageSection extends StatelessWidget {
); );
} }
}, },
child: Container( child: Padding(
margin: const EdgeInsets.symmetric(horizontal: 4), padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration( child: Container(
color: DesignConfig.isDark margin: const EdgeInsets.symmetric(horizontal: 4),
? const Color.fromARGB(255, 36, 36, 36) decoration: BoxDecoration(
: Colors.white, color: DesignConfig.isDark
borderRadius: BorderRadius.circular(16), ? const Color.fromARGB(255, 36, 36, 36)
border: Border.all( : Colors.white,
color: const Color.fromARGB(255, 184, 184, 184), borderRadius: BorderRadius.circular(16),
width: 1), border: Border.all(
), color: const Color.fromARGB(255, 184, 184, 184),
clipBehavior: Clip.antiAlias, width: 1),
child: Row( ),
crossAxisAlignment: CrossAxisAlignment.stretch, clipBehavior: Clip.antiAlias,
children: [ child: Row(
Expanded( crossAxisAlignment: CrossAxisAlignment.stretch,
child: Padding( children: [
padding: const EdgeInsets.all(15.0), Expanded(
child: Column( child: Padding(
crossAxisAlignment: CrossAxisAlignment.start, padding: const EdgeInsets.all(15.0),
mainAxisAlignment: MainAxisAlignment.center, child: Column(
children: [ crossAxisAlignment: CrossAxisAlignment.start,
Text( mainAxisAlignment: MainAxisAlignment.center,
item.title, children: [
textAlign: TextAlign.start, Text(
style: TextStyle( item.title,
color: DesignConfig.isDark textAlign: TextAlign.start,
? Colors.white style: TextStyle(
: Colors.black, color: DesignConfig.isDark
fontWeight: FontWeight.bold), ? Colors.white
maxLines: 3, : Colors.black,
overflow: TextOverflow.ellipsis, fontWeight: FontWeight.bold),
), maxLines: 3,
const SizedBox( overflow: TextOverflow.ellipsis,
height: 10, ),
), const SizedBox(
Row( height: 10,
children: [ ),
SvgPicture.asset( Row(
'lib/assets/icons/ion_extension-puzzle-outline.svg'), children: [
const SizedBox( SvgPicture.asset(
width: 5, 'lib/assets/icons/ion_extension-puzzle-outline.svg'),
), const SizedBox(
Text( width: 5,
item.subtitles.last, ),
style: TextStyle( Text(
color: Theme.of(context) item.subtitles.last,
.colorScheme style: TextStyle(
.caption), color: Theme.of(context)
), .colorScheme
], .caption),
) ),
], ],
)
],
),
), ),
), ),
), SizedBox(
SizedBox(
width: 150,
child: SkeletonImage(
imageUrl: item.image,
height: 10,
width: 150, width: 150,
borderRadius: const BorderRadius.only( child: SkeletonImage(
topLeft: Radius.circular(16), imageUrl: item.image,
bottomLeft: Radius.circular(16), height: 10,
topRight: Radius.circular(0), width: 150,
bottomRight: Radius.circular(0)), borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
bottomLeft: Radius.circular(16),
topRight: Radius.circular(0),
bottomRight: Radius.circular(0)),
),
), ),
), ],
], ),
), ),
), ),
); );

View File

@ -120,7 +120,7 @@ class _MainPageState extends State<MainPage> {
).animate().fadeIn(delay: 1000.ms, duration: 500.ms), ).animate().fadeIn(delay: 1000.ms, duration: 500.ms),
], ],
if (state.swotItems.isNotEmpty) if (state.swotItems.isNotEmpty)
SwotSection(swotItems: state.swotItems) SwotSection(swotItems: state.swotItems,headerSize: 13,moreSize: 12,)
.animate() .animate()
.fadeIn(delay: 1100.ms, duration: 500.ms), .fadeIn(delay: 1100.ms, duration: 500.ms),
if (state.didvanVoice != null) ...[ if (state.didvanVoice != null) ...[

View File

@ -126,7 +126,7 @@ class _DidvanPlusSectionState extends State<DidvanPlusSection> {
], ],
), ),
DidvanText( DidvanText(
publishedDate.toPersianDateStr(), publishedDate.toPersianDate(),
fontSize: 13, fontSize: 13,
color: DesignConfig.isDark color: DesignConfig.isDark
? const Color.fromARGB(255, 0, 125, 166) ? const Color.fromARGB(255, 0, 125, 166)

View File

@ -73,8 +73,7 @@ class _DidvanVoiceSectionState extends State<DidvanVoiceSection> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
ClipRRect( ClipRRect(
borderRadius: BorderRadius.only( borderRadius: const BorderRadius.all(Radius.circular(8)),
bottomRight: Radius.circular(8), topRight: Radius.circular(8)),
child: Image.network( child: Image.network(
imageUrl, imageUrl,
width: 80, width: 80,

View File

@ -161,7 +161,7 @@ class InfographyItem extends StatelessWidget {
}, },
), ),
child: SvgPicture.asset( child: SvgPicture.asset(
'lib/assets/icons/send-2.svg', 'lib/assets/icons/fluent_mention-32-regular.svg',
width: 25, width: 25,
height: 25, height: 25,
colorFilter: const ColorFilter.mode( colorFilter: const ColorFilter.mode(
@ -179,19 +179,19 @@ class InfographyItem extends StatelessWidget {
svgIconOn: 'lib/assets/icons/bookmark_fill.svg', svgIconOn: 'lib/assets/icons/bookmark_fill.svg',
svgIconOff: 'lib/assets/icons/archive-tick.svg', svgIconOff: 'lib/assets/icons/archive-tick.svg',
), ),
Padding( // Padding(
padding: const EdgeInsets.only(left: 8), // padding: const EdgeInsets.only(left: 8),
child: LikedButton( // child: LikedButton(
itemId: id, // itemId: id,
type: 'infography', // type: 'infography',
gestureSize: 32, // gestureSize: 32,
value: liked, // value: liked,
onMarkChanged: (value) => // onMarkChanged: (value) =>
onLikedChanged(id, value, true), // onLikedChanged(id, value, true),
likes: likes, // likes: likes,
unlikedColor: Colors.white, // unlikedColor: Colors.white,
), // ),
), // ),
], ],
), ),
], ],

View File

@ -46,7 +46,7 @@ class SimpleExploreCard extends StatelessWidget {
case 'tech': case 'tech':
return 'lib/assets/icons/Technology.svg'; return 'lib/assets/icons/Technology.svg';
case 'risk': // رادار ریسک case 'risk': // رادار ریسک
return 'lib/assets/icons/risk-radar.svg'; return 'lib/assets/icons/RiskRadar.svg';
case 'survey': case 'survey':
case 'delphi': case 'delphi':
return 'lib/assets/icons/Saha.svg'; return 'lib/assets/icons/Saha.svg';

View File

@ -77,7 +77,7 @@ class _SwotItemCardState extends State<SwotItemCard> {
'Authorization': 'Bearer ${RequestService.token}' 'Authorization': 'Bearer ${RequestService.token}'
}, },
placeholder: (context, url) => const ShimmerPlaceholder( placeholder: (context, url) => const ShimmerPlaceholder(
width: 135, width: 120,
height: double.infinity, height: double.infinity,
), ),
errorWidget: (context, url, error) { errorWidget: (context, url, error) {
@ -85,9 +85,12 @@ class _SwotItemCardState extends State<SwotItemCard> {
print('image fetch complete with Error: $error'); print('image fetch complete with Error: $error');
} }
return Container( return Container(
width: 135, width: 120,
height: double.infinity, height: double.infinity,
color: theme.colorScheme.surfaceContainerHighest, decoration: BoxDecoration(
color: theme.colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(16),
),
child: Icon( child: Icon(
Icons.image_not_supported_outlined, Icons.image_not_supported_outlined,
color: theme.colorScheme.onSurfaceVariant, color: theme.colorScheme.onSurfaceVariant,

View File

@ -11,6 +11,7 @@ import 'package:didvan/views/home/new_statistic/widgets/statistic_cat.dart';
import 'package:didvan/views/widgets/didvan/card.dart'; import 'package:didvan/views/widgets/didvan/card.dart';
import 'package:didvan/views/widgets/didvan/divider.dart'; 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/home_app_bar.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:didvan/views/widgets/state_handlers/state_handler.dart'; import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
@ -45,6 +46,9 @@ class _NewStatisticState extends State<NewStatistic> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
HomeAppBar(
showSearchField: true,
),
Center( Center(
child: Container( child: Container(
padding: padding:

View File

@ -330,29 +330,29 @@ class _MonthlyListPageState extends State<MonthlyListPage> {
Row( Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
children: [ children: [
GestureDetector( // GestureDetector(
onTap: () { // onTap: () {
context // context
.read<MonthlyListState>() // .read<MonthlyListState>()
.toggleLike(item.id); // .toggleLike(item.id);
}, // },
child: SvgPicture.asset( // child: SvgPicture.asset(
item.isLiked // item.isLiked
? 'lib/assets/icons/heart_fill.svg' // ? 'lib/assets/icons/heart_fill.svg'
: 'lib/assets/icons/heart2.svg', // : 'lib/assets/icons/heart2.svg',
width: 20, // width: 20,
height: 20, // height: 20,
colorFilter: ColorFilter.mode( // colorFilter: ColorFilter.mode(
item.isLiked // item.isLiked
? const Color.fromARGB( // ? const Color.fromARGB(
255, 175, 4, 54) // 255, 175, 4, 54)
: const Color.fromARGB( // : const Color.fromARGB(
255, 102, 102, 102), // 255, 102, 102, 102),
BlendMode.srcIn, // BlendMode.srcIn,
), // ),
), // ),
), // ),
const SizedBox(width: 16), // const SizedBox(width: 16),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
context context

View File

@ -110,7 +110,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
children: [ children: [
Image.asset( Image.asset(
page.imagePath, page.imagePath,
height: 400, height: 350,
width: double.infinity, width: double.infinity,
fit: BoxFit.fitWidth, fit: BoxFit.fitWidth,
errorBuilder: (context, error, stackTrace) { errorBuilder: (context, error, stackTrace) {
@ -129,15 +129,14 @@ class _OnboardingPageState extends State<OnboardingPage> {
); );
}, },
), ),
const SizedBox(height: 20),
SizedBox( SizedBox(
width: 80, width: 60,
height: 80, height: 60,
child: Stack( child: Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
CustomPaint( CustomPaint(
size: const Size(80, 80), size: const Size(60, 60),
painter: OnboardingIndicatorPainter( painter: OnboardingIndicatorPainter(
pageCount: onboardingPages.length, pageCount: onboardingPages.length,
currentPage: _currentPage, currentPage: _currentPage,
@ -150,7 +149,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
onTap: _onNextTap, onTap: _onNextTap,
child: Container( child: Container(
width: 60, width: 60,
height: 60, height: 45,
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Color.fromARGB(255, 1, 35, 72), color: Color.fromARGB(255, 1, 35, 72),
shape: BoxShape.circle, shape: BoxShape.circle,
@ -158,8 +157,8 @@ class _OnboardingPageState extends State<OnboardingPage> {
child: Center( child: Center(
child: SvgPicture.asset( child: SvgPicture.asset(
'lib/assets/icons/Arrow - Right 2.svg', 'lib/assets/icons/Arrow - Right 2.svg',
width: 35, width: 25,
height: 35, height: 25,
colorFilter: const ColorFilter.mode( colorFilter: const ColorFilter.mode(
Colors.white, Colors.white,
BlendMode.srcIn, BlendMode.srcIn,
@ -171,11 +170,10 @@ class _OnboardingPageState extends State<OnboardingPage> {
], ],
), ),
), ),
const SizedBox(height: 20),
Expanded( Expanded(
flex: 2, flex: 2,
child: Padding( child: Padding(
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.fromLTRB(20, 0, 20, 0),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.end,
@ -186,17 +184,19 @@ class _OnboardingPageState extends State<OnboardingPage> {
style: style:
theme.textTheme.headlineMedium?.copyWith( theme.textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
fontSize: 22,
color: const Color.fromARGB(255, 0, 126, 167), color: const Color.fromARGB(255, 0, 126, 167),
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 13),
Text( Text(
page.description, page.description,
textDirection: TextDirection.rtl, textDirection: TextDirection.rtl,
textAlign: TextAlign.start, textAlign: TextAlign.start,
style: theme.textTheme.bodyLarge?.copyWith( style: theme.textTheme.bodyMedium?.copyWith(
color: const Color.fromARGB(255, 41, 41, 41), color: const Color.fromARGB(255, 41, 41, 41),
height: 1.6, height: 1.6,
fontSize: 15
), ),
), ),
], ],
@ -245,7 +245,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
Row( Row(
children: [ children: [
Expanded( Expanded(
flex: 2, flex: 4,
child: ElevatedButton( child: ElevatedButton(
onPressed: _onNextTap, onPressed: _onNextTap,
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
@ -266,7 +266,7 @@ class _OnboardingPageState extends State<OnboardingPage> {
), ),
const SizedBox(width: 16), const SizedBox(width: 16),
Expanded( Expanded(
flex: 1, flex: 2,
child: OutlinedButton( child: OutlinedButton(
onPressed: _skipOnboarding, onPressed: _skipOnboarding,
style: OutlinedButton.styleFrom( style: OutlinedButton.styleFrom(
@ -278,11 +278,15 @@ class _OnboardingPageState extends State<OnboardingPage> {
borderRadius: BorderRadius.circular(16), borderRadius: BorderRadius.circular(16),
), ),
), ),
child: Text( child: Padding(
'صرف نظر کردن', padding: const EdgeInsets.all(5.0),
style: theme.textTheme.titleMedium?.copyWith( child: Text(
color: Colors.black, 'صرف نظر کردن',
fontWeight: FontWeight.normal, style: theme.textTheme.titleMedium?.copyWith(
color: Colors.black,
fontSize: 12,
fontWeight: FontWeight.bold,
),
), ),
), ),
), ),

View File

@ -685,24 +685,24 @@ class _StudioDetailsState extends State<StudioDetails>
return Row( return Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
CircleAvatar( // CircleAvatar(
radius: 20, // radius: 20,
backgroundColor: DesignConfig.isDark // backgroundColor: DesignConfig.isDark
? Colors.transparent // ? Colors.transparent
: Colors.white, // : Colors.white,
backgroundImage: // backgroundImage:
hasProfileImage ? NetworkImage(user.photo!) : null, // hasProfileImage ? NetworkImage(user.photo!) : null,
child: !hasProfileImage // child: !hasProfileImage
? Icon( // ? Icon(
DidvanIcons.avatar_light, // DidvanIcons.avatar_light,
size: 50, // size: 50,
color: DesignConfig.isDark // color: DesignConfig.isDark
? Colors.white // ? Colors.white
: Colors.black, // : Colors.black,
) // )
: null, // : null,
), // ),
const SizedBox(width: 8), // const SizedBox(width: 8),
Expanded( Expanded(
child: CommentMessageBox(focusNode: _focusNode), child: CommentMessageBox(focusNode: _focusNode),
), ),

View File

@ -200,7 +200,6 @@ class _NewPasswordScreenState extends State<NewPasswordScreen>
), ),
], ],
children: [ children: [
const SizedBox(height: 24),
SlideTransition( SlideTransition(
position: _headerSlideAnimation, position: _headerSlideAnimation,
child: FadeTransition( child: FadeTransition(
@ -215,7 +214,7 @@ class _NewPasswordScreenState extends State<NewPasswordScreen>
'lib/assets/images/empty_states/Change_Password.png', 'lib/assets/images/empty_states/Change_Password.png',
fit: BoxFit.fitWidth, fit: BoxFit.fitWidth,
width: 500, width: 500,
height: 400, height: 300,
), ),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),

View File

@ -178,6 +178,7 @@ class _VerifyOtpScreenState extends State<VerifyOtpScreen>
left: 0, left: 0,
right: 0, right: 0,
), ),
// physics: const NeverScrollableScrollPhysics(),
appBarData: null, appBarData: null,
showSliversFirst: true, showSliversFirst: true,
slivers: [ slivers: [
@ -240,7 +241,7 @@ class _VerifyOtpScreenState extends State<VerifyOtpScreen>
child: FadeTransition( child: FadeTransition(
opacity: _fadeAnimation, opacity: _fadeAnimation,
child: Padding( child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0), padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -248,9 +249,8 @@ class _VerifyOtpScreenState extends State<VerifyOtpScreen>
'lib/assets/images/empty_states/Change_Password.png', 'lib/assets/images/empty_states/Change_Password.png',
fit: BoxFit.fitWidth, fit: BoxFit.fitWidth,
width: double.infinity, width: double.infinity,
height: 400, height: 300,
), ),
const SizedBox(height: 24),
DidvanText( DidvanText(
'تغییر رمز عبور', 'تغییر رمز عبور',
style: Theme.of(context) style: Theme.of(context)
@ -300,8 +300,8 @@ class _VerifyOtpScreenState extends State<VerifyOtpScreen>
Theme.of(context).colorScheme.onSurface, Theme.of(context).colorScheme.onSurface,
hintCharacter: '-', hintCharacter: '-',
pinTheme: PinTheme( pinTheme: PinTheme(
fieldHeight: 58, fieldHeight: 50,
fieldWidth: 58, fieldWidth: 50,
selectedColor: _hasError selectedColor: _hasError
? const Color.fromARGB(255, 190, 18, 60) ? const Color.fromARGB(255, 190, 18, 60)
: Theme.of(context).colorScheme.primary, : Theme.of(context).colorScheme.primary,

View File

@ -155,7 +155,7 @@ class _RadarStateView extends State<Radar> {
if (state.categories.length >= 6) if (state.categories.length >= 6)
Padding( Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
top: 12, left: 20, right: 20), top: 12, left: 35, right: 35),
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: state.categories children: state.categories
@ -291,49 +291,52 @@ class _RadarStateView extends State<Radar> {
category.asset = _getCategoryIcon(category.id, isDark); category.asset = _getCategoryIcon(category.id, isDark);
return Expanded( return Expanded(
child: GestureDetector( child: Padding(
onTap: () => _onCategorySelected(category), padding: const EdgeInsets.all(8.0),
child: Column( child: GestureDetector(
mainAxisSize: MainAxisSize.min, onTap: () => _onCategorySelected(category),
children: [ child: Column(
AnimatedContainer( mainAxisSize: MainAxisSize.min,
duration: const Duration(milliseconds: 300), children: [
curve: Curves.fastOutSlowIn, AnimatedContainer(
padding: EdgeInsets.zero, duration: const Duration(milliseconds: 300),
decoration: BoxDecoration( curve: Curves.fastOutSlowIn,
color: isSelected padding: EdgeInsets.zero,
? theme.colorScheme.primary.withOpacity(0.1) decoration: BoxDecoration(
: Colors.transparent,
borderRadius: BorderRadius.circular(30),
border: Border.all(
color: isSelected color: isSelected
? theme.colorScheme.primary ? theme.colorScheme.primary.withOpacity(0.1)
: Colors.transparent, : Colors.transparent,
width: 2, borderRadius: BorderRadius.circular(30),
border: Border.all(
color: isSelected
? theme.colorScheme.primary
: Colors.transparent,
width: 2,
),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(30),
child: SvgPicture.asset(
category.asset!,
width: 100,
height: 100,
),
), ),
), ),
child: ClipRRect( const SizedBox(height: 3),
borderRadius: BorderRadius.circular(30), Text(
child: SvgPicture.asset( category.label,
category.asset!, textAlign: TextAlign.center,
width: 100, maxLines: 1,
height: 100, overflow: TextOverflow.ellipsis,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.primary,
fontWeight: isSelected ? FontWeight.bold : FontWeight.bold,
fontSize: 13,
), ),
), ),
), ],
const SizedBox(height: 8), ),
Text(
category.label,
textAlign: TextAlign.center,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.primary,
fontWeight: isSelected ? FontWeight.bold : FontWeight.bold,
fontSize: 15,
),
),
],
), ),
), ),
); );

View File

@ -847,59 +847,59 @@ class _AiChatDialogState extends State<AiChatDialog>
); );
} }
Widget _buildRecordingIndicator() { // Widget _buildRecordingIndicator() {
return Container( // return Container(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), // padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
margin: const EdgeInsets.only(bottom: 6), // margin: const EdgeInsets.only(bottom: 6),
decoration: BoxDecoration( // decoration: BoxDecoration(
gradient: LinearGradient( // gradient: LinearGradient(
begin: Alignment.topLeft, // begin: Alignment.topLeft,
end: Alignment.bottomRight, // end: Alignment.bottomRight,
colors: [ // colors: [
const Color(0xFFFF3366).withOpacity(0.1), // const Color(0xFFFF3366).withOpacity(0.1),
const Color(0xFFFF6699).withOpacity(0.05), // const Color(0xFFFF6699).withOpacity(0.05),
], // ],
), // ),
border: Border.all( // border: Border.all(
color: const Color(0xFFFF3366).withOpacity(0.3), // color: const Color(0xFFFF3366).withOpacity(0.3),
width: 1, // width: 1,
), // ),
), // ),
child: Row( // child: Row(
children: [ // children: [
Container( // Container(
width: 8, // width: 8,
height: 8, // height: 8,
decoration: BoxDecoration( // decoration: BoxDecoration(
color: const Color(0xFFFF3366), // color: const Color(0xFFFF3366),
shape: BoxShape.circle, // shape: BoxShape.circle,
boxShadow: [ // boxShadow: [
BoxShadow( // BoxShadow(
color: const Color(0xFFFF3366).withOpacity(0.4), // color: const Color(0xFFFF3366).withOpacity(0.4),
blurRadius: 6, // blurRadius: 6,
spreadRadius: 1, // spreadRadius: 1,
), // ),
], // ],
), // ),
), // ),
const SizedBox(width: 10), // const SizedBox(width: 10),
const Expanded( // const Expanded(
child: DidvanText( // child: DidvanText(
'🎙️ در حال ضبط...', // '🎙️ در حال ضبط...',
fontSize: 12, // fontSize: 12,
color: Color(0xFFFF3366), // color: Color(0xFFFF3366),
fontWeight: FontWeight.w600, // fontWeight: FontWeight.w600,
), // ),
), // ),
const Icon( // const Icon(
Icons.mic_rounded, // Icons.mic_rounded,
color: Color(0xFFFF3366), // color: Color(0xFFFF3366),
size: 16, // size: 16,
), // ),
], // ],
), // ),
); // );
} // }
Widget _buildInputField() { Widget _buildInputField() {
return Container( return Container(

View File

@ -4,6 +4,7 @@ import 'package:didvan/services/ai_rag_service.dart';
import 'package:didvan/services/ai_voice_service.dart'; import 'package:didvan/services/ai_voice_service.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:record/record.dart'; import 'package:record/record.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:just_audio/just_audio.dart'; import 'package:just_audio/just_audio.dart';
@ -25,6 +26,7 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
bool _isProcessing = false; bool _isProcessing = false;
bool _isAiSpeaking = false; bool _isAiSpeaking = false;
String _statusText = 'برای شروع مکالمه، دکمه میکروفون را نگه دارید'; String _statusText = 'برای شروع مکالمه، دکمه میکروفون را نگه دارید';
String? _currentAiResponse;
late AnimationController _waveController; late AnimationController _waveController;
late AnimationController _pulseController; late AnimationController _pulseController;
@ -62,7 +64,7 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
'در حال آماده‌سازی پاسخ...', 'در حال آماده‌سازی پاسخ...',
'در حال محاسبه‌ی بهترین گزینه...', 'در حال محاسبه‌ی بهترین گزینه...',
'در حال درک مفهوم پرسش شما...', 'در حال درک مفهوم پرسش شما...',
'در حال به‌روزرسانی اطلاعات...', 'در حال به‌روزرسانی اطلاعات...',
'در حال استخراج پاسخ مناسب...', 'در حال استخراج پاسخ مناسب...',
'در حال هماهنگ‌سازی با پایگاه دانش...' 'در حال هماهنگ‌سازی با پایگاه دانش...'
]; ];
@ -148,6 +150,14 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
}); });
} }
// <<< راهحل مشکل ۲: ریست کردن پاسخ قبلی
if (_currentAiResponse != null) {
setState(() {
_currentAiResponse = null;
});
}
// >>>
setState(() { setState(() {
_isPreparing = true; _isPreparing = true;
_statusText = '⏳ در حال آماده سازی...'; _statusText = '⏳ در حال آماده سازی...';
@ -168,6 +178,7 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
await Future.delayed(const Duration(seconds: 2)); await Future.delayed(const Duration(seconds: 2));
// <<< راهحل مشکل ۲: بررسی وضعیت قبل از شروع ضبط
if (mounted && _isPreparing) { if (mounted && _isPreparing) {
setState(() { setState(() {
_isPreparing = false; _isPreparing = false;
@ -176,7 +187,12 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
}); });
_preparingController.stop(); _preparingController.stop();
_waveController.repeat(); _waveController.repeat();
} else if (mounted) {
// اگر کاربر انگشت خود را برداشته بود (_isPreparing false شده)
await _audioRecorder.stop(); // ضبط را متوقف کن
_waveController.stop();
} }
// >>>
} }
} catch (e) { } catch (e) {
setState(() { setState(() {
@ -184,8 +200,10 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
_isRecording = false; _isRecording = false;
_statusText = 'خطا در شروع ضبط صدا'; _statusText = 'خطا در شروع ضبط صدا';
}); });
// <<< راهحل مشکل ۲: توقف انیمیشنها در صورت خطا
_preparingController.stop(); _preparingController.stop();
_waveController.stop(); _waveController.stop();
// >>>
debugPrint('Error starting recording: $e'); debugPrint('Error starting recording: $e');
} }
} }
@ -230,6 +248,7 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
_statusText = '🔊 دستیار در حال پاسخ دادن است...'; _statusText = '🔊 دستیار در حال پاسخ دادن است...';
_isProcessing = false; _isProcessing = false;
_isAiSpeaking = true; _isAiSpeaking = true;
_currentAiResponse = ragResponse.output;
}); });
await _audioPlayer.setUrl(ragResponse.audioUrl!); await _audioPlayer.setUrl(ragResponse.audioUrl!);
@ -443,13 +462,39 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
} }
Widget _buildMainContent() { Widget _buildMainContent() {
return Center( return SingleChildScrollView(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
_buildVisualization(), const SizedBox(height: 20),
const SizedBox(height: 40), SizedBox(
_buildStatusText(), height: 500,
child: Stack(
alignment: Alignment.topCenter,
children: [
Positioned(
top: 0,
child: _buildVisualization(),
),
// وقتی پاسخ هست، کارت پاسخ را نمایش میدهیم
if (_currentAiResponse != null && _currentAiResponse!.isNotEmpty)
Positioned(
bottom: 120,
left: 24,
right: 24,
child: _buildResponsePreview(),
)
else
// در غیر این صورت، متن status را نمایش میدهیم
Positioned(
bottom: 130,
left: 24,
right: 24,
child: _buildStatusText(),
),
],
),
),
const SizedBox(height: 20),
], ],
), ),
); );
@ -597,6 +642,375 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
); );
} }
Widget _buildResponsePreview() {
return TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: const Duration(milliseconds: 600),
curve: Curves.easeOutCubic,
builder: (context, value, child) {
return Transform.translate(
offset: Offset(0, 30 * (1 - value)),
child: Opacity(
opacity: value,
child: child,
),
);
},
child: Container(
margin: const EdgeInsets.fromLTRB(2, 8, 2, 0),
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 15, sigmaY: 15),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
Colors.white.withOpacity(0.15),
Colors.white.withOpacity(0.08),
],
),
borderRadius: BorderRadius.circular(24),
border: Border.all(
color: Colors.white.withOpacity(0.2),
width: 1.5,
),
boxShadow: [
BoxShadow(
color: const Color(0xFF00AAFF).withOpacity(0.2),
blurRadius: 20,
spreadRadius: 0,
),
],
),
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: _showFullResponseDialog,
borderRadius: BorderRadius.circular(24),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [
Color(0xFF00AAFF),
Color(0xFF0066AA),
],
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: const Color(0xFF00AAFF).withOpacity(0.4),
blurRadius: 12,
spreadRadius: 2,
),
],
),
child: const Icon(
Icons.chat_bubble_rounded,
color: Colors.white,
size: 20,
),
),
const SizedBox(width: 12),
const Expanded(
child: DidvanText(
'پاسخ دستیار',
fontSize: 13,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 6,
),
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.white.withOpacity(0.2),
Colors.white.withOpacity(0.1),
],
),
borderRadius: BorderRadius.circular(12),
),
child: const Row(
mainAxisSize: MainAxisSize.min,
children: [
DidvanText(
'مشاهده کامل',
fontSize: 11,
color: Colors.white,
fontWeight: FontWeight.w600,
),
SizedBox(width: 4),
Icon(
Icons.arrow_back_ios_rounded,
color: Colors.white,
size: 12,
),
],
),
),
],
),
const SizedBox(height: 16),
DidvanText(
_currentAiResponse!,
fontSize: 13.5,
color: Colors.white.withOpacity(0.95),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(
3,
(index) => Container(
margin: const EdgeInsets.symmetric(horizontal: 2),
width: 4,
height: 4,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.6),
shape: BoxShape.circle,
),
),
),
),
],
),
),
),
),
),
),
),
),
);
}
void _showFullResponseDialog() {
showDialog(
context: context,
barrierColor: Colors.black.withOpacity(0.7),
builder: (context) => Dialog(
backgroundColor: Colors.transparent,
insetPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 40),
child: ScaleTransition(
scale: CurvedAnimation(
parent: AnimationController(
vsync: this,
duration: const Duration(milliseconds: 300),
)..forward(),
curve: Curves.easeOutBack,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(28),
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 20, sigmaY: 20),
child: Container(
constraints: const BoxConstraints(maxHeight: 600),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
const Color(0xFF0A0E27).withOpacity(0.95),
const Color(0xFF1A1F3A).withOpacity(0.95),
],
),
borderRadius: BorderRadius.circular(28),
border: Border.all(
color: Colors.white.withOpacity(0.15),
width: 1.5,
),
boxShadow: [
BoxShadow(
color: const Color(0xFF00AAFF).withOpacity(0.3),
blurRadius: 40,
spreadRadius: 0,
),
],
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [
Colors.white.withOpacity(0.08),
Colors.transparent,
],
),
),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
gradient: const LinearGradient(
colors: [
Color(0xFF00AAFF),
Color(0xFF0066AA),
],
),
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: const Color(0xFF00AAFF).withOpacity(0.4),
blurRadius: 15,
spreadRadius: 2,
),
],
),
child: const Icon(
Icons.chat_bubble_rounded,
color: Colors.white,
size: 24,
),
),
const SizedBox(width: 14),
const Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DidvanText(
'پاسخ کامل دستیار',
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
SizedBox(height: 2),
DidvanText(
'دیدوان AI',
fontSize: 11,
color: Colors.white60,
),
],
),
),
IconButton(
icon: const Icon(
Icons.close_rounded,
color: Colors.white70,
size: 24,
),
onPressed: () => Navigator.pop(context),
),
],
),
),
Flexible(
child: SingleChildScrollView(
padding: const EdgeInsets.fromLTRB(24, 8, 24, 24),
child: Container(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.05),
borderRadius: BorderRadius.circular(20),
border: Border.all(
color: Colors.white.withOpacity(0.1),
width: 1,
),
),
child: DidvanText(
_currentAiResponse ?? '',
fontSize: 14,
color: Colors.white.withOpacity(0.95),
),
),
),
),
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Colors.white.withOpacity(0.05),
Colors.transparent,
],
),
),
child: Row(
children: [
Expanded(
child: ElevatedButton.icon(
onPressed: () {
Clipboard.setData(ClipboardData(text: _currentAiResponse ?? ''));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const DidvanText(
'متن کپی شد',
color: Colors.white,
fontSize: 13,
),
backgroundColor: const Color(0xFF00AAFF),
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
duration: const Duration(seconds: 2),
),
);
},
icon: const Icon(
Icons.copy_rounded,
size: 18,
color: Colors.white,
),
label: const DidvanText(
'کپی متن',
fontSize: 13,
color: Colors.white,
fontWeight: FontWeight.w600,
),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.white.withOpacity(0.15),
padding: const EdgeInsets.symmetric(
horizontal: 24,
vertical: 14,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: BorderSide(
color: Colors.white.withOpacity(0.2),
width: 1,
),
),
elevation: 0,
),
),
),
],
),
),
],
),
),
),
),
),
),
);
}
Widget _buildControls() { Widget _buildControls() {
return Container( return Container(
padding: const EdgeInsets.all(24), padding: const EdgeInsets.all(24),
@ -619,11 +1033,21 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
_startRecording(); _startRecording();
} }
}, },
// <<< راهحل مشکل ۲: (بدون تغییر، همچنان پابرجاست)
onLongPressEnd: (_) { onLongPressEnd: (_) {
if (_isRecording) { if (_isRecording) {
_stopRecording(); _stopRecording();
} else if (_isPreparing) {
// کاربر انگشت خود را در حین آمادهسازی برداشت
setState(() {
_isPreparing = false;
_statusText = 'برای شروع مکالمه، دکمه میکروفون را نگه دارید';
});
_preparingController.stop();
_waveController.stop();
} }
}, },
// >>>
child: AnimatedBuilder( child: AnimatedBuilder(
animation: _pulseController, animation: _pulseController,
builder: (context, child) { builder: (context, child) {

View File

@ -312,7 +312,7 @@ class _NavBarItemState extends State<_NavBarItem>
duration: const Duration(milliseconds: 150), duration: const Duration(milliseconds: 150),
child: DidvanText( child: DidvanText(
widget.title, widget.title,
style: Theme.of(context).textTheme.bodySmall, style: Theme.of(context).textTheme.bodySmall?.copyWith(fontSize: 11),
color: Theme.of(context).colorScheme.caption, color: Theme.of(context).colorScheme.caption,
fontWeight: FontWeight.w500, fontWeight: FontWeight.w500,
), ),

View File

@ -164,7 +164,8 @@ class _DidvanTextFieldState extends State<DidvanTextField> {
? Theme.of(context).textTheme.bodySmall! ? Theme.of(context).textTheme.bodySmall!
: Theme.of(context).textTheme.bodyMedium!) : Theme.of(context).textTheme.bodyMedium!)
.copyWith( .copyWith(
color: Theme.of(context).colorScheme.hint), color: Theme.of(context).colorScheme.hint,
fontSize: 12),
), ),
), ),
)); ));

View File

@ -45,6 +45,7 @@ class HomeAppBar extends StatelessWidget {
return Column( return Column(
children: [ children: [
const SizedBox(height: 10,),
Padding( Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Row( child: Row(
@ -102,6 +103,11 @@ class HomeAppBar extends StatelessWidget {
Navigator.of(context).pushNamed(Routes.bookmarks); Navigator.of(context).pushNamed(Routes.bookmarks);
}, },
), ),
Container(
color: const Color.fromARGB(255, 224, 224, 224),
height: 20,
width: 1.5,
),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
Navigator.pushNamed(context, Routes.profile); Navigator.pushNamed(context, Routes.profile);

View File

@ -7,6 +7,7 @@ import 'package:didvan/views/ai/history_ai_chat_state.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart'; import 'package:flutter_svg/flutter_svg.dart';
import 'package:persian_number_utility/persian_number_utility.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:didvan/routes/routes.dart'; import 'package:didvan/routes/routes.dart';
// ignore: depend_on_referenced_packages // ignore: depend_on_referenced_packages
@ -529,12 +530,13 @@ class _HistoryDrawerContentState extends State<HistoryDrawerContent> {
_buildChatItem(chat, historyState)), _buildChatItem(chat, historyState)),
], ],
if (last7DaysChats.isNotEmpty) ...[ if (last7DaysChats.isNotEmpty) ...[
_buildSectionHeader('7 روز اخیر'), _buildSectionHeader('۷ روز اخیر'),
...last7DaysChats.map((chat) => ...last7DaysChats.map((chat) =>
_buildChatItem(chat, historyState)), _buildChatItem(chat, historyState)),
], ],
// '۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'
if (last30DaysChats.isNotEmpty) ...[ if (last30DaysChats.isNotEmpty) ...[
_buildSectionHeader('30 روز اخیر'), _buildSectionHeader('۳۰ روز اخیر'),
...last30DaysChats.map((chat) => ...last30DaysChats.map((chat) =>
_buildChatItem(chat, historyState)), _buildChatItem(chat, historyState)),
], ],
@ -614,7 +616,7 @@ class _HistoryDrawerContentState extends State<HistoryDrawerContent> {
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
DidvanText( DidvanText(
_formatDateFromString(chat.updatedAt), _formatDateFromString(chat.updatedAt).toPersianDigit(),
fontSize: 12, fontSize: 12,
color: Colors.grey[600], color: Colors.grey[600],
), ),

View File

@ -116,20 +116,20 @@ class NewsOverview extends StatelessWidget {
), ),
Row( Row(
children: [ children: [
LikedButton( // LikedButton(
itemId: news.id, // itemId: news.id,
type: 'news', // type: 'news',
gestureSize: 32, // gestureSize: 32,
value: news.liked, // value: news.liked,
onMarkChanged: (value) => // onMarkChanged: (value) =>
onLikedChanged(news.id, value, false), // onLikedChanged(news.id, value, false),
askForConfirmation: hasUnmarkConfirmation, // askForConfirmation: hasUnmarkConfirmation,
likes: news.likes, // likes: news.likes,
unlikedColor: Theme.of(context).colorScheme.caption, // unlikedColor: Theme.of(context).colorScheme.caption,
), // ),
const SizedBox( // const SizedBox(
width: 4.0, // width: 4.0,
), // ),
BookmarkButton( BookmarkButton(
itemId: news.id, itemId: news.id,
type: 'news', type: 'news',

View File

@ -151,20 +151,20 @@ class RadarOverview extends StatelessWidget {
padding: const EdgeInsets.fromLTRB(8, 0, 8, 5), padding: const EdgeInsets.fromLTRB(8, 0, 8, 5),
child: Row( child: Row(
children: [ children: [
LikedButton( // LikedButton(
itemId: radar.id, // itemId: radar.id,
type: 'radar', // type: 'radar',
gestureSize: 32, // gestureSize: 32,
value: radar.liked, // value: radar.liked,
onMarkChanged: (value) => // onMarkChanged: (value) =>
onLikedChanged(radar.id, value, false), // onLikedChanged(radar.id, value, false),
askForConfirmation: hasUnmarkConfirmation, // askForConfirmation: hasUnmarkConfirmation,
likes: radar.likes, // likes: radar.likes,
unlikedColor: Theme.of(context).colorScheme.caption, // unlikedColor: Theme.of(context).colorScheme.caption,
), // ),
const SizedBox( // const SizedBox(
width: 4.0, // width: 4.0,
), // ),
BookmarkButton( BookmarkButton(
itemId: radar.id, itemId: radar.id,
type: 'radar', type: 'radar',

View File

@ -7,21 +7,14 @@ class EmptyConnection extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return EmptyState(
children: [ asset: 'lib/assets/images/empty_states/Connection.png',
const SizedBox( height: 400,
height: 65, title: 'اتصال اینترنت برقرار نیست',
), titleColor: const Color.fromARGB(255, 0, 126, 167),
EmptyState( action: onRetry,
asset: 'lib/assets/images/empty_states/Connection.png', subtitle: 'لطفاً اتصال وای‌فای یا داده تلفن همراه خود را بررسی کنید.',
height: 550, buttonTitle: 'تلاش مجدد' ,
title: 'اتصال اینترنت برقرار نیست',
titleColor: const Color.fromARGB(255, 0, 126, 167),
action: onRetry,
subtitle: 'لطفاً اتصال وای‌فای یا داده تلفن همراه خود را بررسی کنید.',
buttonTitle: 'تلاش مجدد',
),
],
); );
} }
} }

View File

@ -11,6 +11,7 @@ class EmptyResult extends StatelessWidget {
asset: 'lib/assets/images/empty_states/Empty-Result.png', asset: 'lib/assets/images/empty_states/Empty-Result.png',
height: 600, height: 600,
title: 'نتیجه‌ای یافت نشد', title: 'نتیجه‌ای یافت نشد',
titleColor: const Color.fromARGB(255, 0, 126, 167),
subtitle: subtitle:
'لطفاً از کلمات کلیدی دیگری استفاده کنید یا املای کلمه را بررسی نمایید.', 'لطفاً از کلمات کلیدی دیگری استفاده کنید یا املای کلمه را بررسی نمایید.',
buttonTitle: 'بازگشت به صفحه اصلی', buttonTitle: 'بازگشت به صفحه اصلی',

View File

@ -29,8 +29,8 @@ class EmptyState extends StatelessWidget {
final isSvg = asset.toLowerCase().endsWith('.svg'); final isSvg = asset.toLowerCase().endsWith('.svg');
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
SizedBox( SizedBox(
width: double.infinity, width: double.infinity,
@ -40,25 +40,26 @@ class EmptyState extends StatelessWidget {
: Image.asset(asset, : Image.asset(asset,
fit: BoxFit.fitWidth, height: height, width: double.infinity), fit: BoxFit.fitWidth, height: height, width: double.infinity),
), ),
const SizedBox(height: 10),
Padding( Padding(
padding: const EdgeInsets.only(right: 20, left: 20), padding: const EdgeInsets.only(right: 20, left: 20),
child: DidvanText( child: DidvanText(
title, title,
style: Theme.of(context).textTheme.displaySmall, style: Theme.of(context).textTheme.displaySmall,
color: titleColor ?? Theme.of(context).colorScheme.caption, color: titleColor ?? Theme.of(context).colorScheme.caption,
textAlign: TextAlign.start, textAlign: TextAlign.center,
), ),
), ),
if (subtitle != null) const SizedBox(height: 8), // if (subtitle != null) const SizedBox(height: 8),
if (subtitle != null) // if (subtitle != null)
Padding( // Padding(
padding: const EdgeInsets.only(right: 20, left: 20), // padding: const EdgeInsets.only(right: 20, left: 20),
child: DidvanText( // child: DidvanText(
subtitle!, // subtitle!,
color: Theme.of(context).colorScheme.caption, // color: Theme.of(context).colorScheme.caption,
), // ),
), // ),
if (action != null) const SizedBox(height: 16), // if (action != null) const SizedBox(height: 16),
if (action != null) if (action != null)
Center( Center(
child: Padding( child: Padding(

View File

@ -38,7 +38,7 @@ class SliverStateHandler<T extends CoreProvier> extends SliverList {
if (state.appState == AppState.failed) { if (state.appState == AppState.failed) {
return Padding( return Padding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
top: centerEmptyState ? deviceHight / 4 : deviceHight / 8, top: 0,
bottom: 20, bottom: 20,
), ),
child: EmptyConnection(onRetry: onRetry), child: EmptyConnection(onRetry: onRetry),

View File

@ -3,7 +3,7 @@ FLUTTER_ROOT=C:\flutter
FLUTTER_APPLICATION_PATH=C:\Flutter Projects\didvan-app\didvan-app FLUTTER_APPLICATION_PATH=C:\Flutter Projects\didvan-app\didvan-app
COCOAPODS_PARALLEL_CODE_SIGN=true COCOAPODS_PARALLEL_CODE_SIGN=true
FLUTTER_BUILD_DIR=build FLUTTER_BUILD_DIR=build
FLUTTER_BUILD_NAME=4.0.1 FLUTTER_BUILD_NAME=5.0.0
FLUTTER_BUILD_NUMBER=6000 FLUTTER_BUILD_NUMBER=6000
DART_OBFUSCATION=false DART_OBFUSCATION=false
TRACK_WIDGET_CREATION=true TRACK_WIDGET_CREATION=true

View File

@ -4,7 +4,7 @@ export "FLUTTER_ROOT=C:\flutter"
export "FLUTTER_APPLICATION_PATH=C:\Flutter Projects\didvan-app\didvan-app" export "FLUTTER_APPLICATION_PATH=C:\Flutter Projects\didvan-app\didvan-app"
export "COCOAPODS_PARALLEL_CODE_SIGN=true" export "COCOAPODS_PARALLEL_CODE_SIGN=true"
export "FLUTTER_BUILD_DIR=build" export "FLUTTER_BUILD_DIR=build"
export "FLUTTER_BUILD_NAME=4.0.1" export "FLUTTER_BUILD_NAME=5.0.0"
export "FLUTTER_BUILD_NUMBER=6000" export "FLUTTER_BUILD_NUMBER=6000"
export "DART_OBFUSCATION=false" export "DART_OBFUSCATION=false"
export "TRACK_WIDGET_CREATION=true" export "TRACK_WIDGET_CREATION=true"

View File

@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at # Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 4.0.1+6000 version: 5.0.0+6000
environment: environment:
sdk: ">=3.0.0 <4.0.0" sdk: ">=3.0.0 <4.0.0"