diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..b12b914
--- /dev/null
+++ b/.vscode/launch.json
@@ -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"
+ ]
+ }
+ ]
+}
diff --git a/lib/assets/icons/Arrow - Right 2.svg b/lib/assets/icons/Arrow - Right 2.svg
new file mode 100644
index 0000000..f263a21
--- /dev/null
+++ b/lib/assets/icons/Arrow - Right 2.svg
@@ -0,0 +1,3 @@
+
diff --git a/lib/assets/icons/RiskRadar.svg b/lib/assets/icons/RiskRadar.svg
new file mode 100644
index 0000000..96422b3
--- /dev/null
+++ b/lib/assets/icons/RiskRadar.svg
@@ -0,0 +1,14 @@
+
diff --git a/lib/assets/icons/login_img.png b/lib/assets/icons/login_img.png
new file mode 100644
index 0000000..6e9f61b
Binary files /dev/null and b/lib/assets/icons/login_img.png differ
diff --git a/lib/assets/images/empty_states/Bookmark2.png b/lib/assets/images/empty_states/Bookmark2.png
new file mode 100644
index 0000000..ca0146a
Binary files /dev/null and b/lib/assets/images/empty_states/Bookmark2.png differ
diff --git a/lib/assets/images/empty_states/Change_Password.png b/lib/assets/images/empty_states/Change_Password.png
new file mode 100644
index 0000000..7619cbb
Binary files /dev/null and b/lib/assets/images/empty_states/Change_Password.png differ
diff --git a/lib/assets/images/empty_states/Connection.png b/lib/assets/images/empty_states/Connection.png
new file mode 100644
index 0000000..0dbcee3
Binary files /dev/null and b/lib/assets/images/empty_states/Connection.png differ
diff --git a/lib/assets/images/empty_states/Empty-Result.png b/lib/assets/images/empty_states/Empty-Result.png
new file mode 100644
index 0000000..91b8eda
Binary files /dev/null and b/lib/assets/images/empty_states/Empty-Result.png differ
diff --git a/lib/assets/images/empty_states/Empty_List.png b/lib/assets/images/empty_states/Empty_List.png
new file mode 100644
index 0000000..c96220d
Binary files /dev/null and b/lib/assets/images/empty_states/Empty_List.png differ
diff --git a/lib/assets/images/empty_states/Image1_fx.jpg b/lib/assets/images/empty_states/Image1_fx.jpg
deleted file mode 100644
index 6a2d427..0000000
Binary files a/lib/assets/images/empty_states/Image1_fx.jpg and /dev/null differ
diff --git a/lib/assets/images/empty_states/Image1_fx.png b/lib/assets/images/empty_states/Image1_fx.png
deleted file mode 100644
index 5f1d379..0000000
Binary files a/lib/assets/images/empty_states/Image1_fx.png and /dev/null differ
diff --git a/lib/assets/images/empty_states/Support.png b/lib/assets/images/empty_states/Support.png
new file mode 100644
index 0000000..0e858f6
Binary files /dev/null and b/lib/assets/images/empty_states/Support.png differ
diff --git a/lib/assets/images/empty_states/bookmark.png b/lib/assets/images/empty_states/bookmark.png
new file mode 100644
index 0000000..8d41f92
Binary files /dev/null and b/lib/assets/images/empty_states/bookmark.png differ
diff --git a/lib/assets/images/empty_states/change password.png b/lib/assets/images/empty_states/change password.png
deleted file mode 100644
index 01d17f5..0000000
Binary files a/lib/assets/images/empty_states/change password.png and /dev/null differ
diff --git a/lib/assets/images/onboarding/1.png b/lib/assets/images/onboarding/1.png
new file mode 100644
index 0000000..8a42968
Binary files /dev/null and b/lib/assets/images/onboarding/1.png differ
diff --git a/lib/assets/images/onboarding/2.png b/lib/assets/images/onboarding/2.png
new file mode 100644
index 0000000..b834179
Binary files /dev/null and b/lib/assets/images/onboarding/2.png differ
diff --git a/lib/assets/images/onboarding/3.png b/lib/assets/images/onboarding/3.png
new file mode 100644
index 0000000..fea3aa7
Binary files /dev/null and b/lib/assets/images/onboarding/3.png differ
diff --git a/lib/assets/images/onboarding/4.png b/lib/assets/images/onboarding/4.png
new file mode 100644
index 0000000..2afefb9
Binary files /dev/null and b/lib/assets/images/onboarding/4.png differ
diff --git a/lib/constants/assets.dart b/lib/constants/assets.dart
index aaf4899..8b5ee28 100644
--- a/lib/constants/assets.dart
+++ b/lib/constants/assets.dart
@@ -75,7 +75,7 @@ class Assets {
static String get emptyConnection =>
'$_baseEmptyStatesPath/connection-$_themeSuffix.svg';
static String get emptyResult =>
- '$_baseEmptyStatesPath/result-$_themeSuffix.svg';
+ 'lib/assets/images/empty_states/Empty_List.png';
static String get fooladWorld =>
'$_baseFeaturesPath/foolad-world-$_themeSuffix.svg';
@@ -84,7 +84,8 @@ class Assets {
static String get risk => '$_baseFeaturesPath/risk-$_themeSuffix.svg';
static String get saha => '$_baseFeaturesPath/saha-$_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 stats => '$_baseFeaturesPath/stats-$_themeSuffix.svg';
static String get tech => '$_baseFeaturesPath/tech-$_themeSuffix.svg';
diff --git a/lib/models/didvan_voice_model.dart b/lib/models/didvan_voice_model.dart
new file mode 100644
index 0000000..2d521da
--- /dev/null
+++ b/lib/models/didvan_voice_model.dart
@@ -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 json) {
+ return DidvanVoiceModel(
+ id: json['id'],
+ title: json['title'],
+ file: json['file'],
+ image: json['image'],
+ publishedAt: json['publishedAt'],
+ );
+ }
+}
diff --git a/lib/models/onboarding_entity.dart b/lib/models/onboarding_entity.dart
new file mode 100644
index 0000000..a702b20
--- /dev/null
+++ b/lib/models/onboarding_entity.dart
@@ -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,
+ });
+}
diff --git a/lib/models/top_banner_model.dart b/lib/models/top_banner_model.dart
new file mode 100644
index 0000000..eb6b1a4
--- /dev/null
+++ b/lib/models/top_banner_model.dart
@@ -0,0 +1,16 @@
+class TopBannerModel {
+ final int id;
+ final String image;
+
+ TopBannerModel({
+ required this.id,
+ required this.image,
+ });
+
+ factory TopBannerModel.fromJson(Map json) {
+ return TopBannerModel(
+ id: json['id'],
+ image: json['image'],
+ );
+ }
+}
diff --git a/lib/routes/route_generator.dart b/lib/routes/route_generator.dart
index a111787..4b27ab3 100644
--- a/lib/routes/route_generator.dart
+++ b/lib/routes/route_generator.dart
@@ -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/monthly/monthly_list_page.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/routes/routes.dart';
import 'package:didvan/views/story_viewer/story_viewer_page.dart';
@@ -124,6 +125,10 @@ class RouteGenerator {
return _createRoute(
const Splash(),
);
+ case Routes.onboarding:
+ return _createRoute(
+ const OnboardingPage(),
+ );
case Routes.aiSection:
return _createRoute(
const AiSectionPage(),
diff --git a/lib/routes/routes.dart b/lib/routes/routes.dart
index 2e48e32..724f080 100644
--- a/lib/routes/routes.dart
+++ b/lib/routes/routes.dart
@@ -1,5 +1,6 @@
class Routes {
static const String splash = '/';
+ static const String onboarding = '/onboarding';
static const String aiChat = '/ai-chat';
static const String aiHistory = '/ai-history';
static const String botAssistants = '/bot-assistants-page';
diff --git a/lib/services/network/request_helper.dart b/lib/services/network/request_helper.dart
index d4cd53f..2ffb706 100644
--- a/lib/services/network/request_helper.dart
+++ b/lib/services/network/request_helper.dart
@@ -24,6 +24,8 @@ class RequestHelper {
static const String mainPageContent = _baseHomeUrl;
static const String didvanPlus = '$baseUrl/didvanPlus';
+ static const String didvanVoice = '$baseUrl/didvanVoice';
+ static const String topBanner = '$baseUrl/topBanner';
static String searchAll({
required int page,
diff --git a/lib/views/ai/ai.dart b/lib/views/ai/ai.dart
index 7164423..b305bc3 100644
--- a/lib/views/ai/ai.dart
+++ b/lib/views/ai/ai.dart
@@ -60,7 +60,7 @@ class _AiState extends State {
child: SingleChildScrollView(
child: Padding(
padding:
- const EdgeInsets.only(bottom: 24, top: 70),
+ const EdgeInsets.only(bottom: 24, top: 0),
child: Column(
children: [
Consumer(
@@ -68,8 +68,10 @@ class _AiState extends State {
AiState aiState, Widget? child) {
final tools = aiState.tools;
if (tools != null && tools.isEmpty) {
- return EmptyState(
- asset: Assets.emptyResult,
+ return const EmptyState(
+ asset:
+ 'lib/assets/images/empty_states/Empty-Result.png',
+ height: 500,
title: 'لیست خالی است',
);
}
diff --git a/lib/views/ai/history_ai_chat_page.dart b/lib/views/ai/history_ai_chat_page.dart
index 8a08d47..2df5f5d 100644
--- a/lib/views/ai/history_ai_chat_page.dart
+++ b/lib/views/ai/history_ai_chat_page.dart
@@ -100,7 +100,9 @@ class _HistoryAiChatPageState extends State {
style: TextStyle(
fontSize: 20,
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(
// title: 'گفتوگوها',
@@ -153,11 +155,16 @@ class _HistoryAiChatPageState extends State {
Consumer(
builder: (context, state, child) {
return SliverStateHandler(
+ paddingEmptyState: 0,
state: state,
centerEmptyState: false,
emptyState: EmptyState(
asset: Assets.emptyResult,
+ height: 550,
title: 'لیست خالی است',
+ titleColor: const Color.fromARGB(255, 2, 126, 167),
+ subtitle:
+ 'در حال حاضر آیتمی در این بخش ثبت نشده است. هر زمان مورد جدیدی اضافه شود، در اینجا نمایش داده میشود.',
),
enableEmptyState: archived
? state.archivedChats.isEmpty
diff --git a/lib/views/authentication/screens/password.dart b/lib/views/authentication/screens/password.dart
index fca1350..03e5f2b 100644
--- a/lib/views/authentication/screens/password.dart
+++ b/lib/views/authentication/screens/password.dart
@@ -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/text.dart';
import 'package:didvan/views/widgets/didvan/text_field.dart';
+import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
+import 'package:url_launcher/url_launcher_string.dart';
class PasswordInput extends StatefulWidget {
const PasswordInput({Key? key}) : super(key: key);
@@ -23,8 +25,8 @@ class _PasswordInputState extends State {
Widget build(BuildContext context) {
final AuthenticationState state = context.read();
return AuthenticationLayout(
- appBarTitle:
- 'ورود با ${state.username.contains('09') ? 'شماره موبایل' : 'نام کاربری'} ${state.username}',
+ // appBarTitle:
+ // 'ورود با ${state.username.contains('09') ? 'شماره موبایل' : 'نام کاربری'} ${state.username}',
children: [
Form(
key: _formKey,
@@ -42,7 +44,7 @@ class _PasswordInputState extends State {
),
),
const SizedBox(
- height: 32,
+ height: 15,
),
GestureDetector(
onTap: () => state.currentPageIndex++,
@@ -52,13 +54,54 @@ class _PasswordInputState extends State {
color: Theme.of(context).colorScheme.primary,
),
),
- const Spacer(),
+ const SizedBox(
+ height: 50,
+ ),
DidvanButton(
onPressed: () => _onLogin(context),
- title: 'ورود',
+ title: 'تایید و ادامه',
),
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: 'را میپذیرم'),
+ ],
+ ),
+ ),
),
],
);
diff --git a/lib/views/authentication/screens/username.dart b/lib/views/authentication/screens/username.dart
index 70b7023..ed65b6d 100644
--- a/lib/views/authentication/screens/username.dart
+++ b/lib/views/authentication/screens/username.dart
@@ -1,14 +1,13 @@
+import 'package:didvan/config/theme_data.dart';
import 'package:didvan/utils/extension.dart';
import 'package:didvan/views/authentication/authentication_state.dart';
import 'package:didvan/views/authentication/widgets/authentication_layout.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:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
-// import 'package:url_launcher/url_launcher.dart';
class UsernameInput extends StatefulWidget {
const UsernameInput({
@@ -50,12 +49,16 @@ class _UsernameInputState extends State {
},
),
),
- Padding(
- padding: const EdgeInsets.all(8.0),
- child: DidvanText(
- 'نام کاربری میتواند شامل کاراکترهای انگلیسی و اعداد باشد.',
- style: Theme.of(context).textTheme.labelSmall,
- ),
+ // Padding(
+ // padding: const EdgeInsets.all(8.0),
+ // child: DidvanText(
+ // 'نام کاربری میتواند شامل کاراکترهای انگلیسی و اعداد باشد.',
+ // style: Theme.of(context).textTheme.labelSmall,
+ // ),
+ // ),
+
+ const SizedBox(
+ height: 70,
),
DidvanButton(
title: 'ادامه',
@@ -65,13 +68,18 @@ class _UsernameInputState extends State {
}
},
),
- const Spacer(),
+ const SizedBox(
+ height: 20,
+ ),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 60),
child: RichText(
textAlign: TextAlign.center,
text: TextSpan(
- style: Theme.of(context).textTheme.bodySmall,
+ style: Theme.of(context)
+ .textTheme
+ .bodySmall
+ ?.copyWith(color: Theme.of(context).colorScheme.caption),
children: [
const TextSpan(text: 'با ورود به اپلیکیشن دیدوان،'),
TextSpan(
diff --git a/lib/views/authentication/screens/verification.dart b/lib/views/authentication/screens/verification.dart
index aaf69e0..a4496b4 100644
--- a/lib/views/authentication/screens/verification.dart
+++ b/lib/views/authentication/screens/verification.dart
@@ -2,12 +2,9 @@
import 'dart:async';
-// START: Added Imports
import 'package:flutter/gestures.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:url_launcher/url_launcher_string.dart';
-// END: Added Imports
-
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/providers/user.dart';
@@ -36,10 +33,7 @@ class _VerificationState extends State {
bool _isResendButtonEnabled = false;
bool _isConfirmButtonEnabled = false;
String _completedCode = "";
-
- // START: Added state for error
bool _hasError = false;
- // END: Added state
@override
void initState() {
@@ -75,30 +69,26 @@ class _VerificationState extends State {
Widget build(BuildContext context) {
final AuthenticationState state = context.read();
- // START: Define colors for PinTheme
final errorColor = Theme.of(context).colorScheme.error;
final primaryColor = Theme.of(context).colorScheme.primary;
final borderColor = Theme.of(context).colorScheme.border;
final textColor = Theme.of(context).colorScheme.text;
- // END: Define colors
return WillPopScope(
onWillPop: () async {
_timer?.cancel();
- // منطق بازگشت هماهنگ با authentication.dart
if (state.currentPageIndex == 0) {
return true;
}
if (state.currentPageIndex == 2 && !state.hasPassword) {
- state.currentPageIndex = 0; // برگرد به صفحه نام کاربری
+ state.currentPageIndex = 0;
return false;
}
- state.currentPageIndex--; // در غیر این صورت یکی برگرد
+ state.currentPageIndex--;
return false;
},
child: AuthenticationLayout(
- appBarTitle: 'تغییر رمز عبور',
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
@@ -125,8 +115,8 @@ class _VerificationState extends State {
const SizedBox(height: 8),
InkWell(
onTap: () {
- _timer?.cancel(); // لغو تایمر
- state.currentPageIndex = 0; // بازگشت به صفحه ورود شماره
+ _timer?.cancel();
+ state.currentPageIndex = 0;
},
borderRadius: DesignConfig.lowBorderRadius,
child: Padding(
@@ -157,58 +147,46 @@ class _VerificationState extends State {
keyboardType: TextInputType.number,
animationType: AnimationType.scale,
cursorColor: textColor,
- // START: Added textStyle for error color
textStyle: TextStyle(
- fontSize: 20, // اندازه فونت را متناسب تنظیم کنید
+ fontSize: 20,
color: _hasError ? const Color.fromARGB(255, 178, 4, 54) : textColor,
),
- // END: Added textStyle
pinTheme: PinTheme(
fieldHeight: 58,
fieldWidth: 58,
- // START: Dynamic border/bg colors
selectedColor: _hasError ? const Color.fromARGB(255, 178, 4, 54) : primaryColor,
inactiveColor: _hasError ? const Color.fromARGB(255, 178, 4, 54) : borderColor,
activeFillColor: _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,
borderWidth: 1,
shape: PinCodeFieldShape.box,
),
appContext: context,
length: 6,
- // START: Modified onCompleted
onCompleted: (value) async {
final result = await state.verifyOtpToken(value);
if (result) {
_timer?.cancel();
}
},
- // END: Modified onCompleted
- // START: Modified onChanged
onChanged: (value) {
setState(() {
_completedCode = value;
_isConfirmButtonEnabled = value.length == 6;
if (_hasError) {
- _hasError = false; // پاک کردن خطا با شروع تایپ
+ _hasError = false;
}
});
},
- // END: Modified onChanged
),
),
- // ویجت ارسال مجدد کد
- const SizedBox(
- height: 24,
- ),
_isResendButtonEnabled
? InkWell(
onTap: () {
- _handleTimer(); // ریاستارت تایمر
- state.sendOtpToken(); // ارسال مجدد کد
+ _handleTimer();
+ state.sendOtpToken();
},
borderRadius: DesignConfig.lowBorderRadius,
child: Padding(
@@ -243,26 +221,22 @@ class _VerificationState extends State {
],
),
- const Spacer(), // دکمه را به پایین صفحه میچسباند
+ const SizedBox(height: 10,),
DidvanButton(
enabled: _isConfirmButtonEnabled,
- // START: Modified onPressed
onPressed: () async {
if (_completedCode.length == 6) {
final result = await state.verifyOtpToken(_completedCode);
if (!result) {
if (mounted) setState(() => _hasError = true);
}
- // در صورت موفقیت، onCompleted خودش کار را انجام میدهد
}
},
- // END: Modified onPressed
title: 'تایید کد',
),
- // START: Added Terms and Conditions RichText
const SizedBox(
- height: 24, // فاصله بین دکمه و متن قوانین
+ height: 24,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 60),
@@ -301,9 +275,8 @@ class _VerificationState extends State {
),
),
const SizedBox(
- height: 24, // پدینگ نهایی پایین صفحه
+ height: 24,
),
- // END: Added RichText
],
),
);
diff --git a/lib/views/authentication/widgets/authentication_layout.dart b/lib/views/authentication/widgets/authentication_layout.dart
index 579ea4c..3331477 100644
--- a/lib/views/authentication/widgets/authentication_layout.dart
+++ b/lib/views/authentication/widgets/authentication_layout.dart
@@ -1,8 +1,8 @@
// ignore_for_file: deprecated_member_use
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_svg/flutter_svg.dart';
class AuthenticationLayout extends StatelessWidget {
final List children;
@@ -26,7 +26,7 @@ class AuthenticationLayout extends StatelessWidget {
padding: const EdgeInsets.only(
left: 16,
right: 16,
- top: 7,
+ // top: 7,
),
child: AuthenticationAppBar(
title: appBarTitle,
@@ -34,17 +34,28 @@ class AuthenticationLayout extends StatelessWidget {
),
),
SliverPadding(
- padding: EdgeInsets.only(
- top: appBarTitle == null ? 100 : 44,
- left: 100,
- right: 100,
- bottom: 40,
- ),
- sliver: const SliverToBoxAdapter(
- child: DidvanVerticalLogo(
- height: 145,
- ),
- ),
+ padding: const EdgeInsets.only(
+ // top: appBarTitle == null ? 100 : 44,
+ // left: 100,
+ // right: 100,
+ // bottom: 40,
+ ),
+ sliver: SliverToBoxAdapter(
+ child: Column(
+ 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(
padding: const EdgeInsets.symmetric(horizontal: 20),
diff --git a/lib/views/direct/direct.dart b/lib/views/direct/direct.dart
index d815824..653da6a 100644
--- a/lib/views/direct/direct.dart
+++ b/lib/views/direct/direct.dart
@@ -3,7 +3,6 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
-import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/enums.dart';
import 'package:didvan/providers/server_data.dart';
import 'package:didvan/services/media/voice.dart';
@@ -82,14 +81,17 @@ class _DirectState extends State {
? EdgeInsets.zero
: const EdgeInsets.only(bottom: 68),
sliver: SliverStateHandler(
- itemPadding: const EdgeInsets.only(bottom: 12),
state: state,
enableEmptyState: state.messages.isEmpty,
- emptyState: Padding(
- padding: const EdgeInsets.only(bottom: 160),
+ paddingEmptyState: 0,
+ emptyState: const Padding(
+ padding: EdgeInsets.only(top: 1),
child: EmptyState(
- asset: Assets.emptyChat,
- title: 'اولین پیام را بنویسید...',
+ asset: 'lib/assets/images/empty_states/Support.png',
+ height: 600,
+ title: 'پشتیبانی دیدوان',
+ titleColor: Color.fromARGB(255, 0, 126, 167),
+ subtitle: 'چطور میتوانیم به شما کمک کنیم؟',
),
),
builder: (context, state, index) => Message(
@@ -139,7 +141,9 @@ class _DirectState extends State {
.textTheme
.headlineSmall
?.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,
fontSize: 19),
),
diff --git a/lib/views/home/bookmarks/filtered_bookmark/filtered_bookmark.dart b/lib/views/home/bookmarks/filtered_bookmark/filtered_bookmark.dart
index 12b806d..12e64a6 100644
--- a/lib/views/home/bookmarks/filtered_bookmark/filtered_bookmark.dart
+++ b/lib/views/home/bookmarks/filtered_bookmark/filtered_bookmark.dart
@@ -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/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/sliver_state_handler.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:didvan/views/home/main/widgets/swot_bookmark.dart';
@@ -71,62 +71,106 @@ class _FilteredBookmarksState extends State {
Widget build(BuildContext context) {
final state = context.watch();
- return DidvanScaffold(
- appBarData: AppBarData(title: _appBarTitle, hasBack: true),
- padding: const EdgeInsets.all(16),
- slivers: [
- if (widget.type == 8)
- SliverStateHandler(
- state: state,
- enableEmptyState:
- state.bookmarkedSwotItems.isEmpty && !state.swotItemsLoading,
- emptyState: const EmptyList(),
- placeholder: MultitypeOverview.placeholder,
- 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
- SliverStateHandler(
- 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),
+ return Scaffold(
+ appBar: PreferredSize(
+ preferredSize: const Size.fromHeight(90.0),
+ child: AppBar(
+ backgroundColor: Theme.of(context).colorScheme.surface,
+ elevation: 0,
+ automaticallyImplyLeading: false,
+ flexibleSpace: SafeArea(
+ child: Container(
+ padding:
+ const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Center(
+ child: SvgPicture.asset(
+ 'lib/assets/images/logos/logo-horizontal-light.svg',
+ height: 55,
+ ),
+ ),
+ IconButton(
+ icon: SvgPicture.asset(
+ 'lib/assets/icons/arrow-left.svg',
+ color: Theme.of(context).colorScheme.caption,
+ height: 24,
+ ),
+ onPressed: () {
+ Navigator.pop(context);
+ },
+ ),
+ ],
+ ),
+ ),
),
- ],
+ ),
+ ),
+ body: CustomScrollView(
+ slivers: [
+ if (widget.type == 8)
+ SliverPadding(
+ padding: const EdgeInsets.symmetric(horizontal: 0),
+ sliver: SliverStateHandler(
+ 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(
+ 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)),
+ ],
+ ),
);
}
}
diff --git a/lib/views/home/explore/explore.dart b/lib/views/home/explore/explore.dart
index 7c36593..f3449e0 100644
--- a/lib/views/home/explore/explore.dart
+++ b/lib/views/home/explore/explore.dart
@@ -376,7 +376,7 @@ class MainPageSection extends StatelessWidget {
case 'technology':
return 'lib/assets/icons/Technology.svg';
case 'risk':
- return 'lib/assets/icons/risk-radar.svg'; //TODO:FIX ICON
+ return 'lib/assets/icons/RiskRadar.svg'; //TODO:FIX ICON
case 'startup':
return 'lib/assets/icons/Startup.svg';
case 'survey':
diff --git a/lib/views/home/main/main_page.dart b/lib/views/home/main/main_page.dart
index 626a05e..f049834 100644
--- a/lib/views/home/main/main_page.dart
+++ b/lib/views/home/main/main_page.dart
@@ -1,3 +1,5 @@
+// ignore_for_file: unnecessary_import
+
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.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/simple_explore_card.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/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/state_handlers/state_handler.dart';
@@ -66,6 +69,10 @@ class _MainPageState extends State {
onRetry: () => context.read().init(),
state: context.watch(),
builder: (context, state) {
+ debugPrint(
+ '🏠 MainPage build - didvanPlus: ${state.didvanPlus != null}');
+ debugPrint(
+ '🏠 MainPage build - didvanVoice: ${state.didvanVoice != null}');
return Column(
children: [
const HomeAppBar(
@@ -116,6 +123,16 @@ class _MainPageState extends State {
SwotSection(swotItems: state.swotItems)
.animate()
.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()
// .animate()
// .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 {
const _ExploreLatestTitle();
@@ -192,7 +244,9 @@ class _ExploreLatestTitle extends StatelessWidget {
DidvanText(
"تازهترینهای کاوش",
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,
),
],
@@ -227,8 +281,10 @@ class _ExploreLatestSlider extends StatelessWidget {
@override
Widget build(BuildContext context) {
final List items = [];
- final List<({String type, MainPageContentType? content, SwotItem? swotItem})> itemsData = [];
-
+ final List<
+ ({String type, MainPageContentType? content, SwotItem? swotItem})>
+ itemsData = [];
+
for (var list in lists) {
if (list.type == 'video' ||
list.type == 'podcast' ||
@@ -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) {
context.read().navigationHandler(
- data.type,
- data.content!.id,
- data.content!.link,
- description: data.content!.title,
- );
+ data.type,
+ data.content!.id,
+ data.content!.link,
+ description: data.content!.title,
+ );
}
},
);
diff --git a/lib/views/home/main/main_page_state.dart b/lib/views/home/main/main_page_state.dart
index 82b2ca9..9aed956 100644
--- a/lib/views/home/main/main_page_state.dart
+++ b/lib/views/home/main/main_page_state.dart
@@ -1,5 +1,7 @@
import 'package:didvan/main.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/home_page_content/content.dart';
import 'package:didvan/models/home_page_content/home_page_content.dart';
@@ -24,6 +26,8 @@ class MainPageState extends CoreProvier {
List stories = [];
List swotItems = [];
DidvanPlusModel? didvanPlus;
+ DidvanVoiceModel? didvanVoice;
+ TopBannerModel? topBanner;
Future _getMainPageContent() async {
final service = RequestService(RequestHelper.mainPageContent);
@@ -53,27 +57,83 @@ class MainPageState extends CoreProvier {
Future _getDidvanPlus() async {
debugPrint('🎬 Fetching Didvan Plus data...');
+ debugPrint('🎬 URL: ${RequestHelper.didvanPlus}');
+ debugPrint('🎬 Token exists: ${RequestService.token != null}');
+
try {
final service = RequestService(RequestHelper.didvanPlus);
await service.httpGet();
- debugPrint('🎬 Didvan Plus response: ${service.isSuccess}');
- debugPrint('🎬 Didvan Plus result: ${service.result}');
-
- if (service.isSuccess) {
- final data = service.result['result'] ?? service.result;
- didvanPlus = DidvanPlusModel.fromJson(data);
- debugPrint('✅ Didvan Plus loaded: ${didvanPlus?.id}');
- debugPrint('✅ Video file: ${didvanPlus?.file}');
- debugPrint('✅ Image: ${didvanPlus?.image}');
- notifyListeners();
+
+ debugPrint('🎬 Didvan Plus statusCode: ${service.statusCode}');
+
+ // Check status code directly since response came
+ if (service.statusCode == 200) {
+ // Get the raw data using data() method
+ final rawData = service.data('result');
+ 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.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.from(rawData));
+ debugPrint('✅ Didvan Plus loaded: ${didvanPlus?.id}');
+ notifyListeners();
+ } else {
+ debugPrint('⚠️ Didvan Plus: Empty or unexpected data format');
+ }
} else {
- debugPrint('⚠️ Didvan Plus: No result in response');
+ debugPrint('⚠️ Didvan Plus: Request failed with status ${service.statusCode}');
}
} catch (e) {
debugPrint('❌ Failed to load Didvan Plus: $e');
}
}
+ Future _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 _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 _fetchStories() async {
try {
stories = await StoryService.getStories();
@@ -94,6 +154,8 @@ class MainPageState extends CoreProvier {
_fetchStories(),
_getSwotItems(),
_getDidvanPlus(),
+ _getDidvanVoice(),
+ _getTopBanner(),
]);
debugPrint("✅ All main page data loaded");
appState = AppState.idle;
diff --git a/lib/views/home/main/widgets/didvan_voice_section.dart b/lib/views/home/main/widgets/didvan_voice_section.dart
new file mode 100644
index 0000000..aa519a2
--- /dev/null
+++ b/lib/views/home/main/widgets/didvan_voice_section.dart
@@ -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 createState() => _DidvanVoiceSectionState();
+}
+
+class _DidvanVoiceSectionState extends State {
+ 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(
+ 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(
+ stream: _audioPlayer.durationStream,
+ builder: (context, snapshot) {
+ final duration = snapshot.data;
+ return StreamBuilder(
+ 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(
+ stream: _audioPlayer.durationStream,
+ builder: (context, snapshot) {
+ final duration = snapshot.data ?? Duration.zero;
+ return StreamBuilder(
+ 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()));
+ },
+ ),
+ );
+ },
+ );
+ },
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/lib/views/home/media/media_page.dart b/lib/views/home/media/media_page.dart
index eaa5879..3787db0 100644
--- a/lib/views/home/media/media_page.dart
+++ b/lib/views/home/media/media_page.dart
@@ -71,8 +71,7 @@ class _MediaPageState extends State {
),
Container(
width: double.infinity,
- padding:
- const EdgeInsets.symmetric(horizontal: 7),
+ padding: const EdgeInsets.symmetric(horizontal: 7),
child: Container(
height: 2,
color: const Color.fromRGBO(184, 184, 184, 1),
@@ -89,14 +88,14 @@ class _MediaPageState extends State {
children: [
ChangeNotifierProvider(
create: (_) => PodcastsState(),
- child: const PodcastTabPage(key: ValueKey('PodcastTabPage')),
+ child:
+ const PodcastTabPage(key: ValueKey('PodcastTabPage')),
),
ChangeNotifierProvider(
create: (_) => PodcastsState(),
- child:
- const VideoCastTabPage(key: ValueKey('VideoCastTabPage')),
+ child: const VideoCastTabPage(
+ key: ValueKey('VideoCastTabPage')),
),
-
const SingleChildScrollView(
key: ValueKey('MainPageBanner'),
child: Padding(
@@ -124,4 +123,4 @@ class _MediaPageState extends State {
),
);
}
-}
\ No newline at end of file
+}
diff --git a/lib/views/home/media/widgets/media_banner_with_thumbnails.dart b/lib/views/home/media/widgets/media_banner_with_thumbnails.dart
index 60ce9f5..e933bbe 100644
--- a/lib/views/home/media/widgets/media_banner_with_thumbnails.dart
+++ b/lib/views/home/media/widgets/media_banner_with_thumbnails.dart
@@ -27,6 +27,21 @@ class _MediaBannerWithThumbnailsState extends State {
return Column(
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(
carouselController: _carouselController,
itemCount: banners.length,
diff --git a/lib/views/onboarding/onboarding_indicator_painter.dart b/lib/views/onboarding/onboarding_indicator_painter.dart
new file mode 100644
index 0000000..ccafffc
--- /dev/null
+++ b/lib/views/onboarding/onboarding_indicator_painter.dart
@@ -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;
+ }
+}
diff --git a/lib/views/onboarding/onboarding_page.dart b/lib/views/onboarding/onboarding_page.dart
new file mode 100644
index 0000000..e249b28
--- /dev/null
+++ b/lib/views/onboarding/onboarding_page.dart
@@ -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 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 createState() => _OnboardingPageState();
+}
+
+class _OnboardingPageState extends State {
+ 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 _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,
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/lib/views/podcasts/studio_details/studio_details.web.dart b/lib/views/podcasts/studio_details/studio_details.web.dart
index 99ebb64..94fbe6e 100644
--- a/lib/views/podcasts/studio_details/studio_details.web.dart
+++ b/lib/views/podcasts/studio_details/studio_details.web.dart
@@ -1,6 +1,6 @@
// 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/services/media/media.dart';
@@ -48,7 +48,7 @@ class _StudioDetailsState extends State {
if (state.studio.type == 'video') {
debugPrint("Playing video from URL: ${state.studio.link}");
- ui.platformViewRegistry.registerViewFactory(
+ ui_web.platformViewRegistry.registerViewFactory(
viewType,
(int viewId) => html.IFrameElement()
..allowFullscreen = true
diff --git a/lib/views/profile/change_password/widgets/new_password_screen.dart b/lib/views/profile/change_password/widgets/new_password_screen.dart
index a8421bd..fa5685e 100644
--- a/lib/views/profile/change_password/widgets/new_password_screen.dart
+++ b/lib/views/profile/change_password/widgets/new_password_screen.dart
@@ -1,3 +1,5 @@
+// ignore_for_file: deprecated_member_use
+
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/assets.dart';
@@ -204,20 +206,22 @@ class _NewPasswordScreenState extends State
child: FadeTransition(
opacity: _fadeAnimation,
child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 25.0),
+ padding: const EdgeInsets.symmetric(horizontal: 0.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Center(
child: Image.asset(
- 'lib/assets/images/empty_states/change password.png',
- width: 300,
- height: 300,
+ 'lib/assets/images/empty_states/Change_Password.png',
+ fit: BoxFit.fitWidth,
+ width: 500,
+ height: 400,
),
),
const SizedBox(height: 24),
Padding(
- padding: const EdgeInsets.all(8.0),
+ padding:
+ const EdgeInsets.only(right: 25, bottom: 8),
child: DidvanText(
'تغییر رمز عبور',
style: Theme.of(context)
diff --git a/lib/views/profile/change_password/widgets/verify_otp_screen.dart b/lib/views/profile/change_password/widgets/verify_otp_screen.dart
index 9277014..c2c0111 100644
--- a/lib/views/profile/change_password/widgets/verify_otp_screen.dart
+++ b/lib/views/profile/change_password/widgets/verify_otp_screen.dart
@@ -235,24 +235,20 @@ class _VerifyOtpScreenState extends State
),
],
children: [
- const SizedBox(height: 24),
SlideTransition(
position: _contentSlideAnimation,
child: FadeTransition(
opacity: _fadeAnimation,
child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 25.0),
+ padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
- Padding(
- padding: const EdgeInsets.all(8.0),
- child: Center(
- child: Image.asset(
- 'lib/assets/images/empty_states/change password.png',
- height: 320,
- ),
- ),
+ Image.asset(
+ 'lib/assets/images/empty_states/Change_Password.png',
+ fit: BoxFit.fitWidth,
+ width: double.infinity,
+ height: 400,
),
const SizedBox(height: 24),
DidvanText(
diff --git a/lib/views/profile/direct_list/direct_list.dart b/lib/views/profile/direct_list/direct_list.dart
index 8a7ba5b..93c7269 100644
--- a/lib/views/profile/direct_list/direct_list.dart
+++ b/lib/views/profile/direct_list/direct_list.dart
@@ -204,9 +204,14 @@ class _DirectListState extends State
),
childCount: state.chatRooms.length,
enableEmptyState: state.chatRooms.isEmpty,
- emptyState: EmptyState(
- asset: Assets.emptyBookmark,
- title: 'پیامی وجود ندارد',
+ paddingEmptyState: 0,
+ emptyState: const EmptyState(
+ asset: 'lib/assets/images/empty_states/Empty_List.png',
+ height: 500,
+ title: 'لیست خالی است',
+ titleColor: Color.fromARGB(255, 0, 126, 167),
+ subtitle:
+ 'در حال حاضر آیتمی در این بخش ثبت نشده است. هر زمان مورد جدیدی اضافه شود، در اینجا نمایش داده میشود.',
),
),
],
diff --git a/lib/views/profile/profile.dart b/lib/views/profile/profile.dart
index afc0a24..b935e89 100644
--- a/lib/views/profile/profile.dart
+++ b/lib/views/profile/profile.dart
@@ -119,7 +119,9 @@ class _ProfilePageState extends State
Navigator.of(context).pushNamed(Routes.bookmarks);
},
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(
onPressed: () => Navigator.of(context).pop(),
icon: SvgPicture.asset(
@@ -223,7 +225,9 @@ class _ProfilePageState extends State
.textTheme
.titleMedium
?.copyWith(
- color: DesignConfig.isDark? Colors.white : Colors.black,
+ color: DesignConfig.isDark
+ ? Colors.white
+ : Colors.black,
fontWeight: FontWeight.normal,
fontSize: 15),
),
@@ -233,8 +237,9 @@ class _ProfilePageState extends State
MenuOption(
titleWidget: DidvanText('ویرایش پروفایل',
style: TextStyle(
- color:
- Theme.of(context).colorScheme.caption)),
+ color: Theme.of(context)
+ .colorScheme
+ .caption)),
onTap: () => Navigator.of(context)
.pushNamed(Routes.editProfile),
iconWidget: SvgPicture.asset(
@@ -244,8 +249,9 @@ class _ProfilePageState extends State
MenuOption(
titleWidget: DidvanText('تغییر رمز عبور ',
style: TextStyle(
- color:
- Theme.of(context).colorScheme.caption)),
+ color: Theme.of(context)
+ .colorScheme
+ .caption)),
iconWidget:
SvgPicture.asset('lib/assets/icons/lock.svg'),
onTap: () => Navigator.of(context).pushNamed(
@@ -314,7 +320,9 @@ class _ProfilePageState extends State
.textTheme
.titleMedium
?.copyWith(
- color: DesignConfig.isDark? Colors.white : Colors.black,
+ color: DesignConfig.isDark
+ ? Colors.white
+ : Colors.black,
fontWeight: FontWeight.normal,
fontSize: 15),
),
@@ -324,8 +332,9 @@ class _ProfilePageState extends State
MenuOption(
titleWidget: DidvanText('تنظیمات اعلانها',
style: TextStyle(
- color:
- Theme.of(context).colorScheme.caption)),
+ color: Theme.of(context)
+ .colorScheme
+ .caption)),
onTap: () => Navigator.of(context)
.pushNamed(Routes.notificationSettings),
iconWidget: SvgPicture.asset(
@@ -335,7 +344,9 @@ class _ProfilePageState extends State
MenuOption(
titleWidget: DidvanText('ظاهر اپلیکیشن',
style: TextStyle(
- color: Theme.of(context).colorScheme.caption)),
+ color: Theme.of(context)
+ .colorScheme
+ .caption)),
iconWidget: SvgPicture.asset(
'lib/assets/icons/fluent_paint-brush-16-regular.svg'),
onTap: () {
@@ -347,8 +358,7 @@ class _ProfilePageState extends State
? 'lib/assets/icons/arrow-up2.svg'
: 'lib/assets/icons/arrow-down.svg',
height: 25,
- color:
- Theme.of(context).colorScheme.caption,
+ color: Theme.of(context).colorScheme.caption,
)),
const SizedBox(height: 8),
AnimatedVisibility(
@@ -370,7 +380,9 @@ class _ProfilePageState extends State
: 'ایران یکان',
titleWidget: DidvanText('فونت',
style: TextStyle(
- color: Theme.of(context).colorScheme.caption)),
+ color: Theme.of(context)
+ .colorScheme
+ .caption)),
onTap: _showFontFamilyBottomSheet,
iconWidget: SvgPicture.asset(
'lib/assets/icons/hugeicons_edit-01.svg'),
@@ -382,10 +394,11 @@ class _ProfilePageState extends State
child: MenuOption(
suffix: fontScaleSuffix(
state.fontSizeScale),
- titleWidget: DidvanText(
- 'اندازه متن',
+ titleWidget: DidvanText('اندازه متن',
style: TextStyle(
- color: Theme.of(context).colorScheme.caption)),
+ color: Theme.of(context)
+ .colorScheme
+ .caption)),
onTap: _showFontScaleBottomSheet,
iconWidget: SvgPicture.asset(
'lib/assets/icons/smallcaps.svg'),
@@ -404,7 +417,9 @@ class _ProfilePageState extends State
const SizedBox(width: 4),
DidvanText('تم برنامه',
style: TextStyle(
- color: Theme.of(context).colorScheme.caption)),
+ color: Theme.of(context)
+ .colorScheme
+ .caption)),
],
),
const SizedBox(height: 16),
@@ -466,7 +481,9 @@ class _ProfilePageState extends State
.textTheme
.titleMedium
?.copyWith(
- color: DesignConfig.isDark? Colors.white : Colors.black,
+ color: DesignConfig.isDark
+ ? Colors.white
+ : Colors.black,
fontWeight: FontWeight.normal,
fontSize: 15),
),
@@ -478,8 +495,9 @@ class _ProfilePageState extends State
'lib/assets/icons/info-circle.svg'),
titleWidget: DidvanText('درباره دیدوان',
style: TextStyle(
- color:
- Theme.of(context).colorScheme.caption)),
+ color: Theme.of(context)
+ .colorScheme
+ .caption)),
onTap: () => launchUrlString(
'https://didvan.com/#info',
mode: LaunchMode.inAppWebView),
@@ -490,7 +508,9 @@ class _ProfilePageState extends State
'lib/assets/icons/call-calling.svg'),
titleWidget: DidvanText('تماس با ما',
style: TextStyle(
- color: Theme.of(context).colorScheme.caption)),
+ color: Theme.of(context)
+ .colorScheme
+ .caption)),
onTap: () {
Navigator.of(context)
.pushNamed(Routes.directList)
@@ -599,8 +619,9 @@ class _ProfilePageState extends State
'lib/assets/icons/security-safe.svg'),
titleWidget: DidvanText('حریم خصوصی',
style: TextStyle(
- color:
- Theme.of(context).colorScheme.caption)),
+ color: Theme.of(context)
+ .colorScheme
+ .caption)),
onTap: () => launchUrlString(
'https://didvan.com/terms-of-use#privacy',
mode: LaunchMode.inAppWebView),
@@ -685,7 +706,8 @@ class _ProfilePageState extends State
),
),
title: 'انتخاب فونت برنامه',
- titleIcon: DidvanIcons.font_regular,
+ titleIconWidget:
+ SvgPicture.asset('lib/assets/icons/hugeicons_edit-01.svg'),
onDismissed: () => state.fontFamily = family,
onConfirmed: () => themeProvider.fontFamily = state.fontFamily,
),
@@ -734,7 +756,7 @@ class _ProfilePageState extends State
),
),
title: 'انتخاب اندازه متن',
- titleIcon: DidvanIcons.font_regular,
+ titleIconWidget: SvgPicture.asset('lib/assets/icons/smallcaps.svg'),
onDismissed: () => state.fontSizeScale = scale,
onConfirmed: () => themeProvider.fontScale = state.fontSizeScale,
),
@@ -760,7 +782,7 @@ class _ProfilePageState extends State
width: 70,
height: 37,
decoration: BoxDecoration(
- color: const Color.fromARGB(255, 200, 224, 244), // بکگراند آبی
+ color: const Color.fromARGB(255, 200, 224, 244),
borderRadius: BorderRadius.circular(22),
),
child: AnimatedAlign(
diff --git a/lib/views/splash/splash.dart b/lib/views/splash/splash.dart
index 7733bfc..6adeeff 100644
--- a/lib/views/splash/splash.dart
+++ b/lib/views/splash/splash.dart
@@ -1,3 +1,5 @@
+// ignore_for_file: deprecated_member_use, avoid_print
+
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/assets.dart';
@@ -36,7 +38,6 @@ class _SplashState extends State with TickerProviderStateMixin {
void initState() {
super.initState();
- // Initialize animation controller
_pulseController = AnimationController(
duration: const Duration(milliseconds: 1500),
vsync: this,
@@ -50,7 +51,6 @@ class _SplashState extends State with TickerProviderStateMixin {
curve: Curves.easeInOut,
));
- // Start pulse animation
_pulseController.repeat(reverse: true);
themeProvider = context.read();
@@ -69,7 +69,7 @@ class _SplashState extends State with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
- final isDark = Theme.of(context).brightness == Brightness.light;
+ final isDark = Theme.of(context).brightness == Brightness.dark;
return AnnotatedRegion(
value: DesignConfig.systemUiOverlayStyle.copyWith(
@@ -79,309 +79,136 @@ class _SplashState extends State with TickerProviderStateMixin {
),
child: Scaffold(
backgroundColor: colorScheme.background,
- body: Container(
- decoration: BoxDecoration(
- gradient: LinearGradient(
- begin: Alignment.topLeft,
- end: Alignment.bottomRight,
- colors: [
- colorScheme.background,
- colorScheme.background.withOpacity(0.8),
- colorScheme.primary.withOpacity(0.05),
- ],
- ),
- ),
- child: Stack(
- children: [
- // Floating particles background
- _buildFloatingParticles(colorScheme),
-
- SafeArea(
- child: Column(
- children: [
- // Top section with logo
- Expanded(
- flex: 3,
- child: Container(
- width: double.infinity,
- padding: const EdgeInsets.symmetric(horizontal: 32),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- // Logo with subtle animation
- TweenAnimationBuilder(
- 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(
- 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,
- ),
+ body: SafeArea(
+ child: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Spacer(flex: 2),
+
+ TweenAnimationBuilder(
+ duration: const Duration(milliseconds: 800),
+ tween: Tween(begin: 0.0, end: 1.0),
+ builder: (context, value, child) {
+ return Opacity(
+ opacity: value,
+ child: AnimatedBuilder(
+ animation: _pulseAnimation,
+ builder: (context, child) {
+ return Transform.scale(
+ scale: _pulseAnimation.value,
+ child: SvgPicture.asset(
+ Assets.horizontalLogoWithText,
+ height: 80,
),
);
},
),
+ );
+ },
+ ),
+
+ const SizedBox(height: 60),
+
+ if (!_errorOccured) ...[
+ SizedBox(
+ width: 60,
+ height: 60,
+ child: Image.asset(
+ Assets.loadingAnimation,
+ fit: BoxFit.contain,
),
- ],
- ),
- ),
- ],
- ),
- ),
- ),
- );
- }
-
- Widget _buildFloatingParticles(ColorScheme colorScheme) {
- return AnimatedBuilder(
- animation: _pulseController,
- builder: (context, child) {
- return Stack(
- children: [
- // Particle 1
- Positioned(
- top: 100 + (20 * _pulseController.value),
- right: 50 + (10 * _pulseController.value),
- child: Container(
- width: 6,
- height: 6,
- decoration: BoxDecoration(
- color: colorScheme.primary.withOpacity(0.3),
- 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,
+ ),
+ const SizedBox(height: 24),
+ AnimatedBuilder(
+ animation: _pulseController,
+ builder: (context, child) {
+ return Opacity(
+ opacity: 0.4 + (0.4 * _pulseController.value),
+ child: Text(
+ 'در حال بارگذاری...',
+ style: TextStyle(
+ fontSize: 14,
+ color: colorScheme.onBackground.withOpacity(0.6),
+ fontWeight: FontWeight.w400,
+ ),
+ ),
+ );
+ },
+ ),
+ ] else ...[
+ Container(
+ width: 56,
+ height: 56,
+ decoration: BoxDecoration(
+ color: colorScheme.error.withOpacity(0.1),
+ shape: BoxShape.circle,
),
- ],
+ child: Icon(
+ Icons.wifi_off_rounded,
+ size: 24,
+ color: colorScheme.error,
+ ),
+ ),
+ const SizedBox(height: 16),
+ Text(
+ 'مشکل در اتصال',
+ style: TextStyle(
+ fontSize: 16,
+ color: colorScheme.onBackground,
+ fontWeight: FontWeight.w500,
+ ),
+ ),
+ 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,
+ ),
+ ),
+ ),
+ ],
+
+ const Spacer(flex: 3),
+
+ Padding(
+ padding: const EdgeInsets.only(bottom: 24),
+ child: Text(
+ 'توسعه یافته توسط فرتاک',
+ style: TextStyle(
+ fontSize: 12,
+ color: colorScheme.onBackground.withOpacity(0.4),
+ fontWeight: FontWeight.w400,
+ ),
+ ),
),
- ),
+ ],
),
- // Particle 4
- Positioned(
- bottom: 120 + (18 * (1 - _pulseController.value)),
- left: 60 + (15 * _pulseController.value),
- child: Container(
- width: 5,
- height: 5,
- decoration: BoxDecoration(
- color: colorScheme.tertiary.withOpacity(0.35),
- shape: BoxShape.circle,
- ),
- ),
- ),
- ],
- );
- },
- );
- }
-
- Widget _buildModernLoader(ColorScheme colorScheme) {
- // استفاده از loading animation اصلی و قدیمی دیدوان
- return SizedBox(
- width: 60,
- height: 60,
- child: Image.asset(
- Assets.loadingAnimation,
- width: 60,
- height: 60,
- fit: BoxFit.contain,
- ),
- );
- }
-
- Widget _buildRetryButton(ColorScheme colorScheme) {
- return SizedBox(
- width: 140,
- height: 44,
- child: ElevatedButton(
- onPressed: () {
- setState(() {
- _errorOccured = false;
- });
- _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 with TickerProviderStateMixin {
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();
print("detected token as $token");
@@ -442,7 +276,6 @@ class _SplashState extends State with TickerProviderStateMixin {
print("token route is $token");
String extractedPath = initialURI?.path.toString() == '/' ? Routes.home : initialURI?.path.toString() ?? Routes.home;
final String destinationRoute = token == null ? Routes.authenticaion : extractedPath;
- // Set showDialogs to false to prevent welcome popups
dynamic routeArguments = token == null ? {'isResetPassword': false} : {'showDialogs': false};
diff --git a/lib/views/widgets/ai_chat_dialog.dart b/lib/views/widgets/ai_chat_dialog.dart
index 3e602f2..5486ef3 100644
--- a/lib/views/widgets/ai_chat_dialog.dart
+++ b/lib/views/widgets/ai_chat_dialog.dart
@@ -444,10 +444,13 @@ class _AiChatDialogState extends State
),
shape: BoxShape.circle,
),
- child: IconButton(
- icon: const Icon(Icons.headset_mic_rounded,
- color: Colors.white, size: 20),
- onPressed: () {
+ child: GestureDetector(
+ child: Padding(
+ padding: const EdgeInsets.all(6.0),
+ child: SvgPicture.asset('lib/assets/icons/voice-square.svg',
+ color: Colors.white, height: 35),
+ ),
+ onTap: () {
Navigator.pop(context);
showDialog(
context: context,
@@ -455,8 +458,6 @@ class _AiChatDialogState extends State
builder: (context) => const AiVoiceChatDialog(),
);
},
- splashRadius: 20,
- tooltip: 'گفتگوی صوتی',
),
),
Container(
diff --git a/lib/views/widgets/state_handlers/empty_connection.dart b/lib/views/widgets/state_handlers/empty_connection.dart
index c6543bb..cc353f6 100644
--- a/lib/views/widgets/state_handlers/empty_connection.dart
+++ b/lib/views/widgets/state_handlers/empty_connection.dart
@@ -1,4 +1,3 @@
-import 'package:didvan/constants/assets.dart';
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
import 'package:flutter/material.dart';
@@ -8,11 +7,21 @@ class EmptyConnection extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return EmptyState(
- asset: Assets.emptyConnection,
- title: 'ارتباط با اینترنت قطع شد...',
- action: onRetry,
- buttonTitle: 'تلاش دوباره',
+ return Column(
+ children: [
+ const SizedBox(
+ height: 65,
+ ),
+ EmptyState(
+ asset: 'lib/assets/images/empty_states/Connection.png',
+ height: 550,
+ title: 'اتصال اینترنت برقرار نیست',
+ titleColor: const Color.fromARGB(255, 0, 126, 167),
+ action: onRetry,
+ subtitle: 'لطفاً اتصال وایفای یا داده تلفن همراه خود را بررسی کنید.',
+ buttonTitle: 'تلاش مجدد',
+ ),
+ ],
);
}
}
diff --git a/lib/views/widgets/state_handlers/empty_list.dart b/lib/views/widgets/state_handlers/empty_list.dart
index 9be66b6..8976bff 100644
--- a/lib/views/widgets/state_handlers/empty_list.dart
+++ b/lib/views/widgets/state_handlers/empty_list.dart
@@ -1,4 +1,3 @@
-import 'package:didvan/constants/assets.dart';
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
import 'package:flutter/material.dart';
@@ -7,9 +6,13 @@ class EmptyList extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return EmptyState(
- asset: Assets.emptyBookmark,
- title: 'لیست خالی است',
+ return const EmptyState(
+ asset: 'lib/assets/images/empty_states/Bookmark2.png',
+ height: 600,
+ title: 'رصدخانه شما هنوز خالی است',
+ titleColor: Color.fromARGB(255, 0, 126, 167),
+ subtitle:
+ "اینجا فضای شخصی شما برای ذخیره و دسترسی سریع به مهمترین تحلیلها، گزارشها و رسانههاست. برای افزودن محتوا، کافیست روی آیکون بوکمارک در کنار هر مطلب کلیک کنید.",
);
}
}
diff --git a/lib/views/widgets/state_handlers/empty_result.dart b/lib/views/widgets/state_handlers/empty_result.dart
index 1ae6b8f..738e714 100644
--- a/lib/views/widgets/state_handlers/empty_result.dart
+++ b/lib/views/widgets/state_handlers/empty_result.dart
@@ -1,4 +1,3 @@
-import 'package:didvan/constants/assets.dart';
import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
import 'package:flutter/material.dart';
@@ -9,10 +8,15 @@ class EmptyResult extends StatelessWidget {
@override
Widget build(BuildContext context) {
return EmptyState(
- asset: Assets.emptyResult,
- title: 'نتیجهای پیدا نشد',
- buttonTitle: 'تغییر جستجو',
- action: onNewSearch,
+ asset: 'lib/assets/images/empty_states/Empty-Result.png',
+ height: 600,
+ title: 'نتیجهای یافت نشد',
+ subtitle:
+ 'لطفاً از کلمات کلیدی دیگری استفاده کنید یا املای کلمه را بررسی نمایید.',
+ buttonTitle: 'بازگشت به صفحه اصلی',
+ action: () {
+ Navigator.of(context).popUntil((route) => route.isFirst);
+ },
);
}
}
diff --git a/lib/views/widgets/state_handlers/empty_state.dart b/lib/views/widgets/state_handlers/empty_state.dart
index ce76662..d52311d 100644
--- a/lib/views/widgets/state_handlers/empty_state.dart
+++ b/lib/views/widgets/state_handlers/empty_state.dart
@@ -26,29 +26,50 @@ class EmptyState extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final isSvg = asset.toLowerCase().endsWith('.svg');
+
return Column(
- mainAxisAlignment: MainAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.start,
children: [
- SizedBox(height: height ?? 210, child: SvgPicture.asset(asset)),
- const SizedBox(height: 16),
- DidvanText(
- title,
- style: Theme.of(context).textTheme.displaySmall,
- color: titleColor ?? Theme.of(context).colorScheme.caption,
+ SizedBox(
+ width: double.infinity,
+ height: height ?? 300,
+ child: isSvg
+ ? SvgPicture.asset(asset, fit: BoxFit.contain)
+ : 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)
- DidvanText(
- subtitle!,
- color: Theme.of(context).colorScheme.caption,
+ Padding(
+ padding: const EdgeInsets.only(right: 20, left: 20),
+ child: DidvanText(
+ subtitle!,
+ color: Theme.of(context).colorScheme.caption,
+ ),
),
if (action != null) const SizedBox(height: 16),
if (action != null)
- DidvanButton(
- height: 40,
- onPressed: action,
- title: buttonTitle,
- width: 112,
+ Center(
+ child: Padding(
+ padding: const EdgeInsets.all(25.0),
+ child: DidvanButton(
+ height: 50,
+ onPressed: action,
+ title: buttonTitle,
+ width: double.infinity,
+ ),
+ ),
),
],
);
diff --git a/pubspec.yaml b/pubspec.yaml
index d6f4284..1e3f80f 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -148,6 +148,7 @@ flutter:
- lib/assets/images/records/
- lib/assets/images/empty_states/
- lib/assets/images/features/
+ - lib/assets/images/onboarding/
- lib/assets/icons/
- lib/assets/animations/
- lib/assets/js/