Dart null-safety breaks Future<bool> anonymous method? - flutter

Prior to migrating to null safety this worked. This is a user login verification method.
Future<bool> loginValidate() async {
final String dir = await getDocDir(); //getting documents directory
try {
await File('$dir/$_userLoginString.json')
.readAsString()
.then((String contents) {
final json = jsonDecode(contents) as Map<String, dynamic>;
final user = PersonLoginJson.fromJson(json);
if (_userPasswordString != user.password) {
//invalid password
return Future<bool>.value(false);
}
});
} on FileSystemException {
//invalid username
return Future<bool>.value(false);
} catch (e) {
return Future<bool>.value(false);
}
//success
return Future<bool>.value(true);
}
This error occurs when the app is being built.
I believe it has to do with the anonymous function argument within the .then() method.

You need to return the await function and also need to set the true value to make it work:
return await File('$dir/$_userLoginString.json')
.readAsString()
.then((String contents) {
final json = jsonDecode(contents) as Map<String, dynamic>;
final user = PersonLoginJson.fromJson(json);
if (_userPasswordString != user.password) {
//invalid password
return Future<bool>.value(false);
}
//Also need this code as it will return null if not set
return Future<bool>.value(true);
});
BETTER WAY I THINK:
Let's change this code:
await File('$dir/$_userLoginString.json')
.readAsString()
.then((String contents) {
final json = jsonDecode(contents) as Map<String, dynamic>;
final user = PersonLoginJson.fromJson(json);
if (_userPasswordString != user.password) {
//invalid password
return Future<bool>.value(false);
}
});
to this:
String contents = await File('$dir/$_userLoginString.json').readAsString();
final json = jsonDecode(contents) as Map<String, dynamic>;
final user = PersonLoginJson.fromJson(json);
if (_userPasswordString != user.password) {
//invalid password
return Future<bool>.value(false);
}
Then it will be easier to debug your code now.

if (_userPasswordString != user.password) {
//invalid password
return Future<bool>.value(false);
} else {
return Future<bool>.value(true); // <--- here needed
}

Related

My DocumentSnapshot type variable remains null despite the fact that I passed it to my function

I use flutter to retrieve a document from my firestore database. to do this, I create a DocumentSnapshot type variable which will be passed as a parameter to my getClasseName() function to retrieve the documents. Inside my function the document exists, so I assign it to my variable but my variable remains null when I want to use it
this is a snippet of my code
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:ude/model/enseignant.dart';
class DBServices {
static final CollectionReference lyceecol =
FirebaseFirestore.instance.collection('Lycee');
static Future getUser(String? lycee, String? id) async {
try {
final QuerySnapshot<Object?> snapshot = await lyceecol
.doc(lycee)
.collection('Enseignant')
.where('Id', isEqualTo: id)
.get();
final EnseignantM userData = snapshot.docs
.map((QueryDocumentSnapshot<Object?> e) => EnseignantM.fromSnapshot(
e as DocumentSnapshot<Map<String?, dynamic>>))
.single;
return userData;
} catch (e) {
print('ENSEIGNANT NOT FOUND');
print(e);
throw e;
}
}
static Future getClasseName(
String? lycee, String? id, List<String> list) async {
try {
String classeName;
int i = 0;
final snapshot = await lyceecol
.doc(lycee)
.collection('Enseignant')
.doc(id)
.collection('Classes')
.get();
for (var classe in snapshot.docs) {
DocumentReference<Map<String, dynamic>> classeRef =
classe.data()["reference"];
DocumentSnapshot<Map<String, dynamic>>? classRoom;
await DBServices.getClasse(classeRef.path, classRoom);
if (classRoom != null) {
print('CLASSENAME ${classRoom.data()!["Nom"]}');
classeName = classRoom.data()!["Nom"];
list[i] = classeName;
print(list[i]);
i++;
} else {
print('Impossible de charger les classes affilees a cet enseignant');
}
}
} catch (e) {
print(e);
rethrow;
}
}
static Future<void> getClasse(
String? path, DocumentSnapshot<Map<String, dynamic>>? classroom) async {
try {
await FirebaseFirestore.instance.doc(path!).get().then((snapshot) {
if (snapshot.exists) {
classroom = snapshot; //.data()!["Nom"];
} else {
debugPrint("Document not found");
}
});
debugPrint('CLASSROOMMMM: ${classroom!.data()!["Nom"]}');
} catch (e) {
debugPrint('CLASSE Not EXIST');
print(e);
rethrow;
}
}
}
---
/*this is where the problem is*/
static Future getClasseName(
String? lycee, String? id, List<String> list) async {
try {
String classeName;
int i = 0;
final snapshot = await lyceecol
.doc(lycee)
.collection('Enseignant')
.doc(id)
.collection('Classes')
.get();
for (var classe in snapshot.docs) {
DocumentReference<Map<String, dynamic>> classeRef =
classe.data()["reference"];
DocumentSnapshot<Map<String, dynamic>>? classRoom;
await DBServices.getClasse(classeRef.path, classRoom);
if (classRoom != null) {
print('CLASSENAME ${classRoom.data()!["Nom"]}');
classeName = classRoom.data()!["Nom"];
list[i] = classeName;
print(list[i]);
i++;
} else {
print('Impossible de charger les classes affilees a cet enseignant');
}
}
} catch (e) {
print(e);
rethrow;
}
}
static Future<void> getClasse(
String? path, DocumentSnapshot<Map<String, dynamic>>? classroom) async {
try {
await FirebaseFirestore.instance.doc(path!).get().then((snapshot) {
if (snapshot.exists) {
classroom = snapshot; //.data()!["Nom"];
} else {
debugPrint("Document not found");
}
});
debugPrint('CLASSROOMMMM: ${classroom!.data()!["Nom"]}');
} catch (e) {
debugPrint('CLASSE Not EXIST');
print(e);
rethrow;
}
}
I tried not to pass my variable as a parameter and to directly assign the Document returned by the function to it, but it didn't work.
Your help will be invaluable to me

sharedp references Getting Stored Data in Another Flutter Page

I want to get user details such as id, email, username in my HomePage when the user login into the login page. I was able to get the data via this
SPUtil.putString('user', user);
print(user);
Now, the issue is how should I pick it up in another page for usage or display those data. I have a file called sputils.dart where all the code that I used to get the data.
class AuthService {
Future<String> login({
required String username,
required String password,
}) async {
try {
final body = {
'username': username,
'password': password,
};
final response = await http.post(
Uri.parse('$BASE_URL/login'),
headers: {'Content-Type': 'application/json; charset=UTF-8'},
body: jsonEncode(body),
);
if (response.statusCode != 200) {
throw LoginError.unexpected;
}
Map<String, dynamic> data = jsonDecode(response.body);
User loggedInUser = User.fromJson(data['user']);
String user = jsonEncode(loggedInUser);
SPUtil.putString('user', user);
print(user);
return jsonDecode(response.body)['token'];
} on LoginError {
print('login error');
rethrow;
} catch (e) {
print(e);
throw LoginError.unexpected;
}
}
import 'dart:async';
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:synchronized/synchronized.dart';
// SharedPreferences
class SPUtil {
static SPUtil? _singleton;
static SharedPreferences? _prefs;
static final Lock _lock = Lock();
static Future<SPUtil> getInstance() async {
if (_singleton == null) {
await _lock.synchronized(() async {
if (_singleton == null) {
// keep local instance till it is fully initialized.
final singleton = SPUtil._();
await singleton._init();
_singleton = singleton;
}
});
}
return _singleton!;
}
SPUtil._();
Future _init() async {
_prefs = await SharedPreferences.getInstance();
}
// put object
static Future<bool> putObject(String key, Object value) {
return _prefs!.setString(key, json.encode(value));
}
// get string
static String getString(String key, {String defValue = ''}) {
if (_prefs == null) return defValue;
return _prefs!.getString(key) ?? defValue;
}
// put string
static Future<bool> putString(String key, String value) async {
return _prefs!.setString(key, value);
}
// get bool
static bool getBool(String key, {bool defValue = false}) {
if (_prefs == null) return defValue;
return _prefs!.getBool(key) ?? defValue;
}
// put bool
static Future<bool> putBool(String key, {bool value = false}) {
return _prefs!.setBool(key, value);
}
// get int
static int getInt(String key, {int defValue = 0}) {
if (_prefs == null) return defValue;
return _prefs!.getInt(key) ?? defValue;
}
// put int.
static Future<bool> putInt(String key, int value) {
return _prefs!.setInt(key, value);
}
// get double
static double getDouble(String key, {double defValue = 0.0}) {
if (_prefs == null) return defValue;
return _prefs!.getDouble(key) ?? defValue;
}
// put double
static Future<bool> putDouble(String key, double value) {
return _prefs!.setDouble(key, value);
}
// get string list
static List<String> getStringList(String key,
{List<String> defValue = const []}) {
if (_prefs == null) return defValue;
return _prefs!.getStringList(key) ?? defValue;
}
// put string list
static Future<bool> putStringList(String key, List<String> value) {
return _prefs!.setStringList(key, value);
}
// clear
static Future<bool> clear() {
return _prefs!.clear();
}
// clear a string
static Future<bool> clearString(String key) {
return _prefs!.remove(key);
}
//Sp is initialized
static bool isInitialized() {
return _prefs != null;
}
}
To use saved data from another page,
make your data instance to global(static)
save the data inside of static class
If your application is simple and just test something, go to No.1.
Or else, go to No.2.
make class static : https://dev.to/lucianojung/global-variable-access-in-flutter-3ijm
Using getController is easier to make it global : https://pub.dev/packages/get

error: The argument type 'UserModel? Function(User?)' can't be assigned to the parameter type 'UserModel Function(User?)'

I am getting the following error in flutter.
UserModel is a class
class UserModel {
final String uid;
UserModel({this.uid});
}
And the code where this error is coming up is
Stream<UserModel> get user {
return _auth.authStateChanges()
.map(_userFromFirebaseUser);
}
Complete code:
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
UserModel? _userFromFirebaseUser(User? user) {
return user != null ? UserModel(uid: user.uid) : null;
}
Stream<UserModel> get user {
return _auth.authStateChanges()
.map(_userFromFirebaseUser);
}
Future signInAnon() async {
try {
UserCredential result = await _auth.signInAnonymously();
User user = result.user!;
return _userFromFirebaseUser(user);
} catch (e) {
print(e.toString());
return null;
}
}
Future signInWithEmailAndPassword( String email, String password) async {
try {
UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
User user = result.user!;
return _userFromFirebaseUser(user);
} catch(e){
print(e.toString());
return null;
}
}
Future signUpWithEmailAndPassword( String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
User user = result.user!;
return _userFromFirebaseUser(user);
} catch(e){
print(e.toString());
return null;
}
}
Future signOut() async {
try {
return await _auth.signOut();
} catch (e){
print(e.toString());
return null;
}
}
}
This is happening because your _userFromFirebaseUser is defined something like this,
UserModel? _userFromFirebaseUser(User? user) {
So this means that you are saying, your _userFromFirebaseUser might return a UserModel or might return a null.
One way to fix this is to make your getter return Stream<UserModel?> instead of Stream<UserModel>.
Stream<UserModel?> get user {
return _auth.authStateChanges()
.map(_userFromFirebaseUser);
}
Now your getter might return a UserModel or it might return a null.
I had the same problem and I'm glad I found the solution :
UserModel _userFromFirebase (User? user){
return UserModel(uid: user!.uid);
}
Stream<UserModel> get user{
return _auth.authStateChanges().map(_userFromFirebase);
}
and if _userFromFirebase appears error use:
_userFromFirebase(user.user) ;

Flutter & Firebase - Get a specific field from document

I have been trying to get a specific field from a specific document. I need token for toWho. But I always got null. How do I fix this?
Main Code is
Future<String> getUserToken(String toWho) async {
DocumentSnapshot _doc = await FirebaseFirestore.instance.doc("tokens/" + toWho).get();
if (_doc != null) {
Map<String, dynamic> _data = _doc.data();
return _data["token"];
} else {
return null;
}
}
in Repository
Future<bool> sendMessage(
MessageModel sendingMessage, UserModel currentUser) async {
if (appMode == AppMode.DEBUG) {
return true;
} else {
var _writePrcs = await _firestoreDBService.saveMessage(sendingMessage);
if (_writePrcs) {
var _token = "";
if (_userToken.containsKey(sendingMessage.toWho)) {
_token = _userToken[sendingMessage.toWho];
print("Token lokalden geldi.");
} else {
_token = await _firestoreDBService.getUserToken(sendingMessage.toWho);
_userToken[sendingMessage.toWho] = _token;
print("Token veritabanından geldi.");
}
Thanks for your help from now on
Try ...........
Future<String> getUserToken(String toWho) async {
DocumentSnapshot _doc = await
FirebaseFirestore.instance.collection("tokens/groupChatId/message").doc(toWho).get();
if (_doc != null) {
Map<String, dynamic> _data = _doc.data();
return _data["token"];
} else {
return null;
}
}

On Flutter, how to keep login persistent by exchanging refresh token for an id token after 1 hour?

I am developing a Flutter app using Firebase authentication (https://firebase.google.com/docs/reference/rest/auth#section-api-usage).
This is the function that refreshes the token
Future<void> refreshSession() async {
final url =
'https://securetoken.googleapis.com/v1/token?[API-KEY]';
try {
print(_refreshToken);
final response = await http.post(url,
headers: {
"Content-Type": "application/json",
},
body: json.encode(
{"grant_type": "refresh_token", "refresh_token": _refreshToken}));
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
} else {
_token = responseData['id_token'];
_refreshToken =
responseData['refresh_token']; // Also save your refresh token
_userId = responseData['user_id'];
_expiryDate = DateTime.now()
.add(Duration(seconds: int.parse(responseData['expires_in'])));
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'token': _token,
'refresh_token': _refreshToken,
'userId': _userId,
'expiryDate': _expiryDate.toIso8601String(),
});
prefs.setString('userData', userData);
}
} catch (error) {
throw error;
}
}
This getter checks if the user is within the 1 hour expiry window or else it refreshes the session - if neither of these is true, it returns null.
String get token {
if (_expiryDate != null &&
_expiryDate.isAfter(DateTime.now()) &&
_token != null) {
print("Inside the 1hour loop:" + _refreshToken);
return _token;
} else if (_refreshToken != null) {
print("Inside persistent login:" + _refreshToken);
refreshSession();
return _token;
} else {
return null;
}
}
The problem is my getter is not getting into the RefreshSession loop at all. Can someone help me with this?