i am trying to get data to a screen by shared prefrences so i set data here on my login screen:
var displayName=jsondata["username"];
SharedPreferences prefs=await SharedPreferences.getInstance();
prefs.setString('displayName', displayName);
and getting this data on my drawer:
String displayName="0";
initState() {
getData();
super.initState();
}
getData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
displayName=prefs.getString('displayName')?? "0";
}
**it gets the data when i click on drawer button but when go back to main screen and re-enter in my drawer it gets value "0" **here is the demo of issue
what is wrong?
and here is the code of the other screen which I move in between:
late String displayName;
getData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
displayName=prefs.getString('displayName')?? "0";
print(displayName);
}
initState() {
getData();
super.initState();
}
first of all always use a method after super.initState(); inside initState.
after that assuming that variable displayName is not updated anywhere else try to log when ever displayName changes in your code.
I can see that another screen is opening and then he value changes. so please show us more code of what you write.
Please refer to below code
String displayName="0";
initState() {
getData();
super.initState();
}
getData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if(displayName.isEmpty || displayName == "0"){
displayName=prefs.getString('displayName')?? "0";
await prefs.reload();
}
}
to Keep listening to code even you moved between pages,use StreamBuilder
StreamBuilder<DocumentSnapshot>(
stream:getData(),
builder: ( context, snapshot) {
if(snapshot.connectionState==ConnectionState.waiting){
return CircularProgressIndicator();
}else {
if(snapshot.hasData){
// .......
}
}
}
)
Related
I am using the shared_preferences within my app to save some data
but they only update the second time I open them
prefs.setInt is Future method, try putting await before all of it.
async{
final prefs = await SharedPreferences.getInstance ();
///others
await prefs.setInt ('s$current month $current year', s);
setState (() { });
}
And create another method to fetch data on initState like
fetchData()async{
final prefs = await SharedPreferences.getInstance ();
hours = prefs.getInt(...);
///....
setState(() { });
}
#override
void initState() {
super.initState();
fetchData();
}
I am using shared_preferences to store a bool value locally but I think I am doing something wrong.
So first of all, here is my initState:
#override
initState(){
super.initState();
checkIfUserHasData();
getBoolValuesSF();
}
on checkIfUserHasData, Im calling another function at the end (addBoolToSF)
Future<void> checkIfUserHasData ()async {
var collection = FirebaseFirestore.instance.
collection('users').doc(userID).collection('personalInfo');
var querySnapshots = await collection.get();
for (var snapshot in querySnapshots.docs) {
documentID = snapshot.id;
}
await FirebaseFirestore.instance
.collection('users')
.doc(userID)
.collection('personalInfo').doc(documentID)
.get().then((value) {
if (!mounted) return;
setState(() {
gender = value.get('gender');
profileImageUrl = value.get('url');
print(profileImageUrl);
print(gender);
});
});
if (gender != null){
if (!mounted) return;
setState((){
isUserNew = false;
});
if(gender == "Male"){
setState(() => genderIsMale = true);
addBoolToSF();
}else{
setState(() => genderIsMale = false);
addBoolToSF();
}
}else {
return;
}
}
Then addBoolToSF:
addBoolToSF() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool('genderType', genderIsMale);
}
Lastely getBoolValuesSF:
getBoolValuesSF() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
bool _genderType = ((prefs.getBool('genderType') ?? true)) ;
genderType = _genderType;
});
}
When the genderType value is obtained I then decide which image to be the background image on the screen:
CachedNetworkImage(
placeholder: (context, url) =>
CircularProgressIndicator(),
imageUrl: genderType ? // : //
With all of that said, here is what is happening when the gender is changed on the firebase firestore:
The first time I navigate or refresh the screen nothing is changed and I get this error:
type 'Null' is not a subtype of type 'bool'
The second time I refresh or navigate to the screen, I do get the correct image on place but I get the same error message again
type 'Null' is not a subtype of type 'bool'
I have tried several ways to solve this issue but i dont seem to get it right.
Edit: I have noticed that when I removed the last part for CachedNetworkImage, I get no error so I think the problem might be on this part
In case like that when you need to wait for a future to build some UI, the go to way is to use a FutureBuilder
You use it like this
FutureBuilder<bool>(
future: getBoolValuesSF,
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
// build your UI here based on snapshot value
},
)
checkIfUserHasData() and getBoolValuesSF() both are future method. you can create another async method and put it inside initState.
#override
initState(){
super.initState();
newMthod();
}
newMthod() async{
await checkIfUserHasData();
await getBoolValuesSF();
}
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 {
// …
}
}
I have to initialise the state of widget variable with the value stored in StoredProcedure.
void initState() {
widget.query = fetchMake();
super.initState();
}
Future<String> fetchMake() async{
final prefs = await SharedPreferences.getInstance();
return prefs.getString(key);
query.toString();
}
But the issue is that it cant assign that value to query variable and showing error value to type future string cannot assign to string flutter
How can I do that?
fetchMake is async so you have to put await where you call that method but it will not work because you are calling it in initState.
So You have to assign widget.query variable value in that function only. Moreover, as you get data you have to call setState, so data you receive reflect in ui.
In addition to that you have to check query is null or not where you are using it because when first time build method call it will not have any data.
String query;
void initState() {
fetchMake();
super.initState();
}
fetchMake() async{
final prefs = await SharedPreferences.getInstance();
setState((){
query = prefs.getString(key) ?? 'default';
});
}
You can try:
String query;
void initState() {
super.initState();
fetchMake().then((value) {
setState(() {
query = value;
});
});
}
Future<String> fetchMake() async{
final prefs = await SharedPreferences.getInstance();
return prefs.getString(key);
}
1, You can not set state with widget.query.
2, Create a variable query on state of Widget.
3, fetchMake is a async function => using then to wait result.
I'm working with SharedPreferences to make feature offline bookmark News . i can saved and fetching single value with this code :
Saved Value
void _testingSavePref(String judulBerita) async {
SharedPreferences pref = await SharedPreferences.getInstance();
pref.setString("tokenbookmark", judulBerita);
}
Fetching Value
#override
void initState() {
super.initState();
setState(() {
_testingLoadPref();
});
}
_testingLoadPref() async {
SharedPreferences pref = await SharedPreferences.getInstance();
setState(() {
tokenBookmark = pref.getString("tokenbookmark");
});
}
Everything is oke , but it's possible to saved and fetching multiple value with SharedPreferences ?
Example, i have 2 or more data, i want all data saved and not overwrite.
Thank's
Updated Code:
For Saving Values
void _testingSavePref(List<String> judulBerita) async {
SharedPreferences pref = await SharedPreferences.getInstance();
await pref.setStringList("tokenbookmark", judulBerita); //judulBerita is a list of string now
}
For Fetching Values
#override
void initState() {
super.initState();
setState(() {
_testingLoadPref();
});
}
_testingLoadPref() async {
SharedPreferences pref = await SharedPreferences.getInstance();
setState(() {
final List<String>? tokenBookmark = pref.getStringList("tokenbookmark");
});
}
Now, You can get the data from tokenBookmark list by below code
for(String s in tokenBookmark){
print(s);
}
You shouldn't use SharedPreferences to save that kind of data.
Use a local sql or nosql database (sqflite / sembast).
Also don't call setState inside the initState method is wrong and unnecessary.