Where should put the condition to send push notification in flutter? - firebase

I would like to build an app like when I save a new data with latitude and longitude information to firebase then in my app I calculate the distance between these latitude&longitude and the user's current location. If the distance is less than 60 kilometres then send an onBackgroundMessage notification. I do not store the user's current location on firebase. I get the user's current location with function _getCurrentLocation.
The problem is that I do not undertsand where and how to put the isValidDistance to check if the distance is under 60 kilimetres.
Currently my app send notifications but not by distance.
index.js
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.myFunction = functions.firestore
.document("animal/{message}")
.onCreate((snapshot, context) => {
return admin.messaging().sendToTopic("animal", {
data: {
latitude: snapshot.data()["latitude"].toString(),
longitude: snapshot.data()["longitude"].toString(),
},
notification: {
title: snapshot.data().username,
body: snapshot.data().description,
clickAction: "FLUTTER_NOTIFICATION_CLICK",
},
});
});
main.dart
Future<void> _messageHandler(RemoteMessage message) async {
print('background message ${message.data}');
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
double? currentLatitude;
double? currentLongitude;
Future<void> _getCurrentLocation() async {
final locData = await Location().getLocation();
setState(() {
currentLatitude = locData.latitude;
currentLongitude = locData.longitude;
});
}
int getDistanceInMeters(currLat, currLng, lat, lng) {
return Geolocator.distanceBetween(
currLat,
currLng,
lat,
lng,
).round();
}
bool isValidDistance(RemoteMessage messaging) {
Map<String, dynamic> data = messaging.data;
var _list = data.values.toList();
var lat = double.parse(_list[0]);
var lng = double.parse(_list[1]);
print(_list);
int distance =
getDistanceInMeters(currentLatitude, currentLongitude, lat, lng);
var distanceInKm = (distance / 1000).round();
print('Distance is: ${distanceInKm.toString()}');
if (distance < 60000) {
return true;
}
return false;
}
#override
void initState() {
super.initState();
_getCurrentLocation();
final messaging = FirebaseMessaging.instance;
messaging.subscribeToTopic('animal');
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
if (isValidDistance(message)) {
print('onMessageListen');
}
});
FirebaseMessaging.onMessageOpenedApp.listen((message) {
if (isValidDistance(message)) {
print('onMessageOpened');
}
});
FirebaseMessaging.onBackgroundMessage(_messageHandler);
}
...

First you need to change your fcm from notification message to data message to allow app handle messages while in background. Check here.
exports.myFunction = functions.firestore
.document("animal/{message}")
.onCreate((snapshot, context) => {
return admin.messaging().sendToTopic("animal", {
data: {
latitude: snapshot.data()["latitude"].toString(),
longitude: snapshot.data()["longitude"].toString(),
title: snapshot.data().username,
body: snapshot.data().description,
},
});
});
Check here to show push notification while app is open. Your code should like this.
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
if (isValidDistance(message)) {
print('onMessageListen');
showNotification(message);
}
});
Yo can access the data sent through the following
Map<String, dynamic> data = message.data;
Then the background handler will be as follows.
_messageHandler(RemoteMessaging message){
if (isValidDistance(message)) {
print('onMessageListen');
showNotification(message);
}
}
Or create a Notification class as follows and use.
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:geolocator/geolocator.dart';
import 'package:location/location.dart';
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
description: 'This channel is used for important notifications.',
importance: Importance.max,
);
Future<LocationData> _getCurrentLocation() => Location().getLocation();
int getDistanceInMeters(currLat, currLng, lat, lng) {
return Geolocator.distanceBetween(
currLat,
currLng,
lat,
lng,
).round();
}
Future<bool> isValidDistance(RemoteMessage messaging) async {
Map<String, dynamic> data = messaging.data;
var _list = data.values.toList();
var lat = double.parse(_list[0]);
var lng = double.parse(_list[1]);
print(_list);
var location = await _getCurrentLocation();
int distance =
getDistanceInMeters(location.latitude, location.longitude, lat, lng);
var distanceInKm = (distance / 1000).round();
print('Distance is: ${distanceInKm.toString()}');
if (distance < 60000) {
return true;
}
return false;
}
class NotificationServices {
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
bool isValid = await isValidDistance(message);
if (isValid) {
print('onMessageListen');
showNotification(message);
}
}
backgroundNotification() {
final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
final AndroidInitializationSettings _initialzationSettingsAndriod =
AndroidInitializationSettings('#mipmap/ic_launcher');
final IOSInitializationSettings _initialzationSettingsIOS =
IOSInitializationSettings();
final InitializationSettings _initializationSettings =
InitializationSettings(
android: _initialzationSettingsAndriod,
iOS: _initialzationSettingsIOS);
_flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
/// Update the iOS foreground notification presentation options to allow
/// heads up notifications.
FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
_flutterLocalNotificationsPlugin.initialize(_initializationSettings);
FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage? message) async {
if (message != null) await onClickNotificationHandler(message);
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
bool isValid = await isValidDistance(message);
if (isValid) {
print('onMessageListen');
showNotification(message);
}
});
FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler);
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) async {
await onClickNotificationHandler(message);
});
}
onClickNotificationHandler(RemoteMessage message) async {
Map<String, dynamic> data = message.data;
print(data);
//you can handle notificationand navigate to necessary screen here.
}
showNotification(RemoteMessage message) {
Map<String, dynamic> data = message.data;
if (data["body"] != null) {
flutterLocalNotificationsPlugin.show(
data.hashCode,
data["title"],
data["body"],
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
icon: '#mipmap/ic_launcher',
),
iOS: IOSNotificationDetails(
presentAlert: true, presentBadge: true, presentSound: true),
),
);
}
}
}

Related

FirebaseMessaging.onMessageOpenedApp.listen method from firebase_messaging (last version) not working

This is my first function where I get backgound messsage and everything work's fine.
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(
options: const FirebaseOptions(
/// Svi podatci uzeti iz google-services.json-a
apiKey: 'XXXXXXXXXXXXXX',
appId: 'XXXXXXXXXXX',
messagingSenderId: 'XXXXXXXXXXX',
projectId: 'XXXXXXXXXXX',
),
);
print('Background message received!');
var decoded = NotificationModelPusherAG.fromJson(message.data);
var encodedMessage = json.decode(decoded.message);
var decodedMessage = PusherMessage.fromJson(encodedMessage);
FirebaseNotifications.showNotification(
decodedMessage.title, decodedMessage.description);
}
Here is my main method and here also everything works fine :
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
final String INSTANCE_ID = 'xxxxxxxxxx';
await PusherBeams.start(INSTANCE_ID);
await Firebase.initializeApp(
options: const FirebaseOptions(
/// Svi podatci uzeti iz google-services.json-a
apiKey: 'xxxxxxxxxx',
appId: 'xxxxxxxxx',
messagingSenderId: 'xxxxxxxx',
projectId: 'xxxxxxx',
),
);
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(MyApp());
}
In init state I get message but FirebaseMessaging.onMessageOpenedApp.listen is not triggered.
#override
void initState() {
super.initState();
FirebaseMessaging.instance
.getInitialMessage()
.then((RemoteMessage message) {
if (message != null) {
print('eeeeeeeej');
}
});
FirebaseMessaging.onMessage.listen((RemoteMessage message) async {
var decoded = NotificationModelPusherAG.fromJson(message.data);
var encodedMessage = json.decode(decoded.message);
var decodedMessage = PusherMessage.fromJson(encodedMessage);
print(decoded.creatorId);
print(decodedMessage.description);
// AndroidNotification android = message.notification?.android;
if (message.data != null) {
FirebaseNotifications.showNotification(
decodedMessage.title, decodedMessage.description);
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('Heeeeey');
if (message.data['type'] == 'malfunction') {
print ('aaaa');
}
if (message.data['type'] == 'notice') {
print ('bbbbbbb');
}
if (message.data['type'] == 'poll') {
print ('cccccccc');
}
});
}
In debug console when click on message I get nothing.
My flutter version : (Channel stable, 2.8.1)
First of all create a new file push_notification_service.dart and put below code as it is.
final FirebaseMessaging _fcm = FirebaseMessaging.instance;
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'0', // id
'High Importance Notifications', // title
importance: Importance.max,
);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
class PushNotificationService {
Future initialise() async {
await _fcm.requestPermission(
alert: true,
announcement: false,
badge: true,
carPlay: false,
criticalAlert: false,
provisional: false,
sound: true,
);
String fcmToken = await _fcm.getToken();
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
FirebaseMessaging.onMessage.listen((event) {
_showMessage(event);
RemoteNotification notification = event.notification;
AndroidNotification android = event.notification?.android;
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
var initializationSettingsAndroid = AndroidInitializationSettings(
'#mipmap/ic_launcher'); // <- default icon name is #mipmap/ic_launcher
var initializationSettings =
InitializationSettings(android: initializationSettingsAndroid);
flutterLocalNotificationsPlugin.initialize(initializationSettings);
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
icon: android?.smallIcon,
// other properties...
),
));
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
try {
if (message.data['item_type'] == 'category') {
//Navigate to category screen
} else if (message.data['item_type'] == 'profile') {
// Go to profile screen
}
} catch (e) {
print("object-----ERROR------>$e");
}
});
}
void _showMessage(RemoteMessage message) {
// Show your In-App messaging Dialog
}
}
Now in your main.dart just replace your initstate() function with mine
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
Future.delayed(Duration.zero).then(
(value) async {
Firebase.initializeApp().then(
(value) {
Future.delayed(Duration(milliseconds: 10), () async {
PushNotificationService().initialise();
});
},
);
},
);
}

Firebase _upload writes random downloadURL

Below is a simple firebase image uploader. The problem is that it sometimes uses another image's downloadURL as the value when it writes to Firestore. It uploads my image to cloud storage without a problem but then when it goes to write the location to firestore, it often uses the URL of another image. The full code is below but I have omitted the UI. How do I ensure that it writes the correct URL to firestore?
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'dart:io';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:path/path.dart' as path;
import 'package:image_picker/image_picker.dart';
class ImagePicky2 extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
// Remove the debug banner
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.green),
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
FirebaseStorage storage = FirebaseStorage.instance;
double? lat, lng;
File? file;
String? name, detail, pathImage, dateString;
// Select an image from the gallery or take a picture with the camera
// Then upload to Firebase Storage
Future<XFile?> _upload(String inputSource) async {
FirebaseAuth auth = FirebaseAuth.instance;
User firebaseUser = auth.currentUser!;
final picker = ImagePicker();
try {
final pickedImage = await picker.pickImage(
source: inputSource == 'camera'
? ImageSource.camera
: ImageSource.gallery,
imageQuality: 25,
maxWidth: 1920);
final String fileName = path.basename(pickedImage!.path);
File imageFile = File(pickedImage.path);
try {
// Uploading the selected image with some custom meta data
await storage.ref(fileName).putFile(
imageFile,
SettableMetadata(customMetadata: {
'uploaded_by': firebaseUser.displayName!,
'description': 'Some description...'
}));
// Refresh the UI
setState(() {});
} on FirebaseException catch (error) {
print(error);
}
} catch (err) {
print(err);
}
photoUploadFirestoreDetails();
}
// Retriew the uploaded images
// This function is called when the app launches for the first time or when an image is uploaded or deleted
Future<List<Map<String, dynamic>>> _loadImages() async {
FirebaseAuth auth = FirebaseAuth.instance;
User firebaseUser = auth.currentUser!;
List<Map<String, dynamic>> files = [];
final ListResult result = await storage.ref().list();
final List<Reference> allFiles = result.items;
await Future.forEach<Reference>(allFiles, (file) async {
final String fileUrl = await file.getDownloadURL();
pathImage = await file.getDownloadURL();
final FullMetadata fileMeta = await file.getMetadata();
files.add({
"url": fileUrl,
"path": file.fullPath,
"uploaded_by": fileMeta.customMetadata?['uploaded_by'] ?? firebaseUser.displayName,
"description":
fileMeta.customMetadata?['description'] ?? 'No description'
});
});
return files;
}
Future<Null> photoUploadFirestoreDetails() async {
Firebase.initializeApp();
Map<String, dynamic> map = Map();
map['PathImage'] = pathImage;
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference collectionReference =
firestore.collection('MarkerCollect');
await collectionReference.doc().set(map).then((
value) {
});
}
}```
The code is uploading random download urls to Firestore because you're getting the image path from the _loadImages method which loads up the files on storage instead of using the download url of the just uploaded file.
This is the problematic code:
Future<Null> photoUploadFirestoreDetails() async {
...
map['PathImage'] = pathImage;
...
}
Solution:
You can fix this by retrieving the download url just after the upload and passing it to the photoUploadFirestoreDetails method to be used in the Firestore upload.
You should also put the photoUploadFirestoreDetails in the try-catch.
Checkout the updated code below:
// _upload method
Future<XFile?> _upload(String inputSource) async {
FirebaseAuth auth = FirebaseAuth.instance;
User firebaseUser = auth.currentUser!;
final picker = ImagePicker();
try {
final pickedImage = await picker.pickImage(
source: inputSource == 'camera'
? ImageSource.camera
: ImageSource.gallery,
imageQuality: 25,
maxWidth: 1920);
final String fileName = path.basename(pickedImage!.path);
File imageFile = File(pickedImage.path);
try {
// Uploading the selected image with some custom meta data
final Reference storageReference = storage.ref(fileName);
await storageReference.putFile(
imageFile,
SettableMetadata(customMetadata: {
'uploaded_by': firebaseUser.displayName!,
'description': 'Some description...'
}));
final String downloadUrl = await storageReference.getDownloadURL();
// Refresh the UI
setState(() {});
await photoUploadFirestoreDetails(downloadUrl: downloadUrl);
} on FirebaseException catch (error) {
print(error);
}
} catch (err) {
print(err);
}
}
// photoUploadFirestoreDetails method
Future<Null> photoUploadFirestoreDetails({#required String downloadUrl}) async {
Firebase.initializeApp();
Map<String, dynamic> map = Map();
map['PathImage'] = downloadUrl;
FirebaseFirestore firestore = FirebaseFirestore.instance;
CollectionReference collectionReference =
firestore.collection('MarkerCollect');
var value = await collectionReference.doc().set(map);
}
Try this function to upload image to fire-storage and get Url
Future<String?> uploadAndGetUrl(File file) async {
try {
final Reference ref = FirebaseStorage.instance
.ref()
.child('profilePhoto')
.child(DateTime.now().microsecondsSinceEpoch.toString());
UploadTask uploadTask = ref.putFile(file);
await uploadTask.whenComplete(() {});
String url = await ref.getDownloadURL();
return url;
} catch (e) {
print('Firebase Storage Error is : $e');
return null;
}
}
OR you can just upload an image and get the image URL later.
Your upload image function looks okay. the name should be unique. otherwise, it returns a different image url.
Future<String> getUrl(String imageName) async {
try {
Reference storageRef = FirebaseStorage.instance.ref().child('profilePhoto/$logo');
String url = await storageRef.getDownloadURL();
return url;
} catch (e) {
return null;
}
}

Flutter Firebase Messaging Deactivate Visible Notification

How can I deactivate the visible notification in Firebase Messaging and handle the notification manually with the flutter_local_notifications package so the notification doesn't show twice? I'm not able to edit the server integration side since am I using a wordpress plugin (https://wordpress.org/plugins/fcm-push-notification-from-wp/) to send a notification request for me.
The payload looks like this: https://ps.w.org/fcm-push-notification-from-wp/assets/screenshot-6.png?rev=2446404
String selectedNotificationPayload = "";
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
final BehaviorSubject<String> selectNotificationSubject =
BehaviorSubject<String>();
FirebaseMessaging messaging;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('share_icon');
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
onDidReceiveLocalNotification:
(int id, String title, String body, String payload) async {});
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid,
iOS: initializationSettingsIOS,
);
NotificationAppLaunchDetails notificationAppLaunchDetails =
await flutterLocalNotificationsPlugin.getNotificationAppLaunchDetails();
if (notificationAppLaunchDetails?.didNotificationLaunchApp ?? false) {
selectedNotificationPayload = notificationAppLaunchDetails.payload;
}
await flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: (String payload) async {
if (payload != null) {
selectedNotificationPayload = payload;
selectNotificationSubject.add(payload);
}
});
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
runApp(Start());
}
class Start extends StatefulWidget {
#override
_StartState createState() => _StartState();
}
class _StartState extends State<Start>{
void initializeFlutterFire() async {
try {
await Firebase.initializeApp();
print("Firebase - initialzed successfully");
} catch (e) {
print("Firebase - initialzed FAILED");
}
messaging = FirebaseMessaging.instance;
}
void _configureSelectNotificationSubject() async {
selectNotificationSubject.stream.listen((String _url) async {
try {
await Future.delayed(Duration(milliseconds: 500), () async {
await Navigator.of(context).push(new Route(_url, ""));
});
} catch (e) {
print("Fehler beim öffnen der Website");
}
});
}
#override
initState() {
super.initState();
initializeFlutterFire();
}
...
}
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
Map<String, dynamic> data = message.data;
String _title = data['title'];
String _description = data['message'];
sendNotification(_title, _description);
}

Unable to update data in firebase database while using phone authentication in Flutter

I am trying to add user registering form data to firebase database. But have tough time doing it effectivily. I am new to the flutter. I successfully register the user via phone number but unable to add corresponding details. I tried using getter and setter but it obviously is not working. I don't know what is idealic method of doing that. I tried googling it but didn't got any help. I am just stuck. I will really appreciate it.
Here is my code
import 'package:firebase_auth/firebase_auth.dart';
import 'package:home_crisp/models/user.dart';
import 'package:home_crisp/services/auth.dart';
import 'package:provider/provider.dart';
class ChefRegisterScreen extends StatefulWidget {
ChefRegisterScreen();
#override
_ChefRegisterScreenState createState() => _ChefRegisterScreenState();
}
class _ChefRegisterScreenState extends State<ChefRegisterScreen> {
final AuthService _auth = AuthService();
bool loading = false;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
String chefName = "", phoneNo = "";
String smsCode, verificationID;
String phnCode = "+92";
DateTime dateOfBirth;
bool codeSent = false;
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
final deviceSize = MediaQuery.of(context).size;
return loading
? Loading()
: Material(
type: MaterialType.card,
color: Color(0xffD4EBD3),
child: Stack(
children: <Widget>[
// --> Here is the code of getting "chefname, phone and dateOfBirth" and setting them using setState() method
//
// >>>>>>>>> Textfeild for getting OTP code
//
codeSent
? // Here is the code for textfeild which get OTP code
: Container(),
// ------------------------------------------------------ F I N I S H B U T T O N
FlatButton(
onPressed: () async {
if (user != null) {
print(
"TheRe IS uSer already logging in so signing out logging in new user");
AuthService().signOut();
}
if (codeSent) {
AuthService().signInWithOTP(smsCode, verificationID);
} else {
verifyPhone(phoneNo);
}
// ----> Here I tried to several methods to sent register form data to the seperate class named
// ----> "user.dart" from where I tried to extract that info in "auth" signinWithPhoneNumber method.
// ----> I first tried to send the info via constructor
ChefData(chefName: chefName,
chefPhNo: phoneNo,
chefDateOfBirth: dateOfBirth);
});
// ----> Then I tried "setter" but in vain
// ChefData().setChefName(chefName);
// ChefData().setChefPhNo(phoneNo);
// ChefData().setChefDateOfBirth(dateOfBirth);
child: ClipRRect(
child: Text(
"FINISH",
style: TextStyle(
color: Colors.white,
fontFamily: 'Montserrat',
fontSize: 20,
),
),
),
),
],
),
),
)
],
),
),
],
),
);
}
//
// >>>>>>>>> S I G N I N W I T H P H O M E N U M B E R P R O C E S S
//
Future<void> verifyPhone(phoneNo) async {
final PhoneVerificationCompleted verificationComplete =
(AuthCredential authResult) {
print('1. Auto retrieving verification code');
}
};
final PhoneCodeAutoRetrievalTimeout autoRetrieve = (String verID) {
verificationID = verID;
print("\n2. Auto retrieval time out");
};
final PhoneCodeSent smsCodeSent =
(String verID, [int forceCodeResend]) async {
verificationID = verID;
setState(() {
this.codeSent = true;
});
print("\n 3. Code Sent to " + phoneNo);
};
final PhoneVerificationFailed verificationFailed =
(AuthException authException) {
print('${AuthException(smsCode, "message")}');
if (authException.message.contains('not authorized'))
print('App not authroized');
else if (authException.message.contains('Network'))
print('Please check your internet connection and try again');
else
print('Something has gone wrong, please try later ' +
authException.message);
};
await FirebaseAuth.instance
.verifyPhoneNumber(
phoneNumber: phoneNo,
timeout: Duration(seconds: 50),
verificationCompleted: verificationComplete,
verificationFailed: verificationFailed,
codeSent: smsCodeSent,
codeAutoRetrievalTimeout: autoRetrieve,
)
.then((value) {})
.catchError((error) {
print(error.toString());
});
}
}
Here is the "auth.dart" class
import 'package:firebase_auth/firebase_auth.dart';
import 'package:home_crisp/models/user.dart';
import 'package:home_crisp/services/database.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create user obj based on FirebaseUser
User _userFormFirebaseUser(FirebaseUser user) {
return user != null ? User(uid: user.uid) : null;
}
// auth change user stream
Stream<User> get user {
return _auth.onAuthStateChanged.map(_userFormFirebaseUser);
}
// ------------------------------------------------------ S I G N I N W I T H P H O M E N U M B E R
signInWithPhoneNumber(AuthCredential authCreds) async {
try {
AuthResult result = await _auth.signInWithCredential(authCreds);
FirebaseUser user = result.user;
if (user != null) {
print('AUTHENTICATONI SUCCESSFULL. Id: ' + user.uid);
// ---->> Now here I tried create a new document for the chef with the uid by extracting the chef data // // ---->> from "user.dart" class
// ---->> I used getter method. I know there is going to be some better way to get that data
await DatabaseService(uid: user.uid).updateChefData(
ChefData().getChefName(),
ChefData().getChefPhNo(),
ChefData().getChefDateOfBirth());
return _userFormFirebaseUser(user);
} else {
print('Invalid code/invalid authentication');
}
} catch (e) {
print(e.toString());
return null;
}
}
signInWithOTP(smsCode, verId) {
AuthCredential authCreds = PhoneAuthProvider.getCredential(
verificationId: verId, smsCode: smsCode);
signInWithPhoneNumber(authCreds);
}
}
Here is "user.dart" file containing "ChefData" class acting as intermediary between "auth.dart" and "chefRegisterScreen.dart"
class User {
final String uid;
User({this.uid});
}
class ChefData {
String chefId;
String chefName;
String chefPhNo;
DateTime chefDateOfBirth;
ChefData({this.chefId, this.chefName, this.chefPhNo, this.chefDateOfBirth});
// void setChefId(String _chefId) {
// this.chefId = _chefId;
// }
// void setChefName(String _chefName) {
// this.chefName = _chefName;
// }
// void setChefPhNo(String _chefPhNo) {
// this.chefPhNo = chefPhNo;
// }
// DateTime setChefDateOfBirth(DateTime _chefDateOfBirth) {
// this.chefDateOfBirth = _chefDateOfBirth;
// }
String getChefId() {
return chefId;
}
String getChefName() {
return chefName;
}
String getChefPhNo() {
return chefPhNo;
}
DateTime getChefDateOfBirth() {
return chefDateOfBirth;
}
}
I had a similar problem and I used shared preferences to store the user details during registration then uploaded the details after phone authentication was successful.
Shared preferences is a flutter plugin that allows you to store simple data in key-value pair form.Here is a more detailed article about shared preferences and how to use it.
In your case, I suggest you create a class for shared preferences.
class SharedPreference{
static Future<String> storeChefData(ChefData chefData) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String storeUser = userToJson(chefData);
await prefs.setString('user', storeUser);
return storeUser;
}
static Future<ChefData> getChefData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if (prefs.getString('user') != null) {
ChefData chefData = userFromJson(prefs.getString('user'));
return chefData;
} else {
return null;
}
}
}
The storeChefData() function is for storing users details during registration. While the getChefData() function is for retrieving the users details in order to upload to the database after authentication.
In your user.dart file you will be required to add functions that convert the chefData to jsonData for storage and from jsonData to ChefData for retrieval as shown in the above functions.
ChefData userFromJson(String str) {
final jsonData = json.decode(str);
return ChefData.fromJson(jsonData);
}
String userToJson(ChefData data) {
final dyn = data.toJson();
return json.encode(dyn);
}
class ChefData{
final String chefName;
final String chefPhNo;
final DateTime chefDateOfBirth;
ChefData({this.chefName,this.chefPhNo,this.chefDateOfBirth});
factory ChefData.fromJson(Map<String, dynamic> json) => ChefData(
chefName: json["chefName"],
chefPhNo: json["chefPhNo"]
chefDateOfBirth: json["chefDateOfBirth"]
);
Map<String, dynamic> toJson() => {
"chefName": chefName,
"chefPhNo": chefPhNo
"chefDateOfBirth": chefDateOfBirth
};
}
In your ChefRegistrationScreen you will add:
await SharedPreference.storeChefData(ChefData(chefName: chefName,chefPhNo: phoneNo,chefDateOfBirth: dateOfBirth));
at the point where you would like to insert the chefData.
Then in your auth.dart file you will add:
ChefData getChefData = await SharedPreference.getChefData();
await DatabaseService(uid: user.uid).updateUserData(getChefData);
in order to update your database.

Flutter - setData to firebase on successfull payment

1) list_profile:
class DetailPage extends StatefulWidget {
final DocumentSnapshot post;
DetailPage({this.post});
#override
_DetailPageState createState() => _DetailPageState();
}
class _DetailPageState extends State<DetailPage> {
bool pressed = false;
String taskname,tasksector,taskpocket,tasklandmark;
int _myTaskType = 0;
String taskVal;
StateModel appState;
bool _loadingVisible = false;
#override
Widget build(BuildContext context) {
// final CounterBloc _counterBloc = BlocProvider.of<CounterBloc>(context);
DateTime now = DateTime.now();
String formattedDate = DateFormat('EEE d MMM y').format(now);
// firebase location to be set
confirm() { // for driver
appState = StateWidget.of(context).state;
//final userId = appState?.firebaseUserAuth?.uid ?? '';
final w_fl = appState?.user?.w_fl ?? '';
final firstName = appState?.user?.firstName ?? '';
final number = appState?.user?.number ?? '';
DocumentReference ds = Firestore.instance
.collection("customer2")
.document("${widget.post.data["driverNumber"]}")
.collection("1")
.document(formattedDate);
Map<String, dynamic> data = {
//"Status": firstName + " $lastName",
"customerName":firstName,
"customerNumber":number,
"time":taskVal,
"status": "Waiting...",
"address":taskname,
"sector":tasksector,
"pocket":taskpocket,
"landmark":tasklandmark,
"payment":"X"
};
ds.setData(data).whenComplete(() {
print('Task created');
});
}
confirm2() { // to fetched only on customer side i.e yourBookings
appState = StateWidget.of(context).state;
//final userId = appState?.firebaseUserAuth?.uid ?? '';
final w_fl = appState?.user?.w_fl ?? '';
final firstName = appState?.user?.firstName ?? '';
final lastName = appState?.user?.lastName ?? '';
final number = appState?.user?.number ?? '';
DocumentReference ds = Firestore.instance
.collection("confirmed_c_rides2")
.document(number)
.collection('1')
.document(formattedDate);
Map<String, dynamic> data = {
//"Status": firstName + " $lastName",
"carImage": "${widget.post.data["carImage"]}",
"driverImage": "${widget.post.data["driverImage"]}",
"experience": "${widget.post.data["experience"]}",
"firstName": "${widget.post.data["driverName"]}",
"gender": "${widget.post.data["gender"]}",
"time": taskVal,
"driverNumber": "${widget.post.data["driverNumber"]}",
//"status": "Waiting..."
"payment":"Complete your payment to confirm"
};
ds.setData(data).whenComplete(() {
print('Task created');
});
}
return Scaffold()
2) check.dart
class CheckRazor extends StatefulWidget {
#override
_CheckRazorState createState() => _CheckRazorState();
}
class _CheckRazorState extends State<CheckRazor> {
Razorpay _razorpay = Razorpay();
var options;
Future payData() async {
try {
_razorpay.open(options);
} catch (e) {
print("errror occured here is ......................./:$e");
}
_razorpay.on(Razorpay.EVENT_PAYMENT_SUCCESS, _handlePaymentSuccess);
_razorpay.on(Razorpay.EVENT_PAYMENT_ERROR, _handlePaymentError);
}
void _handlePaymentSuccess(PaymentSuccessResponse response) async {
print("payment has succedded");
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (BuildContext context) => SuccessPage(
response: response,
),
),
(Route<dynamic> route) => false,
);
_razorpay.clear();
// Do something when payment succeeds
}
void _handlePaymentError(PaymentFailureResponse response) {..............}
#override
void initState() {
// TODO: implement initState
super.initState();
options = {.......}
#override
Widget build(BuildContext context) {
return Scaffold(
RaisedButton(onPressed(){
confirm();
confirm2();}
);
}
What i want is to create data to firebase only on successful payment only.
In above codes...i have just connected two different codes that is data creation(to firebase) and payment .
1) confirm() & confirm2() are responsible for creating data to firebase.
2) _handlePaymentSuccess() is responsible for successful payment.
Please help!!!
Use shared_preferences,
Add in list.dart:
void saveName() {
savedNamePreferences(taskVal).then((_) {});}
Future<bool> savedNamePreferences(String name) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString("name", name);
return prefs.commit();
}
Future<String> getNamePreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String name = prefs.getString("name");
return name;
}
And call getNamePreferences() in another class i.e check.dart

Resources