I have a question about using async function in initstate - flutter

I want to initialize'LikesList' by calling the init function I implemented in initstate and render the related widgets accordingly. However, when it is actually executed, LikesList cannot be initialized, and, curiously, once hot reload is performed, LikesList is initialized. How do I get LikesList to be initialized normally when I first run it as I want?
List<String> LikesList = [];
int ListIndex = 0;
Future<List<String>> init() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
LikesList = await prefs.getStringList('LikesList');
ListIndex = await LikesList.length;
print("${LikesList}");
}
#override
void initState() {
// TODO: implement initState
// Future.delayed(Duration(milliseconds: 100)).then((_) {
// init();
// });
Timer.run(() {
init();//This is the function I implemented.
});
super.initState();
print("++++++++${LikesList}");
}

As #whatamelon suggested you can use FutureBuilder to serve the purpose.
Future<List<String>> init() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getStringList('LikesList');
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: init(),
builder: (ctx, snapshot) {
if(snapshot.hasData) {
print(snapshot.data);
return Text("${snapshot.data}");
}
else
return Text("loading");
}
);
}

How about using Future method?
Using Future and Future builder makes you can get data in initialized state.

Related

Can't get value correctly from shared preferences

I'm trying to save a value in the shared preferences in flutter then get it. But it's always returning null. The value is being retrieved from an API that is working fine in the backend.
Here is my code:
Method in which i'm getting the data from the api:
List<LastOrder>? lastOrders;
var isLoaded3 = false;
int od_id = 0;
getLastOrderMethod() async {
lastOrders = await RemoteService().getLastOrder(2);
if (lastOrders != null) {
setState(() {
isLoaded = true;
});
return ListView.builder(
itemCount: 1,
itemBuilder: (BuildContext context, int index) {
setState(() {
od_id = lastOrders![0].id;
print('getLastOrderMethod: $od_id');
saveIdOrder(od_id);
});
return;
});
}
}
Method in which i'm trying to save the variable value in the shared preferences:
Future<bool> saveIdOrder(value) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
print('save: $od_id');
return await sharedPreferences.setInt('order_id', value);
}
Method in which i'm trying to get the variable value in the shared preferences:
static Future getIdOrder() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
final x = sharedPreferences.getInt('order_id');
print('get: $x');
return x;
}
#override
void initState() {
// TODO: implement initState
print('intial ${od_id}'); => 0
getIdOrder(); => null
getLastOrderMethod();
super.initState();
}
I'd be glad for any kind of help!
getIdOrder() is a future method, it will take some time to fetch the data. While initState cant be async method, you can use .then and inside it call setState to update the ui. but Using FutureBuilder will be best option.
late final future = getIdOrder();
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: future,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text("${snapshot.data}"); // your widget
}
return CircularProgressIndicator();
},
),
floatingActionButton: FloatingActionButton(onPressed: () {}),
);
}
More about using FutureBuilder
Solved the issue by doing all the logic inside the listView.builder(), then updated the variable value inside a setState()

flutter - FutureBuilder auto rebuild each time press a button in the screen

I try to use FutureBuilder in Flutter to wait ulti my initState is finished then buil the UI for the app.
But when the app is running, the screen keep rebuilding each time I press another button (the button does totally different thing).
Future loadUser() async {
String jsonString = await storage.read(key: "jwt");
final jsonResponse = json.decode(jsonString);
loggedUser = new LoggedUser.fromJson(jsonResponse);
print(loggedUser.token);
getProfile();
getJourneyByUserId()
.then((receivedList){
addRanges(receivedList);});
}
Future<List<Journey>>getJourneyByUserId() async {
var res = await http.get(
Uri.parse("$baseUrl/journeys/userid=${loggedUser.user.userId}"),
headers: {
'Content_Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer ${loggedUser.token}',
},
);
if (res.statusCode == 200) {
print("Get journeys successfully");
}
var data = jsonDecode(res.body);
List idList = [];
for (var i in data) {
idList.add(i["journeyId"]);
}
for (var i in idList) {
var res = await http.get(
Uri.parse("$baseUrl/journeys/$i"),
);
var data = jsonDecode(res.body);
Journey userJourney = new Journey.fromJson(data);
setState(() {
journeyList.add(userJourney);
});
}
print("Journey ${journeyList.length}");
return journeyList;
}
addRanges(journeyList){
setState(() {
rangeList=[];
});
if (journeyList.isNotEmpty) {
for (var i in journeyList) {
DateTime startDate =
DateTime(i.startDate.year, i.startDate.month, i.startDate.day);
DateTime endDate =
DateTime(i.endDate.year, i.endDate.month, i.endDate.day);
setState(() {
rangeList.add(PickerDateRange(startDate, endDate));
});
}
}
print("Range ${rangeList.length}");
return rangeList;
}
returnRange() {
List<PickerDateRange> list = [];
for(int i =0; i<rangeList.length;i++){
list.add(rangeList[i]);
}
return list;
}
Future functionForBuilder() async {
return await returnRange();
}
//initState function
#override
void initState() {
super.initState();
loadUser();
functionForBuilder();
}
//build the UI
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("$_name's Profile",style: TextStyle(color: kColorPalette4),),
centerTitle: true,
),
body: Container(
child: FutureBuilder(
future: functionForBuilder(),
builder: (BuildContext context,AsyncSnapshot snapshot){
//here I set the condition for each case of snapshot
}
I have read some documents say that I should assign the functionForBuilder() to a Future variable when initState then use it in the future child of FutureBuilder. Example:
Future _future;
//initState function
#override
void initState() {
super.initState();
loadUser();
_future=functionForBuilder();
}
// then with the FutureBuilder
future: _future
With this way the screen is not rebuild anymore but my function returnRange() seems like not running as my expextation (I called the returnRange() once in the build() function).
Thanks in advance for your answer!
Whenever you assign to the _future variable again, you must do that inside a setState block, otherwise the widget will not rebuild with the new future.
For example:
void updateData() {
setState(() {
_future = functionForBuilder();
});
}
If you use FutureBuilder, it rebuild items again and again.
Try two ways:
Don't use `future: functionForBuilder(), comment it.
Remove FutureBuilder(), simply use Container().
And let me know any issue?
Code:
call your future in the initstate method not in the build as shown in the example.
class MyPage extends StatefulWidget { #override State<MyPage> createState() => _MyPageState(); } class _MyPageState extends State<MyPage> { // Declare a variable. late final Future<int> _future; #override void initState() { super.initState(); _future = _calculate(); // Assign your Future to it. } // This is your actual Future. Future<int> _calculate() => Future.delayed(Duration(seconds: 3), () => 42); #override Widget build(BuildContext context) { return Scaffold( body: FutureBuilder<int>( future: _future, // Use your variable here (not the actual Future) builder: (_, snapshot) { if (snapshot.hasData) return Text('Value = ${snapshot.data!}'); return Text('Loading...'); }, ), ); } }

type Future<List<Item>> is not subtype of List error in flutter

My function defination is below
Future<List<Item>> fetchGitUsers() async {
final response = await http.get('https://');
if (response.statusCode == 200) {
GitUsers gitUser = GitUsers.fromJson(json.decode(response.body));
return gitUser.items;
}
} / Function end
class GitUsers {
List<Item> items;
}
class ... extends State<SearchController> {
#override
void initState() {
super.initState();
gitUsers = fetchGitUsers() as List<Item>;
}
}
But I am getting below error on emulator screen..
in type cast.
You didn't add await
Try this
void getUsers() async{
gitUsers = await fetchGitUsers();
}
#override
void initState() {
super.initState();
getUsers();
}
}
If you want to use the git users in a UI (e.g ListView), consider using FutureBuilder.
Like this
FutureBuilder(
future: fetchGitUsers(),
builder: (context, snapshot){
if(!snapshot.hasData) return CircularProgressIndicator();
return ListView();
}
)

Flutter - How to pause application while shared preferences is loading?

I use method, that I call from InitState() where load SP with await.
But Widget is constructing before SP is loaded and have got empty SP values.
void getSP() async {
var prefs = await SharedPreferences.getInstance();
_todoItems = prefs.getStringList("key") ?? _todoItems;
}
Full code: https://pastebin.com/EnxfKgPH
there are many options, one i like is to use boolean variable like this
bool isLoaded = false;
#override
void initState() {
getSP();
super.initState();
}
void getSP() async {
var prefs = await SharedPreferences.getInstance();
_todoItems = prefs.getStringList("key") ?? _todoItems;
setState(() => isLoaded = true);
}
then check it to determine if build tree should load or not, like this..
#override
Widget build(BuildContext context) {
return !isLoaded ? CircularProgressIndicator() : Scaffold(...);
}

Problem in receiving Future data (SharedPreferences) in Flutter

I am trying to save the value of Switch in SharedPreferences. Here is my code :
bool isDarkTheme;
static const String KEY_DARK_THEME = "dark";
void setTheme(bool value) async {
SharedPreferences pref = await SharedPreferences.getInstance();
isDarkTheme = value;
pref.setBool(KEY_DARK_THEME, isDarkTheme);
print("DARKSet? $isDarkTheme");
}
void getTheme() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
isDarkTheme = sharedPreferences.getBool(KEY_DARK_THEME);
print("dark? $isDarkTheme");
}
#override
void initState() {
// TODO: implement initState
super.initState();
print("MYINIT");
getTheme();
}
And inside Build method...
#override
Widget build(BuildContext context) {
print("BUILD $isDarkTheme");
...
...
ListTile(
title: Text("Dark Theme"),
trailing: Switch(
value: isDarkTheme ?? false,
onChanged: (val) {
setState(() {
setTheme(val);
});
},
),
),
...
...
}
Though I get the correct value inside debug console, but Switch widget is not changed accordingly. I found build() method is run before getting the value from SharedPrefernces, as a result Switch widget is not getting value from SharedPreferences. How to solve this problem of receiving Future value?
You have two option
1). I think when you get value from SharedPreference at that time you just call setState() method
void getTheme() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
setState(() {
isDarkTheme = sharedPreferences.getBool(KEY_DARK_THEME);
print("dark? $isDarkTheme");
});}
2) You can use Provider for StateManagement so, when isDarkTheme value is changed notifyListener is called and your build method is rebuild and you see the change
Your main issue is that you're retrieving the SharedPreferences instance whenever you want to store or retrieve a value, beside the fact you're also using two instances of it (pref and sharedPreferences) :
retrieve a single SharedPreferences instance using a separate function:
SharedPreferences pref ;
Future loadPreferences() async {
pref = await SharedPreferences.getInstance();
}
Then modify getTheme and setTheme :
void setTheme(bool value) async {
isDarkTheme = value;
pref.setBool(KEY_DARK_THEME, isDarkTheme);
print("DARKSet? $isDarkTheme");
}
void getTheme() async {
isDarkTheme = pref.getBool(KEY_DARK_THEME);
print("dark? $isDarkTheme");
}
Also, call loadPreferences during initialization, so pref will be loaded by the time build is called:
#override
void initState() {
super.initState();
print("MYINIT");
loadPreferences().then((_){
getTheme();
});
}