I am learning how to use the SharedPreferences library in Flutter.
I created this code and I would like the counter and counter2 variables once I close and reopen the app to remain as the last save.
However, when I reopen the app the counter and counter2 values return to 0. Can anyone explain to me where I am going wrong?
Thank you.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'data.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int counter = 0;
int counter2 = 0;
increment() {
setState(() {
counter += 1;
counter2 += 2;
});
}
loadData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
String? json = prefs.getString('UserData');
print('loaded json: $json');
if (json == null) {
print('NO DATA (null)');
} else {
Map<String, dynamic> map = jsonDecode(json);
print('map $map');
final data = Data.fromJson(map);
print('Data ${data.counter}, ${data.counter2}');
}
});
}
saveData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final _data = Data(counter: counter, counter2: counter2);
String json = jsonEncode(_data);
print('saved json: $json');
prefs.setString('UserData', json);
}
clearData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.clear();
print('data cleared');
}
/// dichiarare l' initState()
@override
void initState() {
super.initState();
loadData();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'You have pushed the button this many times:',
),
Text(
'c: $counter, c2: $counter2',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
increment();
saveData();
},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class Data {
int counter = 0;
int counter2 = 0;
Data({required this.counter, required this.counter2});
Map<String, dynamic> toJson() {
return {
'counter': counter,
'counter2': counter2,
};
}
Data.fromJson(Map<String, dynamic> json) {
counter = json['counter'];
counter2 = json['counter2'];
}
}
I agree with the other answer, the best is to use a FutureBuilder
. But you can make your current code work with simply adding two lines at the end of loadData
:
loadData() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
String? json = prefs.getString('UserData');
print('loaded json: $json');
if (json == null) {
print('NO DATA (null)');
} else {
Map<String, dynamic> map = jsonDecode(json);
print('map $map');
final data = Data.fromJson(map);
print('Data ${data.counter}, ${data.counter2}');
// add these lines
counter = data.counter;
counter2 = data.counter2;
}
});
}
What happens (as the other answer says) is that your widget is first built without knowing the values from SharedPreferences
. After a little time this first build is done, the loadData
future completes, and with setState
the widget is rebuilt.
In a real application you'd like to avoid unnecessary builds, so you'd rather display a progress indicator while async data is being loaded, check FutureBuilder
.