Flutter Firebase Messaging Deactivate Visible Notification - firebase

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);
}

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();
});
},
);
},
);
}

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

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),
),
);
}
}
}

Flutter : Dynamic Link not launching the app

I am using firebase dynamic links to open the email verification link in my app, but unfortunetly the link doesn't launch the app when tapped.
What I've done so far
When a new user is created, a link is sent by email to be verified :
if(firebaseUser != null && !firebaseUser.emailVerified){
await createUserInDatabaseIfNew(firebaseUser);
var actionCodeSettings = auth.ActionCodeSettings(
url: 'https://muslimcoloc.page.link/?email=${firebaseUser.email}',
dynamicLinkDomain: "muslimcoloc.page.link",
androidInstallApp: true,
androidMinimumVersion: "12",
androidPackageName: "com.app.muslim_coloc",
iOSBundleId: "com.muslim_coloc.ios",
handleCodeInApp: true,
);
await firebaseUser.sendEmailVerification(actionCodeSettings);
}
I got the dynamicLinkDomain in the firebase console :
Then, I handle the reception of the link in my main.dart file, with the firebase dynamic links package :
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(
MyApp(),
);
}
class MyApp extends StatelessWidget {
MyApp({Key key, }) : super(key: key);
#override
Widget build(BuildContext context) {
return AppView();
}
}
class AppView extends StatefulWidget {
const AppView({
Key key,
}) : super(key: key);
#override
_AppViewState createState() => _AppViewState();
}
class _AppViewState extends State<AppView> with WidgetsBindingObserver {
#override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
this.initDynamicLinks();
}
}
void initDynamicLinks() async {
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLink) async {
final Uri deepLink = dynamicLink?.link;
FirebaseAuth auth = FirebaseAuth.instance;
//Get actionCode from the dynamicLink
var actionCode = deepLink.queryParameters['oobCode'];
try {
await auth.checkActionCode(actionCode);
await auth.applyActionCode(actionCode);
// If successful, reload the user:
auth.currentUser.reload();
} on FirebaseAuthException catch (e) {
if (e.code == 'invalid-action-code') {
print('The code is invalid.');
}
}
if (deepLink != null) {
Navigator.pushNamed(context, deepLink.path);
}
},
onError: (OnLinkErrorException e) async {
print('onLinkError');
print(e.message);
}
);
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
final Uri deepLink = data?.link;
if (deepLink != null) {
Navigator.pushNamed(context, deepLink.path);
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(...)
}
When I tap the link of the email, the app doesn't start nor does the browser. Here's what happens :
It tries to launch something on the browser, but then comes back to gmail.
However if I click on the link in a desktop browser, it works fine, the email is validated.
I'm having a hard time understanding what it going on. Is there something wrong about how I did things ?
You should write a function to handle your dynamic links, as per the documentation, and this is working for me in an app being used currently:
void handleDynamicLinks() async {
///To bring INTO FOREGROUND FROM DYNAMIC LINK.
FirebaseDynamicLinks.instance.onLink(
onSuccess: (PendingDynamicLinkData dynamicLinkData) async {
await _handleDeepLink(dynamicLinkData);
},
onError: (OnLinkErrorException e) async {
print('DynamicLink Failed: ${e.message}');
return e.message;
},
);
final PendingDynamicLinkData data =
await FirebaseDynamicLinks.instance.getInitialLink();
_handleDeepLink(data);
}
// bool _deeplink = true;
_handleDeepLink(PendingDynamicLinkData data) async {
final Uri? deeplink = data.link;
if (deeplink != null) {
print('Handling Deep Link | deepLink: $deeplink');
}
}
and in initState:
#override
void initState() {
handleDynamicLinks();
super.initState();
}
write this logic in your home page. Not in void(main..etc)
But in your first widget after that, and it should work.
Also, be sure to double check your package name, i.e com.example.yourAwesomeApp123, it's what lets the whole system know what app is to be opened when the dynamic link is pressed.

flutter firebase auto refresh user session with refreshToken

I want user in my app to stay logged in. I'm using the firebase authentification with IDToken which lasts for 1hour until it expires. I want to auto refresh the session everytime if it is going to expire.
what Ive read so far here https://firebase.google.com/docs/reference/rest/auth/#section-refresh-token it should be somehow possible with https://securetoken.googleapis.com/v1/token?key=[API_KEY]
This is my full code for authentification right now (flutter)
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import '../provider/http_exception.dart';
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
class Auth with ChangeNotifier {
String _token;
DateTime _expiryDate;
String _userId;
Timer _authTimer;
bool wasLoggedOut = false;
bool onBoarding = false;
Future<void> createUser(String email, String firstName, String lastName) async {
final url = 'https://test45.firebaseio.com/users/$userId.json?auth=$token';
final response = await http.put(url, body: json.encode({
'userEmail': email,
'userIsArtist': false,
'userFirstName': firstName,
'userLastName': lastName,
}));
print('post ist done');
print(json.decode(response.body));
}
bool get isAuth {
return token != null;
}
String get userId {
return _userId;
}
String get token {
if (_expiryDate != null &&
_expiryDate.isAfter(DateTime.now()) &&
_token != null) {
return _token;
}
return null;
}
Future<void> authenticate(
String email, String password, String urlSegement) async {
final url = 'https://identitytoolkit.googleapis.com/v1/accounts:$urlSegement?key=AIzaSyD8pb3M325252dfsDC-4535dfd';
try {
final response = await http.post(url,
body: json.encode({
'email': email,
'password': password,
'returnSecureToken': true,
}));
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['idToken'];
_userId = responseData['localId'];
_expiryDate = DateTime.now().add(Duration(seconds: int.parse(responseData['expiresIn'])));
_autoLogout();
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'token': _token,
'userId': _userId,
'expiryDate': _expiryDate.toIso8601String(),
});
prefs.setString('userData', userData);
} catch (error) {
throw error;
}
}
Future<void> signup(String email, String password) async {
return authenticate(email, password, 'signUp');
}
Future<void> signin(String email, String password) async {
return authenticate(email, password, 'signInWithPassword');
}
Future<bool> tryAutoLogin() async {
final prefs = await SharedPreferences.getInstance();
if(!prefs.containsKey('userData')){
return false;
}
final extractedUserData = json.decode(prefs.getString('userData')) as Map<String, Object>;
final expiryDate = DateTime.parse(extractedUserData['expiryDate']);
if(expiryDate.isBefore(DateTime.now())) {
return false;
}
_token = extractedUserData['token'];
_userId = extractedUserData['userId'];
_expiryDate = expiryDate;
notifyListeners();
_autoLogout();
return true;
}
Future<void> logout() async {
_token = null;
_userId = null;
_expiryDate = null;
if(_authTimer != null){
_authTimer.cancel();
_authTimer = null;
}
notifyListeners();
final prefs = await SharedPreferences.getInstance();
prefs.remove('userData');
}
void _autoLogout() {
if(_authTimer != null) {
_authTimer.cancel();
}
final timetoExpiry = _expiryDate.difference(DateTime.now()).inSeconds;
_authTimer = Timer(Duration(seconds: timetoExpiry), logout);
}
}
how to modify my auth.dart to achieve the auto refreshing?
EDIT:
As mentioned in the comments, im working with providers where I have the following functions to retrieve the token:
update(String token, id, List<items> itemsList) {
authToken = token;
userId = id;
}
also in every of my API calls im using the auth parameter already:
var url = 'https://test45.firebaseio.com/folder/$inside/$ym.json?auth=$authToken';
I just need somebody who can show me how to modify my code with the refresh token.
Thanks in advance!
EDIT:
I tried to implement it, but im getting an infinite loop, please help:
String get token {
if (_expiryDate != null &&
_expiryDate.isAfter(DateTime.now()) &&
_token != null) {
return _token;
}
refreshSession();
}
Future<void> refreshSession() async {
final url = 'https://securetoken.googleapis.com/v1/token?key=5437fdjskfsdk38438?grant_type=refresh_token?auth=$token';
try {
final response = await http.post(url,
body: json.encode({
'token_type': 'Bearer',
}));
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['id_token'];
_userId = responseData['user_id'];
_expiryDate = DateTime.now().add(Duration(seconds: int.parse(responseData['expires_in'])));
_autoLogout();
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'token': _token,
'userId': _userId,
'expiryDate': _expiryDate.toIso8601String(),
});
prefs.setString('userData', userData);
} catch (error) {
throw error;
}
}
I edited your refresh_token() function.
Firstly, you should use your web api key on your firebase project with the link. You should also save the refresh token. And if you post like this, it will work. If don't work, try without json.encode() function on your body as I commit.
Future<void> refreshSession() async {
final url =
'https://securetoken.googleapis.com/v1/token?key=$WEB_API_KEY';
//$WEB_API_KEY=> You should write your web api key on your firebase project.
try {
final response = await http.post(
url,
headers: {
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded"
},
body: json.encode({
'grant_type': 'refresh_token',
'refresh_token': '[REFRESH_TOKEN]', // Your refresh token.
}),
// Or try without json.encode.
// Like this:
// body: {
// 'grant_type': 'refresh_token',
// 'refresh_token': '[REFRESH_TOKEN]',
// },
);
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['id_token'];
_refresh_token = responseData['refresh_token']; // Also save your refresh token
_userId = responseData['user_id'];
_expiryDate = DateTime.now()
.add(Duration(seconds: int.parse(responseData['expires_in'])));
_autoLogout();
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'token': _token,
'refresh_token': _refresh_token,
'userId': _userId,
'expiryDate': _expiryDate.toIso8601String(),
});
prefs.setString('userData', userData);
} catch (error) {
throw error;
}
}
This is your full auth.dart file which I edited.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import '../provider/http_exception.dart';
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
class Auth with ChangeNotifier {
String _token;
String _refresh_token;
DateTime _expiryDate;
String _userId;
Timer _authTimer;
bool wasLoggedOut = false;
bool onBoarding = false;
Future<void> createUser(String email, String firstName, String lastName) async {
final url = 'https://test45.firebaseio.com/users/$userId.json?auth=$token';
final response = await http.put(url, body: json.encode({
'userEmail': email,
'userIsArtist': false,
'userFirstName': firstName,
'userLastName': lastName,
}));
print('post ist done');
print(json.decode(response.body));
}
bool get isAuth {
return token != null;
}
String get userId {
return _userId;
}
String get token {
if (_expiryDate != null &&
_expiryDate.isAfter(DateTime.now()) &&
_token != null && _refresh_token!=null) {
return _token;
}
refreshSession();
return null;
}
Future<void> authenticate(
String email, String password, String urlSegement) async {
final url = 'https://identitytoolkit.googleapis.com/v1/accounts:$urlSegement?key=AIzaSyD8pb3M325252dfsDC-4535dfd';
try {
final response = await http.post(url,
body: json.encode({
'email': email,
'password': password,
'returnSecureToken': true,
}));
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['idToken'];
_refresh_token = responseData['refreshToken'];
_userId = responseData['localId'];
_expiryDate = DateTime.now().add(Duration(seconds: int.parse(responseData['expiresIn'])));
_autoLogout();
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'token': _token,
'refresh_token': _refresh_token,
'userId': _userId,
'expiryDate': _expiryDate.toIso8601String(),
});
prefs.setString('userData', userData);
} catch (error) {
throw error;
}
}
Future<void> signup(String email, String password) async {
return authenticate(email, password, 'signUp');
}
Future<void> signin(String email, String password) async {
return authenticate(email, password, 'signInWithPassword');
}
Future<bool> tryAutoLogin() async {
final prefs = await SharedPreferences.getInstance();
if(!prefs.containsKey('userData')){
return false;
}
final extractedUserData = json.decode(prefs.getString('userData')) as Map<String, Object>;
final expiryDate = DateTime.parse(extractedUserData['expiryDate']);
if(expiryDate.isBefore(DateTime.now())) {
return false;
}
_token = extractedUserData['token'];
_refresh_token = extractedUserData['refresh_token'];
_userId = extractedUserData['userId'];
_expiryDate = expiryDate;
notifyListeners();
_autoLogout();
return true;
}
Future<void> logout() async {
_token = null;
_refresh_token = null;
_userId = null;
_expiryDate = null;
if(_authTimer != null){
_authTimer.cancel();
_authTimer = null;
}
notifyListeners();
final prefs = await SharedPreferences.getInstance();
prefs.remove('userData');
}
void _autoLogout() {
if(_authTimer != null) {
_authTimer.cancel();
}
final timetoExpiry = _expiryDate.difference(DateTime.now()).inSeconds;
_authTimer = Timer(Duration(seconds: timetoExpiry), logout);
}
Future<void> refreshSession() async {
final url =
'https://securetoken.googleapis.com/v1/token?key=$WEB_API_KEY';
//$WEB_API_KEY=> You should write your web api key on your firebase project.
try {
final response = await http.post(
url,
headers: {
"Accept": "application/json",
"Content-Type": "application/x-www-form-urlencoded"
},
body: json.encode({
'grant_type': 'refresh_token',
'refresh_token': '[REFRESH_TOKEN]', // Your refresh token.
}),
// Or try without json.encode.
// Like this:
// body: {
// 'grant_type': 'refresh_token',
// 'refresh_token': '[REFRESH_TOKEN]',
// },
);
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['id_token'];
_refresh_token = responseData['refresh_token']; // Also save your refresh token
_userId = responseData['user_id'];
_expiryDate = DateTime.now()
.add(Duration(seconds: int.parse(responseData['expires_in'])));
_autoLogout();
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'token': _token,
'refresh_token': _refresh_token,
'userId': _userId,
'expiryDate': _expiryDate.toIso8601String(),
});
prefs.setString('userData', userData);
} catch (error) {
throw error;
}
}
}
You need to save the refresh token.
Follow this topic to refresh your IDToken using the refresh token: https://firebase.google.com/docs/reference/rest/auth#section-refresh-token
When making any calls to the API, use a function to retrieve the IDToken. This function must check if the current IDToken is still valid and, if not, ask for a new one (using the link provided).
I think the Dio library is right for you
dio = Dio();
dio.options.baseUrl = URL_API_PROD;
dio.interceptors.add(InterceptorsWrapper(
onRequest: (Options option) async{
//getToken() : you can check token expires and renew in this function
await getToken().then((result) {
token = result;
});
option.headers = {
"Authorization": "Bearer $token"
};
}
));
Response response = await dio.get('/api/users');
body expects string...Hence change body in refreshSession() to body: 'grant_type=refresh_token&refresh_token=[YOUR REFRESH TOKEN]',.
You need to load 'refreshToken' from SharedPreferences before sending http.post request.

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