This expression has a type of 'void' so its value can't be used - Flutter - flutter

import 'package:demo_app/services/api.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AuthProvider extends ChangeNotifier{
bool isAuthenticated = false;
late String token;
late ApiService apiService;
AuthProvider() {
init();
}
Future<void> init() async {
token = await getToken();
if (token.isNotEmpty) {
isAuthenticated = true;
}
apiService = ApiService(token);
notifyListeners();
}
Future<void> register(String name, String email, String password, String passwordConfirm, String deviceName) async{
token = await apiService.register(name, email, password, passwordConfirm, deviceName);
isAuthenticated = true;
setToken();
notifyListeners();
}
Future<void> logIn(String email, String password, String deviceName) async{
token = await apiService.login(email, password, deviceName);
isAuthenticated = true;
setToken();
notifyListeners();
}
Future<void> logOut() async{
token = '';
isAuthenticated = false;
setToken();
notifyListeners();
}
Future<void> setToken() async{
final pref = await SharedPreferences.getInstance();
pref.setString('token', token);
}
Future<void> getToken() async{
final pref = await SharedPreferences.getInstance();
pref.getString('token') ?? '';
}
}
token = await getToken();
gives this error
This expression has a type of 'void' so its value can't be used.
Try checking to see if you're using the correct API; there might be a function or call that returns void you didn't expect. Also check type parameters and variables which might also be void.
Any clue on solving this issue?

Try the following code:
Future<void> init() async {
token = await getToken();
if (token.isNotEmpty) {
isAuthenticated = true;
}
apiService = ApiService(token);
notifyListeners();
}
Future<String> getToken() async {
final pref = await SharedPreferences.getInstance();
final token = pref.getString("token") ?? "";
return token;
}

Related

How can I return two string values in dart flutter

I am working on my flutter project where I want to use sharedpreferences. Look at the code below:
Future<String?> getCredentials() async {
final localStorage = await SharedPreferences.getInstance();
final email = localStorage.getString('email');
final password = localStorage.getString('password');
return email, password;
}
This is my getCredentials funtion I want this function to return email and password as different parameters but dart doesn't allow me can you please help me How can I do it?
Whole SharedPreference Code:
import 'package:shared_preferences/shared_preferences.dart';
class sharedPreference {
Future<String?> saveCredentials({
required String email,
required String password,
}) async {
final localStorage = await SharedPreferences.getInstance();
await localStorage.setString('email', email);
await localStorage.setString('password', password);
}
Future<String?> getCredentials() async {
final localStorage = await SharedPreferences.getInstance();
final email = localStorage.getString('email');
final password = localStorage.getString('password');
return email, password;
}
}
Just create class. You can even add methods to Credentials later. Like secure compare to compare passwordHash with constant time.
class Credentials {
Credentials(this.email, this.passwordHash);
final String email;
final String passwordHash;
}
Future<Credentials> getCredentials() async {
final localStorage = await SharedPreferences.getInstance();
final email = localStorage.getString('email');
final passwordHash = localStorage.getString('passwordHash');
return Credentials(email, passwordHash));
}
Edit use crypto to get hash of password:
import 'dart:convert';
import 'package:crypto/crypto.dart';
String getHash(String plainPassword) {
return sha256.convert(utf8.encode(plainPassword)).toString();
}
change return type String to Map<String,dynamic>
Future<Map<String,dynamic>> getCredentials() async {
final localStorage = await SharedPreferences.getInstance();
final email = localStorage.getString('email');
final password = localStorage.getString('password');
return {
'email':email,
'password':password
};
}

An optimize way for tryAutoLogin function in flutter?

I want to create a function for auto login like Facebook in flutter but don't know the best way to do it.
My function for login and auto login, I used SharedPreferences plugin for store data.
SignIn function:
Future<void> signIn(String userName, String pass) async {
final url = Uri.parse('MyAPI_login');// sorry it for privacy
debugPrint("$userName / $pass");
try {
var respone = await http.post(url, body: {
'user_name': userName,
'password': pass,
'platform': 'mobile',
'device_token': '',
});
final reponseData = jsonDecode(respone.body);
_userName = userName;
_token = reponseData['data']['accessToken'];
_expiryDate = DateTime.now().add(Duration(
seconds: int.parse(reponseData['data']['tokenExpireAt'].toString())));
_refreshToken = reponseData['data']['refreshToken'].toString();
_timerRefreshToken =
int.parse(reponseData['data']['refreshTokenExpireAt'].toString());
// debugPrint(
// '$_token \n $_expiryDate \n $_refreshToken \n $_timerRefreshToken');
notifyListeners();
final prefs = await SharedPreferences.getInstance();
final userData = json.encode({
'_userId': _userName.toString(),
'token': _token.toString(),
'expiryDate': _expiryDate!.toIso8601String(),
'refreshToken': _refreshToken,
'timerRefreshToken': _timerRefreshToken.toString(),
});
await prefs.setString('userData', userData);
} catch (error) {
throw Exception(error.toString());
}}
TryAutoLogin function:
Future<bool> tryAutoLogin() async {
final prefs = await SharedPreferences.getInstance();
if (!prefs.containsKey('userData')) {
return false;
}
final extractedUserData = json
.decode(prefs.getString('userData').toString()) as Map<String, dynamic>;
final expiryDate =
DateTime.parse(extractedUserData['expiryDate'].toString());
if (expiryDate.isBefore(DateTime.now())) {
_token = extractedUserData['refreshToken'].toString();
_expiryDate = DateTime.now().add(
Duration(seconds: int.parse(extractedUserData['timerRefreshToken'])));
_refreshNewToken(extractedUserData['refreshToken'].toString());
}
return true;}
RefreshNewToken function:
Future<void> _refreshNewToken(String oldRefreshToken) async {
final url =
Uri.parse('MyAPI_refreshtoken');
var respone = await http.post(url, body: {'refreshToken': oldRefreshToken});
debugPrint(respone.body);}
My API for login response is like this:
{"data":{"tokenKey":"Authorization","tokenType":"Bearer","accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl9pZCI6ImE1YzkyMTQwLTA3Y2YtMTFlZC1hNDQ2LTYzY2YyNjNiZjllMiIsInVzZXJfaWQiOiJDODAzQ0I3RS1CQTcyLTQ4NjgtQjdEMC05NkRBOUNCREQyMTkiLCJ1c2VyX25hbWUiOiIxMDAyMCIsImZ1bGxfbmFtZSI6IkzDqiBUaOG7iyBMacOqbiIsImlzQWRtaW5pc3RyYXRvciI6MCwidXNlcl9jb21wYW5pZXMiOltdLCJpYXQiOjE2NTgyODIzOTMsImV4cCI6MTY1ODI4NTk5M30.3kMByfweUhzQM-4d5S0G7tUaC0e-nZLJF3_dbdV_7fM","tokenExpireAt":1658285940964,"refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl9pZCI6ImE1YzkyMTQwLTA3Y2YtMTFlZC1hNDQ2LTYzY2YyNjNiZjllMiIsInVzZXJfaWQiOiJDODAzQ0I3RS1CQTcyLTQ4NjgtQjdEMC05NkRBOUNCREQyMTkiLCJ1c2VyX25hbWUiOiIxMDAyMCIsImZ1bGxfbmFtZSI6IkzDqiBUaOG7iyBMacOqbiIsImlzQWRtaW5pc3RyYXRvciI6MCwidXNlcl9jb21wYW5pZXMiOltdLCJpYXQiOjE2NTgyODIzOTMsImV4cCI6MTY1ODM2ODc5M30.Bv7PZrnx9zDzwIuxNMppFxlwZlJEnthVjEYBKYl-aWM","refreshTokenExpireAt":1658368740964},"message":"Logged in successfully!","status":200,"errors":null}
Also, my API has a refresh token request, it returns like this:
{"data":{"tokenKey":"Authorization","tokenType":"Bearer","accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl9pZCI6ImE1ZjQyOGUwLTA3Y2YtMTFlZC1hNDQ2LTYzY2YyNjNiZjllMiIsInVzZXJfaWQiOiJDODAzQ0I3RS1CQTcyLTQ4NjgtQjdEMC05NkRBOUNCREQyMTkiLCJ1c2VyX25hbWUiOiIxMDAyMCIsImZ1bGxfbmFtZSI6IkzDqiBUaOG7iyBMacOqbiIsImlzQWRtaW5pc3RyYXRvciI6MCwidXNlcl9jb21wYW5pZXMiOltdLCJpYXQiOjE2NTgyODIzOTQsImV4cCI6MTY1ODI4NTk5NH0.wcyouoprMHFnRD4_oSpP9RSasxMBrktX6nZI2x2PQec","tokenExpireAt":1658285940242,"refreshToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbl9pZCI6ImE1ZjQyOGUwLTA3Y2YtMTFlZC1hNDQ2LTYzY2YyNjNiZjllMiIsInVzZXJfaWQiOiJDODAzQ0I3RS1CQTcyLTQ4NjgtQjdEMC05NkRBOUNCREQyMTkiLCJ1c2VyX25hbWUiOiIxMDAyMCIsImZ1bGxfbmFtZSI6IkzDqiBUaOG7iyBMacOqbiIsImlzQWRtaW5pc3RyYXRvciI6MCwidXNlcl9jb21wYW5pZXMiOltdLCJpYXQiOjE2NTgyODIzOTQsImV4cCI6MTY1ODM2ODc5NH0.y-8MP4M_1LzCwmqo_KQZGyQXkycrxdOLWz_fdqIPRyQ","refreshTokenExpireAt":1658368740242},"message":"Request successfully!","status":200,"errors":null}

how to initialize sharedPreferences flutter?

In order to Authenticate to Api, I created AuthProvider class to authenticate, after getting accessToken from the Api,I stored the token using sharedPreferences,I wanted to check if the user loggedIn or not, so I initialized sharedPreferences to return a boolean value if it contains the token... as shown in the code bellow:
class AuthProvider with ChangeNotifier{
bool _isLoading = false;
bool get isLoading => _isLoading;
User user = User();
late SharedPreferences prefs ;
String token = '';
Map<String, String> _mainHeaders = {
'Content-Type': 'application/json; charset=UTF-8',
};
void updateHeader(String token) {
_mainHeaders = {
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer $token',
};
}
Future<ResponseModel> login(String username, String password) async {
print("Getting token");
//print(authRepo.getUserToken().toString());
_isLoading = true;
notifyListeners();
http.Response response = await dologin(username, password);
// print('${response.body.}');
var answer = User.fromJson(jsonDecode(response.body));
print('the result is : ${answer.token}');
print('level 1: ${response.body.toString()}');
late ResponseModel responseModel;
print(response.statusCode);
if (response.statusCode == 200) {
//authRepo.saveUserToken((response.body["token"]).toString());
print("Backend token: ${response.body.toString()}");
responseModel = ResponseModel(true, answer.token!);
// SharedPreferences prefs = await SharedPreferences.getInstance();
// prefs.setString(ApiConstants.kEY_ACCESS_TOKEN, answer.token!);
storeToken(answer.token!);
} else {
responseModel = ResponseModel(false, response.reasonPhrase!);
}
_isLoading = false;
notifyListeners();
return responseModel;
}
bool userLoggedIn() =>
prefs.containsKey(ApiConstants.kEY_ACCESS_TOKEN) ? true : false;
// bool clearSharedData() => authRepo.clearSharedData();
void storeToken(String token) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString(ApiConstants.kEY_ACCESS_TOKEN, token);
print('the token is here : ${token}');
}
...
in main.dart I created a consumer to check if the user is loggedIn or not :
child: MaterialApp(
home: Consumer<AuthProvider>(
builder: ((context, auth, _){
return auth.userLoggedIn() ? const HomePage() : const loginScreen();
})
),
...
when I hit run I got the following error in my console:
The following LateError was thrown building Consumer(dirty, dependencies: [_InheritedProviderScope<AuthProvider?>]):
LateInitializationError: Field 'prefs' has not been initialized.
The relevant error-causing widget was
Consumer.
PS : I tried to initialize sharedPreferences : SharedPreferences? prefs;
the error would be:
Null check operator used on a null value.
...
You're getting the error because you've marked your prefs as late but you haven't initialised it (as written in the error message). Just get rid of the line late SharedPreferences prefs; at the top and ensure that you initialise it each time you need it within the method. I.e. call await SharedPreferences.getInstance(); each time.
Future<bool> userLoggedIn() async{
prefs = await SharedPreferences.getInstance();
prefs.containsKey(ApiConstants.kEY_ACCESS_TOKEN) ? true : false;
}
Change userLoggedIn getter to this.
You can call a function from init state and check the sharedprefrences in it like
late SharedPreferences prefs;
//initialize shared prefrences then access it
void initState() {
super.initState();
sharedData();
}
void sharedData() async {
prefs = await SharedPreferences.getInstance();
}
void storeToken(String token) async {
prefs = await SharedPreferences.getInstance();
prefs.setString(ApiConstants.kEY_ACCESS_TOKEN, token);
print('the token is here : ${token}');
}

Flutter shared preference code optimization suggestion?

I am using the shared_preferences package. https://pub.dev/packages/shared_preferences/example
In my repository class, for each function, I am doing this to get the instance.
SharedPreferences prefs = await SharedPreferences.getInstance();
class AuthenticationRepository {
Future<dynamic> logIn({required String email, required String password}) async {
SharedPreferences prefs = await SharedPreferences.getInstance(); <--------
....
prefs.clear();
prefs.setString('user', encodedUser);
}
Future<String> logOut() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); <---------
prefs.clear();
if(prefs.containsKey('user')){
return 'failed';
}else{
return 'cleared';
}
}
}
I am just wondering if this is initiating a new sharedPreference object or as the function implies, we are only getting the same instance?
Is there a better way to create the instance once, maybe as a class variable like below?
class AuthenticationRepository {
SharedPreferences prefs = await SharedPreferences.getInstance();
Future<dynamic> logIn({required String email, required String password}) async {
....
this.prefs.clear();
prefs.setString('user', encodedUser);
}
Future<String> logOut() async {
this.prefs.clear();
if(prefs.containsKey('user')){
return 'failed';
}else{
return 'cleared';
}
}
}
Please advice, thanks in advance :)
Yes, you can get the same instance. In the shared_preference.dart file, there is a static value _completer. Here is getInstance() function. You can see the if (_completer == null), and it immediately returns a value when the _completer had been initialized.
static Completer<SharedPreferences>? _completer;
...
static Future<SharedPreferences> getInstance() async {
if (_completer == null) {
final completer = Completer<SharedPreferences>();
try {
final Map<String, Object> preferencesMap =
await _getSharedPreferencesMap();
completer.complete(SharedPreferences._(preferencesMap));
} on Exception catch (e) {
// If there's an error, explicitly return the future with an error.
// then set the completer to null so we can retry.
completer.completeError(e);
final Future<SharedPreferences> sharedPrefsFuture = completer.future;
_completer = null;
return sharedPrefsFuture;
}
_completer = completer;
}
return _completer!.future;
}
I think it is a better way to use the getInstance() function not to create another class.

Access other Class method in Flutter/dart

I was working on login with preference. Everything is working fine when I wrote all code in main.dart.
Problem:
When I create separate class on MySharePref then I am getting some error.
MySharePref.dart
import 'package:first_app/UserModel.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SharePrefClass {
void _saveData(UserModel model) async{
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString("Username",model.userName);
await prefs.setString("Password", model.password);
}
Future<UserModel> _getData() async{
SharedPreferences preferences = await SharedPreferences.getInstance();
String username = preferences.getString("Username");
String password = preferences.getString("Password");
UserModel model = UserModel(username,password);
return model;
}
}
I want to access these both functions in main.dart:
_checkLogin() async {
UserModel userModel = new UserModel(
userNameEditText.text , passwordEditText.text);
SharePrefClass mySharedPref = new SharePrefClass();
final UserModel returnModel = mySharedPref._getData() ;
if(returnModel.userName == ""){
print("No data");
}else{
print("else executed");
}
}
I am getting error:
The prefix "_" means private field in dart.
Change the method name _getData() to getData() will let you can access this method in main.dart