22-02-1403 / Rhmn / changes: notification Fix & Mentions Create no.1

This commit is contained in:
OkaykOrhmn 2024-05-11 17:41:44 +03:30
parent 562c006eb6
commit 2d00d239be
24 changed files with 1306 additions and 188 deletions

View File

@ -4,20 +4,26 @@
<uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<application <application
android:label="Didvan" android:label="Didvan"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="true" android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true"> android:requestLegacyExternalStorage="true">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:launchMode="singleTop" android:launchMode="singleTop"
android:theme="@style/LaunchTheme" android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:showOnLockScreen="true"
android:showWhenLocked="true"
android:turnScreenOn="true"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as <!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user
@ -30,6 +36,7 @@
android:name="io.flutter.embedding.android.NormalTheme" android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme" android:resource="@style/NormalTheme"
/> />
<!-- Displays an Android View that continues showing the launch screen <!-- Displays an Android View that continues showing the launch screen
Drawable until Flutter paints its first frame, then this splash Drawable until Flutter paints its first frame, then this splash
screen fades out. A splash screen is useful to avoid any visual screen fades out. A splash screen is useful to avoid any visual
@ -44,6 +51,16 @@
android:name="com.yalantis.ucrop.UCropActivity" android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait" android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"/> android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
<service
android:name=".MyFirebaseMessagingService"
android:exported="true">
<intent-filter>
<action android:name="com.google.firebase.messaging.RECEIVE" />
</intent-filter>
</service>
<!-- Don't delete the meta-data below. <!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --> This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
</application> </application>

View File

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip

View File

@ -38,6 +38,18 @@ post_install do |installer|
installer.pods_project.targets.each do |target| installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target) flutter_additional_ios_build_settings(target)
end end
################ Awesome Notifications pod modification 1 ###################
awesome_pod_file = File.expand_path(File.join('plugins', 'awesome_notifications', 'ios', 'Scripts', 'AwesomePodFile'), '.symlinks')
require awesome_pod_file
update_awesome_pod_build_settings(installer)
################ Awesome Notifications pod modification 1 ###################
end
################ Awesome Notifications pod modification 2 ###################
awesome_pod_file = File.expand_path(File.join('plugins', 'awesome_notifications', 'ios', 'Scripts', 'AwesomePodFile'), '.symlinks')
require awesome_pod_file
update_awesome_main_target_settings('Runner', File.dirname(File.realpath(__FILE__)), flutter_root)
################ Awesome Notifications pod modification 2 ###################
installer.generated_projects.each do |project| installer.generated_projects.each do |project|
project.targets.each do |target| project.targets.each do |target|
target.build_configurations.each do |config| target.build_configurations.each do |config|

View File

@ -50,5 +50,9 @@
</array> </array>
<key>UIViewControllerBasedStatusBarAppearance</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
<key>UNNotificationServiceExtension</key>
<array>
<string>$(PRODUCT_BUNDLE_IDENTIFIER).MyNotificationServiceExtension</string>
</array>
</dict> </dict>
</plist> </plist>

View File

@ -7,8 +7,12 @@ import 'package:didvan/providers/theme.dart';
import 'package:didvan/providers/user.dart'; import 'package:didvan/providers/user.dart';
import 'package:didvan/routes/route_generator.dart'; import 'package:didvan/routes/route_generator.dart';
import 'package:didvan/services/app_initalizer.dart'; import 'package:didvan/services/app_initalizer.dart';
import 'package:didvan/services/notification/awsome/awsome_notification_handler.dart';
import 'package:didvan/services/notification/fcm/firebase_notification_handler.dart';
import 'package:didvan/views/podcasts/podcasts_state.dart'; import 'package:didvan/views/podcasts/podcasts_state.dart';
import 'package:didvan/views/podcasts/studio_details/studio_details_state.dart'; import 'package:didvan/views/podcasts/studio_details/studio_details_state.dart';
import 'package:didvan/services/notification/lc/local_notification_service.dart';
import 'package:didvan/services/notification/lc/show_notification_handler.dart';
import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
@ -16,36 +20,45 @@ import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// If you're going to use other Firebase services in the background, such as Firestore,
// make sure you call `initializeApp` before using other Firebase services.
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: kIsWeb
? const FirebaseOptions(
apiKey: "AIzaSyA0HZjKpRuPOi1SC3f_EZTvlS3mcj9UVo0",
authDomain: "didvan-9b7da.firebaseapp.com",
projectId: "didvan-9b7da",
storageBucket: "didvan-9b7da.appspot.com",
messagingSenderId: "935017686266",
appId: "1:935017686266:web:a93f7a19bed23c51d2d543",
measurementId: "G-80B4H9E8Y0")
: const FirebaseOptions(
apiKey: 'AIzaSyBp-UHjWeM0H0UHtX5yguFKG-riMzvvCzw',
appId: '1:935017686266:android:f9cbc9aba8e3d65ed2d543',
messagingSenderId: '935017686266',
projectId: 'didvan-9b7da',
),
);
// LocalNotificationService.initialize();
// LocalNotificationService.display(message);
// LocalNotificationService.showBigPictureNotification();
AwsomeNotificationHandler().main();
AwsomeNotificationHandler().show(message);
print("Handling a background message: ${message.messageId}");
}
void main() async { void main() async {
try { try {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
await Firebase.initializeApp( FirebaseNotificationHandler().initial();
options: kIsWeb
? const FirebaseOptions(
apiKey: "AIzaSyA0HZjKpRuPOi1SC3f_EZTvlS3mcj9UVo0",
authDomain: "didvan-9b7da.firebaseapp.com",
projectId: "didvan-9b7da",
storageBucket: "didvan-9b7da.appspot.com",
messagingSenderId: "935017686266",
appId: "1:935017686266:web:a93f7a19bed23c51d2d543",
measurementId: "G-80B4H9E8Y0")
: const FirebaseOptions(
apiKey: 'AIzaSyBp-UHjWeM0H0UHtX5yguFKG-riMzvvCzw',
appId: '1:935017686266:android:f9cbc9aba8e3d65ed2d543',
messagingSenderId: '935017686266',
projectId: 'didvan-9b7da',
),
);
final initMsg = await FirebaseMessaging.instance.getInitialMessage();
if (initMsg != null) {
AppInitializer.clickAction = initMsg.data['click_action'].replaceAll(
'navigate-',
'',
);
}
} catch (e) { } catch (e) {
log(e.toString()); print(e.toString());
} }
runApp(const Didvan()); runApp(const Didvan());
} }
@ -54,6 +67,7 @@ final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
class Didvan extends StatelessWidget { class Didvan extends StatelessWidget {
const Didvan({Key? key}) : super(key: key); const Didvan({Key? key}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MultiProvider( return MultiProvider(
@ -94,7 +108,8 @@ class Didvan extends StatelessWidget {
themeMode: themeProvider.themeMode, themeMode: themeProvider.themeMode,
onGenerateRoute: (settings) => onGenerateRoute: (settings) =>
RouteGenerator.generateRoute(settings), RouteGenerator.generateRoute(settings),
builder: BotToastInit(), //1. call BotToastInit builder: BotToastInit(),
//1. call BotToastInit
navigatorObservers: [BotToastNavigatorObserver()], navigatorObservers: [BotToastNavigatorObserver()],
initialRoute: '/', initialRoute: '/',
localizationsDelegates: const [ localizationsDelegates: const [

View File

@ -6,9 +6,12 @@ class CommentData {
int id; int id;
final String text; final String text;
final String createdAt; final String createdAt;
String? type;
bool liked; bool liked;
bool disliked; bool disliked;
bool private;
int status; int status;
dynamic? mention;
final FeedbackData feedback; final FeedbackData feedback;
final UserOverview user; final UserOverview user;
final List<Reply> replies; final List<Reply> replies;
@ -19,17 +22,21 @@ class CommentData {
required this.createdAt, required this.createdAt,
required this.liked, required this.liked,
required this.disliked, required this.disliked,
required this.private,
required this.feedback, required this.feedback,
required this.user, required this.user,
required this.replies, required this.replies,
required this.status, required this.status,
this.type,
required this.mention,
}); });
factory CommentData.fromJson(Map<String, dynamic> json) => CommentData( factory CommentData.fromJson(Map<String, dynamic> json, bool private) => CommentData(
id: json['id'], id: json['id'],
text: json['text'], text: json['text'],
createdAt: json['createdAt'], createdAt: json['createdAt'],
liked: json['liked'], liked: json['liked'],
private: private,
disliked: json['disliked'], disliked: json['disliked'],
feedback: FeedbackData.fromJson(json['feedback']), feedback: FeedbackData.fromJson(json['feedback']),
user: UserOverview.fromJson(json['user']), user: UserOverview.fromJson(json['user']),
@ -39,14 +46,19 @@ class CommentData {
), ),
), ),
status: json['status'], status: json['status'],
mention: json['mention'],
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
'id': id, 'id': id,
'text': text, 'text': text,
'createdAt': createdAt, 'createdAt': createdAt,
'liked': liked, 'liked': liked,
'disliked': disliked, 'disliked': disliked,
'private': private,
'mention': mention,
'type': type,
'feedback': feedback.toJson(), 'feedback': feedback.toJson(),
'user': user.toJson(), 'user': user.toJson(),
'replies': replies.map((e) => e.toJson()).toList(), 'replies': replies.map((e) => e.toJson()).toList(),

View File

@ -11,6 +11,7 @@ class Reply {
final FeedbackData feedback; final FeedbackData feedback;
final UserOverview user; final UserOverview user;
final UserOverview toUser; final UserOverview toUser;
final String? mention;
Reply({ Reply({
required this.id, required this.id,
@ -22,6 +23,7 @@ class Reply {
required this.user, required this.user,
required this.toUser, required this.toUser,
required this.status, required this.status,
this.mention,
}); });
factory Reply.fromJson(Map<String, dynamic> json) => Reply( factory Reply.fromJson(Map<String, dynamic> json) => Reply(
@ -34,6 +36,7 @@ class Reply {
user: UserOverview.fromJson(json['user']), user: UserOverview.fromJson(json['user']),
toUser: UserOverview.fromJson(json['toUser']), toUser: UserOverview.fromJson(json['toUser']),
status: json['status'], status: json['status'],
mention: json['mention'],
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
@ -45,5 +48,6 @@ class Reply {
'feedback': feedback.toJson(), 'feedback': feedback.toJson(),
'user': user.toJson(), 'user': user.toJson(),
'toUser': toUser.toJson(), 'toUser': toUser.toJson(),
'mention': mention,
}; };
} }

View File

@ -0,0 +1,36 @@
class NotificationMessage {
String? title;
String? body;
String? type;
String? clickAction;
String? url;
String? image;
NotificationMessage(
{this.title,
this.body,
this.type,
this.clickAction,
this.url,
this.image});
NotificationMessage.fromJson(Map<String, dynamic> json) {
title = json['title'];
body = json['body'];
type = json['type'];
clickAction = json['click_action'];
url = json['url'];
image = json['image'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['title'] = this.title;
data['body'] = this.body;
data['type'] = this.type;
data['click_action'] = this.clickAction;
data['url'] = this.url;
data['image'] = this.image;
return data;
}
}

View File

@ -0,0 +1,24 @@
class UsersMention {
int? id;
String? name;
String? type;
String? photo;
UsersMention({this.id, this.name, this.type, this.photo});
UsersMention.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
type = json['type'];
photo = json['photo'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['name'] = this.name;
data['type'] = this.type;
data['photo'] = this.photo;
return data;
}
}

View File

@ -11,6 +11,9 @@ class RequestService {
static late String token; static late String token;
int? statusCode; int? statusCode;
dynamic data(String s){
return _body?[s] ?? const {};
}
Map<String, dynamic> get result => _body?['result'] ?? const {}; Map<String, dynamic> get result => _body?['result'] ?? const {};
Map<String, dynamic> get errors => _body?['errors'] ?? const {}; Map<String, dynamic> get errors => _body?['errors'] ?? const {};

View File

@ -186,12 +186,13 @@ class RequestHelper {
static String mark(int id, String type) => '$baseUrl/$type/$id/mark'; static String mark(int id, String type) => '$baseUrl/$type/$id/mark';
static String tracking(int id, String type) => '$baseUrl/$type/$id/tracking'; static String tracking(int id, String type) => '$baseUrl/$type/$id/tracking';
static String comments(int id, String type) => '$baseUrl/$type/$id/comments'; static String comments(int id, String type) => '$baseUrl/$type/$id/comments/v2';
static String usersMentions(String search) => '$baseUrl/comment/user?search=$search';
static String feedback(int id, int commentId, String type) => static String feedback(int id, int commentId, String type) =>
'$baseUrl/$type/$id/comments/$commentId/feedback'; '$baseUrl/$type/$id/comments/$commentId/feedback/v2';
static String addComment(int id, String type) => static String addComment(int id, String type) =>
'$baseUrl/$type/$id/comments/add'; '$baseUrl/$type/$id/comments/add';
static String deleteComment(int id) => '$baseUrl/comment/$id'; static String deleteComment(int id) => '$baseUrl/comment/$id/v2';
static String reportComment(int id) => '$baseUrl/comment/$id/report'; static String reportComment(int id) => '$baseUrl/comment/$id/report';
static String _urlConcatGenerator(List<MapEntry<String, dynamic>> additions) { static String _urlConcatGenerator(List<MapEntry<String, dynamic>> additions) {

View File

@ -0,0 +1,41 @@
import 'package:awesome_notifications/awesome_notifications.dart';
class NotificationController {
/// Use this method to detect when a new notification or a schedule is created
@pragma("vm:entry-point")
static Future <void> onNotificationCreatedMethod(ReceivedNotification receivedNotification) async {
// Your code goes here
print("onNotificationCreatedMethod--------------------------------------------------------------");
}
/// Use this method to detect every time that a new notification is displayed
@pragma("vm:entry-point")
static Future <void> onNotificationDisplayedMethod(ReceivedNotification receivedNotification) async {
// Your code goes here
print("onNotificationDisplayedMethod--------------------------------------------------------------");
}
/// Use this method to detect if the user dismissed a notification
@pragma("vm:entry-point")
static Future <void> onDismissActionReceivedMethod(ReceivedAction receivedAction) async {
// Your code goes here
print("onDismissActionReceivedMethod--------------------------------------------------------------");
}
/// Use this method to detect when the user taps on a notification or action button
@pragma("vm:entry-point")
static Future <void> onActionReceivedMethod(ReceivedAction receivedAction) async {
// Your code goes here
// Navigate into pages, avoiding to open the notification details page over another details page already opened
// MyApp.navigatorKey.currentState?.pushNamedAndRemoveUntil('/notification-page',
// (route) => (route.settings.name != '/notification-page') || route.isFirst,
// arguments: receivedAction);
print("onActionReceivedMethod--------------------------------------------------------------");
}
}

View File

@ -0,0 +1,154 @@
import 'dart:ui';
import 'package:awesome_notifications/awesome_notifications.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../../../models/notification_message.dart';
import 'awsome_notification_controller.dart';
class AwsomeNotificationHandler {
main() async {
late ReceivedAction? initialAction;
AwesomeNotifications().initialize(
// set the icon to null if you want to use the default app icon
null,
[
NotificationChannel(
channelKey: 'alerts',
channelName: 'Alerts',
channelDescription: 'Notification tests as alerts',
playSound: true,
onlyAlertOnce: true,
groupAlertBehavior: GroupAlertBehavior.Children,
importance: NotificationImportance.High,
defaultPrivacy: NotificationPrivacy.Public,
defaultColor: const Color(0xFF007EA7),
criticalAlerts: true,
ledColor: Colors.white)
],
// Channel groups are only visual and are not required
// channelGroups: [
// NotificationChannelGroup(
// channelGroupKey: 'basic_channel_group',
// channelGroupName: 'Basic group')
// ],
debug: true);
initialAction = await AwesomeNotifications()
.getInitialNotificationAction(removeFromActionEvents: false);
AwesomeNotifications().setListeners(
onActionReceivedMethod: NotificationController.onActionReceivedMethod,
onNotificationCreatedMethod:
NotificationController.onNotificationCreatedMethod,
onNotificationDisplayedMethod:
NotificationController.onNotificationDisplayedMethod,
onDismissActionReceivedMethod:
NotificationController.onDismissActionReceivedMethod);
AwesomeNotifications().isNotificationAllowed().then((isAllowed) {
//It would be more appropriate if you can show your own dialog
//to the user before requesting the notifications permissons.
if (!isAllowed) {
AwesomeNotifications().requestPermissionToSendNotifications(
permissions: [
NotificationPermission.Alert,
NotificationPermission.Sound,
NotificationPermission.Badge,
NotificationPermission.Vibration,
NotificationPermission.Light,
NotificationPermission.FullScreenIntent,
],
);
}
});
}
show(RemoteMessage message) async {
NotificationMessage notificationMessage =
NotificationMessage.fromJson(message.data);
switch (notificationMessage.type) {
case "1":
await showNotificationTypeNews(notificationMessage);
break;
case "2":
await showNotificationTypeMessage(notificationMessage);
break;
case "3":
await showNotificationTypeEmoji(notificationMessage);
break;
}
try {} catch (ex) {}
}
showNotificationTypeNews(NotificationMessage message) async {
AwesomeNotifications().createNotification(
content: NotificationContent(
id: DateTime.now().millisecondsSinceEpoch ~/ 1000,
channelKey: 'alerts',
actionType: ActionType.Default,
title: "\u200f ${message.title} \u200f",
body: "${message.body.toString()}",
notificationLayout: NotificationLayout.BigText,
largeIcon: message.image.toString(),
color: const Color(0xFF007EA7)),
);
}
showNotificationTypeMessage(NotificationMessage message) async {
AwesomeNotifications().createNotification(
content: NotificationContent(
id: DateTime.now().millisecondsSinceEpoch ~/ 1000,
channelKey: 'alerts',
actionType: ActionType.Default,
title: message.title.toString(),
body: message.body.toString(),
largeIcon: message.image.toString(),
roundedLargeIcon: true,
notificationLayout: NotificationLayout.Inbox,
color: const Color(0xFF007EA7)),
actionButtons: [
NotificationActionButton(
key: 'REPLY',
label: 'جواب دادن',
requireInputText: true,
actionType: ActionType.SilentAction),
NotificationActionButton(
key: 'DISMISS',
label: 'خوانده شده',
actionType: ActionType.DismissAction,
isDangerousOption: true)
]);
}
showNotificationTypeEmoji(NotificationMessage message) async {
AwesomeNotifications().createNotification(
content: NotificationContent(
id: DateTime.now().millisecondsSinceEpoch ~/ 1000,
channelKey: 'alerts',
title: 'Emojis are awesome too! ' +
Emojis.animals_lady_beetle +
Emojis.activites_balloon +
Emojis.emotion_red_heart,
body:
'Simple body with a bunch of Emojis! ${Emojis.transport_police_car} ${Emojis.animals_dog} ${Emojis.flag_UnitedStates} ${Emojis.person_baby}',
largeIcon:
'https://cdn.britannica.com/72/232772-050-4E3D86CC/mind-blown-emoji-head-exploding-emoticon.jpg',
notificationLayout: NotificationLayout.BigPicture,
payload: {
'title': 'Notification Title',
'body': 'Notification Body',
'image': 'path/to/smallImage.png', // Path to small image
'largeImage': 'path/to/largeImage.png', // Path to large image
}));
}
}

View File

@ -0,0 +1,161 @@
import 'package:didvan/services/notification/awsome/awsome_notification_handler.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import '../lc/local_notification_service.dart';
import '../../app_initalizer.dart';
class FirebaseNotificationHandler {
Future<void> initial() async {
try {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: kIsWeb
? const FirebaseOptions(
apiKey: "AIzaSyA0HZjKpRuPOi1SC3f_EZTvlS3mcj9UVo0",
authDomain: "didvan-9b7da.firebaseapp.com",
projectId: "didvan-9b7da",
storageBucket: "didvan-9b7da.appspot.com",
messagingSenderId: "935017686266",
appId: "1:935017686266:web:a93f7a19bed23c51d2d543",
measurementId: "G-80B4H9E8Y0")
: const FirebaseOptions(
apiKey: 'AIzaSyBp-UHjWeM0H0UHtX5yguFKG-riMzvvCzw',
appId: '1:935017686266:android:f9cbc9aba8e3d65ed2d543',
messagingSenderId: '935017686266',
projectId: 'didvan-9b7da',
),
);
final initMsg = await FirebaseMessaging.instance.getInitialMessage();
if (initMsg != null) {
AppInitializer.clickAction = initMsg.data['click_action'].replaceAll(
'navigate-',
'',
);
}
// LocalNotificationService.initialize();
NotificationSettings settings =
await FirebaseMessaging.instance.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
debugPrint(
'User granted notifications permission: ${settings.authorizationStatus}');
// Handling background messages using the specified handler
// FirebaseMessaging.onBackgroundMessage(
// _firebaseMessagingBackgroundHandler);
// Listening for incoming messages while the app is in the foreground
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
if (message.data != null) {
/* if (message.notification!.title != null &&
message.notification!.body != null) {
final notificationData = message.data;
final screen = notificationData['screen'];
// Showing an alert dialog when a notification is received (Foreground state)
// showDialog(
// context: context,
// barrierDismissible: false,
// builder: (BuildContext context) {
// return WillPopScope(
// onWillPop: () async => false,
// child: AlertDialog(
// title: Text(message.notification!.title!),
// content: Text(message.notification!.body!),
// actions: [
// if (notificationData.containsKey('screen'))
// TextButton(
// onPressed: () {
// Navigator.pop(context);
// Navigator.of(context).pushNamed(screen);
// },
// child: const Text('Open Screen'),
// ),
// TextButton(
// onPressed: () => Navigator.of(context).pop(),
// child: const Text('Dismiss'),
// ),
// ],
// ),
// );
// },
// );
}*/
// LocalNotificationService.initialize();
// LocalNotificationService.showBigPictureNotification();
// LocalNotificationService.display(message);
AwsomeNotificationHandler().main();
AwsomeNotificationHandler().show(message);
}
});
// Handling the initial message received when the app is launched from dead (killed state)
// When the app is killed and a new notification arrives when user clicks on it
// It gets the data to which screen to open
FirebaseMessaging.instance.getInitialMessage().then((message) {
if (message != null) {
_handleNotificationClick(message);
}
});
// Handling a notification click event when the app is in the background
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
_handleNotificationClick(message);
});
} catch (e) {
print(e.toString());
}
}
// Handling a notification click event by navigating to the specified screen
void _handleNotificationClick(RemoteMessage message) {
final notificationData = message.data;
if (notificationData.containsKey('screen')) {
final screen = notificationData['screen'];
// Navigator.of(context).pushNamed(screen);
}
}
}
// Handler for background messages
// @pragma('vm:entry-point')
// Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
// await Firebase.initializeApp(
// options: kIsWeb
// ? const FirebaseOptions(
// apiKey: "AIzaSyA0HZjKpRuPOi1SC3f_EZTvlS3mcj9UVo0",
// authDomain: "didvan-9b7da.firebaseapp.com",
// projectId: "didvan-9b7da",
// storageBucket: "didvan-9b7da.appspot.com",
// messagingSenderId: "935017686266",
// appId: "1:935017686266:web:a93f7a19bed23c51d2d543",
// measurementId: "G-80B4H9E8Y0")
// : const FirebaseOptions(
// apiKey: 'AIzaSyBp-UHjWeM0H0UHtX5yguFKG-riMzvvCzw',
// appId: '1:935017686266:android:f9cbc9aba8e3d65ed2d543',
// messagingSenderId: '935017686266',
// projectId: 'didvan-9b7da',
// ),
// );
//
// LocalNotificationService.initialize();
// LocalNotificationService.display(message);
// // AwsomeNotificationHandler().main();
// // AwsomeNotificationHandler().show(message);
//
// return Future<void>.value();
// }

View File

@ -0,0 +1,133 @@
import 'dart:io';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:http/http.dart';
import 'package:path_provider/path_provider.dart';
import '../../../constants/assets.dart';
import '../../../models/notification_message.dart';
import '../../../views/widgets/notification/notification_dynamic_dialog.dart';
class LocalNotificationService {
// Instance of Flutternotification plugin
static final FlutterLocalNotificationsPlugin _notificationsPlugin =
FlutterLocalNotificationsPlugin();
static void initialize() {
// Initialization setting for android
const InitializationSettings initializationSettingsAndroid =
InitializationSettings(
android: AndroidInitializationSettings("@mipmap/ic_launcher"));
_notificationsPlugin.initialize(
initializationSettingsAndroid,
// to handle event when we receive notification
onDidReceiveNotificationResponse: (details) {
if (details.input != null) {}
},
);
}
static Future<void> display(RemoteMessage message) async {
// To display the notification in device
try {
final id = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final BigPictureStyleInformation bigPictureStyleInformation =
BigPictureStyleInformation(
FilePathAndroidBitmap(
"lib/assets/aaaa.jpg"),
contentTitle: NotificationMessage.fromJson(message.data).title,
htmlFormatContentTitle: true,
summaryText: NotificationMessage.fromJson(message.data).body,
htmlFormatSummaryText: true,
htmlFormatContent: true,
largeIcon: const FilePathAndroidBitmap(
"lib/assets/aaaa.jpg"));
NotificationDetails notificationDetails = NotificationDetails(
android: AndroidNotificationDetails("Channel Id", "Main Channel",
groupKey: "gfg",
color: Colors.white,
importance: Importance.max,
playSound: true,
priority: Priority.high),
);
if (message.notification == null) {
final NotificationMessage notificationModel =
NotificationMessage.fromJson(message.data);
await _notificationsPlugin.show(id, notificationModel.title,
notificationModel.body, notificationDetails,
payload: message.data['route']);
} else {
await _notificationsPlugin.show(id, message.notification?.title,
message.notification?.body, notificationDetails,
payload: message.data['route']);
}
} catch (e) {
debugPrint(e.toString());
}
}
void messageListener(BuildContext context) {
// Either you can pass buildcontext or you
// can take a context from navigator key
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
print('Got a message in the foreground!');
print('Message data: ${message.data}');
if (message.data != null) {
print(
'Message also contained a notification: so it will pop up ${message.notification!.body}');
showDialog(
context: context,
// context: navigatorKey!.currentContext!,
builder: ((BuildContext context) {
return NotificationDynamicDialog(
title: message.notification!.title,
body: message.notification!.body);
}));
}
});
}
static Future<String> _downloadAndSaveFile(String url, String fileName) async {
final Directory directory = await getApplicationDocumentsDirectory();
final String filePath = '${directory.path}/$fileName';
final Response response = await get(Uri.parse(url));
final File file = File(filePath);
await file.writeAsBytes(response.bodyBytes);
return filePath;
}
static Future<void> showBigPictureNotification() async {
final String largeIconPath = await _downloadAndSaveFile(
'https://via.placeholder.com/48x48', 'largeIcon');
final String bigPicturePath = await _downloadAndSaveFile(
'https://via.placeholder.com/400x800', 'bigPicture');
final BigPictureStyleInformation bigPictureStyleInformation =
BigPictureStyleInformation(FilePathAndroidBitmap(bigPicturePath),
largeIcon: FilePathAndroidBitmap(largeIconPath),
contentTitle: 'overridden <b>big</b> content title',
htmlFormatContentTitle: true,
summaryText: 'summary <i>text</i>',
htmlFormatSummaryText: true);
final AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails(
'big text channel id', 'big text channel name',
channelDescription: 'big text channel description',
largeIcon: FilePathAndroidBitmap(largeIconPath),
channelShowBadge: false,
styleInformation: bigPictureStyleInformation);
final NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
await _notificationsPlugin.show(
0, 'big text title', 'silent body', platformChannelSpecifics);
}
}

View File

@ -0,0 +1,40 @@
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
void showNotificationAndroid(String title, String value) async {
const AndroidNotificationDetails androidNotificationDetails =
AndroidNotificationDetails('channel_id', 'Channel Name',
channelDescription: 'Channel Description',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
int notification_id = 1;
const NotificationDetails notificationDetails =
NotificationDetails(android: androidNotificationDetails);
await FlutterLocalNotificationsPlugin().show(
notification_id, title, value, notificationDetails,
payload: 'Not present');
}
void showNotificationIos(String title, String value) async {
DarwinNotificationDetails iOSPlatformChannelSpecifics = DarwinNotificationDetails(
presentAlert: true,
// Present an alert when the notification is displayed and the application is in the foreground (only from iOS 10 onwards)
presentBadge: true,
// Present the badge number when the notification is displayed and the application is in the foreground (only from iOS 10 onwards)
presentSound: true,
// Play a sound when the notification is displayed and the application is in the foreground (only from iOS 10 onwards)
subtitle: title,
//Secondary description (only from iOS 10 onwards)
threadIdentifier: value);
int notification_id = 1;
NotificationDetails platformChannelSpecifics =
NotificationDetails(iOS: iOSPlatformChannelSpecifics);
await FlutterLocalNotificationsPlugin().show(
notification_id, title, value, platformChannelSpecifics,
payload: 'Not present');
}

View File

@ -1,22 +1,33 @@
import 'dart:ui';
import 'package:didvan/config/design_config.dart'; import 'package:didvan/config/design_config.dart';
import 'package:didvan/config/theme_data.dart'; import 'package:didvan/config/theme_data.dart';
import 'package:didvan/constants/app_icons.dart'; import 'package:didvan/constants/app_icons.dart';
import 'package:didvan/constants/assets.dart'; import 'package:didvan/constants/assets.dart';
import 'package:didvan/models/users_mention.dart';
import 'package:didvan/models/view/app_bar_data.dart'; import 'package:didvan/models/view/app_bar_data.dart';
import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/views/comments/comments_state.dart'; import 'package:didvan/views/comments/comments_state.dart';
import 'package:didvan/views/comments/widgets/comment.dart'; import 'package:didvan/views/comments/widgets/comment.dart';
import 'package:didvan/views/widgets/animated_visibility.dart'; import 'package:didvan/views/widgets/animated_visibility.dart';
import 'package:didvan/views/widgets/didvan/checkbox.dart';
import 'package:didvan/views/widgets/didvan/icon_button.dart'; import 'package:didvan/views/widgets/didvan/icon_button.dart';
import 'package:didvan/views/widgets/didvan/scaffold.dart'; import 'package:didvan/views/widgets/didvan/scaffold.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:didvan/views/widgets/shimmer_placeholder.dart'; import 'package:didvan/views/widgets/shimmer_placeholder.dart';
import 'package:didvan/views/widgets/state_handlers/empty_state.dart'; import 'package:didvan/views/widgets/state_handlers/empty_state.dart';
import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart'; import 'package:didvan/views/widgets/state_handlers/sliver_state_handler.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import '../widgets/skeleton_image.dart';
import '../widgets/user_mention.dart';
class Comments extends StatefulWidget { class Comments extends StatefulWidget {
final Map<String, dynamic> pageData; final Map<String, dynamic> pageData;
const Comments({ const Comments({
Key? key, Key? key,
required this.pageData, required this.pageData,
@ -47,6 +58,8 @@ class _CommentsState extends State<Comments> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final commentsState = context.watch<CommentsState>();
final bottomViewInset = MediaQuery.of(context).viewInsets.bottom; final bottomViewInset = MediaQuery.of(context).viewInsets.bottom;
if (bottomViewInset == 0) { if (bottomViewInset == 0) {
if (_bottomPadding != 0) { if (_bottomPadding != 0) {
@ -97,6 +110,48 @@ class _CommentsState extends State<Comments> {
), ),
], ],
), ),
AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration,
isVisible: commentsState.showUsersForMentionsLayout,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 8.0, sigmaY: 8.0),
child: Container(
decoration: BoxDecoration(
color:
Theme.of(context).colorScheme.focused.withOpacity(0.5)),
child: DidvanScaffold(
appBarData: null,
padding: const EdgeInsets.only(
left: 16, right: 16, top: 16, bottom: 92),
backgroundColor: Colors.white.withOpacity(0.0),
slivers: [
Consumer<CommentsState>(
builder: (context, state, child) =>
SliverStateHandler<CommentsState>(
onRetry: state.getUsersMention,
state: state,
itemPadding: const EdgeInsets.symmetric(vertical: 8),
childCount: state.usersMention.length,
placeholder: const _UsersPlaceholder(),
centerEmptyState: _isPage,
enableEmptyState: state.usersMention.isEmpty,
emptyState: EmptyState(
asset: Assets.emptyBookmark,
title: 'لیست خالی است',
),
builder: (context, state, index) {
return UserMention(
user: state.usersMention[index],
index: index,
);
},
),
),
],
),
),
),
),
Positioned( Positioned(
left: 0, left: 0,
right: 0, right: 0,
@ -111,6 +166,7 @@ class _CommentsState extends State<Comments> {
class _MessageBox extends StatefulWidget { class _MessageBox extends StatefulWidget {
final FocusNode focusNode; final FocusNode focusNode;
const _MessageBox({Key? key, required this.focusNode}) : super(key: key); const _MessageBox({Key? key, required this.focusNode}) : super(key: key);
@override @override
@ -118,59 +174,190 @@ class _MessageBox extends StatefulWidget {
} }
class _MessageBoxState extends State<_MessageBox> { class _MessageBoxState extends State<_MessageBox> {
final _controller = TextEditingController();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final state = context.watch<CommentsState>(); final state = context.watch<CommentsState>();
void _onCheckBoxChange(bool b) {
state.hideMentionedUser = b;
state.update();
}
return Column( return Column(
children: [ children: [
AnimatedVisibility( AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration, duration: DesignConfig.lowAnimationDuration,
isVisible: state.showReplyBox, isVisible: state.showReplyBox,
child: Container( child: Column(
padding: const EdgeInsets.all(8), children: [
decoration: BoxDecoration( Container(
color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.all(8),
border: Border( decoration: BoxDecoration(
top: BorderSide( color: Theme.of(context).colorScheme.secondCTA,
color: Theme.of(context).colorScheme.border, borderRadius: const BorderRadius.only(
topRight: Radius.circular(16),
topLeft: Radius.circular(16)),
boxShadow: [
BoxShadow(
color: Theme.of(context)
.colorScheme
.title
.withOpacity(0.2),
offset: const Offset(
5.0,
5.0,
),
blurRadius: 10.0,
spreadRadius: 2.0,
)
]
// border: Border(
// top: BorderSide(
// color: Theme.of(context).colorScheme.border,
// ),
// ),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (state.replyingTo != null)
DidvanText(
'پاسخ به ${state.replyingTo!.fullName}:',
color: Theme.of(context).colorScheme.primary,
style: Theme.of(context).textTheme.bodySmall,
),
const Spacer(),
DidvanIconButton(
gestureSize: 24,
color: Theme.of(context).colorScheme.primary,
icon: DidvanIcons.close_regular,
onPressed: () {
state.commentId = null;
state.replyingTo = null;
state.showReplyBox = false;
state.update();
},
),
],
), ),
), ),
), Container(
child: Row( color: Theme.of(context).colorScheme.secondCTA,
mainAxisAlignment: MainAxisAlignment.spaceBetween, padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
children: [ child: Divider(
if (state.replyingTo != null) height: 2,
DidvanText( color: Theme.of(context).colorScheme.border,
'پاسخ به ${state.replyingTo!.fullName}:',
color: Theme.of(context).colorScheme.caption,
style: Theme.of(context).textTheme.bodySmall,
),
const Spacer(),
DidvanIconButton(
gestureSize: 24,
color: Theme.of(context).colorScheme.caption,
icon: DidvanIcons.close_regular,
onPressed: () {
state.commentId = null;
state.replyingTo = null;
state.showReplyBox = false;
state.update();
},
), ),
], )
), ],
),
),
AnimatedVisibility(
duration: DesignConfig.lowAnimationDuration,
isVisible: state.usersMentioned.name != null,
child: Column(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondCTA,
borderRadius: !state.showReplyBox
? BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16))
: null
// border: Border(
// top: BorderSide(
// color: Theme.of(context).colorScheme.border,
// ),
// ),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
color: Theme.of(context).colorScheme.primary,
width: 2,
height: 40,
),
SizedBox(
width: 8,
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
DidvanText(
"اشاره به",
color: Theme.of(context).colorScheme.inputText,
style: Theme.of(context).textTheme.labelSmall,
),
DidvanText(
state.usersMentioned.name.toString(),
color: Theme.of(context).colorScheme.navigation,
style: Theme.of(context).textTheme.bodySmall,
),
],
)
],
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
DidvanIconButton(
gestureSize: 24,
color: Theme.of(context).colorScheme.primary,
icon: DidvanIcons.close_regular,
onPressed: () {
state.usersMentioned = UsersMention();
state.update();
},
),
!state.showReplyBox
? DidvanCheckbox(
title: "پنهان کردن فراخوانی",
value: state.hideMentionedUser,
color: Theme.of(context).colorScheme.primary,
onChanged: _onCheckBoxChange,
size: 12,
)
: SizedBox(),
],
)
],
),
),
],
), ),
), ),
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface, boxShadow: state.showReplyBox && state.usersMentioned.name == null
border: Border( ? null
top: BorderSide( : [
color: Theme.of(context).colorScheme.border, BoxShadow(
), color:
), Theme.of(context).colorScheme.title.withOpacity(0.2),
offset: const Offset(
5.0,
5.0,
),
blurRadius: 10.0,
spreadRadius: 2.0,
)
],
color: state.showReplyBox && state.usersMentioned.name == null
? Theme.of(context).colorScheme.secondCTA
: Theme.of(context).colorScheme.surface,
// border: Border(
// top: BorderSide(
// color: Theme.of(context).colorScheme.border,
// ),
// ),
), ),
child: Row( child: Row(
children: [ children: [
@ -183,7 +370,7 @@ class _MessageBoxState extends State<_MessageBox> {
Expanded( Expanded(
child: TextField( child: TextField(
focusNode: widget.focusNode, focusNode: widget.focusNode,
controller: _controller, controller: state.commentTextFieldController,
keyboardType: TextInputType.multiline, keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.send, textInputAction: TextInputAction.send,
style: Theme.of(context).textTheme.bodyMedium, style: Theme.of(context).textTheme.bodyMedium,
@ -195,7 +382,7 @@ class _MessageBoxState extends State<_MessageBox> {
hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith( hintStyle: Theme.of(context).textTheme.bodySmall!.copyWith(
color: Theme.of(context).colorScheme.disabledText), color: Theme.of(context).colorScheme.disabledText),
), ),
onChanged: (value) => state.text = value, onChanged: (value) => _onChange(state, value),
), ),
), ),
], ],
@ -206,9 +393,33 @@ class _MessageBoxState extends State<_MessageBox> {
} }
void _onSend(CommentsState state) { void _onSend(CommentsState state) {
if (state.text.replaceAll(' ', '').isNotEmpty) { if (state.commentTextFieldController.text.replaceAll(' ', '').isNotEmpty) {
state.addComment(); state.addComment();
_controller.text = ''; state.commentTextFieldController.text = '';
}
}
void _onChange(CommentsState state, value) {
state.commentTextFieldController.text = value;
if (state.usersMentioned.name == null) {
if (state.commentTextFieldController.text.contains("@")) {
int index = state.commentTextFieldController.text.indexOf("@");
if (state.commentTextFieldController.text.length > index + 1) {
if (state.commentTextFieldController.text
.substring(index)
.contains(" ")) {
state.showUsersForMentionsLayout = false;
} else {
state.mentionedText =
state.commentTextFieldController.text.substring(index);
state.getUsersMention();
state.showUsersForMentionsLayout = true;
}
}
} else {
state.showUsersForMentionsLayout = false;
}
state.update();
} }
} }
} }
@ -276,3 +487,28 @@ class _CommentPlaceholder extends StatelessWidget {
); );
} }
} }
class _UsersPlaceholder extends StatelessWidget {
const _UsersPlaceholder({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ShimmerPlaceholder(
height: 40,
width: 40,
borderRadius: DesignConfig.highBorderRadius,
),
SizedBox(width: 16),
Expanded(
child: ShimmerPlaceholder(
borderRadius: DesignConfig.highBorderRadius,
height: 40,
),
),
],
);
}
}

View File

@ -4,24 +4,32 @@ import 'package:didvan/models/comment/feedback.dart';
import 'package:didvan/models/comment/reply.dart'; import 'package:didvan/models/comment/reply.dart';
import 'package:didvan/models/comment/user.dart'; import 'package:didvan/models/comment/user.dart';
import 'package:didvan/models/enums.dart'; import 'package:didvan/models/enums.dart';
import 'package:didvan/models/users_mention.dart';
import 'package:didvan/models/view/alert_data.dart'; import 'package:didvan/models/view/alert_data.dart';
import 'package:didvan/providers/core.dart'; import 'package:didvan/providers/core.dart';
import 'package:didvan/providers/user.dart'; import 'package:didvan/providers/user.dart';
import 'package:didvan/services/network/request.dart'; import 'package:didvan/services/network/request.dart';
import 'package:didvan/services/network/request_helper.dart'; import 'package:didvan/services/network/request_helper.dart';
import 'package:didvan/utils/action_sheet.dart'; import 'package:didvan/utils/action_sheet.dart';
import 'package:didvan/views/widgets/user_mention.dart';
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class CommentsState extends CoreProvier { class CommentsState extends CoreProvier {
String text = ''; TextEditingController commentTextFieldController = TextEditingController();
UsersMention usersMentioned = UsersMention();
String mentionedText = '';
int? commentId; int? commentId;
UserOverview? replyingTo; UserOverview? replyingTo;
bool showReplyBox = false; bool showReplyBox = false;
bool showUsersForMentionsLayout = false;
bool hideMentionedUser = false;
late void Function(int count) onCommentsChanged; late void Function(int count) onCommentsChanged;
int _count = 0; int _count = 0;
late String type; late String type;
final List<CommentData> comments = []; final List<CommentData> comments = [];
final List<UsersMention> usersMention = [];
final Map<int, MapEntry<bool, bool>> _feedbackQueue = {}; final Map<int, MapEntry<bool, bool>> _feedbackQueue = {};
int itemId = 0; int itemId = 0;
@ -33,9 +41,9 @@ class CommentsState extends CoreProvier {
await service.httpGet(); await service.httpGet();
if (service.isSuccess) { if (service.isSuccess) {
comments.clear(); comments.clear();
final messages = service.result['comments']; final messages = service.result['comments']['public'];
for (var i = 0; i < messages.length; i++) { for (var i = 0; i < messages.length; i++) {
comments.add(CommentData.fromJson(messages[i])); comments.add(CommentData.fromJson(messages[i], false));
_count++; _count++;
for (var j = 0; j < messages[i]['replies'].length; j++) { for (var j = 0; j < messages[i]['replies'].length; j++) {
_count++; _count++;
@ -47,6 +55,23 @@ class CommentsState extends CoreProvier {
appState = AppState.failed; appState = AppState.failed;
} }
Future<void> getUsersMention() async {
final service = RequestService(
RequestHelper.usersMentions(mentionedText.replaceAll("@", "")),
);
await service.httpGet();
if (service.isSuccess) {
usersMention.clear();
final List<dynamic> users = service.data('users');
usersMention
.addAll(users.map((users) => UsersMention.fromJson(users)).toList());
appState = AppState.idle;
return;
}
appState = AppState.failed;
}
Future<void> feedback({ Future<void> feedback({
required int id, required int id,
required bool like, required bool like,
@ -92,7 +117,7 @@ class CommentsState extends CoreProvier {
comments.firstWhere((comment) => comment.id == commentId).replies.add( comments.firstWhere((comment) => comment.id == commentId).replies.add(
Reply( Reply(
id: 0, id: 0,
text: text, text: commentTextFieldController.text,
createdAt: DateTime.now().toString(), createdAt: DateTime.now().toString(),
liked: false, liked: false,
disliked: false, disliked: false,
@ -104,26 +129,29 @@ class CommentsState extends CoreProvier {
photo: user.photo, photo: user.photo,
), ),
status: 2, status: 2,
mention: usersMentioned.name,
), ),
); );
} else { } else {
comments.insert( comments.insert(
0, 0,
CommentData( CommentData(
id: 0, id: 0,
text: text, text: commentTextFieldController.text,
createdAt: DateTime.now().toString(), createdAt: DateTime.now().toString(),
liked: false, liked: false,
disliked: false, disliked: false,
feedback: FeedbackData(like: 0, dislike: 0), feedback: FeedbackData(like: 0, dislike: 0),
user: UserOverview( user: UserOverview(
id: user.id, id: user.id,
fullName: user.fullName, fullName: user.fullName,
photo: user.photo, photo: user.photo,
), ),
replies: [], replies: [],
status: 2, status: 2,
), private: hideMentionedUser,
mention: usersMentioned.name,
),
); );
} }
@ -138,8 +166,12 @@ class CommentsState extends CoreProvier {
body.addAll({'status': 2}); body.addAll({'status': 2});
showReplyBox = false; showReplyBox = false;
update(); // update();
body.addAll({'text': text}); body.addAll({
'text': commentTextFieldController.text,
"mention": usersMentioned.id,
"private": hideMentionedUser
});
final service = RequestService( final service = RequestService(
RequestHelper.addComment(itemId, type), RequestHelper.addComment(itemId, type),
body: body, body: body,
@ -159,6 +191,9 @@ class CommentsState extends CoreProvier {
} }
commentId = null; commentId = null;
replyingTo = null; replyingTo = null;
usersMentioned = UsersMention();
mentionedText = '';
update();
} }
} }

View File

@ -20,6 +20,7 @@ import 'package:provider/provider.dart';
class Comment extends StatefulWidget { class Comment extends StatefulWidget {
final FocusNode focusNode; final FocusNode focusNode;
final CommentData comment; final CommentData comment;
const Comment({ const Comment({
Key? key, Key? key,
required this.focusNode, required this.focusNode,
@ -109,6 +110,13 @@ class CommentState extends State<Comment> {
), ),
], ],
), ),
comment.mention != null
? DidvanText(
comment.mention.toString(),
color: Theme.of(context).colorScheme.primary,
style: Theme.of(context).textTheme.titleSmall,
)
: SizedBox(),
const SizedBox(height: 8), const SizedBox(height: 8),
if (isReply) if (isReply)
DidvanText( DidvanText(
@ -254,6 +262,7 @@ class _FeedbackButtons extends StatefulWidget {
final bool dislikeValue; final bool dislikeValue;
final void Function(bool like, bool dislike, int likeCount, int dislikeCount) final void Function(bool like, bool dislike, int likeCount, int dislikeCount)
onFeedback; onFeedback;
const _FeedbackButtons({ const _FeedbackButtons({
Key? key, Key? key,
required this.onFeedback, required this.onFeedback,

View File

@ -1,15 +1,19 @@
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/views/widgets/didvan/text.dart'; import 'package:didvan/views/widgets/didvan/text.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class DidvanCheckbox extends StatefulWidget { class DidvanCheckbox extends StatefulWidget {
final String title; final String title;
final bool? value; final bool? value;
final double size;
final Color? color;
final void Function(bool value) onChanged; final void Function(bool value) onChanged;
const DidvanCheckbox({ const DidvanCheckbox({
Key? key, Key? key,
required this.title, required this.title,
required this.value, required this.value,
required this.onChanged, required this.onChanged, this.size = 15, this.color,
}) : super(key: key); }) : super(key: key);
@override @override
@ -29,6 +33,9 @@ class _DidvanCheckboxState extends State<DidvanCheckbox> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
Color? _color = widget.color;
_color ??= Theme.of(context).colorScheme.text;
return GestureDetector( return GestureDetector(
onTap: () { onTap: () {
setState(() { setState(() {
@ -40,16 +47,22 @@ class _DidvanCheckboxState extends State<DidvanCheckbox> {
color: Colors.transparent, color: Colors.transparent,
child: Row( child: Row(
children: [ children: [
Checkbox( Transform.scale(
value: _value, scale: widget.size ==15 ? 1 : 0.8,
onChanged: (value) { child: Checkbox(
setState(() { activeColor: widget.color,
_value = value ?? false;
}); visualDensity: const VisualDensity(horizontal: -4, vertical: -4),
widget.onChanged(_value); side: BorderSide(color: _color, width: 1.5),
}, value: _value,
), onChanged: (value) {
DidvanText(widget.title), setState(() {
_value = value ?? false;
});
widget.onChanged(_value);
},
),
), DidvanText(widget.title,fontSize: widget.size,color: _color, ),
], ],
), ),
), ),

View File

@ -0,0 +1,31 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class NotificationDynamicDialog extends StatefulWidget {
final title;
final body;
const NotificationDynamicDialog({this.title, this.body});
@override
_NotificationDynamicDialogState createState() => _NotificationDynamicDialogState();
}
class _NotificationDynamicDialogState extends State<NotificationDynamicDialog> {
@override
Widget build(BuildContext context) {
// You can change the UI as per
// your requirement or choice
return AlertDialog(
title: Text(widget.title),
actions: <Widget>[
OutlinedButton.icon(
label: const Text('Close'),
onPressed: () {
Navigator.pop(context);
},
icon: const Icon(Icons.close))
],
content: Text(widget.body),
);
}
}

View File

@ -0,0 +1,77 @@
import 'package:didvan/config/theme_data.dart';
import 'package:didvan/views/widgets/ink_wrapper.dart';
import 'package:didvan/views/widgets/skeleton_image.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../config/design_config.dart';
import '../../constants/app_icons.dart';
import '../../models/users_mention.dart';
import '../comments/comments_state.dart';
import 'didvan/text.dart';
class UserMention extends StatefulWidget {
final UsersMention user;
final int index;
const UserMention({
super.key,
required this.user,
required this.index,
});
@override
State<UserMention> createState() => _UserMentionState();
}
class _UserMentionState extends State<UserMention> {
@override
Widget build(BuildContext context) {
final state = context.watch<CommentsState>();
return InkWrapper(
onPressed: () {
state.usersMentioned = widget.user;
state.commentTextFieldController.text = state
.commentTextFieldController.text
.replaceAll(state.mentionedText.toString(), "")
.replaceAll("@", "");
state.showUsersForMentionsLayout = false;
state.update();
},
child: Column(
children: [
Row(
children: [
widget.user.photo == null
? const Icon(DidvanIcons.avatar_light,size: 40,)
: SkeletonImage(
imageUrl: widget.user.photo.toString(),
height: 40,
width: 40,
borderRadius: BorderRadius.circular(360),
),
const SizedBox(
width: 8,
),
DidvanText(
widget.user.name.toString(),
style: Theme.of(context).textTheme.bodyMedium,
),
],
),
const SizedBox(
height: 8,
),
widget.index == state.usersMention.length - 1
? const SizedBox()
: Divider(
height: 2,
color: Theme.of(context).colorScheme.splash,
)
],
),
);
}
}

View File

@ -5,10 +5,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: _flutterfire_internals name: _flutterfire_internals
sha256: "4eec93681221723a686ad580c2e7d960e1017cf1a4e0a263c2573c2c6b0bf5cd" sha256: "2350805d7afefb0efe7acd325cb19d3ae8ba4039b906eade3807ffb69938a01f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.25" version: "1.3.33"
animated_custom_dropdown: animated_custom_dropdown:
dependency: "direct main" dependency: "direct main"
description: description:
@ -29,10 +29,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: args name: args
sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.2" version: "2.5.0"
assets_audio_player: assets_audio_player:
dependency: "direct main" dependency: "direct main"
description: description:
@ -65,6 +65,22 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.2" version: "2.0.2"
awesome_notifications:
dependency: "direct main"
description:
name: awesome_notifications
sha256: "65f730f9c0e73a346039ef746384bcff1773f9f03821b859705a7ab8db977b23"
url: "https://pub.dev"
source: hosted
version: "0.8.2"
awesome_notifications_core:
dependency: "direct main"
description:
name: awesome_notifications_core
sha256: "38c9f9d2618c0c2abe0cea267dc161aa8c4a0a239ca2263400900b9610f33e6f"
url: "https://pub.dev"
source: hosted
version: "0.9.3"
better_player: better_player:
dependency: "direct main" dependency: "direct main"
description: description:
@ -110,10 +126,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: cached_network_image_web name: cached_network_image_web
sha256: "42a835caa27c220d1294311ac409a43361088625a4f23c820b006dd9bffb3316" sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.1" version: "1.2.0"
carousel_slider: carousel_slider:
dependency: "direct main" dependency: "direct main"
description: description:
@ -158,10 +174,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: cross_file name: cross_file
sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.3+8" version: "0.3.4+1"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -182,10 +198,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: cupertino_icons name: cupertino_icons
sha256: d57953e10f9f8327ce64a508a355f0b1ec902193f66288e8cb5070e7c47eeb2d sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.6" version: "1.0.8"
day_night_time_picker: day_night_time_picker:
dependency: "direct main" dependency: "direct main"
description: description:
@ -194,6 +210,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.3.1" version: "1.3.1"
dbus:
dependency: transitive
description:
name: dbus
sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac"
url: "https://pub.dev"
source: hosted
version: "0.7.10"
equatable: equatable:
dependency: transitive dependency: transitive
description: description:
@ -222,10 +246,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: ffi name: ffi
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878" sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.0" version: "2.1.2"
file: file:
dependency: transitive dependency: transitive
description: description:
@ -246,10 +270,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: file_selector_macos name: file_selector_macos
sha256: b15c3da8bd4908b9918111fa486903f5808e388b8d1c559949f584725a6594d6 sha256: f42eacb83b318e183b1ae24eead1373ab1334084404c8c16e0354f9a3e55d385
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.9.3+3" version: "0.9.4"
file_selector_platform_interface: file_selector_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -270,10 +294,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: firebase_core name: firebase_core
sha256: "53316975310c8af75a96e365f9fccb67d1c544ef0acdbf0d88bbe30eedd1c4f9" sha256: "372d94ced114b9c40cb85e18c50ac94a7e998c8eec630c50d7aec047847d27bf"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.27.0" version: "2.31.0"
firebase_core_platform_interface: firebase_core_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -286,34 +310,34 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: firebase_core_web name: firebase_core_web
sha256: c8e1d59385eee98de63c92f961d2a7062c5d9a65e7f45bdc7f1b0b205aab2492 sha256: "43d9e951ac52b87ae9cc38ecdcca1e8fa7b52a1dd26a96085ba41ce5108db8e9"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.11.5" version: "2.17.0"
firebase_messaging: firebase_messaging:
dependency: "direct main" dependency: "direct main"
description: description:
name: firebase_messaging name: firebase_messaging
sha256: e41586e0fd04fe9a40424f8b0053d0832e6d04f49e020cdaf9919209a28497e9 sha256: e0882a7426821f7caccaabfc15a535155cd15b4daa73a5a7b3af701a552d73ab
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.7.19" version: "14.9.2"
firebase_messaging_platform_interface: firebase_messaging_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: firebase_messaging_platform_interface name: firebase_messaging_platform_interface
sha256: f7a9d74ff7fc588a924f6b2eaeaa148b0db521b13a9db55f6ad45864fa98c06e sha256: "52e12cc50e1395ad7ea3552dcbe9958fb1994b5afcf58ee4c0db053932a6fce5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.5.27" version: "4.5.35"
firebase_messaging_web: firebase_messaging_web:
dependency: transitive dependency: transitive
description: description:
name: firebase_messaging_web name: firebase_messaging_web
sha256: fc21e771166860c55b103701c5ac7cdb2eec28897b97c42e6e5703cbedf9e02e sha256: "8812cc5929380b783f92290d934bf32e2fea06701583f47cdccd5f13f4f24522"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.6.8" version: "3.8.5"
fl_chart: fl_chart:
dependency: "direct main" dependency: "direct main"
description: description:
@ -351,6 +375,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.3" version: "2.0.3"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
sha256: "40e6fbd2da7dcc7ed78432c5cdab1559674b4af035fddbfb2f9a8f9c2112fcef"
url: "https://pub.dev"
source: hosted
version: "17.1.2"
flutter_local_notifications_linux:
dependency: transitive
description:
name: flutter_local_notifications_linux
sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03"
url: "https://pub.dev"
source: hosted
version: "4.0.0+1"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
name: flutter_local_notifications_platform_interface
sha256: "340abf67df238f7f0ef58f4a26d2a83e1ab74c77ab03cd2b2d5018ac64db30b7"
url: "https://pub.dev"
source: hosted
version: "7.1.0"
flutter_localizations: flutter_localizations:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
@ -360,10 +408,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_plugin_android_lifecycle name: flutter_plugin_android_lifecycle
sha256: b068ffc46f82a55844acfa4fdbb61fad72fa2aef0905548419d97f0f95c456da sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.17" version: "2.0.19"
flutter_secure_storage: flutter_secure_storage:
dependency: "direct main" dependency: "direct main"
description: description:
@ -376,34 +424,34 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_secure_storage_linux name: flutter_secure_storage_linux
sha256: "3d5032e314774ee0e1a7d0a9f5e2793486f0dff2dd9ef5a23f4e3fb2a0ae6a9e" sha256: "4d91bfc23047422cbcd73ac684bc169859ee766482517c22172c86596bf1464b"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.0" version: "1.2.1"
flutter_secure_storage_macos: flutter_secure_storage_macos:
dependency: transitive dependency: transitive
description: description:
name: flutter_secure_storage_macos name: flutter_secure_storage_macos
sha256: bd33935b4b628abd0b86c8ca20655c5b36275c3a3f5194769a7b3f37c905369c sha256: "8cfa53010a294ff095d7be8fa5bb15f2252c50018d69c5104851303f3ff92510"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.1" version: "3.1.0"
flutter_secure_storage_platform_interface: flutter_secure_storage_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: flutter_secure_storage_platform_interface name: flutter_secure_storage_platform_interface
sha256: "0d4d3a5dd4db28c96ae414d7ba3b8422fd735a8255642774803b2532c9a61d7e" sha256: "301f67ee9b87f04aef227f57f13f126fa7b13543c8e7a93f25c5d2d534c28a4a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.2" version: "1.1.1"
flutter_secure_storage_web: flutter_secure_storage_web:
dependency: transitive dependency: transitive
description: description:
name: flutter_secure_storage_web name: flutter_secure_storage_web
sha256: "30f84f102df9dcdaa2241866a958c2ec976902ebdaa8883fbfe525f1f2f3cf20" sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.2" version: "1.2.1"
flutter_secure_storage_windows: flutter_secure_storage_windows:
dependency: transitive dependency: transitive
description: description:
@ -424,10 +472,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_svg name: flutter_svg
sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.9" version: "2.0.10+1"
flutter_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
@ -482,10 +530,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: http name: http
sha256: "5895291c13fa8a3bd82e76d5627f69e0d85ca6a30dcac95c4ea19a5d555879c2" sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.13.6" version: "1.2.1"
http_parser: http_parser:
dependency: "direct main" dependency: "direct main"
description: description:
@ -506,34 +554,34 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: image_picker name: image_picker
sha256: "26222b01a0c9a2c8fe02fc90b8208bd3325da5ed1f4a2acabf75939031ac0bdd" sha256: "33974eca2e87e8b4e3727f1b94fa3abcb25afe80b6bc2c4d449a0e150aedf720"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.7" version: "1.1.1"
image_picker_android: image_picker_android:
dependency: transitive dependency: transitive
description: description:
name: image_picker_android name: image_picker_android
sha256: "42c098e7fb6334746be37cdc30369ade356ed4f14d48b7a0313f95a9159f4321" sha256: "79455f6cff4cbef583b2b524bbf0d4ec424e5959f4d464e36ef5323715b98370"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.9+5" version: "0.8.12"
image_picker_for_web: image_picker_for_web:
dependency: transitive dependency: transitive
description: description:
name: image_picker_for_web name: image_picker_for_web
sha256: e2423c53a68b579a7c37a1eda967b8ae536c3d98518e5db95ca1fe5719a730a3 sha256: "5d6eb13048cd47b60dbf1a5495424dea226c5faf3950e20bf8120a58efb5b5f3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.0.2" version: "3.0.4"
image_picker_ios: image_picker_ios:
dependency: transitive dependency: transitive
description: description:
name: image_picker_ios name: image_picker_ios
sha256: "917a5cadd67d052554cfb258595e54217de53fac5b52939426e26319a02e6297" sha256: cb0db0ec0d3e2cd49674f2e6053be25ccdb959832607c1cbd215dd6cf10fb0dd
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.8.9+2" version: "0.8.11"
image_picker_linux: image_picker_linux:
dependency: transitive dependency: transitive
description: description:
@ -554,10 +602,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: image_picker_platform_interface name: image_picker_platform_interface
sha256: fa4e815e6fcada50e35718727d83ba1c92f1edf95c0b4436554cec301b56233b sha256: "9ec26d410ff46f483c5519c29c02ef0e02e13a543f882b152d4bfd2f06802f80"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.9.3" version: "2.10.0"
image_picker_windows: image_picker_windows:
dependency: transitive dependency: transitive
description: description:
@ -690,26 +738,26 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: path_provider name: path_provider
sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.2" version: "2.1.3"
path_provider_android: path_provider_android:
dependency: transitive dependency: transitive
description: description:
name: path_provider_android name: path_provider_android
sha256: "477184d672607c0a3bf68fbbf601805f92ef79c82b64b4d6eb318cbca4c48668" sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.2" version: "2.2.4"
path_provider_foundation: path_provider_foundation:
dependency: transitive dependency: transitive
description: description:
name: path_provider_foundation name: path_provider_foundation
sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.4.0"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
@ -746,10 +794,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_android name: permission_handler_android
sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474" sha256: "8bb852cd759488893805c3161d0b2b5db55db52f773dbb014420b304055ba2c5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "12.0.5" version: "12.0.6"
permission_handler_apple: permission_handler_apple:
dependency: transitive dependency: transitive
description: description:
@ -935,18 +983,18 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: sqflite name: sqflite
sha256: a9016f495c927cb90557c909ff26a6d92d9bd54fc42ba92e19d4e79d61e798c6 sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.3.3+1"
sqflite_common: sqflite_common:
dependency: transitive dependency: transitive
description: description:
name: sqflite_common name: sqflite_common
sha256: "28d8c66baee4968519fb8bd6cdbedad982d6e53359091f0b74544a9f32ec72d5" sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.5.3" version: "2.5.4"
stack_trace: stack_trace:
dependency: transitive dependency: transitive
description: description:
@ -995,6 +1043,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.6.1" version: "0.6.1"
timezone:
dependency: transitive
description:
name: timezone
sha256: a6ccda4a69a442098b602c44e61a1e2b4bf6f5516e875bbf0f427d5df14745d5
url: "https://pub.dev"
source: hosted
version: "0.9.3"
toggle_switch: toggle_switch:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1031,26 +1087,26 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: url_launcher name: url_launcher
sha256: "0ecc004c62fd3ed36a2ffcbe0dd9700aee63bd7532d0b642a488b1ec310f492e" sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.2.5" version: "6.2.6"
url_launcher_android: url_launcher_android:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_android name: url_launcher_android
sha256: d4ed0711849dd8e33eb2dd69c25db0d0d3fdc37e0a62e629fe32f57a22db2745 sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.3.0" version: "6.3.1"
url_launcher_ios: url_launcher_ios:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_ios name: url_launcher_ios
sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" sha256: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.2.5" version: "6.3.0"
url_launcher_linux: url_launcher_linux:
dependency: transitive dependency: transitive
description: description:
@ -1063,10 +1119,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_macos name: url_launcher_macos
sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 sha256: "9a1a42d5d2d95400c795b2914c36fdcb525870c752569438e4ebb09a2b5d90de"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.0" version: "3.2.0"
url_launcher_platform_interface: url_launcher_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -1079,10 +1135,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_web name: url_launcher_web
sha256: fff0932192afeedf63cdd50ecbb1bc825d31aed259f02bb8dba0f3b729a5e88b sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.3" version: "2.3.1"
url_launcher_windows: url_launcher_windows:
dependency: transitive dependency: transitive
description: description:
@ -1103,26 +1159,26 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vector_graphics name: vector_graphics
sha256: "4ac59808bbfca6da38c99f415ff2d3a5d7ca0a6b4809c71d9cf30fba5daf9752" sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.10+1" version: "1.1.11+1"
vector_graphics_codec: vector_graphics_codec:
dependency: transitive dependency: transitive
description: description:
name: vector_graphics_codec name: vector_graphics_codec
sha256: f3247e7ab0ec77dc759263e68394990edc608fb2b480b80db8aa86ed09279e33 sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.10+1" version: "1.1.11+1"
vector_graphics_compiler: vector_graphics_compiler:
dependency: transitive dependency: transitive
description: description:
name: vector_graphics_compiler name: vector_graphics_compiler
sha256: "18489bdd8850de3dd7ca8a34e0c446f719ec63e2bab2e7a8cc66a9028dd76c5a" sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.1.10+1" version: "1.1.11+1"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@ -1191,10 +1247,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: web name: web
sha256: "4188706108906f002b3a293509234588823c8c979dc83304e229ff400c996b05" sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.4.2" version: "0.5.1"
webview_flutter: webview_flutter:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1207,10 +1263,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_android name: webview_flutter_android
sha256: f038ee2fae73b509dde1bc9d2c5a50ca92054282de17631a9a3d515883740934 sha256: dad3313c9ead95517bb1cae5e1c9d20ba83729d5a59e5e83c0a2d66203f27f91
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.16.0" version: "3.16.1"
webview_flutter_platform_interface: webview_flutter_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -1252,5 +1308,5 @@ packages:
source: hosted source: hosted
version: "6.5.0" version: "6.5.0"
sdks: sdks:
dart: ">=3.2.3 <4.0.0" dart: ">=3.3.0 <4.0.0"
flutter: ">=3.16.6" flutter: ">=3.19.0"

View File

@ -78,6 +78,10 @@ dependencies:
http: any http: any
http_parser: any http_parser: any
cached_network_image_platform_interface: any cached_network_image_platform_interface: any
flutter_local_notifications: ^17.1.2
awesome_notifications_core: ^0.9.0
awesome_notifications: any
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter