liquid glass for ai

This commit is contained in:
Mr.Jebelli 2025-11-26 09:47:32 +03:30
parent 461eee1ed7
commit 63ab4c38d2
49 changed files with 1434 additions and 1193 deletions

View File

@ -116,6 +116,9 @@
android:screenOrientation="portrait"
android:exported="true"
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
<meta-data
android:name="io.flutter.embedding.android.EnableImpeller"
android:value="true" />
</application>

View File

@ -1,42 +1,39 @@
// ignore_for_file: deprecated_member_use
import 'dart:async';
import 'dart:io';
import 'package:app_links/app_links.dart';
import 'package:bot_toast/bot_toast.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:get/get.dart';
import 'package:home_widget/home_widget.dart';
import 'package:provider/provider.dart';
import 'package:provider/single_child_widget.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/firebase_options.dart';
import 'package:didvan/models/notification_message.dart';
import 'package:didvan/models/requests/news.dart';
import 'package:didvan/models/requests/radar.dart';
import 'package:didvan/providers/media.dart';
import 'package:didvan/providers/theme.dart';
import 'package:didvan/providers/user.dart';
import 'package:didvan/routes/route_generator.dart';
import 'package:didvan/routes/routes.dart';
import 'package:didvan/services/app_home_widget/home_widget_repository.dart';
import 'package:didvan/services/media/media.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/notification/firebase_api.dart';
import 'package:didvan/services/notification/notification_service.dart';
import 'package:didvan/utils/my_custom_scroll_behavior.dart';
import 'package:didvan/providers/media.dart';
import 'package:didvan/providers/theme.dart';
import 'package:didvan/providers/user.dart';
import 'package:didvan/views/ai/ai_chat_state.dart';
import 'package:didvan/views/ai/ai_state.dart';
import 'package:didvan/views/ai/bot_assistants_state.dart';
import 'package:didvan/views/ai/history_ai_chat_state.dart';
import 'package:didvan/views/podcasts/podcasts_state.dart';
import 'package:didvan/views/podcasts/studio_details/studio_details_state.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:get/get.dart';
import 'package:home_widget/home_widget.dart';
import 'package:provider/provider.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:app_links/app_links.dart';
import 'services/network/request.dart';
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
Uri? initialURI;
@ -52,29 +49,34 @@ void main() async {
runZonedGuarded(
() async {
WidgetsFlutterBinding.ensureInitialized();
try {
if (!kIsWeb) {
HomeWidget.registerBackgroundCallback(_backgroundCallbackHomeWidget);
HomeWidget.registerInteractivityCallback(_backgroundCallbackHomeWidget);
HomeWidget.registerInteractivityCallback(
_backgroundCallbackHomeWidget);
await NotificationService.initializeNotification();
try {
if (Platform.isAndroid) {
if (Platform.isAndroid) {
try {
await FlutterDownloader.initialize(debug: true, ignoreSsl: true);
} catch (e) {
e.printError();
}
} catch (e) {
e.printError();
}
}
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform);
await FirebaseApi().initNotification();
} catch (e) {
debugPrint(e.toString());
debugPrint("Initialization Error: $e");
}
await SentryFlutter.init(
(options) {
options.dsn = 'https://a4cfcaa7d67471240d295c25c968d91d@o4508585857384448.ingest.de.sentry.io/4508585886548048';
options.dsn =
'https://a4cfcaa7d67471240d295c25c968d91d@o4508585857384448.ingest.de.sentry.io/4508585886548048';
options.tracesSampleRate = 1.0;
options.profilesSampleRate = 1.0;
},
@ -95,9 +97,24 @@ class Didvan extends StatefulWidget {
}
class _DidvanState extends State<Didvan> with WidgetsBindingObserver {
late AppLinks _appLinks;
late final AppLinks _appLinks;
StreamSubscription<Uri>? _linkSubscription;
final List<SingleChildWidget> _applicationProviders = [
ChangeNotifierProvider<PodcastsState>(create: (_) => PodcastsState()),
ChangeNotifierProvider<MediaProvider>(create: (_) => MediaProvider()),
ChangeNotifierProvider<UserProvider>(create: (_) => UserProvider()),
ChangeNotifierProvider<ThemeProvider>(create: (_) => ThemeProvider()),
ChangeNotifierProvider<StudioDetailsState>(
create: (_) => StudioDetailsState()),
ChangeNotifierProvider<HistoryAiChatState>(
create: (_) => HistoryAiChatState()),
ChangeNotifierProvider<AiState>(create: (_) => AiState()),
ChangeNotifierProvider<AiChatState>(create: (_) => AiChatState()),
ChangeNotifierProvider<BotAssistantsState>(
create: (_) => BotAssistantsState()),
];
@override
void initState() {
super.initState();
@ -124,49 +141,47 @@ class _DidvanState extends State<Didvan> with WidgetsBindingObserver {
}
void _navigateTo(Uri uri) {
if (mounted) {
String path = uri.path;
final Map<String, String> params = uri.queryParameters;
if (!mounted) return;
final String? token = params['token'];
final Map<String, String> params = uri.queryParameters;
final String? token = params['token'];
if (token != null) {
//todo: this didnt work
print("DEBUG: received token in url, token: $token, path: $path");
RequestService.token = token;
if (token != null) {
debugPrint("DeepLink Token Received: $token");
RequestService.token = token;
}
final String path = uri.path;
if (path.startsWith('/news/')) {
final String idStr = path.split('/news/').last;
final int? id = int.tryParse(idStr);
if (id != null) {
navigatorKey.currentState?.pushNamed(
Routes.newsDetails,
arguments: {'id': id, 'args': const NewsRequestArgs(page: 0)},
);
}
if (path.startsWith('/news/')) {
final id = path.split('/news/').last;
if (id.isNotEmpty) {
navigatorKey.currentState?.pushNamed(
Routes.newsDetails,
arguments: {'id': int.parse(id), 'args': const NewsRequestArgs(page: 0)},
);
}
} else if (path.startsWith('/radar/')) {
final id = path.split('/radar/').last;
if (id.isNotEmpty) {
navigatorKey.currentState?.pushNamed(
Routes.radarDetails,
arguments: {'id': int.parse(id), 'args': const RadarRequestArgs(page: 0)},
);
}
} else if (path.startsWith('/radar/')) {
final String idStr = path.split('/radar/').last;
final int? id = int.tryParse(idStr);
if (id != null) {
navigatorKey.currentState?.pushNamed(
Routes.radarDetails,
arguments: {'id': id, 'args': const RadarRequestArgs(page: 0)},
);
}
}
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) async {
if (!kIsWeb) {
if (state == AppLifecycleState.resumed) {
var r = await HomeWidget.getWidgetData("cRoute", defaultValue: '');
if (r.toString() != Routes.splash) {
await HomeWidgetRepository.decideWhereToGo();
NotificationMessage? data = HomeWidgetRepository.data;
if (data != null) {
await HomeWidgetRepository.decideWhereToGoNotif();
}
if (!kIsWeb && state == AppLifecycleState.resumed) {
final route = await HomeWidget.getWidgetData("cRoute", defaultValue: '');
if (route.toString() != Routes.splash) {
await HomeWidgetRepository.decideWhereToGo();
if (HomeWidgetRepository.data != null) {
await HomeWidgetRepository.decideWhereToGoNotif();
}
}
}
@ -175,60 +190,60 @@ class _DidvanState extends State<Didvan> with WidgetsBindingObserver {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<PodcastsState>(create: (context) => PodcastsState()),
ChangeNotifierProvider<MediaProvider>(create: (context) => MediaProvider()),
ChangeNotifierProvider<UserProvider>(create: (context) => UserProvider()),
ChangeNotifierProvider<ThemeProvider>(create: (context) => ThemeProvider()),
ChangeNotifierProvider<StudioDetailsState>(create: (context) => StudioDetailsState()),
ChangeNotifierProvider<HistoryAiChatState>(create: (context) => HistoryAiChatState()),
ChangeNotifierProvider<AiState>(create: (context) => AiState()),
ChangeNotifierProvider<AiChatState>(create: (context) => AiChatState()),
ChangeNotifierProvider<BotAssistantsState>(create: (context) => BotAssistantsState()),
],
providers: _applicationProviders,
child: Consumer<ThemeProvider>(
builder: (context, themeProvider, child) => Container(
color: Theme.of(context).colorScheme.surface,
child: SafeArea(
child: MaterialApp(
scrollBehavior: MyCustomScrollBehavior(),
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
title: 'Didvan',
theme: LightThemeConfig.themeData.copyWith(
bottomSheetTheme: const BottomSheetThemeData(
surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent),
textTheme: LightThemeConfig.themeData.textTheme.apply(
fontFamily: themeProvider.fontFamily,
)),
darkTheme: DarkThemeConfig.themeData.copyWith(
bottomSheetTheme: const BottomSheetThemeData(
surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent),
textTheme: DarkThemeConfig.themeData.textTheme.apply(
fontFamily: themeProvider.fontFamily,
)),
color: LightThemeConfig.themeData.primaryColor,
themeMode: themeProvider.themeMode,
onGenerateRoute: (settings) =>
RouteGenerator.generateRoute(settings),
builder: BotToastInit(),
navigatorObservers: [BotToastNavigatorObserver()],
initialRoute: "/",
localizationsDelegates: const [
GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: const [
Locale("fa", "IR"),
],
locale: const Locale("fa", "IR"),
builder: (context, themeProvider, child) {
final lightTheme = LightThemeConfig.themeData.copyWith(
bottomSheetTheme: const BottomSheetThemeData(
surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent,
),
),
),
textTheme: LightThemeConfig.themeData.textTheme.apply(
fontFamily: themeProvider.fontFamily,
),
);
final darkTheme = DarkThemeConfig.themeData.copyWith(
bottomSheetTheme: const BottomSheetThemeData(
surfaceTintColor: Colors.transparent,
backgroundColor: Colors.transparent,
),
textTheme: DarkThemeConfig.themeData.textTheme.apply(
fontFamily: themeProvider.fontFamily,
),
);
return Container(
color: Theme.of(context).colorScheme.surface,
child: SafeArea(
child: MaterialApp(
scrollBehavior: MyCustomScrollBehavior(),
navigatorKey: navigatorKey,
debugShowCheckedModeBanner: false,
title: 'Didvan',
theme: lightTheme,
darkTheme: darkTheme,
color: lightTheme.primaryColor,
themeMode: themeProvider.themeMode,
onGenerateRoute: (settings) =>
RouteGenerator.generateRoute(settings),
builder: BotToastInit(),
navigatorObservers: [BotToastNavigatorObserver()],
initialRoute: "/",
localizationsDelegates: const [
GlobalCupertinoLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: const [
Locale("fa", "IR"),
],
locale: const Locale("fa", "IR"),
),
),
);
},
),
);
}
}
}

View File

@ -8,6 +8,7 @@ class MainPageContentType {
final List<String> subtitles;
final int? duration;
final String? description;
final String? riskScore;
MainPageContentType({
required this.id,
@ -19,6 +20,7 @@ class MainPageContentType {
required this.subtitles,
this.duration,
this.description,
this.riskScore,
});
factory MainPageContentType.fromJson(Map<String, dynamic> json) =>
@ -32,5 +34,6 @@ class MainPageContentType {
subtitles: List<String>.from(json['subtitles']),
duration: json['duration'],
description: json['description'],
riskScore: json['score'],
);
}

View File

@ -34,9 +34,6 @@ class UserProvider extends CoreProvier {
int get unreadMessageCount => _unreadMessageCount;
// static final List<MapEntry> _radarMarkQueue = [];
// static final List<MapEntry> _newsMarkQueue = [];
// static final List<MapEntry> _studioMarkQueue = [];
static final List<MapEntry> _statisticMarkQueue = [];
static final List<Map> _itemMarkQueue = [];
@ -76,7 +73,7 @@ class UserProvider extends CoreProvier {
}
} else {
print(
"UserProvider: Welcome message API failed. Status: ${service.statusCode}, Error: ${service.errorMessage}"); // Log failure
"UserProvider: Welcome message API failed. Status: ${service.statusCode}, Error: ${service.errorMessage}");
_welcomeMessage = null;
}
} catch (e) {
@ -109,6 +106,7 @@ class UserProvider extends CoreProvier {
final RequestService service = RequestService(RequestHelper.userInfo);
await service.httpGet();
// اگر توکن نامعتبر است (401)، فالس برمیگردانیم تا توکن پاک شود
if (service.statusCode == 401) {
print("UserProvider: getUserInfo failed - Unauthorized (401).");
isAuthenticated = false;
@ -150,6 +148,13 @@ class UserProvider extends CoreProvier {
print(
"UserProvider: getUserInfo failed. Status: ${service.statusCode}, Error: ${service.errorMessage}");
isAuthenticated = false;
// اصلاح مهم: اگر خطا 401 نیست (مثلاً مشکل سرور یا اینترنت)، Exception پرتاب میکنیم
// تا در Splash وارد بخش catch شود و توکن پاک نشود.
if (service.statusCode != 401) {
throw Exception("Server Error or Connection Issue: ${service.statusCode}");
}
return false;
}
@ -295,56 +300,6 @@ class UserProvider extends CoreProvier {
}
}
// static Future<void> changeRadarMark(int id, bool value) async {
//   _radarMarkQueue.add(MapEntry(id, value));
//   Future.delayed(const Duration(milliseconds: 500), () async {
//     final MapEntry? lastChange =
//         _radarMarkQueue.lastWhereOrNull((item) => item.key == id);
//     if (lastChange == null) return;
//     final service = RequestService(RequestHelper.mark(id, 'radar'));
//     if (lastChange.value) {
//       await service.post();
//     } else {
//       await service.delete();
//     }
//     _radarMarkQueue.removeWhere((element) => element.key == id);
//   });
// }
// static Future<void> changeStudioMark(int id, bool value) async {
//   _studioMarkQueue.add(MapEntry(id, value));
//   Future.delayed(const Duration(milliseconds: 500), () async {
//     final MapEntry? lastChange =
//         _studioMarkQueue.lastWhereOrNull((item) => item.key == id);
//     if (lastChange == null) return;
//     final service = RequestService(RequestHelper.mark(id, 'studio'));
//     if (lastChange.value) {
//       await service.post();
//     } else {
//       await service.delete();
//     }
//     _studioMarkQueue.removeWhere((element) => element.key == id);
//   });
// }
// static Future<void> changeNewsMark(int id, bool value) async {
//   _newsMarkQueue.add(MapEntry(id, value));
//   Future.delayed(const Duration(milliseconds: 500), () async {
//     final MapEntry? lastChange =
//         _newsMarkQueue.lastWhereOrNull((item) => item.key == id);
//     if (lastChange == null) return;
//     final service = RequestService(RequestHelper.mark(id, 'news'));
//     if (lastChange.value) {
//       await service.post();
//     } else {
//       await service.delete();
//   ----------------------------------------------------------------
//   }
//     _newsMarkQueue.removeWhere((element) => element.key == id);
//   });
// }
static Future<void> changeStatisticMark(int id, bool value) async {
_statisticMarkQueue.add(MapEntry(id, value));
Future.delayed(const Duration(milliseconds: 500), () async {
@ -387,12 +342,4 @@ class UserProvider extends CoreProvier {
return false;
}
}
// Future<void> getUnreadMessageCount() async {
// final RequestService service = RequestService(RequestHelper.directs);
// await service.httpGet();
// if (service.isSuccess) {
// _unreadMessageCount = service.result['unread'] ?? 0;
// }
// }
}
}

View File

@ -0,0 +1,82 @@
#include <flutter/runtime_effect.glsl>
uniform vec2 uResolution;
uniform float uDistortionStrength;
uniform float uBlurIntensity;
uniform float uDispersion;
uniform sampler2D uTexture;
uniform vec2 uWidgetOffset;
uniform vec2 uTextureSize;
out vec4 fragColor;
void main() {
vec2 fragCoord = FlutterFragCoord();
// 1. محاسبه مختصات UV جهانی
vec2 globalPos = fragCoord + uWidgetOffset;
vec2 uv = globalPos / uTextureSize;
// 2. ساخت ماسک لبه (فقط 8 درصد دور کادر برای لیکویید شدن)
vec2 localUV = fragCoord / uResolution;
float distToEdgeX = min(localUV.x, 1.0 - localUV.x);
float distToEdgeY = min(localUV.y, 1.0 - localUV.y);
float distToEdge = min(distToEdgeX, distToEdgeY);
// ضخامت لبه شیشه‌ای
float borderThickness = 0.1;
// نرم کردن ماسک (از 0 تا 1)
float borderMask = 1.0 - smoothstep(borderThickness - 0.09, borderThickness, distToEdge);
// اگر کاملاً در مرکز هستیم، فقط تکسچر را نشان بده
if (borderMask <= 0.01) {
fragColor = texture(uTexture, uv);
return;
}
// 3. اعوجاج (Distortion) در لبه‌ها
vec2 distortionDir = vec2(0.5) - localUV;
// اعمال اعوجاج فقط در محدوده ماسک
vec2 distortedUV = uv + (distortionDir * uDistortionStrength * 0.01 * borderMask);
// 4. بلور (Blur) و تجزیه رنگ (Dispersion)
vec4 finalColor = vec4(0.0);
if (uBlurIntensity > 0.0) {
// --- تغییر اصلی: افزایش ضریب برای منشور بیشتر ---
// مقدار قبلی 0.008 بود، به 0.035 افزایش یافت تا رنگ‌ها بیشتر پخش شوند
float dispersion = uDispersion * 0.010 * borderMask;
// نمونه‌برداری با فاصله بیشتر برای کانال‌های رنگی
vec4 colR = texture(uTexture, distortedUV + vec2(dispersion, 0.0));
vec4 colG = texture(uTexture, distortedUV);
vec4 colB = texture(uTexture, distortedUV - vec2(dispersion, 0.0));
vec4 baseColor = vec4(colR.r, colG.g, colB.b, 1.0);
// بلور گاوسی ساده شده
float blurStep = uBlurIntensity * 0.001 * borderMask;
vec4 blurCol = (
texture(uTexture, distortedUV + vec2(blurStep, blurStep)) +
texture(uTexture, distortedUV + vec2(-blurStep, -blurStep)) +
texture(uTexture, distortedUV + vec2(blurStep, -blurStep)) +
texture(uTexture, distortedUV + vec2(-blurStep, blurStep))
) * 0.25; // اصلاح: ضریب 0.02 خیلی تیره بود، 0.25 میانگین دقیق‌تری است
// ترکیب رنگ اصلی با بلور
// کاهش شدت میکس بلور (از 0.7 به 0.5) تا رنگ‌های منشور شفاف‌تر دیده شوند
finalColor = mix(baseColor, blurCol, 0.5);
} else {
finalColor = texture(uTexture, distortedUV);
}
// 5. میکس نهایی (مرکز شفاف + لبه‌های شیشه‌ای)
vec4 clearCenter = texture(uTexture, uv);
fragColor = mix(clearCenter, finalColor, borderMask);
// هایلایت سفید لبه
float shine = smoothstep(0.0, borderThickness * 0.5, distToEdge) * (1.0 - smoothstep(borderThickness * 0.5, borderThickness, distToEdge));
fragColor += vec4(shine * 0.15 * borderMask);
}

View File

@ -964,7 +964,8 @@ class _AiChatPageState extends State<AiChatPage> with TickerProviderStateMixin {
.withOpacity(0.9),
border: Border.all(
color: message.role.toString().contains('user')
? const Color.fromARGB(0, 0, 0, 0) :Theme.of(context).colorScheme.border,
? const Color.fromARGB(0, 0, 0, 0)
: Theme.of(context).colorScheme.border,
width: 0.5,
),
),
@ -1332,7 +1333,7 @@ class _AiChatPageState extends State<AiChatPage> with TickerProviderStateMixin {
margin: const EdgeInsets.fromLTRB(8, 8, 8, 0),
child: Row(
children: [
const Icon(Icons.file_copy),
SvgPicture.asset('lib/assets/icons/copy.svg',),
const SizedBox(width: 12),
Expanded(
child: Column(

View File

@ -454,7 +454,9 @@ class _HistoryAiChatPageState extends State<HistoryAiChatPage> {
DateTime.parse(chat
.updatedAt
.toString())
.toPersianDateStr(monthString: ''),
.toPersianDateStr(
monthString:
''),
style:
const TextStyle(
fontSize:

View File

@ -51,7 +51,10 @@ class _PasswordInputState extends State<PasswordInput> {
onTap: () => state.currentPageIndex++,
child: DidvanText(
'فراموشی رمز عبور',
style: Theme.of(context).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.normal),
style: Theme.of(context)
.textTheme
.titleSmall
?.copyWith(fontWeight: FontWeight.normal),
color: Theme.of(context).colorScheme.primary,
),
),

View File

@ -23,6 +23,8 @@ class _ResetPasswordState extends State<ResetPassword> {
@override
Widget build(BuildContext context) {
final authState = context.watch<AuthenticationState>();
final bottomInset = MediaQuery.of(context).viewInsets.bottom;
return Form(
key: _formKey,
child: AuthenticationLayout(
@ -50,7 +52,7 @@ class _ResetPasswordState extends State<ResetPassword> {
obsecureText: true,
prefixSvgPath: 'lib/assets/icons/key.svg',
),
const Spacer(),
const SizedBox(height: 32),
DidvanButton(
onPressed: () async {
if (!_formKey.currentState!.validate()) {
@ -79,8 +81,8 @@ class _ResetPasswordState extends State<ResetPassword> {
},
title: authState.hasPassword ? 'تغییر رمز عبور' : 'تایید رمز عبور',
),
const SizedBox(
height: 48,
SizedBox(
height: 48 + bottomInset,
),
],
),

View File

@ -2,7 +2,6 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/view/app_bar_data.dart';
import 'package:didvan/views/comments/comments_state.dart';
import 'package:didvan/views/comments/widgets/comment.dart';
@ -75,17 +74,22 @@ class _CommentsState extends State<Comments> {
builder: (context, state, child) => SliverStateHandler<CommentsState>(
onRetry: state.getComments,
state: state,
itemPadding: const EdgeInsets.only( bottom: 20),
itemPadding: (widget.pageData['type'] == 'news' ||
widget.pageData['type'] == 'radar')
? const EdgeInsets.only(bottom: 20, left: 20, right: 20)
: const EdgeInsets.only(bottom: 20),
childCount: state.comments.length,
placeholder: const _CommentPlaceholder(),
centerEmptyState: false,
enableEmptyState: state.comments.isEmpty,
paddingEmptyState: 0,
emptyState: EmptyState(
asset: Assets.emptyChat,
emptyState: const EmptyState(
asset: 'lib/assets/images/empty_states/Empty_List.png',
title: 'لیست خالی است',
titleColor: const Color.fromARGB(255, 0, 126, 167),
subtitle: 'در حال حاضر آیتمی در این بخش ثبت نشده است. هر زمان مورد جدیدی اضافه شود، در اینجا نمایش داده می‌شود.',
height: 500,
titleColor: Color.fromARGB(255, 0, 126, 167),
subtitle:
'در حال حاضر آیتمی در این بخش ثبت نشده است. هر زمان مورد جدیدی اضافه شود، در اینجا نمایش داده می‌شود.',
),
builder: (context, state, index) => Comment(
key: ValueKey(
@ -337,4 +341,4 @@ class _CommentPlaceholder extends StatelessWidget {
],
);
}
}
}

View File

@ -262,8 +262,8 @@ class _MessageState extends State<Message> with SingleTickerProviderStateMixin {
),
),
DidvanText(
DateTimeUtils.timeWithAmPm(
widget.message.createdAt).toPersianDigit(),
DateTimeUtils.timeWithAmPm(widget.message.createdAt)
.toPersianDigit(),
style: Theme.of(context).textTheme.labelSmall,
color: Theme.of(context).colorScheme.caption,
),

View File

@ -641,7 +641,6 @@ class _FadeInSlide extends StatefulWidget {
Key? key,
required this.child,
this.delay = Duration.zero,
// ignore: unused_element_parameter
this.duration = const Duration(milliseconds: 400),
this.slideOffset = 50.0,
}) : super(key: key);

View File

@ -161,7 +161,7 @@ class _FilteredBookmarksState extends State<FilteredBookmarks> {
onMarkChanged: (id, value) =>
_onBookmarkChanged(id, value, true, item.type),
enableBookmark: true,
useCardStyle: true, // **اعمال استایل کارت در لیست بوکمارک**
useCardStyle: true,
);
},
childCount: state.bookmarks.length +
@ -174,4 +174,4 @@ class _FilteredBookmarksState extends State<FilteredBookmarks> {
),
);
}
}
}

View File

@ -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/main.dart';
@ -258,7 +260,8 @@ class SwotSection extends StatelessWidget {
final double? headerSize;
final double? moreSize;
const SwotSection({super.key, required this.swotItems, this.headerSize, this.moreSize});
const SwotSection(
{super.key, required this.swotItems, this.headerSize, this.moreSize});
@override
Widget build(BuildContext context) {
@ -314,7 +317,9 @@ class SwotSection extends StatelessWidget {
? Theme.of(context).textTheme.titleSmall
: TextStyle(fontSize: moreSize),
color: Theme.of(context).colorScheme.primary,
fontWeight: moreSize == null ? FontWeight.bold : FontWeight.normal,
fontWeight: moreSize == null
? FontWeight.bold
: FontWeight.normal,
),
],
),
@ -1074,6 +1079,8 @@ class MainPageSection extends StatelessWidget {
Padding(
padding: const EdgeInsets.all(9.5),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
@ -1093,15 +1100,42 @@ class MainPageSection extends StatelessWidget {
],
),
const SizedBox(
height: 13,
height: 9,
),
const Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(
height: 19,
)
],
Builder(
builder: (context) {
final riskValue = double.tryParse(item.riskScore ?? '0') ?? 0;
final isNegative = riskValue < 0;
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 4, vertical: 3),
decoration: BoxDecoration(
color: (isNegative ? Colors.red : Colors.green)
.withOpacity(0.1),
borderRadius: BorderRadius.circular(6),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
SvgPicture.asset(
isNegative
? "lib/assets/images/features/Badge.svg"
: "lib/assets/images/features/Badge-Green.svg",
height: 9,
),
const SizedBox(width: 4),
Text(
"عدد ریسک : ${riskValue.abs().toInt().toString().toPersianDigit()}",
style: TextStyle(
color: isNegative ? Colors.red : Colors.green,
fontWeight: FontWeight.normal,
fontSize: 12,
),
)
],
),
);
},
),
],
),

View File

@ -1,8 +1,13 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:didvan/config/design_config.dart';
import 'package:didvan/models/notification_message.dart';
import 'package:didvan/models/view/action_sheet_data.dart';
import 'package:didvan/providers/theme.dart';
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/services/app_home_widget/home_widget_repository.dart';
import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/views/home/explore/explore.dart';
import 'package:didvan/views/home/main/main_page.dart';
@ -13,12 +18,6 @@ import 'package:didvan/views/home/search/search.dart';
import 'package:didvan/views/ai_section/ai_section_page.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/didvan/bnb.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'package:flutter_animate/flutter_animate.dart';
import '../../services/app_home_widget/home_widget_repository.dart';
final GlobalKey<ScaffoldState> homeScaffKey = GlobalKey<ScaffoldState>();
@ -36,74 +35,71 @@ class _HomeState extends State<Home>
@override
void initState() {
super.initState();
final state = context.read<HomeState>();
DesignConfig.updateSystemUiOverlayStyle();
_tabController = TabController(length: 5, vsync: this, initialIndex: 0);
state.tabController = _tabController;
_tabController.addListener(() {
if (_tabController.indexIsChanging) {
state.currentPageIndex = _tabController.index;
} else {
state.currentPageIndex = _tabController.index;
}
state.currentPageIndex = _tabController.index;
});
if (!kIsWeb) {
Future.delayed(Duration.zero, () {
HomeWidgetRepository.fetchWidget();
HomeWidgetRepository.decideWhereToGo();
NotificationMessage? data = HomeWidgetRepository.data;
if (data != null) {
HomeWidgetRepository.decideWhereToGoNotif();
Future.microtask(() async {
await HomeWidgetRepository.fetchWidget();
await HomeWidgetRepository.decideWhereToGo();
if (HomeWidgetRepository.data != null) {
await HomeWidgetRepository.decideWhereToGoNotif();
}
if (mounted) {
AppInitializer.handleCLick(state, _tabController);
}
AppInitializer.handleCLick(state, _tabController);
});
}
state.refresh();
context.read<ThemeProvider>().addListener(() {
state.refresh();
if (mounted) state.refresh();
});
super.initState();
}
PreferredSizeWidget? getAppBar() {
return null;
Future<void> _handleBackPress() async {
if (context.read<HomeState>().tabController.index == 0) {
if (kIsWeb) {
return;
}
ActionSheetUtils(context).openDialog(
data: ActionSheetData(
content: const DidvanText('آیا قصد خروج از برنامه را دارید؟'),
onConfirmed: () {
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
},
isBackgroundDropBlur: true,
confrimTitle: 'بله',
dismissTitle: 'خیر',
),
);
} else {
_tabController.animateTo(0);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
key: homeScaffKey,
return PopScope(
canPop: false,
// ignore: deprecated_member_use
backgroundColor: Theme.of(context).colorScheme.background,
resizeToAvoidBottomInset: false,
drawer: null,
// ignore: deprecated_member_use
body: WillPopScope(
onWillPop: () async {
if (context.read<HomeState>().tabController.index == 0) {
if (kIsWeb) {
return true;
}
ActionSheetUtils(context).openDialog(
data: ActionSheetData(
content: const DidvanText(
'آیا قصد خروج از برنامه را دارید؟',
),
onConfirmed: () {
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
},
isBackgroundDropBlur: true,
confrimTitle: 'بله',
dismissTitle: 'خیر',
));
} else {
_tabController.animateTo(0);
}
return false;
},
child: Consumer<HomeState>(
onPopInvoked: (didPop) async {
if (didPop) return;
await _handleBackPress();
},
child: Scaffold(
key: homeScaffKey,
backgroundColor: Theme.of(context).colorScheme.surface,
resizeToAvoidBottomInset: false,
body: Consumer<HomeState>(
builder: (context, state, child) => AnimatedCrossFade(
duration: DesignConfig.lowAnimationDuration,
crossFadeState: state.filtering
@ -127,26 +123,24 @@ class _HomeState extends State<Home>
secondChild: const SearchPage(),
),
),
),
bottomNavigationBar: Consumer<HomeState>(
builder: (context, state, child) => DidvanBNB(
currentTabIndex: state.currentPageIndex,
onTabChanged: (index) {
if (index < _tabController.length) {
state.currentPageIndex = index;
FocusScope.of(context).unfocus();
state.resetFilters(false);
_tabController.animateTo(index);
}
},
),
)
.animate()
.slideY(
duration: 500.ms,
begin: 1,
curve: Curves.easeOut,
bottomNavigationBar: Consumer<HomeState>(
builder: (context, state, child) => DidvanBNB(
currentTabIndex: state.currentPageIndex,
onTabChanged: (index) {
if (index < _tabController.length) {
state.currentPageIndex = index;
FocusScope.of(context).unfocus();
state.resetFilters(false);
_tabController.animateTo(index);
}
},
),
).animate().slideY(
duration: 500.ms,
begin: 1,
curve: Curves.easeOut,
),
),
);
}
}
}

View File

@ -1,36 +1,34 @@
// ignore_for_file: unnecessary_import
// lib/views/home/main/main_page.dart
// ignore_for_file: unnecessary_import, deprecated_member_use
import 'package:didvan/views/home/explore/explore.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:universal_html/html.dart' as html;
import 'package:url_launcher/url_launcher_string.dart';
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/models/home_page_content/content.dart';
import 'package:didvan/models/home_page_content/home_page_list.dart';
import 'package:didvan/models/home_page_content/swot.dart';
import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/services/network/request.dart';
import 'package:didvan/views/home/explore/explore.dart';
import 'package:didvan/views/home/home_state.dart';
import 'package:didvan/views/home/main/main_page_state.dart';
import 'package:didvan/views/home/new_statistic/new_statistics_state.dart';
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';
import 'package:didvan/views/widgets/carousel_3d.dart';
import 'package:didvan/views/home/home_state.dart';
import 'package:didvan/views/widgets/text_divider.dart';
import 'package:didvan/views/widgets/home_app_bar.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:provider/provider.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:flutter/foundation.dart' show kIsWeb, defaultTargetPlatform;
import 'package:universal_html/html.dart' as html;
bool isAnyMobile() {
if (kIsWeb) {
@ -45,9 +43,7 @@ bool isAnyMobile() {
}
class MainPage extends StatefulWidget {
const MainPage({
super.key,
});
const MainPage({super.key});
@override
State<MainPage> createState() => _MainPageState();
@ -73,6 +69,7 @@ class _MainPageState extends State<MainPage> {
'🏠 MainPage build - didvanPlus: ${state.didvanPlus != null}');
debugPrint(
'🏠 MainPage build - didvanVoice: ${state.didvanVoice != null}');
return Column(
children: [
const HomeAppBar(
@ -83,6 +80,7 @@ class _MainPageState extends State<MainPage> {
child: ListView(
padding: const EdgeInsets.only(top: 0, bottom: 16),
children: [
// --- Stories Section ---
if (state.stories.isNotEmpty) ...[
const TextDivider(text: 'دیده‌بان')
.animate()
@ -95,12 +93,16 @@ class _MainPageState extends State<MainPage> {
child: StorySection(stories: state.stories),
).animate().fadeIn(delay: 600.ms, duration: 500.ms),
],
// --- Didvan Plus ---
if (state.didvanPlus != null) ...[
const SizedBox(height: 16),
DidvanPlusSection(didvanPlus: state.didvanPlus!)
.animate()
.fadeIn(delay: 650.ms, duration: 500.ms),
],
// --- Strategic Dashboard ---
const SizedBox(height: 12),
const TextDivider(text: 'پیشخوان استراتژیک')
.animate()
@ -109,6 +111,8 @@ class _MainPageState extends State<MainPage> {
padding: EdgeInsets.symmetric(horizontal: 16),
child: MainPageMainContent(),
).animate().fadeIn(delay: 800.ms, duration: 500.ms),
// --- Explore Latest ---
if (state.content != null &&
state.content!.lists.isNotEmpty) ...[
const _ExploreLatestTitle()
@ -119,10 +123,16 @@ class _MainPageState extends State<MainPage> {
swotItems: state.swotItems,
).animate().fadeIn(delay: 1000.ms, duration: 500.ms),
],
// --- SWOT Items ---
if (state.swotItems.isNotEmpty)
SwotSection(swotItems: state.swotItems,headerSize: 13,moreSize: 12,)
.animate()
.fadeIn(delay: 1100.ms, duration: 500.ms),
SwotSection(
swotItems: state.swotItems,
headerSize: 13,
moreSize: 12,
).animate().fadeIn(delay: 1100.ms, duration: 500.ms),
// --- Didvan Voice ---
if (state.didvanVoice != null) ...[
const SizedBox(height: 16),
const _DidvanVoiceTitle()
@ -133,6 +143,8 @@ class _MainPageState extends State<MainPage> {
.animate()
.fadeIn(delay: 1200.ms, duration: 500.ms),
],
// --- Commented Out Section (Industry Pulse) ---
// const _IndustryPulseTitle()
// .animate()
// .fadeIn(delay: 1100.ms, duration: 500.ms),
@ -149,18 +161,15 @@ class _MainPageState extends State<MainPage> {
}
}
// --- Sub Widgets ---
class _DidvanSignalsTitle extends StatelessWidget {
const _DidvanSignalsTitle();
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(
left: 16,
right: 16,
bottom: 16,
top: 0,
),
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Row(
children: [
SvgPicture.asset(
@ -221,12 +230,7 @@ class _ExploreLatestTitle extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(
left: 16,
right: 16,
bottom: 16,
top: 0,
),
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
@ -284,10 +288,7 @@ class _ExploreLatestSlider extends StatelessWidget {
itemsData = [];
for (var list in lists) {
if (list.type == 'video' ||
list.type == 'podcast' ||
list.type == 'news' ||
list.type == 'radar') {
if (['video', 'podcast', 'news', 'radar'].contains(list.type)) {
continue;
}
if (list.contents.isNotEmpty) {
@ -318,18 +319,14 @@ class _ExploreLatestSlider extends StatelessWidget {
itemsData.add((type: 'swot', content: null, swotItem: swotItems.first));
}
if (items.isEmpty) {
return const SizedBox.shrink();
}
if (items.isEmpty) return const SizedBox.shrink();
return Carousel3D(
items: items,
height: 220,
autoPlayDuration: const Duration(seconds: 5),
showControls: true,
onItemChanged: (index) {
// Optional: Handle item change if needed
},
onItemChanged: (index) {},
onItemTap: (index) {
final data = itemsData[index];
if (data.swotItem != null) {
@ -351,6 +348,10 @@ class _ExploreLatestSlider extends StatelessWidget {
}
}
// ---------------------------------------------------------------------------
// ------------------------- COMMENTED OUT SECTIONS --------------------------
// ---------------------------------------------------------------------------
// class _IndustryPulseTitle extends StatelessWidget {
// const _IndustryPulseTitle();

View File

@ -1,5 +1,4 @@
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/models/didvan_plus_model.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/services/network/request.dart';

View File

@ -11,7 +11,6 @@ import 'package:didvan/views/widgets/didvan/card.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/infography_tag.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart';
import 'package:didvan/views/widgets/liked_button.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';

View File

@ -39,13 +39,13 @@ class SimpleExploreCard extends StatelessWidget {
return 'lib/assets/icons/Startup.svg';
case 'radar':
return 'lib/assets/icons/Pouyesh_Ofogh_New.svg';
case 'trend': // رادار روند
case 'trend':
case 'trends':
return 'lib/assets/icons/Ravand.svg';
case 'technology': // رادار تکنولوزی/فناوری
case 'technology':
case 'tech':
return 'lib/assets/icons/Technology.svg';
case 'risk': // رادار ریسک
case 'risk':
return 'lib/assets/icons/RiskRadar.svg';
case 'survey':
case 'delphi':

View File

@ -292,14 +292,13 @@ class _PodcastTabPageState extends State<PodcastTabPage> {
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
final podcast = state.studios[index];
// NEW: بررسی اینکه آیا این آیتم، آخرین آیتم در لیست است
final bool isLast = index == state.studios.length - 1;
return PodcastListCard(
podcast: podcast,
onTap: () => _navigateToDetails(index),
isLastItem: isLast, // NEW: ارسال پارامتر به ویجت کارت
isLastItem: isLast,
);
},
),
@ -310,4 +309,4 @@ class _PodcastTabPageState extends State<PodcastTabPage> {
onRetry: () => context.read<PodcastsState>().getStudios(page: 1),
);
}
}
}

View File

@ -3,7 +3,6 @@
import 'package:chewie/chewie.dart';
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/models/studio_details_data.dart';

View File

@ -12,13 +12,13 @@ import 'package:persian_number_utility/persian_number_utility.dart';
class PodcastListCard extends StatelessWidget {
final OverviewData podcast;
final VoidCallback onTap;
final bool isLastItem; // NEW: پارامتر جدید برای تشخیص آیتم آخر
final bool isLastItem;
const PodcastListCard({
super.key,
required this.podcast,
required this.onTap,
this.isLastItem = false, // NEW: مقداردهی اولیه پارامتر
this.isLastItem = false,
});
String _formatDuration(int? duration) {
@ -143,7 +143,6 @@ class PodcastListCard extends StatelessWidget {
),
),
),
// NEW: چک میکنیم که آیا آیتم آخر است یا نه
if (!isLastItem)
Divider(
color: Colors.grey[300],
@ -155,4 +154,4 @@ class PodcastListCard extends StatelessWidget {
),
);
}
}
}

View File

@ -46,13 +46,13 @@ class _NewStatisticState extends State<NewStatistic> {
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// ignore: prefer_const_constructors
HomeAppBar(
showSearchField: true,
),
Center(
child: Container(
padding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16),
color: Theme.of(context).colorScheme.background,
child: DidvanText(
'دسته‌بندی‌های کلان',
@ -102,8 +102,7 @@ class _NewStatisticState extends State<NewStatistic> {
itemsInStatics(context, state, 1, DidvanIcons.currency_solid),
itemsInStatics(context, state, 2, DidvanIcons.bitcoin_solid),
itemsInStatics(context, state, 3, DidvanIcons.metal_solid),
itemsInStatics(
context, state, 4, DidvanIcons.commodity_solid),
itemsInStatics(context, state, 4, DidvanIcons.commodity_solid),
itemsInStatics(context, state, 5, DidvanIcons.industry_solid,
hasDivider: false),
const SizedBox(
@ -369,4 +368,4 @@ class StatHeader extends StatelessWidget {
)
]);
}
}
}

View File

@ -112,11 +112,11 @@ class _MentionsState extends State<Mentions> {
enableEmptyState: state.comments.isEmpty,
paddingEmptyState: 0,
emptyState: const EmptyState(
asset: 'lib/assets/images/empty_states/Empty_List.png',
title: 'دوستان خود را فراخوانی کنید',
titleColor: Color.fromARGB(255, 0, 126, 167),
height: 550,
),
asset: 'lib/assets/images/empty_states/Empty_List.png',
title: 'دوستان خود را فراخوانی کنید',
titleColor: Color.fromARGB(255, 0, 126, 167),
height: 550,
),
builder: (context, state, index) => Mention(
key: ValueKey(
state.comments[index].id.toString() +

View File

@ -71,7 +71,9 @@ class _NewsState extends State<News> {
title: Text(
'دنیای فولاد',
style: theme.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),
),

View File

@ -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';
@ -438,7 +440,6 @@ class _CategoryExpansionGroupState extends State<_CategoryExpansionGroup> {
textDirection: TextDirection.ltr,
child: CupertinoSwitch(
value: item.selected ?? false,
// ignore: deprecated_member_use
activeColor:
Theme.of(context).colorScheme.primary,
onChanged: (value) {

View File

@ -27,7 +27,8 @@ final List<OnboardingEntity> onboardingPages = [
OnboardingEntity(
imagePath: 'lib/assets/images/onboarding/1.png',
title: 'هوشان',
description: 'ارائه ابزارهای هوش مصنوعی مورد نیاز مدیران اعم از خلاصه‌ساز، ساخت عکس، تحلیل و ترسیم نمودار، تبدیل متن به صوت، ساخت ویدیو و ترجمه، امکان پرسش و پاسخ از مدل‌های زبانی مختلف و یا جستجوی هوشمند در محتوای داخلی از طریق دستیار اختصاصی هوش مصنوعی دیدوان.',
description:
'ارائه ابزارهای هوش مصنوعی مورد نیاز مدیران اعم از خلاصه‌ساز، ساخت عکس، تحلیل و ترسیم نمودار، تبدیل متن به صوت، ساخت ویدیو و ترجمه، امکان پرسش و پاسخ از مدل‌های زبانی مختلف و یا جستجوی هوشمند در محتوای داخلی از طریق دستیار اختصاصی هوش مصنوعی دیدوان.',
),
];
@ -168,7 +169,9 @@ class _OnboardingPageState extends State<OnboardingPage> {
],
),
),
const SizedBox(height: 10,),
const SizedBox(
height: 10,
),
Expanded(
flex: 2,
child: Padding(
@ -184,7 +187,8 @@ class _OnboardingPageState extends State<OnboardingPage> {
theme.textTheme.headlineMedium?.copyWith(
fontWeight: FontWeight.bold,
fontSize: 22,
color: const Color.fromARGB(255, 0, 126, 167),
color:
const Color.fromARGB(255, 0, 126, 167),
),
),
const SizedBox(height: 13),
@ -193,10 +197,10 @@ class _OnboardingPageState extends State<OnboardingPage> {
textDirection: TextDirection.rtl,
textAlign: TextAlign.start,
style: theme.textTheme.bodyMedium?.copyWith(
color: const Color.fromARGB(255, 41, 41, 41),
height: 1.6,
fontSize: 14
),
color:
const Color.fromARGB(255, 41, 41, 41),
height: 1.6,
fontSize: 14),
),
],
),
@ -208,7 +212,6 @@ class _OnboardingPageState extends State<OnboardingPage> {
},
),
),
if (_currentPage > 0)
Positioned(
top: 16,
@ -234,7 +237,6 @@ class _OnboardingPageState extends State<OnboardingPage> {
),
),
),
Positioned(
bottom: 30,
left: 24,

View File

@ -4,7 +4,6 @@ import 'package:chewie/chewie.dart';
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/models/studio_details_data.dart';

View File

@ -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/app_icons.dart';
@ -44,7 +46,10 @@ class ChatRoomItem extends StatelessWidget {
Row(
children: [
SvgPicture.asset(
chatRoom.lastMessage.writedByAdmin? chatRoom.lastMessage.readed ?'lib/assets/icons/sms.svg' :'lib/assets/icons/sms-notification.svg'
chatRoom.lastMessage.writedByAdmin
? chatRoom.lastMessage.readed
? 'lib/assets/icons/sms.svg'
: 'lib/assets/icons/sms-notification.svg'
: 'lib/assets/icons/sms-tracking.svg',
height: 24,
width: 24,
@ -87,8 +92,12 @@ class ChatRoomItem extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 8, 10, 8),
child: Text(
chatRoom.lastMessage.writedByAdmin? chatRoom.lastMessage.readed
? 'پاسخ داده شده' : 'خوانده نشده' : 'در انتظار پاسخ',),
chatRoom.lastMessage.writedByAdmin
? chatRoom.lastMessage.readed
? 'پاسخ داده شده'
: 'خوانده نشده'
: 'در انتظار پاسخ',
),
),
),
// Icon(

View File

@ -1,3 +1,5 @@
// ignore_for_file: avoid_print, deprecated_member_use
import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart';
@ -159,11 +161,12 @@ class _ProfilePageState extends State<ProfilePage>
image:
(user.photo != null && user.photo!.isNotEmpty)
? DecorationImage(
image: NetworkImage('https://api.didvan.app${user.photo!}',
headers: {
'Authorization':
'Bearer ${RequestService.token}',
}),
image: NetworkImage(
'https://api.didvan.app${user.photo!}',
headers: {
'Authorization':
'Bearer ${RequestService.token}',
}),
fit: BoxFit.cover,
)
: null,

View File

@ -1,3 +1,5 @@
// ignore_for_file: deprecated_member_use
import 'dart:async';
import 'dart:math';

View File

@ -1,7 +1,6 @@
// 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';
import 'package:didvan/main.dart';
import 'package:didvan/providers/media.dart';
@ -40,7 +39,7 @@ class _SplashState extends State<Splash> with TickerProviderStateMixin {
_pulseController = AnimationController(
duration: const Duration(milliseconds: 1500),
vsync: this,
vsync: this,
);
_pulseAnimation = Tween<double>(
@ -195,18 +194,6 @@ class _SplashState extends State<Splash> with TickerProviderStateMixin {
],
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,
),
),
),
],
),
),
@ -251,15 +238,16 @@ class _SplashState extends State<Splash> with TickerProviderStateMixin {
await mediaProvider.getDownloadsList();
}
// اگر اینجا خطای سرور رخ دهد (به خاطر تغییر در user.dart)، کد به بلوک catch میرود
final result = await userProvider.getUserInfo();
if (!result) {
// این بخش فقط زمانی اجرا میشود که سرور پاسخ ۴۰۱ (نامعتبر بودن توکن) داده باشد
print("no results were returned for user info");
try {
StorageService.delete(key: 'token');
} catch (e) {
print("error in case of no user info result: $e");
// catch
}
navigatorKey.currentState!.pushNamedAndRemoveUntil(
@ -281,7 +269,6 @@ class _SplashState extends State<Splash> with TickerProviderStateMixin {
if (destinationRoute == Routes.home) {
print("destination route was home and init uri is $initialURI");
// (routeArguments as Map)['deepLinkUri'] = initialURI;
initialURI = null;
}
@ -296,6 +283,7 @@ class _SplashState extends State<Splash> with TickerProviderStateMixin {
arguments: routeArguments,
);
} catch (e) {
// اگر خطای سرور رخ دهد، اینجا گرفته میشود و دکمه تلاش مجدد نشان داده میشود
setState(() {
_errorOccured = true;
});

File diff suppressed because it is too large Load Diff

View File

@ -149,8 +149,7 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
_isAiSpeaking = false;
});
}
// <<< راهحل مشکل ۲: ریست کردن پاسخ قبلی
if (_currentAiResponse != null) {
setState(() {
_currentAiResponse = null;
@ -178,7 +177,6 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
await Future.delayed(const Duration(seconds: 2));
// <<< راهحل مشکل ۲: بررسی وضعیت قبل از شروع ضبط
if (mounted && _isPreparing) {
setState(() {
_isPreparing = false;
@ -188,11 +186,9 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
_preparingController.stop();
_waveController.repeat();
} else if (mounted) {
// اگر کاربر انگشت خود را برداشته بود (_isPreparing false شده)
await _audioRecorder.stop(); // ضبط را متوقف کن
await _audioRecorder.stop();
_waveController.stop();
}
// >>>
}
} catch (e) {
setState(() {
@ -200,7 +196,6 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
_isRecording = false;
_statusText = 'خطا در شروع ضبط صدا';
});
// <<< راهحل مشکل ۲: توقف انیمیشنها در صورت خطا
_preparingController.stop();
_waveController.stop();
// >>>
@ -475,8 +470,8 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
top: 0,
child: _buildVisualization(),
),
// وقتی پاسخ هست، کارت پاسخ را نمایش میدهیم
if (_currentAiResponse != null && _currentAiResponse!.isNotEmpty)
if (_currentAiResponse != null &&
_currentAiResponse!.isNotEmpty)
Positioned(
bottom: 120,
left: 24,
@ -484,7 +479,6 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
child: _buildResponsePreview(),
)
else
// در غیر این صورت، متن status را نمایش میدهیم
Positioned(
bottom: 130,
left: 24,
@ -709,7 +703,8 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: const Color(0xFF00AAFF).withOpacity(0.4),
color: const Color(0xFF00AAFF)
.withOpacity(0.4),
blurRadius: 12,
spreadRadius: 2,
),
@ -872,7 +867,8 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: const Color(0xFF00AAFF).withOpacity(0.4),
color:
const Color(0xFF00AAFF).withOpacity(0.4),
blurRadius: 15,
spreadRadius: 2,
),
@ -953,7 +949,8 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
Expanded(
child: ElevatedButton.icon(
onPressed: () {
Clipboard.setData(ClipboardData(text: _currentAiResponse ?? ''));
Clipboard.setData(ClipboardData(
text: _currentAiResponse ?? ''));
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: const DidvanText(
@ -1033,15 +1030,13 @@ class _AiVoiceChatDialogState extends State<AiVoiceChatDialog>
_startRecording();
}
},
// <<< راهحل مشکل ۲: (بدون تغییر، همچنان پابرجاست)
onLongPressEnd: (_) {
if (_isRecording) {
_stopRecording();
} else if (_isPreparing) {
// کاربر انگشت خود را در حین آمادهسازی برداشت
setState(() {
_isPreparing = false;
_statusText = 'برای شروع مکالمه، دکمه میکروفون را نگه دارید';
_statusText = 'برای شروع مکالمه، دکمه میکروفون را نگه دارید';
});
_preparingController.stop();
_waveController.stop();
@ -1236,4 +1231,4 @@ class PreparingSpinnerPainter extends CustomPainter {
bool shouldRepaint(covariant PreparingSpinnerPainter oldDelegate) {
return true;
}
}
}

View File

@ -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/views/widgets/didvan/text.dart';
@ -312,7 +314,10 @@ class _NavBarItemState extends State<_NavBarItem>
duration: const Duration(milliseconds: 150),
child: DidvanText(
widget.title,
style: Theme.of(context).textTheme.bodySmall?.copyWith(fontSize: 11),
style: Theme.of(context)
.textTheme
.bodySmall
?.copyWith(fontSize: 11),
color: Theme.of(context).colorScheme.caption,
fontWeight: FontWeight.w500,
),

View File

@ -491,9 +491,53 @@ class _DidvanPageViewState extends State<DidvanPageView> {
const EdgeInsets.symmetric(
horizontal: 16,
vertical: 4),
child: MultitypeOverview(
item: item.relatedContents[i],
onMarkChanged: (id, value) {},
child: GestureDetector(
onTap: () {
final relatedItem =
item.relatedContents[i];
if (relatedItem.type ==
'news') {
Navigator.of(context)
.pushNamed(
Routes.newsDetails,
arguments: {
'id': relatedItem.id,
'args':
const NewsRequestArgs(
page: 0),
'onMarkChanged':
(id, value) {},
'hasUnmarkConfirmation':
false,
},
);
} else if (relatedItem
.type ==
'radar') {
Navigator.of(context)
.pushNamed(
Routes.radarDetails,
arguments: {
'id': relatedItem.id,
'args':
const RadarRequestArgs(
page: 0),
'onMarkChanged':
(id, value) {},
'onCommentsChanged':
(id, count) {},
'hasUnmarkConfirmation':
false,
},
);
}
},
child: MultitypeOverview(
item:
item.relatedContents[i],
onMarkChanged:
(id, value) {},
),
),
),
const SizedBox(height: 8),

View File

@ -0,0 +1,46 @@
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
class GlassShader {
ui.FragmentProgram? _program;
ui.FragmentShader? _shader;
Future<void> initialize() async {
_program = await ui.FragmentProgram.fromAsset(
'lib/shaders/liquid_glass_lens.frag');
_shader = _program!.fragmentShader();
}
ui.FragmentShader? get shader => _shader;
void updateUniforms({
required Size widgetSize,
required Size textureSize,
required Offset widgetOffset,
required double distortion,
required double blur,
required double dispersion,
required ui.Image texture,
}) {
if (_shader == null) return;
// uniform vec2 uResolution;
// uniform float uDistortionStrength;
// uniform float uBlurIntensity;
// uniform float uDispersion;
// uniform sampler2D uTexture;
// uniform vec2 uWidgetOffset;
// uniform vec2 uTextureSize;
_shader!.setFloat(0, widgetSize.width);
_shader!.setFloat(1, widgetSize.height);
_shader!.setFloat(2, distortion);
_shader!.setFloat(3, blur);
_shader!.setFloat(4, dispersion);
_shader!.setImageSampler(0, texture);
_shader!.setFloat(5, widgetOffset.dx);
_shader!.setFloat(6, widgetOffset.dy);
_shader!.setFloat(7, textureSize.width);
_shader!.setFloat(8, textureSize.height);
}
}

View File

@ -0,0 +1,131 @@
import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/scheduler.dart';
import 'glass_logic.dart';
class ShaderPainter extends CustomPainter {
ShaderPainter(this.shader);
final ui.FragmentShader shader;
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..shader = shader;
canvas.drawRect(Offset.zero & size, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}
class LiquidGlassContainer extends StatefulWidget {
final Widget child;
final GlobalKey backgroundKey;
final GlassShader shader;
final double distortion;
final double blur;
final double dispersion;
final BorderRadius? borderRadius;
const LiquidGlassContainer({
super.key,
required this.child,
required this.backgroundKey,
required this.shader,
this.distortion = 1.0,
this.blur = 5.0,
this.dispersion = 1.0,
this.borderRadius,
});
@override
State<LiquidGlassContainer> createState() => _LiquidGlassContainerState();
}
class _LiquidGlassContainerState extends State<LiquidGlassContainer>
with SingleTickerProviderStateMixin {
ui.Image? _capturedImage;
late Ticker _ticker;
@override
void initState() {
super.initState();
_ticker = createTicker((_) => _captureBackground());
_ticker.start();
}
@override
void dispose() {
_ticker.dispose();
super.dispose();
}
Future<void> _captureBackground() async {
final boundaryContext = widget.backgroundKey.currentContext;
if (boundaryContext == null) return;
final renderObject = boundaryContext.findRenderObject();
if (renderObject is! RenderRepaintBoundary) return;
try {
final image = await renderObject.toImage(pixelRatio: 1.0);
if (mounted) {
setState(() {
_capturedImage = image;
});
}
} catch (e) {
// Ignored
}
}
@override
Widget build(BuildContext context) {
Offset offsetFromBackground = Offset.zero;
Size bgSize = Size.zero;
final RenderBox? box = context.findRenderObject() as RenderBox?;
final RenderBox? backgroundBox =
widget.backgroundKey.currentContext?.findRenderObject() as RenderBox?;
if (box != null && backgroundBox != null) {
final absolutePos = box.localToGlobal(Offset.zero);
final backgroundPos = backgroundBox.localToGlobal(Offset.zero);
offsetFromBackground = absolutePos - backgroundPos;
bgSize = backgroundBox.size;
}
return ClipRRect(
borderRadius: widget.borderRadius ?? BorderRadius.zero,
child: Stack(
children: [
if (_capturedImage != null &&
widget.shader.shader != null &&
backgroundBox != null)
Positioned.fill(
child: CustomPaint(
painter: ShaderPainter(widget.shader.shader!),
),
),
Builder(builder: (ctx) {
if (_capturedImage != null &&
widget.shader.shader != null &&
backgroundBox != null) {
widget.shader.updateUniforms(
widgetSize: (box?.size ?? Size.zero),
textureSize: bgSize,
widgetOffset: offsetFromBackground,
distortion: widget.distortion,
blur: widget.blur,
dispersion: widget.dispersion,
texture: _capturedImage!,
);
}
return const SizedBox.shrink();
}),
widget.child,
],
),
);
}
}

View File

@ -45,7 +45,9 @@ class HomeAppBar extends StatelessWidget {
return Column(
children: [
const SizedBox(height: 10,),
const SizedBox(
height: 10,
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: Row(
@ -105,8 +107,8 @@ class HomeAppBar extends StatelessWidget {
),
Container(
color: const Color.fromARGB(255, 224, 224, 224),
height: 20,
width: 1.5,
height: 20,
width: 1.5,
),
GestureDetector(
onTap: () {

View File

@ -534,7 +534,6 @@ class _HistoryDrawerContentState extends State<HistoryDrawerContent> {
...last7DaysChats.map((chat) =>
_buildChatItem(chat, historyState)),
],
// '۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'
if (last30DaysChats.isNotEmpty) ...[
_buildSectionHeader('۳۰ روز اخیر'),
...last30DaysChats.map((chat) =>
@ -616,7 +615,8 @@ class _HistoryDrawerContentState extends State<HistoryDrawerContent> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
DidvanText(
_formatDateFromString(chat.updatedAt).toPersianDigit(),
_formatDateFromString(chat.updatedAt)
.toPersianDigit(),
fontSize: 12,
color: Colors.grey[600],
),

View File

@ -127,7 +127,6 @@ class LogoAppBar extends StatelessWidget implements PreferredSizeWidget {
void _onChanged(String value, BuildContext context) {
final state = context.read<HomeState>();
// Use the improved search functionality from HomeState
if (value.length >= 2) {
state.onSearchChanged(value);
} else if (value.isEmpty) {

View File

@ -22,7 +22,7 @@ class MultitypeOverview extends StatelessWidget {
final bool enableCaption;
final bool enableBookmark;
final bool showDivider;
final bool useCardStyle; // **جدید: برای کنترل استایل کارت**
final bool useCardStyle;
const MultitypeOverview({
Key? key,
@ -32,11 +32,10 @@ class MultitypeOverview extends StatelessWidget {
this.enableCaption = false,
this.enableBookmark = false,
this.showDivider = true,
this.useCardStyle = false, // **مقدار پیشفرض false**
this.useCardStyle = false,
}) : super(key: key);
get _targetPageArgs {
// ...
if (item.type == 'radar') {
return const RadarRequestArgs(page: 0);
}
@ -47,7 +46,6 @@ class MultitypeOverview extends StatelessWidget {
}
String? get _targetPageRouteName {
// ...
if (item.type == 'radar') {
return Routes.radarDetails;
}
@ -64,7 +62,6 @@ class MultitypeOverview extends StatelessWidget {
}
String get _icon {
// ...
switch (item.type) {
case 'radar':
return 'lib/assets/icons/Pouyesh_Ofogh_New.svg';
@ -88,7 +85,6 @@ class MultitypeOverview extends StatelessWidget {
@override
Widget build(BuildContext context) {
// محتوای داخلی کارت
final innerContent = Column(
children: [
Row(
@ -249,7 +245,6 @@ class MultitypeOverview extends StatelessWidget {
],
],
),
// **اگر useCardStyle فعال نباشد، فاصله و خط جداکننده قبلی را اضافه میکنیم**
if (!useCardStyle) ...[
const SizedBox(
height: 10,
@ -262,7 +257,6 @@ class MultitypeOverview extends StatelessWidget {
],
);
// **اعمال استایل کارت (Container با Border و Padding) به صورت شرطی**
if (useCardStyle) {
return Padding(
padding: const EdgeInsets.all(20.0),
@ -281,7 +275,7 @@ class MultitypeOverview extends StatelessWidget {
);
}
return innerContent; // در غیر این صورت، فقط محتوای داخلی را برمیگرداند.
return innerContent;
}
static Widget get placeholder => const DidvanCard(
@ -306,4 +300,4 @@ class MultitypeOverview extends StatelessWidget {
],
),
);
}
}

View File

@ -6,7 +6,6 @@ import 'package:didvan/views/widgets/bookmark_button.dart';
import 'package:didvan/views/widgets/didvan/card.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/liked_button.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
@ -197,4 +196,4 @@ class NewsOverview extends StatelessWidget {
],
),
);
}
}

View File

@ -6,7 +6,6 @@ import 'package:didvan/views/widgets/bookmark_button.dart';
import 'package:didvan/views/widgets/didvan/card.dart';
import 'package:didvan/views/widgets/didvan/divider.dart';
import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/liked_button.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/material.dart';
@ -176,8 +175,7 @@ class RadarOverview extends StatelessWidget {
svgIconOn: 'lib/assets/icons/bookmark_fill.svg',
svgIconOff: 'lib/assets/icons/archive-tick.svg',
color: const Color.fromARGB(255, 0, 126, 167),
unbookmarkedColor:
Theme.of(context).colorScheme.caption,
unbookmarkedColor: Theme.of(context).colorScheme.caption,
),
],
),

View File

@ -13,7 +13,7 @@ class EmptyConnection extends StatelessWidget {
title: 'اتصال اینترنت برقرار نیست',
titleColor: const Color.fromARGB(255, 0, 126, 167),
action: onRetry,
buttonTitle: 'تلاش مجدد' ,
buttonTitle: 'تلاش مجدد',
);
}
}

View File

@ -29,8 +29,11 @@ class EmptyState extends StatelessWidget {
final isSvg = asset.toLowerCase().endsWith('.svg');
return Column(
mainAxisAlignment: subtitle != null ? MainAxisAlignment.start : MainAxisAlignment.center,
crossAxisAlignment: subtitle != null ? CrossAxisAlignment.start : CrossAxisAlignment.center,
mainAxisAlignment:
subtitle != null ? MainAxisAlignment.start : MainAxisAlignment.center,
crossAxisAlignment: subtitle != null
? CrossAxisAlignment.start
: CrossAxisAlignment.center,
children: [
SizedBox(
width: double.infinity,

View File

@ -1,3 +1,5 @@
// ignore_for_file: prefer_const_constructors
import 'package:didvan/models/enums.dart';
import 'package:didvan/providers/core.dart';
import 'package:didvan/views/widgets/state_handlers/empty_connection.dart';

View File

@ -933,6 +933,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
liquid_glass_renderer:
dependency: "direct main"
description:
name: liquid_glass_renderer
sha256: "789be157494b38cdef7607bb9b63e7fe3fab76fae16149e52fa313c2d858cb3f"
url: "https://pub.dev"
source: hosted
version: "0.2.0-dev.4"
list_counter:
dependency: transitive
description:
@ -949,6 +957,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.5.0"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.3.0"
markdown:
dependency: transitive
description:
@ -1005,6 +1021,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.6"
motor:
dependency: transitive
description:
name: motor
sha256: d6fd00496a2b934f8f54e660acc0d039d0e4bf8e667b6977b0480be97385779e
url: "https://pub.dev"
source: hosted
version: "1.0.1"
nested:
dependency: transitive
description:

View File

@ -117,6 +117,7 @@ dependencies:
syncfusion_flutter_charts: ^31.1.19
flutter_animate: ^4.5.2
syncfusion_flutter_pdfviewer: ^31.1.21
liquid_glass_renderer: ^0.2.0-dev.4
# image_gallery_saver: ^2.0.3
# fading_edge_scrollview: ^4.1.1
@ -135,6 +136,10 @@ dev_dependencies:
# The following section is specific to Flutter.
flutter:
shaders:
- lib/shaders/liquid_glass_lens.frag
# - lib/shaders/static_liquid.frag
# - lib/shaders/water_ripple.frag
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.