finished redesign
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "didvan-app",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "didvan-app (profile mode)",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "profile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "didvan-app (release mode)",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"flutterMode": "release"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "didvan-app (web)",
|
||||||
|
"request": "launch",
|
||||||
|
"type": "dart",
|
||||||
|
"args": [
|
||||||
|
"--web-port=8080"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
<svg width="31" height="31" viewBox="0 0 31 31" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M10.8428 6.37788L19.772 15.3071L10.8428 24.2363" stroke="white" stroke-width="2.29609" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 248 B |
|
|
@ -0,0 +1,14 @@
|
||||||
|
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g clip-path="url(#clip0_12104_5819)">
|
||||||
|
<path d="M1 23.5056C1.2171 23.0459 1.40707 22.566 1.66488 22.1266C5.11817 16.1576 8.57824 10.1886 12.0315 4.21956C12.52 3.36781 13.2256 2.92841 14.2229 3.00953C14.9895 3.07037 15.5459 3.47597 15.9258 4.13168C16.9163 5.8487 17.9136 7.55896 18.9042 9.27597C21.4755 13.7172 24.0536 18.1585 26.6249 22.6065C27.1744 23.5597 27.1202 24.4925 26.4621 25.2564C26.0753 25.7026 25.5801 25.9527 24.9831 25.9932C24.8813 26 24.7795 26 24.6778 26C17.5609 26 10.444 26 3.32707 26C2.37046 26 1.66488 25.6214 1.22389 24.7697C1.12212 24.5804 1.07463 24.3641 1 24.1613C1 23.945 1 23.7287 1 23.5056ZM13.999 25.2429C17.5202 25.2429 21.0413 25.2429 24.5624 25.2429C24.6778 25.2429 24.7999 25.2429 24.9152 25.2361C25.519 25.1888 25.9193 24.8779 26.1432 24.3235C26.3399 23.8436 26.211 23.4042 25.96 22.9783C23.1648 18.1585 20.3696 13.3319 17.5744 8.5121C16.8078 7.18716 16.0344 5.85546 15.2677 4.53051C14.976 4.03028 14.5553 3.75312 13.9651 3.75988C13.3681 3.7734 12.9813 4.09112 12.6964 4.58459C9.76552 9.64777 6.82785 14.7109 3.89696 19.7741C3.25922 20.876 2.6147 21.9779 1.98375 23.0797C1.62417 23.7084 1.71915 24.3911 2.21442 24.8576C2.54007 25.1618 2.92679 25.2429 3.36099 25.2429C6.90248 25.2429 10.4507 25.2429 13.999 25.2429Z" fill="#007EA7" stroke="#007EA7" stroke-width="0.3"/>
|
||||||
|
<path d="M13.9709 6.16173C10.7261 11.7836 7.50322 17.3617 4.25843 22.9836C4.40426 22.9836 4.49905 22.9836 4.59384 22.9836C6.62093 22.9836 8.64801 22.9836 10.6751 22.9836C10.7991 22.9836 10.9303 22.9909 11.0543 23.0273C11.2511 23.093 11.3313 23.2534 11.3095 23.4576C11.2876 23.6544 11.1636 23.7638 10.9741 23.8003C10.8938 23.8148 10.8136 23.8076 10.7261 23.8076C8.40009 23.8076 6.06676 23.793 3.74072 23.8149C3.17926 23.8221 3.02614 23.4649 3.28864 23.0055C6.70843 17.1211 10.1136 11.2149 13.5188 5.31589C13.548 5.27214 13.5699 5.2211 13.5991 5.17735C13.8032 4.86381 14.1532 4.85652 14.3428 5.17735C14.6126 5.62943 14.8751 6.08881 15.1303 6.54819C15.2616 6.78881 15.2032 7.02214 14.9991 7.13881C14.7949 7.25548 14.5688 7.18256 14.423 6.94923C14.2772 6.70131 14.1386 6.46068 13.9709 6.16173Z" fill="#007EA7"/>
|
||||||
|
<path d="M23.676 22.9908C23.6031 22.8522 23.552 22.7574 23.4937 22.6627C20.8249 18.0324 18.1489 13.4095 15.4802 8.77932C15.2614 8.39286 15.2687 8.16682 15.5166 8.02098C15.7645 7.87515 15.9614 7.98453 16.1874 8.37098C19.002 13.2491 21.8239 18.1272 24.6385 23.0054C24.6895 23.0929 24.7406 23.1804 24.777 23.2752C24.8572 23.5377 24.7041 23.771 24.427 23.8001C24.3614 23.8074 24.3031 23.8074 24.2374 23.8074C21.8895 23.8074 19.5416 23.8074 17.1937 23.8074C17.1281 23.8074 17.0624 23.8074 17.0041 23.8001C16.7854 23.7783 16.6541 23.6543 16.6322 23.4356C16.6104 23.2168 16.7197 23.0637 16.9312 23.0127C17.0333 22.9835 17.1499 22.9908 17.2593 22.9908C19.2864 22.9908 21.3135 22.9908 23.3479 22.9908C23.4427 22.9908 23.5302 22.9908 23.676 22.9908Z" fill="#007EA7"/>
|
||||||
|
<path d="M15.4802 15.2903C15.4145 16.508 15.3489 17.7257 15.2906 18.9361C15.2614 19.5559 14.9406 19.8695 14.3281 19.8695C14.0729 19.8695 13.8177 19.8695 13.5624 19.8695C13.052 19.8622 12.6947 19.5413 12.6656 19.0382C12.5708 17.3393 12.4833 15.6403 12.3958 13.9486C12.3593 13.2049 12.3156 12.4611 12.2864 11.7174C12.2645 11.0976 12.5999 10.7549 13.2343 10.7549C13.7447 10.7549 14.2552 10.7549 14.7583 10.7549C15.3416 10.7549 15.6916 11.1049 15.6697 11.7028C15.6114 12.8913 15.5385 14.0872 15.4802 15.2903ZM13.0958 11.5788C13.227 14.0726 13.3583 16.5518 13.4895 19.0309C13.8249 19.0309 14.1385 19.0309 14.4666 19.0309C14.5979 16.5372 14.7291 14.0653 14.8604 11.5788C14.2624 11.5788 13.701 11.5788 13.0958 11.5788Z" fill="#007EA7" stroke="#007EA7" stroke-width="0.2"/>
|
||||||
|
<path d="M13.9709 24.2445C13.023 24.2445 12.2573 23.4861 12.25 22.5382C12.2428 21.583 13.0375 20.7955 13.9855 20.8028C14.9261 20.8101 15.699 21.5903 15.6917 22.5309C15.6917 23.4861 14.9261 24.2445 13.9709 24.2445ZM13.9709 23.4278C14.474 23.4278 14.875 23.034 14.875 22.5382C14.875 22.0424 14.474 21.634 13.9782 21.6267C13.4896 21.6267 13.074 22.0351 13.074 22.5309C13.0667 23.0195 13.4678 23.4205 13.9709 23.4278Z" fill="#007EA7" stroke="#007EA7" stroke-width="0.2"/>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<clipPath id="clip0_12104_5819">
|
||||||
|
<rect width="28" height="28" fill="white"/>
|
||||||
|
</clipPath>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 184 KiB |
|
After Width: | Height: | Size: 236 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 257 KiB |
|
After Width: | Height: | Size: 233 KiB |
|
After Width: | Height: | Size: 215 KiB |
|
Before Width: | Height: | Size: 363 KiB |
|
Before Width: | Height: | Size: 730 KiB |
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 264 KiB |
|
Before Width: | Height: | Size: 1009 KiB |
|
After Width: | Height: | Size: 1.8 MiB |
|
After Width: | Height: | Size: 213 KiB |
|
After Width: | Height: | Size: 202 KiB |
|
After Width: | Height: | Size: 207 KiB |
|
|
@ -75,7 +75,7 @@ class Assets {
|
||||||
static String get emptyConnection =>
|
static String get emptyConnection =>
|
||||||
'$_baseEmptyStatesPath/connection-$_themeSuffix.svg';
|
'$_baseEmptyStatesPath/connection-$_themeSuffix.svg';
|
||||||
static String get emptyResult =>
|
static String get emptyResult =>
|
||||||
'$_baseEmptyStatesPath/result-$_themeSuffix.svg';
|
'lib/assets/images/empty_states/Empty_List.png';
|
||||||
|
|
||||||
static String get fooladWorld =>
|
static String get fooladWorld =>
|
||||||
'$_baseFeaturesPath/foolad-world-$_themeSuffix.svg';
|
'$_baseFeaturesPath/foolad-world-$_themeSuffix.svg';
|
||||||
|
|
@ -84,7 +84,8 @@ class Assets {
|
||||||
static String get risk => '$_baseFeaturesPath/risk-$_themeSuffix.svg';
|
static String get risk => '$_baseFeaturesPath/risk-$_themeSuffix.svg';
|
||||||
static String get saha => '$_baseFeaturesPath/saha-$_themeSuffix.svg';
|
static String get saha => '$_baseFeaturesPath/saha-$_themeSuffix.svg';
|
||||||
static String get ai => '$_baseFeaturesPath/ai-$_themeSuffix.svg';
|
static String get ai => '$_baseFeaturesPath/ai-$_themeSuffix.svg';
|
||||||
static String get hugeideas => '$_baseFeaturesPath/hugeicons_idea-$_themeSuffix.svg';
|
static String get hugeideas =>
|
||||||
|
'$_baseFeaturesPath/hugeicons_idea-$_themeSuffix.svg';
|
||||||
static String get startup => '$_baseFeaturesPath/startup-$_themeSuffix.svg';
|
static String get startup => '$_baseFeaturesPath/startup-$_themeSuffix.svg';
|
||||||
static String get stats => '$_baseFeaturesPath/stats-$_themeSuffix.svg';
|
static String get stats => '$_baseFeaturesPath/stats-$_themeSuffix.svg';
|
||||||
static String get tech => '$_baseFeaturesPath/tech-$_themeSuffix.svg';
|
static String get tech => '$_baseFeaturesPath/tech-$_themeSuffix.svg';
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
class DidvanVoiceModel {
|
||||||
|
final int id;
|
||||||
|
final String title;
|
||||||
|
final String file;
|
||||||
|
final String image;
|
||||||
|
final String publishedAt;
|
||||||
|
|
||||||
|
DidvanVoiceModel({
|
||||||
|
required this.id,
|
||||||
|
required this.title,
|
||||||
|
required this.file,
|
||||||
|
required this.image,
|
||||||
|
required this.publishedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory DidvanVoiceModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return DidvanVoiceModel(
|
||||||
|
id: json['id'],
|
||||||
|
title: json['title'],
|
||||||
|
file: json['file'],
|
||||||
|
image: json['image'],
|
||||||
|
publishedAt: json['publishedAt'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
class OnboardingEntity {
|
||||||
|
final String imagePath;
|
||||||
|
final String title;
|
||||||
|
final String description;
|
||||||
|
|
||||||
|
OnboardingEntity({
|
||||||
|
required this.imagePath,
|
||||||
|
required this.title,
|
||||||
|
required this.description,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
class TopBannerModel {
|
||||||
|
final int id;
|
||||||
|
final String image;
|
||||||
|
|
||||||
|
TopBannerModel({
|
||||||
|
required this.id,
|
||||||
|
required this.image,
|
||||||
|
});
|
||||||
|
|
||||||
|
factory TopBannerModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
return TopBannerModel(
|
||||||
|
id: json['id'],
|
||||||
|
image: json['image'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -71,6 +71,7 @@ import 'package:didvan/views/podcasts/studio_details/studio_details.mobile.dart'
|
||||||
import 'package:didvan/views/pdf_viewer/pdf_viewer_page.dart';
|
import 'package:didvan/views/pdf_viewer/pdf_viewer_page.dart';
|
||||||
import 'package:didvan/views/monthly/monthly_list_page.dart';
|
import 'package:didvan/views/monthly/monthly_list_page.dart';
|
||||||
import 'package:didvan/views/monthly/monthly_list_state.dart';
|
import 'package:didvan/views/monthly/monthly_list_state.dart';
|
||||||
|
import 'package:didvan/views/onboarding/onboarding_page.dart';
|
||||||
import 'package:didvan/views/splash/splash.dart';
|
import 'package:didvan/views/splash/splash.dart';
|
||||||
import 'package:didvan/routes/routes.dart';
|
import 'package:didvan/routes/routes.dart';
|
||||||
import 'package:didvan/views/story_viewer/story_viewer_page.dart';
|
import 'package:didvan/views/story_viewer/story_viewer_page.dart';
|
||||||
|
|
@ -124,6 +125,10 @@ class RouteGenerator {
|
||||||
return _createRoute(
|
return _createRoute(
|
||||||
const Splash(),
|
const Splash(),
|
||||||
);
|
);
|
||||||
|
case Routes.onboarding:
|
||||||
|
return _createRoute(
|
||||||
|
const OnboardingPage(),
|
||||||
|
);
|
||||||
case Routes.aiSection:
|
case Routes.aiSection:
|
||||||
return _createRoute(
|
return _createRoute(
|
||||||
const AiSectionPage(),
|
const AiSectionPage(),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
class Routes {
|
class Routes {
|
||||||
static const String splash = '/';
|
static const String splash = '/';
|
||||||
|
static const String onboarding = '/onboarding';
|
||||||
static const String aiChat = '/ai-chat';
|
static const String aiChat = '/ai-chat';
|
||||||
static const String aiHistory = '/ai-history';
|
static const String aiHistory = '/ai-history';
|
||||||
static const String botAssistants = '/bot-assistants-page';
|
static const String botAssistants = '/bot-assistants-page';
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,8 @@ class RequestHelper {
|
||||||
|
|
||||||
static const String mainPageContent = _baseHomeUrl;
|
static const String mainPageContent = _baseHomeUrl;
|
||||||
static const String didvanPlus = '$baseUrl/didvanPlus';
|
static const String didvanPlus = '$baseUrl/didvanPlus';
|
||||||
|
static const String didvanVoice = '$baseUrl/didvanVoice';
|
||||||
|
static const String topBanner = '$baseUrl/topBanner';
|
||||||
|
|
||||||
static String searchAll({
|
static String searchAll({
|
||||||
required int page,
|
required int page,
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ class _AiState extends State<Ai> {
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.only(bottom: 24, top: 70),
|
const EdgeInsets.only(bottom: 24, top: 0),
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Consumer<AiState>(
|
Consumer<AiState>(
|
||||||
|
|
@ -68,8 +68,10 @@ class _AiState extends State<Ai> {
|
||||||
AiState aiState, Widget? child) {
|
AiState aiState, Widget? child) {
|
||||||
final tools = aiState.tools;
|
final tools = aiState.tools;
|
||||||
if (tools != null && tools.isEmpty) {
|
if (tools != null && tools.isEmpty) {
|
||||||
return EmptyState(
|
return const EmptyState(
|
||||||
asset: Assets.emptyResult,
|
asset:
|
||||||
|
'lib/assets/images/empty_states/Empty-Result.png',
|
||||||
|
height: 500,
|
||||||
title: 'لیست خالی است',
|
title: 'لیست خالی است',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,9 @@ class _HistoryAiChatPageState extends State<HistoryAiChatPage> {
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
color: DesignConfig.isDark? const Color.fromARGB(255, 0, 90, 119) : const Color.fromARGB(255, 0, 53, 70))),
|
color: DesignConfig.isDark
|
||||||
|
? const Color.fromARGB(255, 0, 90, 119)
|
||||||
|
: const Color.fromARGB(255, 0, 53, 70))),
|
||||||
),
|
),
|
||||||
// SearchField(
|
// SearchField(
|
||||||
// title: 'گفتوگوها',
|
// title: 'گفتوگوها',
|
||||||
|
|
@ -153,11 +155,16 @@ class _HistoryAiChatPageState extends State<HistoryAiChatPage> {
|
||||||
Consumer<HistoryAiChatState>(
|
Consumer<HistoryAiChatState>(
|
||||||
builder: (context, state, child) {
|
builder: (context, state, child) {
|
||||||
return SliverStateHandler(
|
return SliverStateHandler(
|
||||||
|
paddingEmptyState: 0,
|
||||||
state: state,
|
state: state,
|
||||||
centerEmptyState: false,
|
centerEmptyState: false,
|
||||||
emptyState: EmptyState(
|
emptyState: EmptyState(
|
||||||
asset: Assets.emptyResult,
|
asset: Assets.emptyResult,
|
||||||
|
height: 550,
|
||||||
title: 'لیست خالی است',
|
title: 'لیست خالی است',
|
||||||
|
titleColor: const Color.fromARGB(255, 2, 126, 167),
|
||||||
|
subtitle:
|
||||||
|
'در حال حاضر آیتمی در این بخش ثبت نشده است. هر زمان مورد جدیدی اضافه شود، در اینجا نمایش داده میشود.',
|
||||||
),
|
),
|
||||||
enableEmptyState: archived
|
enableEmptyState: archived
|
||||||
? state.archivedChats.isEmpty
|
? state.archivedChats.isEmpty
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@ import 'package:didvan/views/authentication/widgets/authentication_layout.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/button.dart';
|
import 'package:didvan/views/widgets/didvan/button.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/text.dart';
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/text_field.dart';
|
import 'package:didvan/views/widgets/didvan/text_field.dart';
|
||||||
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
class PasswordInput extends StatefulWidget {
|
class PasswordInput extends StatefulWidget {
|
||||||
const PasswordInput({Key? key}) : super(key: key);
|
const PasswordInput({Key? key}) : super(key: key);
|
||||||
|
|
@ -23,8 +25,8 @@ class _PasswordInputState extends State<PasswordInput> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final AuthenticationState state = context.read<AuthenticationState>();
|
final AuthenticationState state = context.read<AuthenticationState>();
|
||||||
return AuthenticationLayout(
|
return AuthenticationLayout(
|
||||||
appBarTitle:
|
// appBarTitle:
|
||||||
'ورود با ${state.username.contains('09') ? 'شماره موبایل' : 'نام کاربری'} ${state.username}',
|
// 'ورود با ${state.username.contains('09') ? 'شماره موبایل' : 'نام کاربری'} ${state.username}',
|
||||||
children: [
|
children: [
|
||||||
Form(
|
Form(
|
||||||
key: _formKey,
|
key: _formKey,
|
||||||
|
|
@ -42,7 +44,7 @@ class _PasswordInputState extends State<PasswordInput> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 32,
|
height: 15,
|
||||||
),
|
),
|
||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () => state.currentPageIndex++,
|
onTap: () => state.currentPageIndex++,
|
||||||
|
|
@ -52,13 +54,54 @@ class _PasswordInputState extends State<PasswordInput> {
|
||||||
color: Theme.of(context).colorScheme.primary,
|
color: Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const SizedBox(
|
||||||
|
height: 50,
|
||||||
|
),
|
||||||
DidvanButton(
|
DidvanButton(
|
||||||
onPressed: () => _onLogin(context),
|
onPressed: () => _onLogin(context),
|
||||||
title: 'ورود',
|
title: 'تایید و ادامه',
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 48,
|
height: 15,
|
||||||
|
),
|
||||||
|
const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 60),
|
||||||
|
child: RichText(
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
text: TextSpan(
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
children: [
|
||||||
|
const TextSpan(text: 'با ورود به اپلیکیشن دیدوان،'),
|
||||||
|
TextSpan(
|
||||||
|
text: ' شرایط ',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall!
|
||||||
|
.copyWith(color: Theme.of(context).colorScheme.primary),
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () => launchUrlString(
|
||||||
|
'https://didvan.com/terms-of-use#conditions',
|
||||||
|
mode: LaunchMode.inAppWebView),
|
||||||
|
),
|
||||||
|
const TextSpan(text: 'و\n'),
|
||||||
|
TextSpan(
|
||||||
|
text: ' قوانین حریم خصوصی ',
|
||||||
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall!
|
||||||
|
.copyWith(color: Theme.of(context).colorScheme.primary),
|
||||||
|
recognizer: TapGestureRecognizer()
|
||||||
|
..onTap = () => launchUrlString(
|
||||||
|
'https://didvan.com/terms-of-use#privacy',
|
||||||
|
mode: LaunchMode.inAppWebView),
|
||||||
|
),
|
||||||
|
const TextSpan(text: 'را میپذیرم'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/utils/extension.dart';
|
import 'package:didvan/utils/extension.dart';
|
||||||
import 'package:didvan/views/authentication/authentication_state.dart';
|
import 'package:didvan/views/authentication/authentication_state.dart';
|
||||||
import 'package:didvan/views/authentication/widgets/authentication_layout.dart';
|
import 'package:didvan/views/authentication/widgets/authentication_layout.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/button.dart';
|
import 'package:didvan/views/widgets/didvan/button.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/text.dart';
|
|
||||||
import 'package:didvan/views/widgets/didvan/text_field.dart';
|
import 'package:didvan/views/widgets/didvan/text_field.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
// import 'package:url_launcher/url_launcher.dart';
|
|
||||||
|
|
||||||
class UsernameInput extends StatefulWidget {
|
class UsernameInput extends StatefulWidget {
|
||||||
const UsernameInput({
|
const UsernameInput({
|
||||||
|
|
@ -50,12 +49,16 @@ class _UsernameInputState extends State<UsernameInput> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
// Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
// padding: const EdgeInsets.all(8.0),
|
||||||
child: DidvanText(
|
// child: DidvanText(
|
||||||
'نام کاربری میتواند شامل کاراکترهای انگلیسی و اعداد باشد.',
|
// 'نام کاربری میتواند شامل کاراکترهای انگلیسی و اعداد باشد.',
|
||||||
style: Theme.of(context).textTheme.labelSmall,
|
// style: Theme.of(context).textTheme.labelSmall,
|
||||||
),
|
// ),
|
||||||
|
// ),
|
||||||
|
|
||||||
|
const SizedBox(
|
||||||
|
height: 70,
|
||||||
),
|
),
|
||||||
DidvanButton(
|
DidvanButton(
|
||||||
title: 'ادامه',
|
title: 'ادامه',
|
||||||
|
|
@ -65,13 +68,18 @@ class _UsernameInputState extends State<UsernameInput> {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const Spacer(),
|
const SizedBox(
|
||||||
|
height: 20,
|
||||||
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 60),
|
padding: const EdgeInsets.symmetric(horizontal: 60),
|
||||||
child: RichText(
|
child: RichText(
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
style: Theme.of(context).textTheme.bodySmall,
|
style: Theme.of(context)
|
||||||
|
.textTheme
|
||||||
|
.bodySmall
|
||||||
|
?.copyWith(color: Theme.of(context).colorScheme.caption),
|
||||||
children: [
|
children: [
|
||||||
const TextSpan(text: 'با ورود به اپلیکیشن دیدوان،'),
|
const TextSpan(text: 'با ورود به اپلیکیشن دیدوان،'),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,9 @@
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
// START: Added Imports
|
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter_svg/flutter_svg.dart';
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
// END: Added Imports
|
|
||||||
|
|
||||||
import 'package:didvan/config/design_config.dart';
|
import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/providers/user.dart';
|
import 'package:didvan/providers/user.dart';
|
||||||
|
|
@ -36,10 +33,7 @@ class _VerificationState extends State<Verification> {
|
||||||
bool _isResendButtonEnabled = false;
|
bool _isResendButtonEnabled = false;
|
||||||
bool _isConfirmButtonEnabled = false;
|
bool _isConfirmButtonEnabled = false;
|
||||||
String _completedCode = "";
|
String _completedCode = "";
|
||||||
|
|
||||||
// START: Added state for error
|
|
||||||
bool _hasError = false;
|
bool _hasError = false;
|
||||||
// END: Added state
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
|
@ -75,30 +69,26 @@ class _VerificationState extends State<Verification> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final AuthenticationState state = context.read<AuthenticationState>();
|
final AuthenticationState state = context.read<AuthenticationState>();
|
||||||
|
|
||||||
// START: Define colors for PinTheme
|
|
||||||
final errorColor = Theme.of(context).colorScheme.error;
|
final errorColor = Theme.of(context).colorScheme.error;
|
||||||
final primaryColor = Theme.of(context).colorScheme.primary;
|
final primaryColor = Theme.of(context).colorScheme.primary;
|
||||||
final borderColor = Theme.of(context).colorScheme.border;
|
final borderColor = Theme.of(context).colorScheme.border;
|
||||||
final textColor = Theme.of(context).colorScheme.text;
|
final textColor = Theme.of(context).colorScheme.text;
|
||||||
// END: Define colors
|
|
||||||
|
|
||||||
return WillPopScope(
|
return WillPopScope(
|
||||||
onWillPop: () async {
|
onWillPop: () async {
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
|
|
||||||
// منطق بازگشت هماهنگ با authentication.dart
|
|
||||||
if (state.currentPageIndex == 0) {
|
if (state.currentPageIndex == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (state.currentPageIndex == 2 && !state.hasPassword) {
|
if (state.currentPageIndex == 2 && !state.hasPassword) {
|
||||||
state.currentPageIndex = 0; // برگرد به صفحه نام کاربری
|
state.currentPageIndex = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
state.currentPageIndex--; // در غیر این صورت یکی برگرد
|
state.currentPageIndex--;
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
child: AuthenticationLayout(
|
child: AuthenticationLayout(
|
||||||
appBarTitle: 'تغییر رمز عبور',
|
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|
@ -125,8 +115,8 @@ class _VerificationState extends State<Verification> {
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_timer?.cancel(); // لغو تایمر
|
_timer?.cancel();
|
||||||
state.currentPageIndex = 0; // بازگشت به صفحه ورود شماره
|
state.currentPageIndex = 0;
|
||||||
},
|
},
|
||||||
borderRadius: DesignConfig.lowBorderRadius,
|
borderRadius: DesignConfig.lowBorderRadius,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
@ -157,58 +147,46 @@ class _VerificationState extends State<Verification> {
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
animationType: AnimationType.scale,
|
animationType: AnimationType.scale,
|
||||||
cursorColor: textColor,
|
cursorColor: textColor,
|
||||||
// START: Added textStyle for error color
|
|
||||||
textStyle: TextStyle(
|
textStyle: TextStyle(
|
||||||
fontSize: 20, // اندازه فونت را متناسب تنظیم کنید
|
fontSize: 20,
|
||||||
color: _hasError ? const Color.fromARGB(255, 178, 4, 54) : textColor,
|
color: _hasError ? const Color.fromARGB(255, 178, 4, 54) : textColor,
|
||||||
),
|
),
|
||||||
// END: Added textStyle
|
|
||||||
pinTheme: PinTheme(
|
pinTheme: PinTheme(
|
||||||
fieldHeight: 58,
|
fieldHeight: 58,
|
||||||
fieldWidth: 58,
|
fieldWidth: 58,
|
||||||
// START: Dynamic border/bg colors
|
|
||||||
selectedColor: _hasError ? const Color.fromARGB(255, 178, 4, 54) : primaryColor,
|
selectedColor: _hasError ? const Color.fromARGB(255, 178, 4, 54) : primaryColor,
|
||||||
inactiveColor: _hasError ? const Color.fromARGB(255, 178, 4, 54) : borderColor,
|
inactiveColor: _hasError ? const Color.fromARGB(255, 178, 4, 54) : borderColor,
|
||||||
activeFillColor: _hasError ? const Color.fromARGB(255, 178, 4, 54) : primaryColor, // بستگی به طراحی شما دارد
|
activeFillColor: _hasError ? const Color.fromARGB(255, 178, 4, 54) : primaryColor, // بستگی به طراحی شما دارد
|
||||||
activeColor: _hasError ? const Color.fromARGB(255, 178, 4, 54) : primaryColor,
|
activeColor: _hasError ? const Color.fromARGB(255, 178, 4, 54) : primaryColor,
|
||||||
// END: Dynamic colors
|
|
||||||
borderRadius: DesignConfig.lowBorderRadius,
|
borderRadius: DesignConfig.lowBorderRadius,
|
||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
shape: PinCodeFieldShape.box,
|
shape: PinCodeFieldShape.box,
|
||||||
),
|
),
|
||||||
appContext: context,
|
appContext: context,
|
||||||
length: 6,
|
length: 6,
|
||||||
// START: Modified onCompleted
|
|
||||||
onCompleted: (value) async {
|
onCompleted: (value) async {
|
||||||
final result = await state.verifyOtpToken(value);
|
final result = await state.verifyOtpToken(value);
|
||||||
if (result) {
|
if (result) {
|
||||||
_timer?.cancel();
|
_timer?.cancel();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// END: Modified onCompleted
|
|
||||||
// START: Modified onChanged
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_completedCode = value;
|
_completedCode = value;
|
||||||
_isConfirmButtonEnabled = value.length == 6;
|
_isConfirmButtonEnabled = value.length == 6;
|
||||||
if (_hasError) {
|
if (_hasError) {
|
||||||
_hasError = false; // پاک کردن خطا با شروع تایپ
|
_hasError = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// END: Modified onChanged
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// ویجت ارسال مجدد کد
|
|
||||||
const SizedBox(
|
|
||||||
height: 24,
|
|
||||||
),
|
|
||||||
_isResendButtonEnabled
|
_isResendButtonEnabled
|
||||||
? InkWell(
|
? InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
_handleTimer(); // ریاستارت تایمر
|
_handleTimer();
|
||||||
state.sendOtpToken(); // ارسال مجدد کد
|
state.sendOtpToken();
|
||||||
},
|
},
|
||||||
borderRadius: DesignConfig.lowBorderRadius,
|
borderRadius: DesignConfig.lowBorderRadius,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
@ -243,26 +221,22 @@ class _VerificationState extends State<Verification> {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
||||||
const Spacer(), // دکمه را به پایین صفحه میچسباند
|
const SizedBox(height: 10,),
|
||||||
DidvanButton(
|
DidvanButton(
|
||||||
enabled: _isConfirmButtonEnabled,
|
enabled: _isConfirmButtonEnabled,
|
||||||
// START: Modified onPressed
|
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (_completedCode.length == 6) {
|
if (_completedCode.length == 6) {
|
||||||
final result = await state.verifyOtpToken(_completedCode);
|
final result = await state.verifyOtpToken(_completedCode);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
if (mounted) setState(() => _hasError = true);
|
if (mounted) setState(() => _hasError = true);
|
||||||
}
|
}
|
||||||
// در صورت موفقیت، onCompleted خودش کار را انجام میدهد
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// END: Modified onPressed
|
|
||||||
title: 'تایید کد',
|
title: 'تایید کد',
|
||||||
),
|
),
|
||||||
|
|
||||||
// START: Added Terms and Conditions RichText
|
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 24, // فاصله بین دکمه و متن قوانین
|
height: 24,
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 60),
|
padding: const EdgeInsets.symmetric(horizontal: 60),
|
||||||
|
|
@ -301,9 +275,8 @@ class _VerificationState extends State<Verification> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
height: 24, // پدینگ نهایی پایین صفحه
|
height: 24,
|
||||||
),
|
),
|
||||||
// END: Added RichText
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
// ignore_for_file: deprecated_member_use
|
// ignore_for_file: deprecated_member_use
|
||||||
|
|
||||||
import 'package:didvan/views/authentication/widgets/authentication_app_bar.dart';
|
import 'package:didvan/views/authentication/widgets/authentication_app_bar.dart';
|
||||||
import 'package:didvan/views/widgets/logos/didvan_horizontal_logo.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
class AuthenticationLayout extends StatelessWidget {
|
class AuthenticationLayout extends StatelessWidget {
|
||||||
final List<Widget> children;
|
final List<Widget> children;
|
||||||
|
|
@ -26,7 +26,7 @@ class AuthenticationLayout extends StatelessWidget {
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: 16,
|
left: 16,
|
||||||
right: 16,
|
right: 16,
|
||||||
top: 7,
|
// top: 7,
|
||||||
),
|
),
|
||||||
child: AuthenticationAppBar(
|
child: AuthenticationAppBar(
|
||||||
title: appBarTitle,
|
title: appBarTitle,
|
||||||
|
|
@ -34,17 +34,28 @@ class AuthenticationLayout extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
top: appBarTitle == null ? 100 : 44,
|
// top: appBarTitle == null ? 100 : 44,
|
||||||
left: 100,
|
// left: 100,
|
||||||
right: 100,
|
// right: 100,
|
||||||
bottom: 40,
|
// bottom: 40,
|
||||||
),
|
),
|
||||||
sliver: const SliverToBoxAdapter(
|
sliver: SliverToBoxAdapter(
|
||||||
child: DidvanVerticalLogo(
|
child: Column(
|
||||||
height: 145,
|
children: [
|
||||||
),
|
const SizedBox(height: 20),
|
||||||
),
|
SvgPicture.asset(
|
||||||
|
'lib/assets/images/logos/logo-horizontal-light.svg',
|
||||||
|
height: 90,
|
||||||
|
),
|
||||||
|
Image.asset(
|
||||||
|
'lib/assets/icons/login_img.png',
|
||||||
|
height: 450,
|
||||||
|
width: double.infinity,
|
||||||
|
fit: BoxFit.fill,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)),
|
||||||
),
|
),
|
||||||
SliverPadding(
|
SliverPadding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@
|
||||||
import 'package:didvan/config/design_config.dart';
|
import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/constants/app_icons.dart';
|
import 'package:didvan/constants/app_icons.dart';
|
||||||
import 'package:didvan/constants/assets.dart';
|
|
||||||
import 'package:didvan/models/enums.dart';
|
import 'package:didvan/models/enums.dart';
|
||||||
import 'package:didvan/providers/server_data.dart';
|
import 'package:didvan/providers/server_data.dart';
|
||||||
import 'package:didvan/services/media/voice.dart';
|
import 'package:didvan/services/media/voice.dart';
|
||||||
|
|
@ -82,14 +81,17 @@ class _DirectState extends State<Direct> {
|
||||||
? EdgeInsets.zero
|
? EdgeInsets.zero
|
||||||
: const EdgeInsets.only(bottom: 68),
|
: const EdgeInsets.only(bottom: 68),
|
||||||
sliver: SliverStateHandler<DirectState>(
|
sliver: SliverStateHandler<DirectState>(
|
||||||
itemPadding: const EdgeInsets.only(bottom: 12),
|
|
||||||
state: state,
|
state: state,
|
||||||
enableEmptyState: state.messages.isEmpty,
|
enableEmptyState: state.messages.isEmpty,
|
||||||
emptyState: Padding(
|
paddingEmptyState: 0,
|
||||||
padding: const EdgeInsets.only(bottom: 160),
|
emptyState: const Padding(
|
||||||
|
padding: EdgeInsets.only(top: 1),
|
||||||
child: EmptyState(
|
child: EmptyState(
|
||||||
asset: Assets.emptyChat,
|
asset: 'lib/assets/images/empty_states/Support.png',
|
||||||
title: 'اولین پیام را بنویسید...',
|
height: 600,
|
||||||
|
title: 'پشتیبانی دیدوان',
|
||||||
|
titleColor: Color.fromARGB(255, 0, 126, 167),
|
||||||
|
subtitle: 'چطور میتوانیم به شما کمک کنیم؟',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
builder: (context, state, index) => Message(
|
builder: (context, state, index) => Message(
|
||||||
|
|
@ -139,7 +141,9 @@ class _DirectState extends State<Direct> {
|
||||||
.textTheme
|
.textTheme
|
||||||
.headlineSmall
|
.headlineSmall
|
||||||
?.copyWith(
|
?.copyWith(
|
||||||
color: DesignConfig.isDark? const Color.fromARGB(255, 0, 90, 119) : const Color.fromARGB(255, 0, 53, 70),
|
color: DesignConfig.isDark
|
||||||
|
? const Color.fromARGB(255, 0, 90, 119)
|
||||||
|
: const Color.fromARGB(255, 0, 53, 70),
|
||||||
fontWeight: FontWeight.bold,
|
fontWeight: FontWeight.bold,
|
||||||
fontSize: 19),
|
fontSize: 19),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import 'package:didvan/models/view/app_bar_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/views/home/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart';
|
import 'package:didvan/views/home/bookmarks/filtered_bookmark/filtered_bookmarks_state.dart';
|
||||||
import 'package:didvan/views/widgets/overview/multitype.dart';
|
import 'package:didvan/views/widgets/overview/multitype.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/scaffold.dart';
|
|
||||||
import 'package:didvan/views/widgets/state_handlers/empty_list.dart';
|
import 'package:didvan/views/widgets/state_handlers/empty_list.dart';
|
||||||
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
|
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:didvan/views/home/main/widgets/swot_bookmark.dart';
|
import 'package:didvan/views/home/main/widgets/swot_bookmark.dart';
|
||||||
|
|
||||||
|
|
@ -71,62 +71,106 @@ class _FilteredBookmarksState extends State<FilteredBookmarks> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final state = context.watch<FilteredBookmarksState>();
|
final state = context.watch<FilteredBookmarksState>();
|
||||||
|
|
||||||
return DidvanScaffold(
|
return Scaffold(
|
||||||
appBarData: AppBarData(title: _appBarTitle, hasBack: true),
|
appBar: PreferredSize(
|
||||||
padding: const EdgeInsets.all(16),
|
preferredSize: const Size.fromHeight(90.0),
|
||||||
slivers: [
|
child: AppBar(
|
||||||
if (widget.type == 8)
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
SliverStateHandler<FilteredBookmarksState>(
|
elevation: 0,
|
||||||
state: state,
|
automaticallyImplyLeading: false,
|
||||||
enableEmptyState:
|
flexibleSpace: SafeArea(
|
||||||
state.bookmarkedSwotItems.isEmpty && !state.swotItemsLoading,
|
child: Container(
|
||||||
emptyState: const EmptyList(),
|
padding:
|
||||||
placeholder: MultitypeOverview.placeholder,
|
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
|
||||||
builder: (context, state, index) {
|
child: Row(
|
||||||
final item = state.bookmarkedSwotItems[index];
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
return Padding(
|
children: [
|
||||||
padding: const EdgeInsets.only(bottom: 8.0),
|
Center(
|
||||||
child: SwotBookmark(
|
child: SvgPicture.asset(
|
||||||
item: item,
|
'lib/assets/images/logos/logo-horizontal-light.svg',
|
||||||
onSwotUnbookmarked: (postId) {
|
height: 55,
|
||||||
_onBookmarkChanged(postId, false, true, "swot");
|
),
|
||||||
},
|
),
|
||||||
),
|
IconButton(
|
||||||
);
|
icon: SvgPicture.asset(
|
||||||
},
|
'lib/assets/icons/arrow-left.svg',
|
||||||
itemPadding: EdgeInsets.zero,
|
color: Theme.of(context).colorScheme.caption,
|
||||||
childCount: state.bookmarkedSwotItems.length,
|
height: 24,
|
||||||
onRetry: () => state.getBookmarks(page: 1),
|
),
|
||||||
)
|
onPressed: () {
|
||||||
else
|
Navigator.pop(context);
|
||||||
SliverStateHandler<FilteredBookmarksState>(
|
},
|
||||||
state: state,
|
),
|
||||||
enableEmptyState: state.bookmarks.isEmpty,
|
],
|
||||||
itemPadding: const EdgeInsets.only(bottom: 8),
|
),
|
||||||
placeholder: MultitypeOverview.placeholder,
|
),
|
||||||
emptyState: const EmptyList(),
|
|
||||||
builder: (context, state, index) {
|
|
||||||
if (index >= state.bookmarks.length) {
|
|
||||||
if (state.page < state.lastPage) {
|
|
||||||
state.getBookmarks(page: state.page + 1);
|
|
||||||
return MultitypeOverview.placeholder;
|
|
||||||
}
|
|
||||||
return const SizedBox.shrink();
|
|
||||||
}
|
|
||||||
final item = state.bookmarks[index];
|
|
||||||
return MultitypeOverview(
|
|
||||||
item: item,
|
|
||||||
enableCaption: true,
|
|
||||||
onMarkChanged: (id, value) =>
|
|
||||||
_onBookmarkChanged(id, value, true, item.type),
|
|
||||||
enableBookmark: true,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
childCount:
|
|
||||||
state.bookmarks.length + (state.page < state.lastPage ? 1 : 0),
|
|
||||||
onRetry: () => state.getBookmarks(page: state.page),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
|
),
|
||||||
|
body: CustomScrollView(
|
||||||
|
slivers: [
|
||||||
|
if (widget.type == 8)
|
||||||
|
SliverPadding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||||
|
sliver: SliverStateHandler<FilteredBookmarksState>(
|
||||||
|
state: state,
|
||||||
|
enableEmptyState: state.bookmarkedSwotItems.isEmpty &&
|
||||||
|
!state.swotItemsLoading,
|
||||||
|
emptyState: const EmptyList(),
|
||||||
|
placeholder: MultitypeOverview.placeholder,
|
||||||
|
paddingEmptyState: 0,
|
||||||
|
builder: (context, state, index) {
|
||||||
|
final item = state.bookmarkedSwotItems[index];
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 8.0),
|
||||||
|
child: SwotBookmark(
|
||||||
|
item: item,
|
||||||
|
onSwotUnbookmarked: (postId) {
|
||||||
|
_onBookmarkChanged(postId, false, true, "swot");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
itemPadding: EdgeInsets.zero,
|
||||||
|
childCount: state.bookmarkedSwotItems.length,
|
||||||
|
onRetry: () => state.getBookmarks(page: 1),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
else
|
||||||
|
SliverPadding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||||
|
sliver: SliverStateHandler<FilteredBookmarksState>(
|
||||||
|
state: state,
|
||||||
|
enableEmptyState: state.bookmarks.isEmpty,
|
||||||
|
itemPadding: const EdgeInsets.only(bottom: 8),
|
||||||
|
placeholder: MultitypeOverview.placeholder,
|
||||||
|
emptyState: const EmptyList(),
|
||||||
|
paddingEmptyState: 0,
|
||||||
|
builder: (context, state, index) {
|
||||||
|
if (index >= state.bookmarks.length) {
|
||||||
|
if (state.page < state.lastPage) {
|
||||||
|
state.getBookmarks(page: state.page + 1);
|
||||||
|
return MultitypeOverview.placeholder;
|
||||||
|
}
|
||||||
|
return const SizedBox.shrink();
|
||||||
|
}
|
||||||
|
final item = state.bookmarks[index];
|
||||||
|
return MultitypeOverview(
|
||||||
|
item: item,
|
||||||
|
enableCaption: true,
|
||||||
|
onMarkChanged: (id, value) =>
|
||||||
|
_onBookmarkChanged(id, value, true, item.type),
|
||||||
|
enableBookmark: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
childCount: state.bookmarks.length +
|
||||||
|
(state.page < state.lastPage ? 1 : 0),
|
||||||
|
onRetry: () => state.getBookmarks(page: state.page),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SliverToBoxAdapter(child: SizedBox(height: 16)),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -376,7 +376,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/risk-radar.svg'; //TODO:FIX ICON
|
return 'lib/assets/icons/RiskRadar.svg'; //TODO:FIX ICON
|
||||||
case 'startup':
|
case 'startup':
|
||||||
return 'lib/assets/icons/Startup.svg';
|
return 'lib/assets/icons/Startup.svg';
|
||||||
case 'survey':
|
case 'survey':
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: unnecessary_import
|
||||||
|
|
||||||
import 'package:didvan/config/design_config.dart';
|
import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/models/home_page_content/content.dart';
|
import 'package:didvan/models/home_page_content/content.dart';
|
||||||
|
|
@ -11,6 +13,7 @@ import 'package:didvan/views/home/main/widgets/main_content.dart';
|
||||||
import 'package:didvan/views/home/main/widgets/story_section.dart';
|
import 'package:didvan/views/home/main/widgets/story_section.dart';
|
||||||
import 'package:didvan/views/home/main/widgets/simple_explore_card.dart';
|
import 'package:didvan/views/home/main/widgets/simple_explore_card.dart';
|
||||||
import 'package:didvan/views/home/main/widgets/didvan_plus_section.dart';
|
import 'package:didvan/views/home/main/widgets/didvan_plus_section.dart';
|
||||||
|
import 'package:didvan/views/home/main/widgets/didvan_voice_section.dart';
|
||||||
import 'package:didvan/views/home/new_statistic/new_statistics_state.dart';
|
import 'package:didvan/views/home/new_statistic/new_statistics_state.dart';
|
||||||
import 'package:didvan/views/widgets/didvan/text.dart';
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
|
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
|
||||||
|
|
@ -66,6 +69,10 @@ class _MainPageState extends State<MainPage> {
|
||||||
onRetry: () => context.read<MainPageState>().init(),
|
onRetry: () => context.read<MainPageState>().init(),
|
||||||
state: context.watch<MainPageState>(),
|
state: context.watch<MainPageState>(),
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
|
debugPrint(
|
||||||
|
'🏠 MainPage build - didvanPlus: ${state.didvanPlus != null}');
|
||||||
|
debugPrint(
|
||||||
|
'🏠 MainPage build - didvanVoice: ${state.didvanVoice != null}');
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
const HomeAppBar(
|
const HomeAppBar(
|
||||||
|
|
@ -116,6 +123,16 @@ class _MainPageState extends State<MainPage> {
|
||||||
SwotSection(swotItems: state.swotItems)
|
SwotSection(swotItems: state.swotItems)
|
||||||
.animate()
|
.animate()
|
||||||
.fadeIn(delay: 1100.ms, duration: 500.ms),
|
.fadeIn(delay: 1100.ms, duration: 500.ms),
|
||||||
|
if (state.didvanVoice != null) ...[
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const _DidvanVoiceTitle()
|
||||||
|
.animate()
|
||||||
|
.fadeIn(delay: 1150.ms, duration: 500.ms),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
DidvanVoiceSection(didvanVoice: state.didvanVoice!)
|
||||||
|
.animate()
|
||||||
|
.fadeIn(delay: 1200.ms, duration: 500.ms),
|
||||||
|
],
|
||||||
// const _IndustryPulseTitle()
|
// const _IndustryPulseTitle()
|
||||||
// .animate()
|
// .animate()
|
||||||
// .fadeIn(delay: 1100.ms, duration: 500.ms),
|
// .fadeIn(delay: 1100.ms, duration: 500.ms),
|
||||||
|
|
@ -165,6 +182,41 @@ class _DidvanSignalsTitle extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _DidvanVoiceTitle extends StatelessWidget {
|
||||||
|
const _DidvanVoiceTitle();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
SvgPicture.asset(
|
||||||
|
'lib/assets/icons/voice-square.svg',
|
||||||
|
color: Theme.of(context).colorScheme.title,
|
||||||
|
width: 30,
|
||||||
|
height: 30,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 5),
|
||||||
|
DidvanText(
|
||||||
|
"صدای دیدوان ",
|
||||||
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
|
color: const Color.fromARGB(255, 0, 89, 119),
|
||||||
|
fontSize: 13,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
DidvanText(
|
||||||
|
"(خلاصه تازهترین پویش افق)",
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
color: const Color.fromARGB(255, 0, 89, 119),
|
||||||
|
fontSize: 11,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _ExploreLatestTitle extends StatelessWidget {
|
class _ExploreLatestTitle extends StatelessWidget {
|
||||||
const _ExploreLatestTitle();
|
const _ExploreLatestTitle();
|
||||||
|
|
||||||
|
|
@ -192,7 +244,9 @@ class _ExploreLatestTitle extends StatelessWidget {
|
||||||
DidvanText(
|
DidvanText(
|
||||||
"تازهترینهای کاوش",
|
"تازهترینهای کاوش",
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
color: DesignConfig.isDark ? const Color.fromARGB(255, 0, 125, 166) : const Color.fromARGB(255, 0, 89, 119),
|
color: DesignConfig.isDark
|
||||||
|
? const Color.fromARGB(255, 0, 125, 166)
|
||||||
|
: const Color.fromARGB(255, 0, 89, 119),
|
||||||
fontSize: 13,
|
fontSize: 13,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
@ -227,7 +281,9 @@ class _ExploreLatestSlider extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<Widget> items = [];
|
final List<Widget> items = [];
|
||||||
final List<({String type, MainPageContentType? content, SwotItem? swotItem})> itemsData = [];
|
final List<
|
||||||
|
({String type, MainPageContentType? content, SwotItem? swotItem})>
|
||||||
|
itemsData = [];
|
||||||
|
|
||||||
for (var list in lists) {
|
for (var list in lists) {
|
||||||
if (list.type == 'video' ||
|
if (list.type == 'video' ||
|
||||||
|
|
@ -247,7 +303,8 @@ class _ExploreLatestSlider extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
itemsData.add((type: list.type, content: newestContent, swotItem: null));
|
itemsData
|
||||||
|
.add((type: list.type, content: newestContent, swotItem: null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -285,11 +342,11 @@ class _ExploreLatestSlider extends StatelessWidget {
|
||||||
);
|
);
|
||||||
} else if (data.content != null) {
|
} else if (data.content != null) {
|
||||||
context.read<MainPageState>().navigationHandler(
|
context.read<MainPageState>().navigationHandler(
|
||||||
data.type,
|
data.type,
|
||||||
data.content!.id,
|
data.content!.id,
|
||||||
data.content!.link,
|
data.content!.link,
|
||||||
description: data.content!.title,
|
description: data.content!.title,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import 'package:didvan/main.dart';
|
import 'package:didvan/main.dart';
|
||||||
import 'package:didvan/models/didvan_plus_model.dart';
|
import 'package:didvan/models/didvan_plus_model.dart';
|
||||||
|
import 'package:didvan/models/didvan_voice_model.dart';
|
||||||
|
import 'package:didvan/models/top_banner_model.dart';
|
||||||
import 'package:didvan/models/enums.dart';
|
import 'package:didvan/models/enums.dart';
|
||||||
import 'package:didvan/models/home_page_content/content.dart';
|
import 'package:didvan/models/home_page_content/content.dart';
|
||||||
import 'package:didvan/models/home_page_content/home_page_content.dart';
|
import 'package:didvan/models/home_page_content/home_page_content.dart';
|
||||||
|
|
@ -24,6 +26,8 @@ class MainPageState extends CoreProvier {
|
||||||
List<UserStories> stories = [];
|
List<UserStories> stories = [];
|
||||||
List<SwotItem> swotItems = [];
|
List<SwotItem> swotItems = [];
|
||||||
DidvanPlusModel? didvanPlus;
|
DidvanPlusModel? didvanPlus;
|
||||||
|
DidvanVoiceModel? didvanVoice;
|
||||||
|
TopBannerModel? topBanner;
|
||||||
|
|
||||||
Future<void> _getMainPageContent() async {
|
Future<void> _getMainPageContent() async {
|
||||||
final service = RequestService(RequestHelper.mainPageContent);
|
final service = RequestService(RequestHelper.mainPageContent);
|
||||||
|
|
@ -53,27 +57,83 @@ class MainPageState extends CoreProvier {
|
||||||
|
|
||||||
Future<void> _getDidvanPlus() async {
|
Future<void> _getDidvanPlus() async {
|
||||||
debugPrint('🎬 Fetching Didvan Plus data...');
|
debugPrint('🎬 Fetching Didvan Plus data...');
|
||||||
|
debugPrint('🎬 URL: ${RequestHelper.didvanPlus}');
|
||||||
|
debugPrint('🎬 Token exists: ${RequestService.token != null}');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final service = RequestService(RequestHelper.didvanPlus);
|
final service = RequestService(RequestHelper.didvanPlus);
|
||||||
await service.httpGet();
|
await service.httpGet();
|
||||||
debugPrint('🎬 Didvan Plus response: ${service.isSuccess}');
|
|
||||||
debugPrint('🎬 Didvan Plus result: ${service.result}');
|
|
||||||
|
|
||||||
if (service.isSuccess) {
|
debugPrint('🎬 Didvan Plus statusCode: ${service.statusCode}');
|
||||||
final data = service.result['result'] ?? service.result;
|
|
||||||
didvanPlus = DidvanPlusModel.fromJson(data);
|
// Check status code directly since response came
|
||||||
debugPrint('✅ Didvan Plus loaded: ${didvanPlus?.id}');
|
if (service.statusCode == 200) {
|
||||||
debugPrint('✅ Video file: ${didvanPlus?.file}');
|
// Get the raw data using data() method
|
||||||
debugPrint('✅ Image: ${didvanPlus?.image}');
|
final rawData = service.data('result');
|
||||||
notifyListeners();
|
debugPrint('🎬 Raw data type: ${rawData.runtimeType}');
|
||||||
|
|
||||||
|
if (rawData is List && rawData.isNotEmpty) {
|
||||||
|
debugPrint('🎬 Data is List with ${rawData.length} items, taking first');
|
||||||
|
didvanPlus = DidvanPlusModel.fromJson(Map<String, dynamic>.from(rawData[0]));
|
||||||
|
debugPrint('✅ Didvan Plus loaded: ${didvanPlus?.id}');
|
||||||
|
debugPrint('✅ Video file: ${didvanPlus?.file}');
|
||||||
|
debugPrint('✅ Image: ${didvanPlus?.image}');
|
||||||
|
notifyListeners();
|
||||||
|
} else if (rawData is Map) {
|
||||||
|
debugPrint('🎬 Data is Map, using directly');
|
||||||
|
didvanPlus = DidvanPlusModel.fromJson(Map<String, dynamic>.from(rawData));
|
||||||
|
debugPrint('✅ Didvan Plus loaded: ${didvanPlus?.id}');
|
||||||
|
notifyListeners();
|
||||||
|
} else {
|
||||||
|
debugPrint('⚠️ Didvan Plus: Empty or unexpected data format');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
debugPrint('⚠️ Didvan Plus: No result in response');
|
debugPrint('⚠️ Didvan Plus: Request failed with status ${service.statusCode}');
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('❌ Failed to load Didvan Plus: $e');
|
debugPrint('❌ Failed to load Didvan Plus: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _getDidvanVoice() async {
|
||||||
|
debugPrint('🎙️ Fetching Didvan Voice data...');
|
||||||
|
try {
|
||||||
|
final service = RequestService(RequestHelper.didvanVoice);
|
||||||
|
await service.httpGet();
|
||||||
|
debugPrint('🎙️ Didvan Voice response: ${service.isSuccess}');
|
||||||
|
debugPrint('🎙️ Didvan Voice result: ${service.result}');
|
||||||
|
|
||||||
|
if (service.isSuccess) {
|
||||||
|
final data = service.result['result'] ?? service.result;
|
||||||
|
didvanVoice = DidvanVoiceModel.fromJson(data);
|
||||||
|
debugPrint('✅ Didvan Voice loaded: ${didvanVoice?.id}');
|
||||||
|
debugPrint('✅ Audio file: ${didvanVoice?.file}');
|
||||||
|
notifyListeners();
|
||||||
|
} else {
|
||||||
|
debugPrint('⚠️ Didvan Voice: No result in response');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('❌ Failed to load Didvan Voice: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _getTopBanner() async {
|
||||||
|
try {
|
||||||
|
final service = RequestService(RequestHelper.topBanner);
|
||||||
|
await service.httpGet();
|
||||||
|
if (service.statusCode == 200) {
|
||||||
|
final data = service.result['result'] ?? service.result;
|
||||||
|
topBanner = TopBannerModel.fromJson(data);
|
||||||
|
debugPrint('✅ Top Banner loaded: ${topBanner?.id}');
|
||||||
|
notifyListeners();
|
||||||
|
} else {
|
||||||
|
debugPrint('⚠️ Top Banner: No result in response');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('❌ Failed to load Top Banner: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _fetchStories() async {
|
Future<void> _fetchStories() async {
|
||||||
try {
|
try {
|
||||||
stories = await StoryService.getStories();
|
stories = await StoryService.getStories();
|
||||||
|
|
@ -94,6 +154,8 @@ class MainPageState extends CoreProvier {
|
||||||
_fetchStories(),
|
_fetchStories(),
|
||||||
_getSwotItems(),
|
_getSwotItems(),
|
||||||
_getDidvanPlus(),
|
_getDidvanPlus(),
|
||||||
|
_getDidvanVoice(),
|
||||||
|
_getTopBanner(),
|
||||||
]);
|
]);
|
||||||
debugPrint("✅ All main page data loaded");
|
debugPrint("✅ All main page data loaded");
|
||||||
appState = AppState.idle;
|
appState = AppState.idle;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,243 @@
|
||||||
|
import 'package:didvan/models/didvan_voice_model.dart';
|
||||||
|
import 'package:didvan/services/network/request_helper.dart';
|
||||||
|
import 'package:didvan/services/network/request.dart';
|
||||||
|
import 'package:didvan/views/widgets/didvan/text.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:just_audio/just_audio.dart';
|
||||||
|
|
||||||
|
class DidvanVoiceSection extends StatefulWidget {
|
||||||
|
final DidvanVoiceModel didvanVoice;
|
||||||
|
|
||||||
|
const DidvanVoiceSection({
|
||||||
|
super.key,
|
||||||
|
required this.didvanVoice,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DidvanVoiceSection> createState() => _DidvanVoiceSectionState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DidvanVoiceSectionState extends State<DidvanVoiceSection> {
|
||||||
|
late AudioPlayer _audioPlayer;
|
||||||
|
bool _isInitialized = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_initializeAudio();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _initializeAudio() async {
|
||||||
|
_audioPlayer = AudioPlayer();
|
||||||
|
final audioUrl =
|
||||||
|
'${RequestHelper.baseUrl}${widget.didvanVoice.file}?accessToken=${RequestService.token}';
|
||||||
|
|
||||||
|
debugPrint('🎙️ Didvan Voice Audio URL: $audioUrl');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _audioPlayer.setUrl(audioUrl);
|
||||||
|
setState(() {
|
||||||
|
_isInitialized = true;
|
||||||
|
});
|
||||||
|
debugPrint('✅ Audio initialized successfully');
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('❌ Audio initialization error: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_audioPlayer.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatDuration(Duration? duration) {
|
||||||
|
if (duration == null) return '00:00';
|
||||||
|
final minutes = duration.inMinutes;
|
||||||
|
final seconds = duration.inSeconds % 60;
|
||||||
|
return '${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final imageUrl = '${RequestHelper.baseUrl}${widget.didvanVoice.image}';
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: const Color.fromRGBO(0, 69, 92, 1),
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
ClipRRect(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
bottomRight: Radius.circular(8), topRight: Radius.circular(8)),
|
||||||
|
child: Image.network(
|
||||||
|
imageUrl,
|
||||||
|
width: 80,
|
||||||
|
height: 130,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Container(
|
||||||
|
width: 80,
|
||||||
|
height: 130,
|
||||||
|
color: Colors.grey.shade800,
|
||||||
|
child: const Icon(Icons.music_note, color: Colors.white),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
DidvanText(
|
||||||
|
widget.didvanVoice.title,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
maxLines: 2,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
final newPosition =
|
||||||
|
_audioPlayer.position + const Duration(seconds: 10);
|
||||||
|
_audioPlayer.seek(newPosition);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.forward_10, color: Colors.white),
|
||||||
|
iconSize: 28,
|
||||||
|
),
|
||||||
|
StreamBuilder<PlayerState>(
|
||||||
|
stream: _audioPlayer.playerStateStream,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final playerState = snapshot.data;
|
||||||
|
final processingState = playerState?.processingState ??
|
||||||
|
ProcessingState.idle;
|
||||||
|
final playing = playerState?.playing ?? false;
|
||||||
|
|
||||||
|
if (processingState == ProcessingState.loading ||
|
||||||
|
processingState == ProcessingState.buffering) {
|
||||||
|
return Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
child: const CircularProgressIndicator(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (!playing) {
|
||||||
|
return IconButton(
|
||||||
|
onPressed: _audioPlayer.play,
|
||||||
|
icon: const Icon(Icons.play_circle_filled,
|
||||||
|
color: Colors.white),
|
||||||
|
iconSize: 48,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return IconButton(
|
||||||
|
onPressed: _audioPlayer.pause,
|
||||||
|
icon: const Icon(Icons.pause_circle_filled,
|
||||||
|
color: Colors.white),
|
||||||
|
iconSize: 48,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
final newPosition =
|
||||||
|
_audioPlayer.position - const Duration(seconds: 10);
|
||||||
|
_audioPlayer.seek(newPosition < Duration.zero
|
||||||
|
? Duration.zero
|
||||||
|
: newPosition);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.replay_10, color: Colors.white),
|
||||||
|
iconSize: 28,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
StreamBuilder<Duration?>(
|
||||||
|
stream: _audioPlayer.durationStream,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final duration = snapshot.data;
|
||||||
|
return StreamBuilder<Duration>(
|
||||||
|
stream: _audioPlayer.positionStream,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final position = snapshot.data ?? Duration.zero;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
DidvanText(
|
||||||
|
_formatDuration(duration),
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.white70,
|
||||||
|
),
|
||||||
|
DidvanText(
|
||||||
|
_formatDuration(position),
|
||||||
|
fontSize: 12,
|
||||||
|
color: Colors.white70,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: StreamBuilder<Duration?>(
|
||||||
|
stream: _audioPlayer.durationStream,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
final duration = snapshot.data ?? Duration.zero;
|
||||||
|
return StreamBuilder<Duration>(
|
||||||
|
stream: _audioPlayer.positionStream,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
var position = snapshot.data ?? Duration.zero;
|
||||||
|
if (position > duration) {
|
||||||
|
position = duration;
|
||||||
|
}
|
||||||
|
return SliderTheme(
|
||||||
|
data: const SliderThemeData(
|
||||||
|
thumbShape:
|
||||||
|
RoundSliderThumbShape(enabledThumbRadius: 6),
|
||||||
|
overlayShape:
|
||||||
|
RoundSliderOverlayShape(overlayRadius: 12),
|
||||||
|
trackHeight: 4,
|
||||||
|
activeTrackColor: Colors.white70,
|
||||||
|
inactiveTrackColor: Colors.white30,
|
||||||
|
thumbColor: Colors.transparent,
|
||||||
|
overlayColor: Colors.transparent,
|
||||||
|
),
|
||||||
|
child: Slider(
|
||||||
|
value: position.inMilliseconds.toDouble(),
|
||||||
|
max: duration.inMilliseconds.toDouble(),
|
||||||
|
onChanged: (value) {
|
||||||
|
_audioPlayer.seek(
|
||||||
|
Duration(milliseconds: value.toInt()));
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -71,8 +71,7 @@ class _MediaPageState extends State<MediaPage> {
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
padding:
|
padding: const EdgeInsets.symmetric(horizontal: 7),
|
||||||
const EdgeInsets.symmetric(horizontal: 7),
|
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 2,
|
height: 2,
|
||||||
color: const Color.fromRGBO(184, 184, 184, 1),
|
color: const Color.fromRGBO(184, 184, 184, 1),
|
||||||
|
|
@ -89,14 +88,14 @@ class _MediaPageState extends State<MediaPage> {
|
||||||
children: [
|
children: [
|
||||||
ChangeNotifierProvider(
|
ChangeNotifierProvider(
|
||||||
create: (_) => PodcastsState(),
|
create: (_) => PodcastsState(),
|
||||||
child: const PodcastTabPage(key: ValueKey('PodcastTabPage')),
|
child:
|
||||||
|
const PodcastTabPage(key: ValueKey('PodcastTabPage')),
|
||||||
),
|
),
|
||||||
ChangeNotifierProvider(
|
ChangeNotifierProvider(
|
||||||
create: (_) => PodcastsState(),
|
create: (_) => PodcastsState(),
|
||||||
child:
|
child: const VideoCastTabPage(
|
||||||
const VideoCastTabPage(key: ValueKey('VideoCastTabPage')),
|
key: ValueKey('VideoCastTabPage')),
|
||||||
),
|
),
|
||||||
|
|
||||||
const SingleChildScrollView(
|
const SingleChildScrollView(
|
||||||
key: ValueKey('MainPageBanner'),
|
key: ValueKey('MainPageBanner'),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,21 @@ class _MediaBannerWithThumbnailsState extends State<MediaBannerWithThumbnails> {
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
|
// Top Banner - only show on first section
|
||||||
|
if (widget.isFirst && state.topBanner != null) ...[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||||
|
child: ClipRRect(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
child: SkeletonImage(
|
||||||
|
imageUrl: state.topBanner!.image,
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
],
|
||||||
|
|
||||||
CarouselSlider.builder(
|
CarouselSlider.builder(
|
||||||
carouselController: _carouselController,
|
carouselController: _carouselController,
|
||||||
itemCount: banners.length,
|
itemCount: banners.length,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
import 'dart:math';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class OnboardingIndicatorPainter extends CustomPainter {
|
||||||
|
final int pageCount;
|
||||||
|
final int currentPage;
|
||||||
|
final Color activeColor;
|
||||||
|
final Color inactiveColor;
|
||||||
|
final double strokeWidth;
|
||||||
|
|
||||||
|
OnboardingIndicatorPainter({
|
||||||
|
required this.pageCount,
|
||||||
|
required this.currentPage,
|
||||||
|
required this.activeColor,
|
||||||
|
required this.inactiveColor,
|
||||||
|
this.strokeWidth = 6.0,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
final center = Offset(size.width / 2, size.height / 2);
|
||||||
|
final radius = min(size.width, size.height) / 2;
|
||||||
|
|
||||||
|
const double gapInDegrees = 30.0;
|
||||||
|
const double gapInRadians = gapInDegrees * (pi / 180);
|
||||||
|
|
||||||
|
const double arcAngle = (pi / 2) - gapInRadians;
|
||||||
|
|
||||||
|
final startAngles = [-pi / 2, 0.0, pi / 2, pi];
|
||||||
|
|
||||||
|
for (int i = 0; i < pageCount; i++) {
|
||||||
|
final paint = Paint()
|
||||||
|
..color = (i == currentPage) ? activeColor : inactiveColor
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = strokeWidth
|
||||||
|
..strokeCap = StrokeCap.round;
|
||||||
|
|
||||||
|
final correctedStartAngle = startAngles[i] + (gapInRadians / 2);
|
||||||
|
|
||||||
|
canvas.drawArc(
|
||||||
|
Rect.fromCircle(center: center, radius: radius),
|
||||||
|
correctedStartAngle,
|
||||||
|
arcAngle,
|
||||||
|
false,
|
||||||
|
paint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(covariant CustomPainter oldDelegate) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,300 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:didvan/models/onboarding_entity.dart';
|
||||||
|
import 'package:didvan/routes/routes.dart';
|
||||||
|
import 'package:didvan/services/storage/storage.dart';
|
||||||
|
import 'package:didvan/views/onboarding/onboarding_indicator_painter.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
|
||||||
|
final List<OnboardingEntity> onboardingPages = [
|
||||||
|
OnboardingEntity(
|
||||||
|
imagePath: 'lib/assets/images/onboarding/1.png',
|
||||||
|
title: 'چرا دیدوان؟',
|
||||||
|
description:
|
||||||
|
'جغرافیای زمانی هیچگاه نمیایستد و سفر به آینده یک سفر شگفتانگیز به جایی است که قسمتهایی از آن را قبلاً در گذشته دیدهایم، قسمتهایی از آن را طی مسیر به تدریج میبینیم و قسمتهایی از آن کاملاً بدیع هستند. برای سفر مطمئن در جغرافیای زمانی ما به سیستمهای پیشنگر نیاز داریم و یکی از ارکان این سیستمها، سامانههای رصد راهبردی هستند.',
|
||||||
|
),
|
||||||
|
OnboardingEntity(
|
||||||
|
imagePath: 'lib/assets/images/onboarding/2.png',
|
||||||
|
title: 'رادارهای استراتژیک',
|
||||||
|
description:
|
||||||
|
'رادارهای استراتژیک، قلب دیدوان هستند. رادارهایی که قرار است دید 360 درجهای برای مدیران ایجاد کنند و به صورت مستمر گزارشهایی تحلیلی را از محیطهای دور و نزدیک هر کسب و کار در اختیار مدیران قرار دهند. رادارها به صورت موضوعی شامل رادار روند، رادار ریسک، رادار تکنولوژی، رادار استارتآپ و ماژول فرصت و تهدید دسته بندی شدهاند و مدیران میتوانند پیرامون این گزارشات به بحث و تبادل نظر بپردازند.',
|
||||||
|
),
|
||||||
|
OnboardingEntity(
|
||||||
|
imagePath: 'lib/assets/images/onboarding/3.png',
|
||||||
|
title: 'استودیو دیدوان',
|
||||||
|
description:
|
||||||
|
'''در استودیو آینده شما میتوانید ویدیوهایی مستند از مسائلی مانند آموزش مفاهیم آیندهگرا، تحلیل روندها، تحلیل صنعت و یا هر نوع اطلاعاتی که میتواند برای تصمیمگیری مدیران ارشد راهگشا باشد را ببینید.
|
||||||
|
همچنین در بخش دیگر این استودیو و در قالب پادکست، اطلاعاتی از جنس مسائل پیش گفته که ارزش تبدیل به فایل صوتی دارند را، میشنوید.
|
||||||
|
''',
|
||||||
|
),
|
||||||
|
OnboardingEntity(
|
||||||
|
imagePath: 'lib/assets/images/onboarding/4.png',
|
||||||
|
title: 'نبض صنعت',
|
||||||
|
description: 'در دنیای امروز، تصمیمگیریهای کلیدی در صنایع مختلف به شدت وابسته به دادههای دقیق و بهروز شدهاند. این دادهها شامل اطلاعات اقتصادی، مالی، خرید و فروش، بازار سرمایه، ارزهای دیجیتال، شاخصهای کلان اقتصادی و دیگر حوزهها هستند. استفاده هوشمندانه از دادهها این امکان را به مدیران میدهد تا تصمیماتی آگاهانه بگیرند، ریسکها را کاهش دهند و عملکرد کلی خود را به سطحی بالاتر ارتقاء دهند.',
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
class OnboardingPage extends StatefulWidget {
|
||||||
|
const OnboardingPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<OnboardingPage> createState() => _OnboardingPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _OnboardingPageState extends State<OnboardingPage> {
|
||||||
|
final PageController _pageController = PageController();
|
||||||
|
int _currentPage = 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_pageController.addListener(() {
|
||||||
|
if (_pageController.page != null) {
|
||||||
|
setState(() {
|
||||||
|
_currentPage = _pageController.page!.round();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_pageController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onNextTap() async {
|
||||||
|
if (_currentPage < onboardingPages.length - 1) {
|
||||||
|
_pageController.nextPage(
|
||||||
|
duration: const Duration(milliseconds: 400),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await _completeOnboarding();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _completeOnboarding() async {
|
||||||
|
await StorageService.setValue(key: 'hasSeenOnboarding', value: 'true');
|
||||||
|
if (mounted) {
|
||||||
|
Navigator.of(context).pushReplacementNamed(
|
||||||
|
Routes.authenticaion,
|
||||||
|
arguments: {'isResetPassword': false},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _skipOnboarding() async {
|
||||||
|
await _completeOnboarding();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final colorScheme = theme.colorScheme;
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
backgroundColor: colorScheme.background,
|
||||||
|
body: SafeArea(
|
||||||
|
child: Stack(
|
||||||
|
children: [
|
||||||
|
Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: PageView.builder(
|
||||||
|
controller: _pageController,
|
||||||
|
itemCount: onboardingPages.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final page = onboardingPages[index];
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 0),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
page.imagePath,
|
||||||
|
height: 400,
|
||||||
|
width: double.infinity,
|
||||||
|
fit: BoxFit.fitWidth,
|
||||||
|
errorBuilder: (context, error, stackTrace) {
|
||||||
|
return Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: colorScheme.primary.withOpacity(0.1),
|
||||||
|
borderRadius: BorderRadius.circular(24),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Icon(
|
||||||
|
Icons.image_outlined,
|
||||||
|
size: 80,
|
||||||
|
color: colorScheme.primary.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
SizedBox(
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
CustomPaint(
|
||||||
|
size: const Size(80, 80),
|
||||||
|
painter: OnboardingIndicatorPainter(
|
||||||
|
pageCount: onboardingPages.length,
|
||||||
|
currentPage: _currentPage,
|
||||||
|
activeColor:
|
||||||
|
const Color.fromARGB(255, 1, 35, 72),
|
||||||
|
inactiveColor:
|
||||||
|
const Color.fromARGB(255, 0, 126, 167)),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
onTap: _onNextTap,
|
||||||
|
child: Container(
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Color.fromARGB(255, 1, 35, 72),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: SvgPicture.asset(
|
||||||
|
'lib/assets/icons/Arrow - Right 2.svg',
|
||||||
|
width: 35,
|
||||||
|
height: 35,
|
||||||
|
colorFilter: const ColorFilter.mode(
|
||||||
|
Colors.white,
|
||||||
|
BlendMode.srcIn,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
page.title,
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style:
|
||||||
|
theme.textTheme.headlineMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: const Color.fromARGB(255, 0, 126, 167),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
page.description,
|
||||||
|
textDirection: TextDirection.rtl,
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
style: theme.textTheme.bodyLarge?.copyWith(
|
||||||
|
color: const Color.fromARGB(255, 41, 41, 41),
|
||||||
|
height: 1.6,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
if (_currentPage > 0)
|
||||||
|
Positioned(
|
||||||
|
top: 16,
|
||||||
|
left: 16,
|
||||||
|
child: Container(
|
||||||
|
height: 40,
|
||||||
|
width: 40,
|
||||||
|
decoration: const BoxDecoration(
|
||||||
|
color: Color.fromARGB(230, 234, 234, 233),
|
||||||
|
shape: BoxShape.rectangle,
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(4)),
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: IconButton(
|
||||||
|
icon: SvgPicture.asset('lib/assets/icons/arrow-left.svg'),
|
||||||
|
onPressed: () {
|
||||||
|
_pageController.previousPage(
|
||||||
|
duration: const Duration(milliseconds: 400),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Positioned(
|
||||||
|
bottom: 40,
|
||||||
|
left: 24,
|
||||||
|
right: 24,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
flex: 2,
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: _onNextTap,
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
backgroundColor: colorScheme.primary,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'بعدی',
|
||||||
|
style: theme.textTheme.titleMedium?.copyWith(
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
flex: 1,
|
||||||
|
child: OutlinedButton(
|
||||||
|
onPressed: _skipOnboarding,
|
||||||
|
style: OutlinedButton.styleFrom(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
|
side: BorderSide(
|
||||||
|
color: colorScheme.primary.withOpacity(0.3),
|
||||||
|
),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
'صرف نظر کردن',
|
||||||
|
style: theme.textTheme.titleMedium?.copyWith(
|
||||||
|
color: Colors.black,
|
||||||
|
fontWeight: FontWeight.normal,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// ignore_for_file: deprecated_member_use, undefined_prefixed_name
|
// ignore_for_file: deprecated_member_use, undefined_prefixed_name
|
||||||
|
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui_web' as ui_web;
|
||||||
|
|
||||||
import 'package:didvan/models/view/app_bar_data.dart';
|
import 'package:didvan/models/view/app_bar_data.dart';
|
||||||
import 'package:didvan/services/media/media.dart';
|
import 'package:didvan/services/media/media.dart';
|
||||||
|
|
@ -48,7 +48,7 @@ class _StudioDetailsState extends State<StudioDetails> {
|
||||||
if (state.studio.type == 'video') {
|
if (state.studio.type == 'video') {
|
||||||
debugPrint("Playing video from URL: ${state.studio.link}");
|
debugPrint("Playing video from URL: ${state.studio.link}");
|
||||||
|
|
||||||
ui.platformViewRegistry.registerViewFactory(
|
ui_web.platformViewRegistry.registerViewFactory(
|
||||||
viewType,
|
viewType,
|
||||||
(int viewId) => html.IFrameElement()
|
(int viewId) => html.IFrameElement()
|
||||||
..allowFullscreen = true
|
..allowFullscreen = true
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: deprecated_member_use
|
||||||
|
|
||||||
import 'package:didvan/config/design_config.dart';
|
import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/constants/assets.dart';
|
import 'package:didvan/constants/assets.dart';
|
||||||
|
|
@ -204,20 +206,22 @@ class _NewPasswordScreenState extends State<NewPasswordScreen>
|
||||||
child: FadeTransition(
|
child: FadeTransition(
|
||||||
opacity: _fadeAnimation,
|
opacity: _fadeAnimation,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 25.0),
|
padding: const EdgeInsets.symmetric(horizontal: 0.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Center(
|
Center(
|
||||||
child: Image.asset(
|
child: Image.asset(
|
||||||
'lib/assets/images/empty_states/change password.png',
|
'lib/assets/images/empty_states/Change_Password.png',
|
||||||
width: 300,
|
fit: BoxFit.fitWidth,
|
||||||
height: 300,
|
width: 500,
|
||||||
|
height: 400,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding:
|
||||||
|
const EdgeInsets.only(right: 25, bottom: 8),
|
||||||
child: DidvanText(
|
child: DidvanText(
|
||||||
'تغییر رمز عبور',
|
'تغییر رمز عبور',
|
||||||
style: Theme.of(context)
|
style: Theme.of(context)
|
||||||
|
|
|
||||||
|
|
@ -235,24 +235,20 @@ class _VerifyOtpScreenState extends State<VerifyOtpScreen>
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
children: [
|
children: [
|
||||||
const SizedBox(height: 24),
|
|
||||||
SlideTransition(
|
SlideTransition(
|
||||||
position: _contentSlideAnimation,
|
position: _contentSlideAnimation,
|
||||||
child: FadeTransition(
|
child: FadeTransition(
|
||||||
opacity: _fadeAnimation,
|
opacity: _fadeAnimation,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 25.0),
|
padding: const EdgeInsets.symmetric(horizontal: 10.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Image.asset(
|
||||||
padding: const EdgeInsets.all(8.0),
|
'lib/assets/images/empty_states/Change_Password.png',
|
||||||
child: Center(
|
fit: BoxFit.fitWidth,
|
||||||
child: Image.asset(
|
width: double.infinity,
|
||||||
'lib/assets/images/empty_states/change password.png',
|
height: 400,
|
||||||
height: 320,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
DidvanText(
|
DidvanText(
|
||||||
|
|
|
||||||
|
|
@ -204,9 +204,14 @@ class _DirectListState extends State<DirectList>
|
||||||
),
|
),
|
||||||
childCount: state.chatRooms.length,
|
childCount: state.chatRooms.length,
|
||||||
enableEmptyState: state.chatRooms.isEmpty,
|
enableEmptyState: state.chatRooms.isEmpty,
|
||||||
emptyState: EmptyState(
|
paddingEmptyState: 0,
|
||||||
asset: Assets.emptyBookmark,
|
emptyState: const EmptyState(
|
||||||
title: 'پیامی وجود ندارد',
|
asset: 'lib/assets/images/empty_states/Empty_List.png',
|
||||||
|
height: 500,
|
||||||
|
title: 'لیست خالی است',
|
||||||
|
titleColor: Color.fromARGB(255, 0, 126, 167),
|
||||||
|
subtitle:
|
||||||
|
'در حال حاضر آیتمی در این بخش ثبت نشده است. هر زمان مورد جدیدی اضافه شود، در اینجا نمایش داده میشود.',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,9 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
Navigator.of(context).pushNamed(Routes.bookmarks);
|
Navigator.of(context).pushNamed(Routes.bookmarks);
|
||||||
},
|
},
|
||||||
icon: SvgPicture.asset(
|
icon: SvgPicture.asset(
|
||||||
'lib/assets/icons/hugeicons_telescope-01.svg',color: Theme.of(context).colorScheme.caption,)),
|
'lib/assets/icons/hugeicons_telescope-01.svg',
|
||||||
|
color: Theme.of(context).colorScheme.caption,
|
||||||
|
)),
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
icon: SvgPicture.asset(
|
icon: SvgPicture.asset(
|
||||||
|
|
@ -223,7 +225,9 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
.textTheme
|
.textTheme
|
||||||
.titleMedium
|
.titleMedium
|
||||||
?.copyWith(
|
?.copyWith(
|
||||||
color: DesignConfig.isDark? Colors.white : Colors.black,
|
color: DesignConfig.isDark
|
||||||
|
? Colors.white
|
||||||
|
: Colors.black,
|
||||||
fontWeight: FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
fontSize: 15),
|
fontSize: 15),
|
||||||
),
|
),
|
||||||
|
|
@ -233,8 +237,9 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
MenuOption(
|
MenuOption(
|
||||||
titleWidget: DidvanText('ویرایش پروفایل',
|
titleWidget: DidvanText('ویرایش پروفایل',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color:
|
color: Theme.of(context)
|
||||||
Theme.of(context).colorScheme.caption)),
|
.colorScheme
|
||||||
|
.caption)),
|
||||||
onTap: () => Navigator.of(context)
|
onTap: () => Navigator.of(context)
|
||||||
.pushNamed(Routes.editProfile),
|
.pushNamed(Routes.editProfile),
|
||||||
iconWidget: SvgPicture.asset(
|
iconWidget: SvgPicture.asset(
|
||||||
|
|
@ -244,8 +249,9 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
MenuOption(
|
MenuOption(
|
||||||
titleWidget: DidvanText('تغییر رمز عبور ',
|
titleWidget: DidvanText('تغییر رمز عبور ',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color:
|
color: Theme.of(context)
|
||||||
Theme.of(context).colorScheme.caption)),
|
.colorScheme
|
||||||
|
.caption)),
|
||||||
iconWidget:
|
iconWidget:
|
||||||
SvgPicture.asset('lib/assets/icons/lock.svg'),
|
SvgPicture.asset('lib/assets/icons/lock.svg'),
|
||||||
onTap: () => Navigator.of(context).pushNamed(
|
onTap: () => Navigator.of(context).pushNamed(
|
||||||
|
|
@ -314,7 +320,9 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
.textTheme
|
.textTheme
|
||||||
.titleMedium
|
.titleMedium
|
||||||
?.copyWith(
|
?.copyWith(
|
||||||
color: DesignConfig.isDark? Colors.white : Colors.black,
|
color: DesignConfig.isDark
|
||||||
|
? Colors.white
|
||||||
|
: Colors.black,
|
||||||
fontWeight: FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
fontSize: 15),
|
fontSize: 15),
|
||||||
),
|
),
|
||||||
|
|
@ -324,8 +332,9 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
MenuOption(
|
MenuOption(
|
||||||
titleWidget: DidvanText('تنظیمات اعلانها',
|
titleWidget: DidvanText('تنظیمات اعلانها',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color:
|
color: Theme.of(context)
|
||||||
Theme.of(context).colorScheme.caption)),
|
.colorScheme
|
||||||
|
.caption)),
|
||||||
onTap: () => Navigator.of(context)
|
onTap: () => Navigator.of(context)
|
||||||
.pushNamed(Routes.notificationSettings),
|
.pushNamed(Routes.notificationSettings),
|
||||||
iconWidget: SvgPicture.asset(
|
iconWidget: SvgPicture.asset(
|
||||||
|
|
@ -335,7 +344,9 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
MenuOption(
|
MenuOption(
|
||||||
titleWidget: DidvanText('ظاهر اپلیکیشن',
|
titleWidget: DidvanText('ظاهر اپلیکیشن',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.caption)),
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.caption)),
|
||||||
iconWidget: SvgPicture.asset(
|
iconWidget: SvgPicture.asset(
|
||||||
'lib/assets/icons/fluent_paint-brush-16-regular.svg'),
|
'lib/assets/icons/fluent_paint-brush-16-regular.svg'),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
|
|
@ -347,8 +358,7 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
? 'lib/assets/icons/arrow-up2.svg'
|
? 'lib/assets/icons/arrow-up2.svg'
|
||||||
: 'lib/assets/icons/arrow-down.svg',
|
: 'lib/assets/icons/arrow-down.svg',
|
||||||
height: 25,
|
height: 25,
|
||||||
color:
|
color: Theme.of(context).colorScheme.caption,
|
||||||
Theme.of(context).colorScheme.caption,
|
|
||||||
)),
|
)),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
AnimatedVisibility(
|
AnimatedVisibility(
|
||||||
|
|
@ -370,7 +380,9 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
: 'ایران یکان',
|
: 'ایران یکان',
|
||||||
titleWidget: DidvanText('فونت',
|
titleWidget: DidvanText('فونت',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.caption)),
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.caption)),
|
||||||
onTap: _showFontFamilyBottomSheet,
|
onTap: _showFontFamilyBottomSheet,
|
||||||
iconWidget: SvgPicture.asset(
|
iconWidget: SvgPicture.asset(
|
||||||
'lib/assets/icons/hugeicons_edit-01.svg'),
|
'lib/assets/icons/hugeicons_edit-01.svg'),
|
||||||
|
|
@ -382,10 +394,11 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
child: MenuOption(
|
child: MenuOption(
|
||||||
suffix: fontScaleSuffix(
|
suffix: fontScaleSuffix(
|
||||||
state.fontSizeScale),
|
state.fontSizeScale),
|
||||||
titleWidget: DidvanText(
|
titleWidget: DidvanText('اندازه متن',
|
||||||
'اندازه متن',
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.caption)),
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.caption)),
|
||||||
onTap: _showFontScaleBottomSheet,
|
onTap: _showFontScaleBottomSheet,
|
||||||
iconWidget: SvgPicture.asset(
|
iconWidget: SvgPicture.asset(
|
||||||
'lib/assets/icons/smallcaps.svg'),
|
'lib/assets/icons/smallcaps.svg'),
|
||||||
|
|
@ -404,7 +417,9 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
DidvanText('تم برنامه',
|
DidvanText('تم برنامه',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.caption)),
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.caption)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|
@ -466,7 +481,9 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
.textTheme
|
.textTheme
|
||||||
.titleMedium
|
.titleMedium
|
||||||
?.copyWith(
|
?.copyWith(
|
||||||
color: DesignConfig.isDark? Colors.white : Colors.black,
|
color: DesignConfig.isDark
|
||||||
|
? Colors.white
|
||||||
|
: Colors.black,
|
||||||
fontWeight: FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
fontSize: 15),
|
fontSize: 15),
|
||||||
),
|
),
|
||||||
|
|
@ -478,8 +495,9 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
'lib/assets/icons/info-circle.svg'),
|
'lib/assets/icons/info-circle.svg'),
|
||||||
titleWidget: DidvanText('درباره دیدوان',
|
titleWidget: DidvanText('درباره دیدوان',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color:
|
color: Theme.of(context)
|
||||||
Theme.of(context).colorScheme.caption)),
|
.colorScheme
|
||||||
|
.caption)),
|
||||||
onTap: () => launchUrlString(
|
onTap: () => launchUrlString(
|
||||||
'https://didvan.com/#info',
|
'https://didvan.com/#info',
|
||||||
mode: LaunchMode.inAppWebView),
|
mode: LaunchMode.inAppWebView),
|
||||||
|
|
@ -490,7 +508,9 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
'lib/assets/icons/call-calling.svg'),
|
'lib/assets/icons/call-calling.svg'),
|
||||||
titleWidget: DidvanText('تماس با ما',
|
titleWidget: DidvanText('تماس با ما',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.caption)),
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.caption)),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context)
|
Navigator.of(context)
|
||||||
.pushNamed(Routes.directList)
|
.pushNamed(Routes.directList)
|
||||||
|
|
@ -599,8 +619,9 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
'lib/assets/icons/security-safe.svg'),
|
'lib/assets/icons/security-safe.svg'),
|
||||||
titleWidget: DidvanText('حریم خصوصی',
|
titleWidget: DidvanText('حریم خصوصی',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color:
|
color: Theme.of(context)
|
||||||
Theme.of(context).colorScheme.caption)),
|
.colorScheme
|
||||||
|
.caption)),
|
||||||
onTap: () => launchUrlString(
|
onTap: () => launchUrlString(
|
||||||
'https://didvan.com/terms-of-use#privacy',
|
'https://didvan.com/terms-of-use#privacy',
|
||||||
mode: LaunchMode.inAppWebView),
|
mode: LaunchMode.inAppWebView),
|
||||||
|
|
@ -685,7 +706,8 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: 'انتخاب فونت برنامه',
|
title: 'انتخاب فونت برنامه',
|
||||||
titleIcon: DidvanIcons.font_regular,
|
titleIconWidget:
|
||||||
|
SvgPicture.asset('lib/assets/icons/hugeicons_edit-01.svg'),
|
||||||
onDismissed: () => state.fontFamily = family,
|
onDismissed: () => state.fontFamily = family,
|
||||||
onConfirmed: () => themeProvider.fontFamily = state.fontFamily,
|
onConfirmed: () => themeProvider.fontFamily = state.fontFamily,
|
||||||
),
|
),
|
||||||
|
|
@ -734,7 +756,7 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
title: 'انتخاب اندازه متن',
|
title: 'انتخاب اندازه متن',
|
||||||
titleIcon: DidvanIcons.font_regular,
|
titleIconWidget: SvgPicture.asset('lib/assets/icons/smallcaps.svg'),
|
||||||
onDismissed: () => state.fontSizeScale = scale,
|
onDismissed: () => state.fontSizeScale = scale,
|
||||||
onConfirmed: () => themeProvider.fontScale = state.fontSizeScale,
|
onConfirmed: () => themeProvider.fontScale = state.fontSizeScale,
|
||||||
),
|
),
|
||||||
|
|
@ -760,7 +782,7 @@ class _ProfilePageState extends State<ProfilePage>
|
||||||
width: 70,
|
width: 70,
|
||||||
height: 37,
|
height: 37,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: const Color.fromARGB(255, 200, 224, 244), // بکگراند آبی
|
color: const Color.fromARGB(255, 200, 224, 244),
|
||||||
borderRadius: BorderRadius.circular(22),
|
borderRadius: BorderRadius.circular(22),
|
||||||
),
|
),
|
||||||
child: AnimatedAlign(
|
child: AnimatedAlign(
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
// ignore_for_file: deprecated_member_use, avoid_print
|
||||||
|
|
||||||
import 'package:didvan/config/design_config.dart';
|
import 'package:didvan/config/design_config.dart';
|
||||||
import 'package:didvan/config/theme_data.dart';
|
import 'package:didvan/config/theme_data.dart';
|
||||||
import 'package:didvan/constants/assets.dart';
|
import 'package:didvan/constants/assets.dart';
|
||||||
|
|
@ -36,7 +38,6 @@ class _SplashState extends State<Splash> with TickerProviderStateMixin {
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
// Initialize animation controller
|
|
||||||
_pulseController = AnimationController(
|
_pulseController = AnimationController(
|
||||||
duration: const Duration(milliseconds: 1500),
|
duration: const Duration(milliseconds: 1500),
|
||||||
vsync: this,
|
vsync: this,
|
||||||
|
|
@ -50,7 +51,6 @@ class _SplashState extends State<Splash> with TickerProviderStateMixin {
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
));
|
));
|
||||||
|
|
||||||
// Start pulse animation
|
|
||||||
_pulseController.repeat(reverse: true);
|
_pulseController.repeat(reverse: true);
|
||||||
|
|
||||||
themeProvider = context.read<ThemeProvider>();
|
themeProvider = context.read<ThemeProvider>();
|
||||||
|
|
@ -69,7 +69,7 @@ class _SplashState extends State<Splash> with TickerProviderStateMixin {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
final isDark = Theme.of(context).brightness == Brightness.light;
|
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||||
|
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: DesignConfig.systemUiOverlayStyle.copyWith(
|
value: DesignConfig.systemUiOverlayStyle.copyWith(
|
||||||
|
|
@ -79,309 +79,136 @@ class _SplashState extends State<Splash> with TickerProviderStateMixin {
|
||||||
),
|
),
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
backgroundColor: colorScheme.background,
|
backgroundColor: colorScheme.background,
|
||||||
body: Container(
|
body: SafeArea(
|
||||||
decoration: BoxDecoration(
|
child: Center(
|
||||||
gradient: LinearGradient(
|
child: Column(
|
||||||
begin: Alignment.topLeft,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
end: Alignment.bottomRight,
|
children: [
|
||||||
colors: [
|
const Spacer(flex: 2),
|
||||||
colorScheme.background,
|
|
||||||
colorScheme.background.withOpacity(0.8),
|
|
||||||
colorScheme.primary.withOpacity(0.05),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
// Floating particles background
|
|
||||||
_buildFloatingParticles(colorScheme),
|
|
||||||
|
|
||||||
SafeArea(
|
TweenAnimationBuilder<double>(
|
||||||
child: Column(
|
duration: const Duration(milliseconds: 800),
|
||||||
children: [
|
tween: Tween(begin: 0.0, end: 1.0),
|
||||||
// Top section with logo
|
builder: (context, value, child) {
|
||||||
Expanded(
|
return Opacity(
|
||||||
flex: 3,
|
opacity: value,
|
||||||
child: Container(
|
child: AnimatedBuilder(
|
||||||
width: double.infinity,
|
animation: _pulseAnimation,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
builder: (context, child) {
|
||||||
child: Column(
|
return Transform.scale(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
scale: _pulseAnimation.value,
|
||||||
children: [
|
child: SvgPicture.asset(
|
||||||
// Logo with subtle animation
|
Assets.horizontalLogoWithText,
|
||||||
TweenAnimationBuilder<double>(
|
height: 80,
|
||||||
duration: const Duration(milliseconds: 800),
|
|
||||||
tween: Tween(begin: 0.0, end: 1.0),
|
|
||||||
builder: (context, value, child) {
|
|
||||||
return Transform.scale(
|
|
||||||
scale: 0.7 + (0.3 * value),
|
|
||||||
child: Opacity(
|
|
||||||
opacity: value,
|
|
||||||
child: AnimatedBuilder(
|
|
||||||
animation: _pulseAnimation,
|
|
||||||
builder: (context, child) {
|
|
||||||
return Transform.scale(
|
|
||||||
scale: _pulseAnimation.value,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: BorderRadius.circular(20),
|
|
||||||
gradient: RadialGradient(
|
|
||||||
colors: [
|
|
||||||
colorScheme.primary.withOpacity(0.05),
|
|
||||||
Colors.transparent,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: colorScheme.primary.withOpacity(0.15),
|
|
||||||
blurRadius: 30,
|
|
||||||
spreadRadius: 8,
|
|
||||||
offset: const Offset(0, 15),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
child: SvgPicture.asset(
|
|
||||||
Assets.horizontalLogoWithText,
|
|
||||||
height: 80,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Bottom section with loading/error
|
|
||||||
Expanded(
|
|
||||||
flex: 2,
|
|
||||||
child: Container(
|
|
||||||
width: double.infinity,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 48),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
if (!_errorOccured) ...[
|
|
||||||
// Modern loading indicator
|
|
||||||
_buildModernLoader(colorScheme),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
// Loading text with fade animation
|
|
||||||
AnimatedBuilder(
|
|
||||||
animation: _pulseController,
|
|
||||||
builder: (context, child) {
|
|
||||||
return Opacity(
|
|
||||||
opacity: 0.4 + (0.4 * _pulseController.value),
|
|
||||||
child: Text(
|
|
||||||
'در حال بارگذاری...',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: colorScheme.checkFav.withOpacity(0.7),
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
letterSpacing: 0.8,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
|
|
||||||
if (_errorOccured) ...[
|
|
||||||
// Error state with simple and clean design
|
|
||||||
Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
// Simple error icon
|
|
||||||
Container(
|
|
||||||
width: 60,
|
|
||||||
height: 60,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: colorScheme.error.withOpacity(0.1),
|
|
||||||
borderRadius: BorderRadius.circular(30),
|
|
||||||
),
|
|
||||||
child: Icon(
|
|
||||||
Icons.wifi_off_rounded,
|
|
||||||
size: 28,
|
|
||||||
color: colorScheme.error,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
// Clean error message
|
|
||||||
Text(
|
|
||||||
'مشکل در اتصال',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: colorScheme.checkFav,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
'لطفاً اتصال خود را بررسی کنید',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: colorScheme.checkFav.withOpacity(0.6),
|
|
||||||
fontWeight: FontWeight.w400,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
// Simple retry button
|
|
||||||
_buildRetryButton(colorScheme),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
// Brand tagline at bottom
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 32),
|
|
||||||
child: TweenAnimationBuilder<double>(
|
|
||||||
duration: const Duration(milliseconds: 1000),
|
|
||||||
tween: Tween(begin: 0.0, end: 1.0),
|
|
||||||
builder: (context, value, child) {
|
|
||||||
return Opacity(
|
|
||||||
opacity: value * 0.6,
|
|
||||||
child: Text(
|
|
||||||
'توسعه یافته توسط فرتاک',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
color: colorScheme.checkFav,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
letterSpacing: 1,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
|
||||||
|
const SizedBox(height: 60),
|
||||||
|
|
||||||
|
if (!_errorOccured) ...[
|
||||||
|
SizedBox(
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
child: Image.asset(
|
||||||
|
Assets.loadingAnimation,
|
||||||
|
fit: BoxFit.contain,
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
const SizedBox(height: 24),
|
||||||
),
|
AnimatedBuilder(
|
||||||
],
|
animation: _pulseController,
|
||||||
),
|
builder: (context, child) {
|
||||||
),
|
return Opacity(
|
||||||
),
|
opacity: 0.4 + (0.4 * _pulseController.value),
|
||||||
);
|
child: Text(
|
||||||
}
|
'در حال بارگذاری...',
|
||||||
|
style: TextStyle(
|
||||||
Widget _buildFloatingParticles(ColorScheme colorScheme) {
|
fontSize: 14,
|
||||||
return AnimatedBuilder(
|
color: colorScheme.onBackground.withOpacity(0.6),
|
||||||
animation: _pulseController,
|
fontWeight: FontWeight.w400,
|
||||||
builder: (context, child) {
|
),
|
||||||
return Stack(
|
),
|
||||||
children: [
|
);
|
||||||
// Particle 1
|
},
|
||||||
Positioned(
|
),
|
||||||
top: 100 + (20 * _pulseController.value),
|
] else ...[
|
||||||
right: 50 + (10 * _pulseController.value),
|
Container(
|
||||||
child: Container(
|
width: 56,
|
||||||
width: 6,
|
height: 56,
|
||||||
height: 6,
|
decoration: BoxDecoration(
|
||||||
decoration: BoxDecoration(
|
color: colorScheme.error.withOpacity(0.1),
|
||||||
color: colorScheme.primary.withOpacity(0.3),
|
shape: BoxShape.circle,
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Particle 2
|
|
||||||
Positioned(
|
|
||||||
top: 200 + (15 * (1 - _pulseController.value)),
|
|
||||||
left: 80 + (8 * _pulseController.value),
|
|
||||||
child: Container(
|
|
||||||
width: 4,
|
|
||||||
height: 4,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: colorScheme.secondary.withOpacity(0.4),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Particle 3
|
|
||||||
Positioned(
|
|
||||||
bottom: 180 + (25 * _pulseController.value),
|
|
||||||
right: 100 + (12 * (1 - _pulseController.value)),
|
|
||||||
child: Container(
|
|
||||||
width: 8,
|
|
||||||
height: 8,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: colorScheme.primary.withOpacity(0.2),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
boxShadow: [
|
|
||||||
BoxShadow(
|
|
||||||
color: colorScheme.primary.withOpacity(0.1),
|
|
||||||
blurRadius: 10,
|
|
||||||
spreadRadius: 3,
|
|
||||||
),
|
),
|
||||||
],
|
child: Icon(
|
||||||
),
|
Icons.wifi_off_rounded,
|
||||||
),
|
size: 24,
|
||||||
),
|
color: colorScheme.error,
|
||||||
// Particle 4
|
),
|
||||||
Positioned(
|
),
|
||||||
bottom: 120 + (18 * (1 - _pulseController.value)),
|
const SizedBox(height: 16),
|
||||||
left: 60 + (15 * _pulseController.value),
|
Text(
|
||||||
child: Container(
|
'مشکل در اتصال',
|
||||||
width: 5,
|
style: TextStyle(
|
||||||
height: 5,
|
fontSize: 16,
|
||||||
decoration: BoxDecoration(
|
color: colorScheme.onBackground,
|
||||||
color: colorScheme.tertiary.withOpacity(0.35),
|
fontWeight: FontWeight.w500,
|
||||||
shape: BoxShape.circle,
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 8),
|
||||||
),
|
Text(
|
||||||
],
|
'لطفاً اتصال اینترنت خود را بررسی کنید',
|
||||||
);
|
style: TextStyle(
|
||||||
},
|
fontSize: 13,
|
||||||
);
|
color: colorScheme.onBackground.withOpacity(0.6),
|
||||||
}
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() {
|
||||||
|
_errorOccured = false;
|
||||||
|
});
|
||||||
|
_initialize(themeProvider, userProvider, mediaProvider);
|
||||||
|
},
|
||||||
|
style: ElevatedButton.styleFrom(
|
||||||
|
backgroundColor: colorScheme.primary,
|
||||||
|
foregroundColor: Colors.white,
|
||||||
|
elevation: 0,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'تلاش مجدد',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
|
||||||
Widget _buildModernLoader(ColorScheme colorScheme) {
|
const Spacer(flex: 3),
|
||||||
// استفاده از loading animation اصلی و قدیمی دیدوان
|
|
||||||
return SizedBox(
|
|
||||||
width: 60,
|
|
||||||
height: 60,
|
|
||||||
child: Image.asset(
|
|
||||||
Assets.loadingAnimation,
|
|
||||||
width: 60,
|
|
||||||
height: 60,
|
|
||||||
fit: BoxFit.contain,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildRetryButton(ColorScheme colorScheme) {
|
Padding(
|
||||||
return SizedBox(
|
padding: const EdgeInsets.only(bottom: 24),
|
||||||
width: 140,
|
child: Text(
|
||||||
height: 44,
|
'توسعه یافته توسط فرتاک',
|
||||||
child: ElevatedButton(
|
style: TextStyle(
|
||||||
onPressed: () {
|
fontSize: 12,
|
||||||
setState(() {
|
color: colorScheme.onBackground.withOpacity(0.4),
|
||||||
_errorOccured = false;
|
fontWeight: FontWeight.w400,
|
||||||
});
|
),
|
||||||
_initialize(themeProvider, userProvider, mediaProvider);
|
),
|
||||||
},
|
),
|
||||||
style: ElevatedButton.styleFrom(
|
],
|
||||||
backgroundColor: colorScheme.primary,
|
),
|
||||||
foregroundColor: colorScheme.onPrimary,
|
|
||||||
elevation: 0,
|
|
||||||
shadowColor: Colors.transparent,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(22),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: const Text(
|
|
||||||
'تلاش مجدد',
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 14,
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -408,6 +235,13 @@ class _SplashState extends State<Splash> with TickerProviderStateMixin {
|
||||||
|
|
||||||
await AppInitializer.setupServices(navigatorKey.currentContext!);
|
await AppInitializer.setupServices(navigatorKey.currentContext!);
|
||||||
|
|
||||||
|
final String? hasSeenOnboarding = await StorageService.getValue(key: 'hasSeenOnboarding');
|
||||||
|
|
||||||
|
if (hasSeenOnboarding == null || hasSeenOnboarding != 'true') {
|
||||||
|
await navigatorKey.currentState!.pushReplacementNamed(Routes.onboarding);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
final String? token = await userProvider.setAndGetToken();
|
final String? token = await userProvider.setAndGetToken();
|
||||||
print("detected token as $token");
|
print("detected token as $token");
|
||||||
|
|
||||||
|
|
@ -442,7 +276,6 @@ class _SplashState extends State<Splash> with TickerProviderStateMixin {
|
||||||
print("token route is $token");
|
print("token route is $token");
|
||||||
String extractedPath = initialURI?.path.toString() == '/' ? Routes.home : initialURI?.path.toString() ?? Routes.home;
|
String extractedPath = initialURI?.path.toString() == '/' ? Routes.home : initialURI?.path.toString() ?? Routes.home;
|
||||||
final String destinationRoute = token == null ? Routes.authenticaion : extractedPath;
|
final String destinationRoute = token == null ? Routes.authenticaion : extractedPath;
|
||||||
// Set showDialogs to false to prevent welcome popups
|
|
||||||
dynamic routeArguments = token == null ? {'isResetPassword': false} : {'showDialogs': false};
|
dynamic routeArguments = token == null ? {'isResetPassword': false} : {'showDialogs': false};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -444,10 +444,13 @@ class _AiChatDialogState extends State<AiChatDialog>
|
||||||
),
|
),
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: IconButton(
|
child: GestureDetector(
|
||||||
icon: const Icon(Icons.headset_mic_rounded,
|
child: Padding(
|
||||||
color: Colors.white, size: 20),
|
padding: const EdgeInsets.all(6.0),
|
||||||
onPressed: () {
|
child: SvgPicture.asset('lib/assets/icons/voice-square.svg',
|
||||||
|
color: Colors.white, height: 35),
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
|
@ -455,8 +458,6 @@ class _AiChatDialogState extends State<AiChatDialog>
|
||||||
builder: (context) => const AiVoiceChatDialog(),
|
builder: (context) => const AiVoiceChatDialog(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
splashRadius: 20,
|
|
||||||
tooltip: 'گفتگوی صوتی',
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Container(
|
Container(
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:didvan/constants/assets.dart';
|
|
||||||
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
|
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
@ -8,11 +7,21 @@ class EmptyConnection extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return EmptyState(
|
return Column(
|
||||||
asset: Assets.emptyConnection,
|
children: [
|
||||||
title: 'ارتباط با اینترنت قطع شد...',
|
const SizedBox(
|
||||||
action: onRetry,
|
height: 65,
|
||||||
buttonTitle: 'تلاش دوباره',
|
),
|
||||||
|
EmptyState(
|
||||||
|
asset: 'lib/assets/images/empty_states/Connection.png',
|
||||||
|
height: 550,
|
||||||
|
title: 'اتصال اینترنت برقرار نیست',
|
||||||
|
titleColor: const Color.fromARGB(255, 0, 126, 167),
|
||||||
|
action: onRetry,
|
||||||
|
subtitle: 'لطفاً اتصال وایفای یا داده تلفن همراه خود را بررسی کنید.',
|
||||||
|
buttonTitle: 'تلاش مجدد',
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:didvan/constants/assets.dart';
|
|
||||||
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
|
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
@ -7,9 +6,13 @@ class EmptyList extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return EmptyState(
|
return const EmptyState(
|
||||||
asset: Assets.emptyBookmark,
|
asset: 'lib/assets/images/empty_states/Bookmark2.png',
|
||||||
title: 'لیست خالی است',
|
height: 600,
|
||||||
|
title: 'رصدخانه شما هنوز خالی است',
|
||||||
|
titleColor: Color.fromARGB(255, 0, 126, 167),
|
||||||
|
subtitle:
|
||||||
|
"اینجا فضای شخصی شما برای ذخیره و دسترسی سریع به مهمترین تحلیلها، گزارشها و رسانههاست. برای افزودن محتوا، کافیست روی آیکون بوکمارک در کنار هر مطلب کلیک کنید.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import 'package:didvan/constants/assets.dart';
|
|
||||||
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
|
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
|
@ -9,10 +8,15 @@ class EmptyResult extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return EmptyState(
|
return EmptyState(
|
||||||
asset: Assets.emptyResult,
|
asset: 'lib/assets/images/empty_states/Empty-Result.png',
|
||||||
title: 'نتیجهای پیدا نشد',
|
height: 600,
|
||||||
buttonTitle: 'تغییر جستجو',
|
title: 'نتیجهای یافت نشد',
|
||||||
action: onNewSearch,
|
subtitle:
|
||||||
|
'لطفاً از کلمات کلیدی دیگری استفاده کنید یا املای کلمه را بررسی نمایید.',
|
||||||
|
buttonTitle: 'بازگشت به صفحه اصلی',
|
||||||
|
action: () {
|
||||||
|
Navigator.of(context).popUntil((route) => route.isFirst);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,29 +26,50 @@ class EmptyState extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final isSvg = asset.toLowerCase().endsWith('.svg');
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(height: height ?? 210, child: SvgPicture.asset(asset)),
|
SizedBox(
|
||||||
const SizedBox(height: 16),
|
width: double.infinity,
|
||||||
DidvanText(
|
height: height ?? 300,
|
||||||
title,
|
child: isSvg
|
||||||
style: Theme.of(context).textTheme.displaySmall,
|
? SvgPicture.asset(asset, fit: BoxFit.contain)
|
||||||
color: titleColor ?? Theme.of(context).colorScheme.caption,
|
: Image.asset(asset,
|
||||||
|
fit: BoxFit.fitWidth, height: height, width: double.infinity),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(right: 20, left: 20),
|
||||||
|
child: DidvanText(
|
||||||
|
title,
|
||||||
|
style: Theme.of(context).textTheme.displaySmall,
|
||||||
|
color: titleColor ?? Theme.of(context).colorScheme.caption,
|
||||||
|
textAlign: TextAlign.start,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
if (subtitle != null) const SizedBox(height: 8),
|
if (subtitle != null) const SizedBox(height: 8),
|
||||||
if (subtitle != null)
|
if (subtitle != null)
|
||||||
DidvanText(
|
Padding(
|
||||||
subtitle!,
|
padding: const EdgeInsets.only(right: 20, left: 20),
|
||||||
color: Theme.of(context).colorScheme.caption,
|
child: DidvanText(
|
||||||
|
subtitle!,
|
||||||
|
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)
|
||||||
DidvanButton(
|
Center(
|
||||||
height: 40,
|
child: Padding(
|
||||||
onPressed: action,
|
padding: const EdgeInsets.all(25.0),
|
||||||
title: buttonTitle,
|
child: DidvanButton(
|
||||||
width: 112,
|
height: 50,
|
||||||
|
onPressed: action,
|
||||||
|
title: buttonTitle,
|
||||||
|
width: double.infinity,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -148,6 +148,7 @@ flutter:
|
||||||
- lib/assets/images/records/
|
- lib/assets/images/records/
|
||||||
- lib/assets/images/empty_states/
|
- lib/assets/images/empty_states/
|
||||||
- lib/assets/images/features/
|
- lib/assets/images/features/
|
||||||
|
- lib/assets/images/onboarding/
|
||||||
- lib/assets/icons/
|
- lib/assets/icons/
|
||||||
- lib/assets/animations/
|
- lib/assets/animations/
|
||||||
- lib/assets/js/
|
- lib/assets/js/
|
||||||
|
|
|
||||||