Flutter function inside shared preferences - flutter

Hi all i want to execute a function on load of a page and i have set this:
void initState(){
super.initState();
getPref();
}
getPref() async {
SharedPreferences myPref = await SharedPreferences
.getInstance();
setState(() {
myId = myPref.getString("keyPrefUserId");
myFullName = myPref.getString("keyPrefDisplayName");
myAvatar = myPref.getString("keyPrefPhotoUrl");
customId = myPref.getString("keyPrefUserId");
isLoading = false;
});
await updateFeedNotificationNumber();
}
updateFeedNotificationNumber() async{
QuerySnapshot eventsQuery = await activityFeedRef
.document(myid)
.collection('feedItems')
.where('isRead', isEqualTo: false)
.getDocuments();
eventsQuery.documents.forEach((DocumentSnapshot doc) {
doc.reference.updateData({'isRead' : true });
});
}
But it changes to true if i press every page.. not only to this one. Any idea about that?

Related

Get multiple Image download URLs from Firebase Storage- I can successfully upload multiple images

I am trying to upload multiple images to firebase storage at once. I can upload successfully but cannot retrieve the download URLs;
Future<void> saveImages() async {
int index = images.length;
setState(() {
isLoading = true;
});
try {
final ref = FirebaseStorage.instance
.ref()
.child('images/${FirebaseAuth.instance.currentUser!.uid}');
images.forEach((element) async {
final file = await element.file;
final refPut = ref.child('$element$index');
await refPut.putFile(
File(file!.path),
);
});
final ref2 = ref.child('${imagePciker.path.split('/')}');
await ref2.putFile(File(imagePciker.path));
final ListResult listData = await ref.listAll();
final data =await ref2.getDownloadURL();
print('Im HERE!=Line 95');
Future.forEach(listData.items, (Reference element) async {
await element.getDownloadURL().then((value) => listUrls.add(value));
print('line 101');
print(listUrls.length);
});
print('Line 104');
await FirebaseFirestore.instance.collection('test').add({
'titleDownloadUrl': data,
'ListOfDownloadUrl': listUrls,
});
print('Line 108');
setState(() {
isLoading = false;
});
} catch (e) {
/* setState(() {
isLoading = false;
}); */
print(e);
}
}
The print statements are to debug. This whole function returns and no errors are thrown.
However in the firebase collection 'test' the URLs are not stored correctly.
On the first/fresh run after restart of the app.
the array of ListOfDownloadUrl is empty
On a hot restart(with the no images saved in firebase storage)
the array of ListOfDownloadUrl has one URL
then on multiple restarts, the amount of URLs saved becomes huge(ie. 4 images uploaded-10 URLs saved).
Please comment if I need to add anything else;
Thanks for any Help
.forEach() is NOT ASYNCHRONOUS - it WILL NOT wait. Use await Promise.all(images.map()) (thus creating an array of promises, which must all resolve).
Solved!
Following the thinking of #LeadDreamer
The forEach isn't ASYNCHRONOUS, the problem with his answer is that flutter does not have
Promise.all()
What it does have if Future.wait(), which acts(in my case) the same way.
Future<void> saveImages() async {
int index = images.length;
setState(() {
isLoading = true;
});
try {
final refMain = FirebaseStorage.instance
.ref()
.child('images${FirebaseAuth.instance.currentUser!.uid}');
AssetEntity eleId =
new AssetEntity(id: '', typeInt: 5, width: 5, height: 5);
await Future.wait(images.map((element) async { <-- here instead of the for each
setState(() {
eleId = element;
});
final file = await element.file;
final refPut = refMain.child('suportImage$_counter/$element$index');
await refPut.putFile(
File(file!.path),
);
}));
final ref2 =
refMain.child('headingImage$_counter/${imagePciker.path.split('/')}');
await ref2.putFile(File(imagePciker.path));
final ListResult listData =
await refMain.child('suportImage$_counter').listAll();
final data = await ref2.getDownloadURL();
await Future.wait(listData.items.map((e) async { <-- here instead of for each
await e.getDownloadURL().then((value) => listUrls.add(value));
}));

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 : Flutter Shared Preference don't work ,not save username Edit#1

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?

how to dispose setState properly to avoid memory leak?

It shows me this error.
E/flutter (22343): This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
E/flutter (22343): The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
E/flutter (22343): This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().
#override
void initState() {
super.initState();
if(mounted){
getProfilePosts();
getFollowers();
getFollowing();
checkIfFollowing();
}
}
checkIfFollowing() async {
if(mounted){
setState(() {
_isLoading = true;
});
DocumentSnapshot doc = await followersRef
.document(widget.profileId)
.collection('userFollowers')
.document(currentUserId)
.get();
setState(() {
_isFollowing = doc.exists;
});
setState(() {
_isLoading = false;
});
}
}
getFollowers() async {
if(mounted){
setState(() {
_isLoading = true;
});
QuerySnapshot snapshot = await followersRef
.document(widget.profileId)
.collection('userFollowers')
.getDocuments();
setState(() {
followerCount = snapshot.documents.length;
});
setState(() {
_isLoading = false;
});
}
}
getFollowing() async {
if(mounted){
setState(() {
_isLoading = true;
});
QuerySnapshot snapshot = await followingRef
.document(widget.profileId)
.collection('userFollowing')
.getDocuments();
setState(() {
followingCount = snapshot.documents.length;
});
setState(() {
_isLoading = false;
});
}
}
getProfilePosts() async {
if(mounted){
setState(() {
_isLoading = true;
});
QuerySnapshot snapshot = await postsRef
.document(widget.profileId)
.collection('userPosts')
.orderBy('timestamp', descending: true)
.getDocuments();
setState(() {
postCount = snapshot.documents.length;
posts = snapshot.documents.map((doc) => Post.fromDocument(doc)).toList();
_isLoading = false;
});
}
}
Try to do this in all the setState calls, it seems you check that only once, but the following calls to setState are unprotected
if (mounted == true) {
setState(() {})
}
You can override setState to use it only if the State object is mounted on the widget tree.
#override
void setState(fn) {
if (mounted) super.setState(fn);
}
Use
if (!mounted) return;
setState(){
/* ... */
}
OR
if (mounted) {
setState(() {
/* ... */
});
}
Reference: https://stackoverflow.com/a/74744364/13431819

How to save a list with SharedPreferences?

I tried to save a List (which is called test)with two variables with SharedPreferences. I tried the code below, but I get some errors. Does anybody see the mistake i made? (I think it´s kind of an easy to fix mistake, but I´m a beginner and can´t find it ;)
int counter1 = 0;
int counter2 = 20;
String nameKey = "eins";
var test = [counter1, counter2];
#override
void initState() {
super.initState();
}
Future<bool> save() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
return await preferences.setIntList(nameKey, test);
}
Future<List<int>> load() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
return preferences.getIntList(nameKey);
}
set() {
load().then((value) {
setState(() {
test = value;
});
});
}
Thanks in advance :)
Future<List<String>> load() async {
SharedPreferences preferences = await SharedPreferences.getInstance();
return preferences.getStringList(nameKey);
}