Flutter : Flutter Shared Preference don't work ,not save username Edit#1 - flutter

I have an app contain login and register system ,the system is working correctly.
the problem is I want to keep user logged in by using flutter_session package but don't work.
first I have preloading page contain checking if user is logged in by using functions below :
void gotoTabPae() {
print('ok');
Future.delayed(const Duration(milliseconds: 3000), () {
Navigator.of(context).pushReplacementNamed('tabs');
});
}
void gotoLoginPage() {
print('no');
Future.delayed(const Duration(milliseconds: 3000), () {
Navigator.of(context).pushReplacementNamed('login');
});
}
getuser() async {
var loginedUser;
SharedPreferences preferences = await SharedPreferences.getInstance();
setState(() {
loginedUser= preferences.getString('username');
});
loginedUser != null ? gotoTabPae() : gotoLoginPage();
}
#override
void initState() {
getuser();
super.initState();
}
when I run the app and login then when I restart the app it must go to 'tabs' page ,but the value of username is always null therefore it load login page ,the login function is :
login() async {
var formdata = formLoginKey.currentState;
if (formdata.validate()) {
formdata.save();
var data = {'username': username.text, 'password': password.text};
var url =xxxx/api/controller/users/login_user.php";
var response = await http.post(url, body: data);
var responsebody = jsonDecode(response.body);
if (responsebody['status'] == 'success') {
SharedPreferences pref = await SharedPreferences.getInstance();
pref.setString('username', username.text);
Navigator.of(context).pushReplacementNamed('tabs');
} else {
_showDialog(context, responsebody['status']);
}
} else {
}
}
But in tabs page it is load the session username corret :
getuser() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
var logineduser = preferences.getString('username');
}
#override
void initState() {
getuser();
super.initState();
}
How can I solve this? where is my mistake ?

Your code is running the getuser() method in the initSate() method that too even before the super.initState();. That is the reason the value is no able to load which makes it null. You should use it in the build function.
Your code might look like this:
#override
Widget build(BuildContext context) {
var loginedUser;
loginedUser = await FlutterSession().get('username');
loginedUser != null ? return ClassName()(tabs.dart) : return ClassName()(login.dart);
}

I think that in your login function, pref.setString('username', username.text) is run before the response is received. Can you try this:
http.post(url, body: data).then((response) {
var responsebody = jsonDecode(response.body);
if (responsebody['status'] == 'success') {
SharedPreferences pref = await SharedPreferences.getInstance();
pref.setString('username', username.text);
Navigator.of(context).pushReplacementNamed('tabs');
}
});
and let me know the result?

Related

Not able to see sharedpref folder in phone as well Getting this error :Exception has occurred. _CastError (Null check operator used on a null value)

Hello Guys I am new to flutter and working on a flutter project. Below is the code of my splash screen. What I am doing is when the app launched we get the data from sharedpreference if we got the data we attempt to login from the data if it's successfull then whe move to homescreen else if there is no data or attempt was failed due tou any reason we move to home screen. Right now I haven't added the check for if data is empty so ignore it. The error I am getting in getData it states that Exception has occurred. _CastError (Null check operator used on a null value)
Here is the code:
String password = '';
String email = '';
void getData() async {
email = (await sharedPreference().getCred('email'))!;
password = (await sharedPreference().getCred('password'))!;
setState(() {});
}
#override
void initState() {
super.initState();
sharedPreference().checkValuePresent('email');
sharedPreference().checkValuePresent('password');
getData();
print('Email: $email\nPassword $password');
print('inside initstate');
try {
firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
} on FirebaseAuthException catch (errMsg) {
if (errMsg.code == 'user-not-found' ||
errMsg.code == 'wrong-password' ||
errMsg.code == 'Email format is not valid') {
print('inside if: $errMsg');
sharedPreference().reset();
Timer(const Duration(seconds: 3), () {
/*Move to Login*/
});
} else {
/*Move to HomeScreen*/
}
}
}
This is the code for sharedPreference().getCred
Future<String?> getCred(String email) async {
try {
SharedPreferences prefs = await SharedPreferences.getInstance();
final result = prefs.getString('email');
return result;
} catch (e) {
return 'Error Fetching Data';
}
}
Here is the code of whole sharedPreference Class
import 'package:shared_preferences/shared_preferences.dart';
class sharedPreference {
sharedPrefInit() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
}
checkValuePresent(key) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool CheckValue = prefs.containsKey('$key');
print('printing from: (bool)$CheckValue');
}
saveCred({required String email, required String password}) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('email', email);
prefs.setString('password', password);
}
Future<String?> getCred(String email) async {
try {
SharedPreferences prefs = await SharedPreferences.getInstance();
final result = prefs.getString('email');
return result;
} catch (e) {
return 'Error Fetching Data';
}
}
reset() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.remove('email');
prefs.remove('password');
}
}
Secondly When I run the app I can't see my sharedpreference folder in the file explorer. I don't know that I have to create it? If yes the How? I initialize the sharedPreference in the main function.
Here is the code:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
await sharedPreference().sharedPrefInit();
runApp(MaterialApp(
theme: ThemeData(
primarySwatch: mycolor,
fontFamily: 'Raleway',
),
debugShowCheckedModeBanner: false,
initialRoute: 'AppSplashScreen',
routes: {
'AppSplashScreen': (context) => const AppSplashScreen(),
}));
}
Please name classes with capital case e.g. SharedPreference and use ! operator only if you are sure that the value you get is non-nullable, otherwise use null aware operators:
Future<void> getData() async {
email = (await sharedPreference().getCred('email')) ?? '';
password = (await sharedPreference().getCred('password')) ?? '';
}
In initState() firebaseAuth.signInWithEmailAndPassword() is called before asynchronous function getData() is executed, so put it inside getData() as well:
Future<void> getData() async {
email = (await sharedPreference().getCred('email')) ?? '';
password = (await sharedPreference().getCred('password')) ?? '';
print('Email: $email\nPassword $password');
print('inside initstate');
try {
firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
} on FirebaseAuthException catch (errMsg) {
if (errMsg.code == 'user-not-found' ||
errMsg.code == 'wrong-password' ||
errMsg.code == 'Email format is not valid') {
print('inside if: $errMsg');
sharedPreference().reset();
Timer(const Duration(seconds: 3), () {
/*Move to Login*/
});
} else {
/*Move to HomeScreen*/
}
}
}
#override
void initState() {
super.initState();
sharedPreference().checkValuePresent('email');
sharedPreference().checkValuePresent('password');
getData();
}

Flutter Try correcting the name to one that is defined, or defining the name

I made a function to post some data using http.post, when using the function i pass as parameter the id, now im using this function in other places but it says that it has 1 positional argument but 0 found.
Future<Response?> postLocationId(String id) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String? authorization = prefs.getString('authorization');
var url = 'https://dev.api.wurk.skyver.co/api/$id/workingTimeframes';
try {
response = await http.post(
Uri.parse(url),
headers: <String, String>{
'authorization': authorization ?? basicAuth.toString(),
"Content-Type": "application/json"
},
);
} catch (er) {}
return response;
}
void initState() {
super.initState();
futureData = fetchWorkingLocationData();
futureDataForAccount = fetchAccountData();
futureDataForStatus = postLocationId(id); <<<< getting error here
}
ElevatedButton(
onPressed: () async {
final WorkingLocationData locationData =
workingData!.firstWhere(
(some) => some.id ==
chooseLocation);
await postLocationId(locationData.id); <<<
The main problem is because you are passed a parameter (id == null) that doesn’t exist before initState(),
If you try and change the id word for a '1' string, this work, but doesnt the better solution.
You need to modify your logic in your initState() because you pass an argument that is null you have a lot of options:
void initState() {
super.initState();
getLocationData():
}
void getLocationData() async {
final locationData = {...} // This return location data information
postLocationId(locationData.id)
}
or You can omit the end line in your initState()
void initState() {
super.initState();
futureData = fetchWorkingLocationData();
futureDataForAccount = fetchAccountData();
// futureDataForStatus = postLocationId(id);
}
Edit your logic inside your onPressed function to this:
final locationData =
workingData!.firstWhere((some) => some.id == chooseLocation);
if (locationData != null) {
await postLocationId(locationData.id);
setState(() {
_flag = !_flag;
});
} else {
print('locationData is null);
}

Display Loading spinner waitint for request to complete while using provider package

I am using a provider package. I want to display a loading spinner while waiting for a request to complete. The pattern below is too verbose. Please help me make it less verbose. Here is my code
class APIService with ChangeNotifier {
// Check for working API backend
bool isWorking = false;
bool isLoading = false;
set _isLoading(bool value) {
isLoading = value; <--
notifyListeners();
}
Future<bool> selectAPI(String input) async {
_isLoading = true; <-- 1
final uri = Uri.tryParse('https://$input$url')!;
final response = await http.get(uri);
if (response.statusCode == 200) {
final body = jsonDecode(response.body) as Map<String, dynamic>;
bool isTrue = body['info']['title'] == 'SamFetch';
_isLoading = false; <-- 2
notifyListeners();
return isWorking = isTrue;
}
_isLoading = false; <-- 3
throw response;
}
}
Here is my UI code
IconButton(
icon: apiService.isLoading
? CircularProgressIndicator()
: Icon(Icons.done),
onPressed: () async {
await addAPI(apiService, cache);
}),
}
Below is addAPI() method
Future<void> addAPI(APIService apiService, Cache cache) async {
if (api != null) {
try {
await apiService.selectAPI(api!);
if (apiService.isWorking) {
await cache.saveAppName(api!);
}
} on SocketException catch (e) {
print(e);
} catch (e) {
await cache.clearCache();
}
}
}
Is setState the final solution?
You can use Future Builder and set your Future Function in future attribute. You can control the visible widget based on the status of your function. So you dont have to use isloading variable.

Change bool in initState flutter

I have a page with this code:
class _HomeScreenState extends State<HomeScreen> {
bool isFirstLoading = true;
#override
void initState() {
super.initState();
if (isFirstLoading) {
getInfo();
setState(() {
isFirstLoading = false;
});
} else {
getInfoFromSharedPref();
}
}
Future<http.Response> getInfo() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
Loader.show(context,
isAppbarOverlay: true,
isBottomBarOverlay: true,
progressIndicator: CircularProgressIndicator());
var url = kLinkAPI + "/getInfo";
var response =
await http.post(url, headers: {"Content-Type": "application/json"});
var resObj = jsonDecode(response.body);
if (response != null) {
setState(() {
if (resObj.length > 0) {
address = resObj[0]['address'];
countryInfo = resObj[0]['country_info'];
phone = resObj[0]['phone'];
latitude = resObj[0]['latitude'];
longitude = resObj[0]['longitude'];
isFirstLoading = false;
prefs.setString('address', address);
prefs.setString('countryInfo', countryInfo);
prefs.setString('phone', phone);
prefs.setString('latitude', latitude);
prefs.setString('longitude', longitude);
}
});
}
Loader.hide();
}
void getInfoFromSharedPref() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
address = prefs.getString('address');
countryInfo = prefs.getString('countryInfo');
phone = prefs.getString('phone');
latitude = prefs.getString('latitude');
longitude = prefs.getString('longitude');
});
}
}
I would like to make sure that the first time I enter the page, the isFirstLoading variable is set to false and then calls the getInfo function with the http call while if it is false it takes from the shared preferences.
isFirstLoading is now always true
how could I solve?
I think you're overcomplicating your code. Let me know if this solves your issue.:
class _HomeScreenState extends State<HomeScreen> {
SharedPreferences prefs;
#override
void initState() {
super.initState();
getInfo();
}
// ...
}
Now, the first time this widget is inserted into the tree:
initState() will be called once.
Therefore, getInfo() will be called. getInfo() will make the http call and update the prefs variable using setState, which you have already done.
Whenever the widget is reloaded, the prefs variable will not be lost since it is a stateful widget.
Next, if you would like to save the preference settings locally instead of making an http call every time the user opens the app, you should handle that inside of getInfo() itself. Something like this:
getInfo() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if (prefs.getBool("isFirstLoading") == false) {
// setState to update prefs variable
} else {
// make http call
// save prefs (optional)
// setState to update prefs variable
}
}
If I undestand correctly, you are trying to only call the getInfo method on the first load, and the getInfoFromSharedPref all the other time.
My suggestion is to save the isFirstLoading bool as a preference like so:
class _HomeScreenState extends State<HomeScreen> {
SharedPreferences prefs = await SharedPreferences.getInstance();
bool isFirstLoading = prefs.getBool("isFirstLoading") ?? true;
#override
void initState() async {
super.initState();
if (isFirstLoading) {
await getInfo();
await prefs.setBool("isFirstLoading", false);
isFirstLoading = false;
} else {
getInfoFromSharedPref();
}
}
Future<http.Response> getInfo() async {
// …
}
void getInfoFromSharedPref() async {
// …
}
}

Flutter initState wait for async function to complete

in my main.dart i have among others those two functions:
Future<void> _fetchMasterData() async {
print("Start fetch");
var jwt = await API.attemptLogIn();
if (jwt != null) {
Map<String, dynamic> answer = jsonDecode(jwt);
if (answer['message'] == 'Auth ok') {
jwtToken = 'Bearer ' + answer['token'];
}
}
await _getArticles();
await _getMainCategories();
await _getIngredients();
await _getArticleIngredients();
print("EndMasterData fetch");
}
And
#override
void initState() {
super.initState();
_fetchMasterData();
}
What i would like to have is to wait in initState till _fethcMasterData is done bevore Widgert build is called.
Is that possible? Many thanks for any help!
Here how I use an async func in initstate;
builder() async {
favoriteDatabase =
await $FloorFavoriteDatabase.databaseBuilder('favorite_database.db')
.build();
setState(() {
favoriteDao = favoriteDatabase.favoriteDao;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
WidgetsBinding.instance.addPostFrameCallback((_) =>
getNamePreferences().then(updateName));
});
builder();
favoriteDao.findAllMoviesAsStreamW();
favoriteDao.findAllMoviesAsStream();
}
Also you can check this mini article too.
It is not possible to await in initState, so when you finish all loading process then you can call SetState method which populate your widget with actual data.
Second solution could be use of futurebuilder or streambuilder where you want to show data but it is only possible if any methods data is not dependent on each other.
Future<void> _fetchMasterData() async {
print("Start fetch");
var jwt = await API.attemptLogIn();
if (jwt != null) {
Map<String, dynamic> answer = jsonDecode(jwt);
if (answer['message'] == 'Auth ok') {
jwtToken = 'Bearer ' + answer['token'];
}
}
await _getArticles();
await _getMainCategories();
await _getIngredients();
await _getArticleIngredients();
print("EndMasterData fetch");
SetState((){}); // added line
}