I want to save all push notification which will be send from Firebase and Display all notification in app.
i have tried this but all notification does not save..
This is my Notification Model
class NotificationModel extends Equatable { final String title; final String body;
const NotificationModel({
required this.title,
required this.body, });
NotificationModel copyWith({
String? title,
String? body, }) {
return NotificationModel(
title: title ?? this.title,
body: body ?? this.body,
); }
Map<String, dynamic> toMap() {
return <String, dynamic>{
'title': title,
'body': body,
}; }
factory NotificationModel.fromMap(Map<String, dynamic> map) {
return NotificationModel(
title: map['title'] as String,
body: map['body'] as String,
); }}
and i declare a list of Notification List notificationList = [];
save title and body
void saveNotification(String title, String body) async {
NotificationModel notification =
NotificationModel(title: title, body: body);
String jsonData = jsonEncode(notification);
sharedPreferences.setString('notification', jsonData);
print('SavedNotification: $jsonData'); }
getting data from this method..
void initializeData() async {
sharedPreferences = await SharedPreferences.getInstance();
//final result = sharedPreferences.getString('notification');
final result =
await json.decode(sharedPreferences.getString('notification')!);
print('type: ${result.runtimeType}');
NotificationModel model = NotificationModel.fromJson(result);
if (result.isNotEmpty) {
notificationList
.add(NotificationModel(title: title ?? '', body: description ?? ''));
title = model.title;
description = model.body;
}
}
FirebaseMessaging.onMessage.listen(
(event) {
RemoteNotification? notification = event.notification;
AndroidNotification? android = event.notification!.android;
if (notification != null && android != null) {
_localNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(channel.id, channel.name,
channelDescription: channel.description,
icon: '#mipmap/ic_launcher'),
),
);
}
saveNotification(notification.title!, notification.body!);
print('SaveData success');
// print('Saved Data: ${sharedPreferences.getString('title')}');
},
Every time i got just latest title and body...
You are replacing the last notification JSON data with the most recent notification data in the 'notification' shared preference key. You should store a list of JSON <NotificationModel> JSON strings in notification shared preference.
For that update your saveNotification() method. you are storing only single NotificationModel instead of this add that model in List< NotificationModel> and then store this updated notification list JSON.
Note:
In saveNotification(), also you need to get first old notification list which is previous stored.
data from shared preference and add new notification & save that
updated list.
Related
I tried to fetch document data in firestore subcollection then show this error "Null check operator used on a null value " .
I want to fetch one article in user collection for each users.
database screenshot
user table
article subcollection
all articles UI
how to fetch a article when click view button
View button code in All articles UI
ElevatedButton(child: Text('View'),onPressed: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => ViewOneUserArticleScreen(id: data[index].id,)));
view one article code
Articles? oneArticle;
bool loading = false;
#override
initState() {
super.initState();
loading = true;
getArticle();
}
User? user = FirebaseAuth.instance.currentUser;
UserModel loggedInUser = UserModel();
Future<void> getArticle() async {
final id = widget.id;
final reference = FirebaseFirestore.instance.doc('users/${user?.uid}/articles/$id');
final snapshot = reference.get();
final result = await snapshot.then(
(snap) => snap.data() == null ? null : Articles.fromJson(snap.data()!));
setState(() {
oneArticle = result;
loading = false;
});
}
model
class Articles {
final String id;
final String topic;
final String description;
final String url;
Articles({
required this.id,
required this.topic,
required this.description,
required this.url
});
Articles.fromJson(Map<String, dynamic> json)
: this(
id: json['id'],
topic: json['topic']! as String,
url: json['url']! as String,
description: json['description']! as String,
);
Map<String, Object?> toJson() {
return {
'id': id,
'topic': topic,
'url': url,
'description': description,
};
}
}
new error
Your Issue is in your parsing method, change your Articles.fromJson to this:
Articles.fromJson(Map<String, dynamic> json)
: this(
id: json['id'] ?? '', // <--- change this
topic: json['topic'] as String ?? '', // <--- change this
url: json['url'] as String ?? '', // <--- change this
description: json['description'] as String ?? '', // <--- change this
);
in your json, topic, description and url may be null but you used ! on them and that means you are sure that they aren't null but they are. Also your id may be null to but in your object model you set it as required, so you need to provide default value to it or just remove the required before it.
I am trying to perform user login with my flutter app but then I keep getting data != null error and that a Text Widget must have a non-null string.
Upon further debugging, I realized the response['message'] is printing a null value so I implement a condition to check if it's not null before proceeding but yet still it keeps giving me the same error.
When I use response['message'].toString(), it still gives the same error.
this is the full error being thrown 'data != null': A non-null String must be provided to a Text widget.
the issue seems to be from the response['message'] but I just can't seem to find ways to solve it
This is Auth controller class
class AuthController extends GetxController {
AuthService authService = AuthService();
ProjectApis projectApis = ProjectApis();
String name = '';
String email = '';
String password = '';
String confirmPassword = '';
var isPasswordHidden = true.obs;
Future loginUser(BuildContext context) async {
buildLoader(context, message: 'Loading...');
http.Response response = await authService.signInUser(
email,
password,
);
if (response.statusCode == 200) {
Map<String, dynamic> responseData = json.decode(response.body);
debugPrint(responseData.toString());
debugPrint(responseData['message']);
if (responseData["status"] == true) {
User user = User.fromJson(responseData);
UserPreferences().setUser(user);
Navigator.pop(context);
Get.offAll(() => BottomNavigation());
return;
} else {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(responseData['message']),
));
return;
}
} else {
Navigator.pop(context);
showErrorDialog(context, message: "Server Error");
return;
}
}
}
This is the sign in function
Future<http.Response> signInUser(
String email,
String password,
) async {
Map data = {
'email': email,
'password': password,
};
var body = json.encode(data);
var url = Uri.parse(projectApis.loginUrl);
var response = await client.post(
url,
body: body,
headers: projectApis.headers,
);
return response;
}
This is the User model class
User userFromJson(String str) => User.fromJson(json.decode(str));
String userToJson(User data) => json.encode(data.toJson());
class User {
User({
this.id,
this.name,
this.email,
this.password,
this.passwordConfirm,
this.token,
});
int? id;
String? name;
String? email;
String? password;
String? passwordConfirm;
String? token;
String applicationDirPath = "";
factory User.fromJson(Map<String, dynamic> json) => User(
id: json["id"],
name: json["name"],
email: json["email"],
password: json["password"],
passwordConfirm: json["passwordConfirm"],
token: json["token"],
);
Map<String, dynamic> toJson() => {
"id": id,
"name": name,
"email": email,
"password": password,
"passwordConfirm": passwordConfirm,
"token": token,
};
}
Use null-operator like here
response['message'] ?? ''
If left side was null the right side will assing
But you can use this just if you are sure this happen because of this line
read in medium
Text widget doesn't accept nullable String, and reading map can provide null value. You can provide default value on null case like
Text(myMap["key"]??"defaultValue")
And for perser you can do
if (responseData["status"] != null && responseData["status"]==true ) {
I think problem is with the Text(responseData['message']), line.
Dart can't be sure that me message key exist on responseData Map. So Text(responseData['message']), can be null which is bad for null safety.
Just do:
String message = responseData['message'] ?? '';
The ?? operator will return an empty string in case ResponseData['message'] is null.
Then replace in your Text widget:
Text(message),
I need to retrieve User Data from Firebase and use a builder to pass on the data to UI. When I run the apps, I method is called in on Null.
I tried many ways to call firebase data but I keep receive error message on provider or on calling the data NULL.
The error is most likely coming from the method _getProfileData() below.
_getProfileData(AuthNotifier authNotifier) async {
final uid = await Provider.of(context, listen: false).authNotifier.getCurrentUID();
await Provider.of(context, listen: false)
.collection('Users')
.document(uid)
.get().then((result) {
user.isAdmin = result.data['isAdmin'];
});
}
When I made the changes below by using Provider, another error appears with Provider not working.
final uid = await Provider.of<authNotifier>(context, listen: false).getCurrentUID();
I placed the getter in the API.
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
// GET UID
Future<String> getCurrentUID(User user, AuthNotifier authNotifier) async {
return (await _firebaseAuth.currentUser()).uid;
}
// GET CURRENT USER
Future getCurrentUser(User user, AuthNotifier authNotifier) async {
return await _firebaseAuth.currentUser();
}
Stream<String> get onAuthStateChanged => auth.onAuthStateChanged.map(
(FirebaseUser user) => user?.uid,
);
I structured User Data as below.
class User {
List favorites = [];
String documentID;
String displayName;
String email;
String password;
bool isAdmin;
User({
this.favorites,
this.documentID,
this.displayName,
this.email,
this.password,
this.isAdmin,
});
factory User.fromFirestore(DocumentSnapshot document) {
Map data = document.data;
return User(
favorites: data['favorite'] ?? [],
documentID: document.documentID,
displayName: data['displayName'] ?? '',
email: data['email'] ?? '',
isAdmin: data['isAdmin'] ?? false,
);
}
// get admin => null;
Map<String, dynamic> toMap() {
return {
'displayName': displayName,
'email': email,
'isAdmin': isAdmin,
};
}
}
I have a problem when implementation flutter_local_notifications.
The problem is, how to send custom data from the notification? Because I see on the example only send a payload. Not give the example of how to send title, body, or custom data like my case (want to send model data from the API).
So how to solve my case? Because I already thinking about it still not give me a solution, also already googling it.
As you can see below, on my function already add the parameter model Articles and add it to title and body to make my content on the notification is dynamically based on API. But how to send that model to another screen? because the property payload only support for String but my model is not a String
static Future<void> showNotification(
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin,
Articles articles) async {
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'your channel id', 'your channel name', 'your channel description',
importance: Importance.Max, priority: Priority.High, ticker: 'ticker');
var iOSPlatformChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics = NotificationDetails(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.show(
0, articles.title, articles.description, platformChannelSpecifics,
payload: 'i want to drop articles parameter here'); // the problem here
}
And this is my Article model
class BundleData {
final Source source;
final Articles articles;
BundleData(this.source, this.articles);
}
class Articles {
Articles({
this.source,
this.author,
this.title,
this.description,
this.url,
this.urlToImage,
this.publishedAt,
this.content,
});
Source source;
String author;
String title;
String description;
String url;
String urlToImage;
String publishedAt;
String content;
factory Articles.fromJson(Map<String, dynamic> json) => Articles(
source: Source.fromJson(json["source"]),
author: json["author"],
title: json["title"],
description: json["description"],
url: json["url"],
urlToImage: json["urlToImage"],
publishedAt: json["publishedAt"],
content: json["content"] == null ? null : json["content"],
);
}
class Source {
Source({
this.id,
this.name,
});
int id;
String name;
factory Source.fromJson(Map<String, dynamic> json) => Source(
id: json["id"],
name: json["name"],
);
}
As you can see again, the listen is only support for String, but when the notification is clicked and open the detail page, it's need data from the model that getting from the API.
static void configureSelectNotificationSubject(
BuildContext context, String route) {
selectNotificationSubject.stream.listen((String payload) async { // because i need to get the articles model from the notification to arguments
await Navigator.pushNamed(context, route, arguments: BundleData(payload));
});
}
And also when we initialize the notification in the main, the property onSelectNotification only supports for String only, can't support for custom data like adding a model.
await flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: (String payload) async { // because this property only support String only, so how to send a model from API?
if (payload != null) {
print('notification payload: ' + payload);
}
selectNotificationSubject.add(payload);
Anyone can tell me how to solve my problem?
You could convert your model into JSON format that you will decode/encode. I don't know what variables are inside your Articles and Source classes but supposedly you could try something like that :
class BundleData {
final Source source;
final Articles articles;
BundleData(this.source, this.articles);
factory BundleData.fromJson(Map<String, dynamic> json) {
return BundleData(Source.fromJson(json['source']), Articles.fromJson(json['articles']));
}
Map<String, dynamic> toJson() {
return {
"source": this.source.toJson(),
"articles": this.articles.toJson()
};
}
}
You will need to create a factory fromJson and also a method toJson for both Source and Articles if you go with this solution.
Then you you could pass your parameters like this :
BundleData bundleData = BundleData(/* some source */, /* some articles */);
await flutterLocalNotificationsPlugin.show(
0, articles.title, articles.description, platformChannelSpecifics,
payload: jsonEncode(bundleData.toJson()));
Then get back your model like this:
static void configureSelectNotificationSubject(BuildContext context, String route) {
selectNotificationSubject.stream.listen((String payload) async {
await Navigator.pushNamed(context, route, arguments: BundleData.fromJson(jsonDecode(payload)));
});
}
I am very new to Flutter and Dart.
I have a signup page and I would like to show error in the App. My backend page is returning the errors and status in JSON format. Like below.
{"errors":{"Password1":"Password could not be empty",
"Email1":"Invalid Email Format",
"Name":"Your name must be between 3 to 30 characters!"},
"success":false}
I created a file for JSON parsing like below.
import 'dart:convert';
Signup signupFromJson(String str) => Signup.fromJson(json.decode(str));
String signupToJson(Signup data) => json.encode(data.toJson());
class Signup {
Errors errors;
bool success;
Signup({
this.errors,
this.success,
});
factory Signup.fromJson(Map<String, dynamic> json) => Signup(
errors: Errors.fromJson(json["errors"]),
success: json["success"],
);
Map<String, dynamic> toJson() => {
"errors": errors.toJson(),
"success": success,
};
}
class Errors {
String password1;
String email1;
String name;
Errors({
this.password1,
this.email1,
this.name,
});
factory Errors.fromJson(Map<String, dynamic> json) => Errors(
password1: json["Password1"],
email1: json["Email1"],
name: json["Name"],
);
Map<String, dynamic> toJson() => {
"Password1": password1,
"Email1": email1,
"Name": name,
};
}
Now I need to show this data to App after Async call.
Future userRegistration() async{
try{
// Showing CircularProgressIndicator.
setState(() {
visible = true ;
});
// Getting value from Controller
String name = nameController.text;
String email = emailController.text;
String password = passwordController.text;
// SERVER API URL
var url = 'http://192.168.100.10:8080/app/registerexec.php';
// Store all data with Param Name.
var data = {'name': name, 'email': email, 'password' : password};
// Starting Web API Call.
var response = await http.post(url, body: json.encode(data));
// Getting Server response into a variable.
final message = signupFromJson(response.body);
if(response.statusCode == 200){
setState(() {
visible = false;
});
}
// Showing Alert with Response JSON Message.
}catch(e){
return userRegistration();
}
}
How can I show the JSON data to SnackBar?
Edit
I managed to get the data in Print after manually defining it. Like below. But I want to automate it. So, if there are any errors it can show and if its successful then a different message.
print(message.errors.email1);
print(message.errors.name);
print(message.errors.password1);
print(message.success);
you could use FutureBuilder at your snackBar. I've edited from the code available here:
class SnackBarPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: userRegistration,
initialData: '',
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
// snapshot.data = yourData from your userRegistration
// print(snapshot.data) to show your data here
return snackBar = SnackBar(
content: Text('Yay! A SnackBar!'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
},
)
};
)
},
),
}
}