How to Store API model object in Local Storage in flutter? - flutter

I fatch this issue during use Local Storage(shared_preferences: ^2.0.6) in my code....but i cant store the api model object in local storage...How can i do?
storeModelInPrefs() async {
http.Response response =
await http.get(Uri.parse('http://18.191.193.64/api/view_categories'));
String encodeData = jsonEncode(response.body);
///Write Data in local Storage
GetStorageUtility.prefs!.write('key', encodeData);
///Read Data from local Storage
String data = GetStorageUtility.prefs!.read('key');
if (data == null) {
print('no data in GetStorage');
} else {
Map<String, dynamic> map = jsonDecode(data);
print(map);
}
}

This is the sample example that i have created from the code that you have provided.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_app/utilities.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
void initState() {
GetStorageUtility.init();
super.initState();
getRemoteData();
}
getRemoteData() async {
/// This is where the api is fetching the data
var response =
await http.get(Uri.parse('http://18.191.193.64/api/view_categories'));
/// This is where the string getting
String encodeData = jsonEncode(response.body);
GetStorageUtility.write("key", encodeData);
/// this is where you fetch the data
String data = GetStorageUtility.read("key");
if (data == null) {
print('no data in GetStorage');
} else {
Map<String, dynamic> jsonData = json.decode(data);
jsonData.forEach((key, value) {
print("$key : $value\n");
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(" Page"),
),
);
}
}
SharadPrefs Singleton,
import 'dart:async';
import 'package:shared_preferences/shared_preferences.dart';
class GetStorageUtility {
static Future<SharedPreferences> get _instance async =>
_prefsInstance ??= await SharedPreferences.getInstance();
static SharedPreferences _prefsInstance;
static Future<SharedPreferences> init() async {
_prefsInstance = await _instance;
return _prefsInstance;
}
static String read(String key, [String defValue]) {
return _prefsInstance.getString(key) ?? defValue ?? "";
}
static Future<bool> write(String key, String value) async {
var prefs = await _instance;
return prefs?.setString(key, value) ?? Future.value(false);
}
}
Now there is on thing that you have see that you have added in you android manifest file
<application android:usesCleartextTraffic="true" />
This one should be there and the internet permission should be there in the debug and the main folders manifestfile.
This will work but this is not the best practice to store the data as string in the sharedprefs. Shared Prefs has only the job to manage the small data like bool or string. For your use case you can use a sqlite as a local data base. where you can fetch the data based on the condititions.
Let me know if it works.
Happy Coding.

Related

How to write and read data anywhere by shared_preferences on Flutter 3.7 background isolates?

On Flutter 3.7 platform channels can run on any isolate. So I tried this sample,
import ‘package:flutter/services.dart’;
import ‘package:shared_preferences/shared_preferences.dart’;
void main() {
// Identify the root isolate to pass to the background isolate.
// (API introduced in Flutter 3.7)
RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
Isolate.spawn(_isolateMain, rootIsolateToken);
}
void _isolateMain(RootIsolateToken rootIsolateToken) async {
// Register the background isolate with the root isolate.
BackgroundIsolateBinaryMessenger
.ensureInitialized(rootIsolateToken);
// You can now use the shared_preferences plugin.
SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
print(sharedPreferences.getBool(‘isDebug’));
}
I can read from data on shared_preferences in this sample okey. But how can I use this feature anywhere in my app? How can I set or read data using this isolate on initState for example?
Basically you need to implement communication between isolates. You can read more about it here
Here is an example, you can change flutter_secure_storage that i used with shared_preferences package
import 'dart:async';
import 'dart:isolate';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class CreationEvent {
final RootIsolateToken isolateToken;
final SendPort sendPort;
CreationEvent(this.isolateToken, this.sendPort);
}
class DeletetionEvent {}
class ReadEvent {
final String key;
const ReadEvent(this.key);
}
class ReadResult {
final String key;
final String? content;
const ReadResult(this.key, this.content);
}
class IsolateIO {
IsolateIO._();
final _toBgPort = Completer();
final Map<Object, Completer> _completerMap = {};
Isolate? _isolate;
StreamSubscription? _fromBgListener;
void start() async {
RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
ReceivePort fromBG = ReceivePort();
_fromBgListener = fromBG.listen((message) {
// setup process
if (message is SendPort) {
_toBgPort.complete(message);
return;
}
if (message is ReadResult) {
_completerMap['read:${message.key}']?.complete(message.content);
_completerMap.remove('read:${message.key}');
}
});
_isolate = await Isolate.spawn(
(CreationEvent data) {
final worker = IsolateWorker(data.isolateToken, data.sendPort);
worker.listen();
},
CreationEvent(rootIsolateToken, fromBG.sendPort),
);
}
Future<String?> readFromStorage(String key) async {
// make sure isolate created with ports
final port = await _toBgPort.future;
// store completer
final completer = Completer<String?>();
_completerMap['read:$key'] = completer;
// send key to be read
port.send(ReadEvent(key));
// return result
return completer.future;
}
void stop() async {
if (_toBgPort.isCompleted) {
final port = await _toBgPort.future;
port.send(DeletetionEvent());
}
_fromBgListener?.cancel();
_isolate?.kill(priority: Isolate.immediate);
}
static final i = IsolateIO._();
}
class IsolateWorker {
final RootIsolateToken rootIsolateToken;
final SendPort toMain;
final FlutterSecureStorage storage;
StreamSubscription? subs;
IsolateWorker(
this.rootIsolateToken,
this.toMain, {
this.storage = const FlutterSecureStorage(
aOptions: AndroidOptions(
encryptedSharedPreferences: true,
),
),
}) {
// Register the background isolate with the root isolate.
BackgroundIsolateBinaryMessenger.ensureInitialized(rootIsolateToken);
}
void listen() {
ReceivePort fromMain = ReceivePort();
toMain.send(fromMain.sendPort);
subs = fromMain.listen((message) => onMessage(message));
}
void onMessage(dynamic message) async {
if (message is DeletetionEvent) {
subs?.cancel();
return;
}
if (message is ReadEvent) {
final rawJson = await storage.read(key: message.key);
toMain.send(ReadResult(message.key, rawJson));
}
}
}
class View extends StatefulWidget {
const View({super.key});
#override
State<View> createState() => _ViewState();
}
class _ViewState extends State<View> {
String username = '';
#override
void initState() {
super.initState();
IsolateIO.i.start();
WidgetsBinding.instance.addPostFrameCallback((_) async {
final name = await IsolateIO.i.readFromStorage('username');
setState(() {
username = name ?? '';
});
});
}
#override
void dispose() {
IsolateIO.i.stop();
super.dispose();
}
#override
Widget build(BuildContext context) {
return SizedBox(
child: Text(username),
);
}
}

Flutter - Check the content of a Json before saving it locally and properly

Please i need some help and thanks in advance.
I am reciving over Websocket a Json that i want to safe locally.
But before saving it, i am trying to compare the recieved Json with the existend locad Json. If the value of count_json does not exist inside the local file it would need to save the Json to a new line and if the value of count_json would already exist it would do nothing.
At this moment, i am able to save it localy, and write it down to a new line in the file.
But i have two problems that i do not know how to solve it.
How i am making the comparising is not good. Because it is saving the recieved Json to a new line even the value of count_jsonalready exist, like as follow.
{"count_json":1,"range_json":[5.5,8.9,7.5,6.7,8.7],"force_json":[5.4,5.3,5.2,5.2,5.1]}
{"count_json":1,"range_json":[9.5,8.3,12.4,13.1,8.5],"force_json":[4.9,4.8,4.8,4.9,5]}
{"count_json":1,"range_json":[11.7,9.7,9.9,11.8,10.2],"force_json":[4.9,5,5.2,5.3,5.5]}
{"count_json":2,"range_json":[19.6,19.6,19.6,19.6,19.6],"force_json":[10,10,10,10,10]}
{"count_json":2,"range_json":[19.4,19.6,19.6,19.6,19.6],"force_json":[9.9,10,10,10,10]}
{"count_json":2,"range_json":[19.4,19.6,19.6,19.6,19.6],"force_json":[9.9,10,10,10,10]}
{"count_json":2,"range_json":[19.4,19.7,19.6,19.6,19.6],"force_json":[9.9,10,10,10,10]}
.
.
But i am expenting this
{"count_json":1,"range_json":[5.5,8.9,7.5,6.7,8.7],"force_json":[5.4,5.3,5.2,5.2,5.1]}
{"count_json":2,"range_json":[19.4,19.7,19.6,19.6,19.6],"force_json":[9.9,10,10,10,10]}
.
.
My approach on how to compara and safe is as follow.
Map<String, dynamic> jsondat = json.decode(message);
String data = json.encode(jsondat);
setState(() {
if (data.contains("count_json")) {
istcycles = jsondat['count_json']; //cycles value
connectedS1Status = true;
if (_myjson['count_json'] != 0) {
_filePath.writeAsString('$data\n', mode: FileMode.append);
}
}
});
If i open the local Json file in Visual Studio code it give me the error message
End of file Expected
That means the stuctur of how i a writing and saving to the Json file is not properly. See above how the structure is inside the file.
Follow the complete code.
//https://docs.flutter.dev/cookbook/persistence/reading-writing-files
// ignore_for_file: avoid_print
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:web_socket_channel/io.dart';
import 'dart:io';
import 'dart:async';
const String fileName = 'myJsonFile.json';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(home: HomePage());
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
// ignore: library_private_types_in_public_api
_HomePageState createState() {
return _HomePageState();
}
}
class _HomePageState extends State<HomePage> {
late IOWebSocketChannel channel;
late bool
connectedS1Status; //boolean value to track if WebSocket is connected
late int istcycles; //variable for istcycles
late double istforcesensor;
late double istrangesensor;
#override
void initState() {
connectedS1Status =
false; //initially connection status is "NO" so its FALSE
Future.delayed(Duration.zero, () async {
channelconnect(); //connect to WebSocket wth NodeMCU
});
// Instantiate _controllerKey and _controllerValue
print('0. Initialized _myjson: $_myjson');
_readJson();
istcycles = 0; //initial value of istcycles
istforcesensor = 0;
istrangesensor = 0;
super.initState();
}
channelconnect() {
try {
channel = IOWebSocketChannel.connect(
"ws://192.168.1.100:80"); //channel IP : Port
channel.stream.listen(
(message) {
//print(message);
Map<String, dynamic> jsondat = json.decode(message);
String data = json.encode(jsondat);
setState(() {
if (data.contains("count_json")) {
istcycles = jsondat['count_json']; //cycles value
connectedS1Status = true;
if (_myjson['count_json'] != 0) {
_filePath.writeAsString('$data\n', mode: FileMode.append);
}
}
});
},
onDone: () {
print("Web socket is closed");
setState(() {
connectedS1Status = false;
});
},
onError: (error) {
print(error.toString());
},
);
} catch (_) {
print("error on connecting to websocket.");
}
}
bool _fileExists = false;
late File _filePath;
// First initialization of _json (if there is no json in the file)
late Map<String, dynamic> _myjson = {};
late String _myjsonString;
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _localFile async {
final path = await _localPath;
return File('$path/$fileName');
}
//------------------------------------------------------------------------------------
// _readJson--------------------------------------------------------------------------
//------------------------------------------------------------------------------------
void _readJson() async {
// Initialize _filePath
_filePath = await _localFile;
// 0. Check whether the _file exists
_fileExists = await _filePath.exists();
print('0. File exists? $_fileExists');
// If the _file exists->read it: update initialized _json by what's in the _file
if (_fileExists) {
try {
//1. Read _jsonString<String> from the _file.
_myjsonString = await _filePath.readAsString();
print('1.(_readJson) _jsonString: $_myjsonString');
//2. Update initialized _json by converting _jsonString<String>->_json<Map>
_myjson = jsonDecode(_myjsonString);
print('2.(_readJson) _json: $_myjson \n - \n');
} catch (e) {
// Print exception errors
print('Tried reading _file error: $e');
// If encountering an error, return null
}
}
}
#override
void dispose() {
super.dispose();
}
// Delete Function-------------------------------------------
Future<int> deleteFile() async {
try {
final file = await _localFile;
await file.delete();
} catch (e) {
return 0;
}
return 0;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("WebSocket Json"),
backgroundColor: Colors.redAccent),
body: Container(
alignment: Alignment.topCenter, //inner widget alignment to center
padding: const EdgeInsets.all(20),
child: Column(
children: [
Container(
//showing if websocket is connected or disconnected
child: connectedS1Status
? const Text("WEBSOCKET: CONNECTED")
: const Text("DISCONNECTED")),
Text("Cycles: $istcycles "),
],
)),
);
}
}

I can't get data open weather map

I don't know why but keeps telling me error 400 when it tries to fetch data from open weather map
even i tried to change the api key and tried to change the code it self but it didn't work
so i want to know why this happening and i want to know the solution for this problem
import 'package:clima/services/location.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
const apiKey = '*****53a8**************';
class LoadingScreen extends StatefulWidget {
#override
_LoadingScreenState createState() => _LoadingScreenState();
}
class _LoadingScreenState extends State<LoadingScreen> {
double lontitude;
double latitude;
#override
void initState() {
getLocation();
getData();
super.initState();
}
Future getLocation() async {
Location location = Location();
await location.getCurrentLocation();
latitude = location.latitude;
lontitude = location.lontitude;
}
Future getData() async {
http.Response response = await http.get(
Uri.parse(
'https://api.openweathermap.org/data/2.5/weather?lat=$latitude&lon=$lontitude&appid=$apiKey'),
);
if (response.statusCode == 200) {
String data = response.body;
var temperature = jsonDecode(data)['main']['temp'];
print(temperature);
var condition = jsonDecode(data)['weather'][0]['id'];
print(condition);
var city = jsonDecode(data)['name'];
print(city);
} else {
print(response.statusCode);
}
}
#override
Widget build(BuildContext context) {
return Scaffold();
}
}
Just typing in that URL in a browser and substituting longitude, latitude, and apiKey with values works just fine for me.
https://api.openweathermap.org/data/2.5/weather?lat=50,123123&lon=13.123123&appid=************** // add private key
OUTPUT:
{"coord":{"lon":13.1231,"lat":50.1231},"weather":[{"id":800,"main":"Clear","description":"clear sky","icon":"01n"}],"base":"stations","main":{"temp":281.16,"feels_like":279.56,"temp_min":279.08,"temp_max":283.95,"pressure":1023,"humidity":79},"visibility":10000,"wind":{"speed":2.57,"deg":250},"clouds":{"all":0},"dt":1632954450,"sys":{"type":2,"id":47765,"country":"CZ","sunrise":1632978356,"sunset":1633020533},"timezone":7200,"id":3061350,"name":"Žlutice","cod":200}
You should check the format of the things that get substituted by your code.

Signing out of flutter app with sqflite database

I am trying to write the code for signing out of a flutter app logging in (The database of the map is made with sqflite).
However I am getting the following error message:
flutter: NoSuchMethodError: The method 'notify' was called on null.
Receiver: null
Tried calling: notify(Instance of 'AuthState')
Have provided code of required files below.
I am trying to incorporate a sign out function in the home_screen.dart file but I feel I am missing a link between the auth.dart, auth_provider.dart, login_screen.dart and home_screen.dart files. The codes of the required files for the issue are as follows:
File: database_helper.dart
import 'dart:async';
import 'dart:io' as io;
import 'package:path/path.dart';
import 'package:better_login/user.dart';
import 'package:sqflite/sqflite.dart' ;
import 'package:path_provider/path_provider.dart';
class DatabaseHelper {
static final DatabaseHelper _instance = new
DatabaseHelper.internal();
factory DatabaseHelper() => _instance;
static Database _db;
Future<Database> get db async {
if(_db != null)
return _db;
_db = await initDb();
return _db;
}
DatabaseHelper.internal();
initDb() async {
io.Directory documentsDirectory = await
getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "main.db");
var theDb = await openDatabase(path, version: 1, onCreate:
_onCreate);
return theDb;
}
void _onCreate(Database db, int version) async {
// When creating the db, create the table
await db.execute(
"CREATE TABLE User(username TEXT,password TEXT)");
print("Created tables");
}
Future<int> saveUser(User user) async {
var dbClient = await db;
int res = await dbClient.insert("User", user.toMap());
return res;
}
Future<int> deleteUsers() async {
var dbClient = await db;
int res = await dbClient.delete("User");
return res;
}
Future<bool> isLoggedIn() async {
var dbClient = await db;
var res = await dbClient.query("User");
return res.length > 0? true: false;
}
}
File:auth.dart
import 'package:better_login/database_helper.dart';
enum AuthState{ LOGGED_IN, LOGGED_OUT }
abstract class AuthStateListener {
void onAuthStateChanged(AuthState state);
}
class AuthStateProvider {
static final AuthStateProvider _instance = new
AuthStateProvider.internal();
List<AuthStateListener> _subscribers;
factory AuthStateProvider() => _instance;
AuthStateProvider.internal() {
_subscribers = new List<AuthStateListener>();
initState();
}
void initState() async {
var db = new DatabaseHelper();
var isLoggedIn = await db.isLoggedIn();
if(isLoggedIn)
notify(AuthState.LOGGED_IN);
else
notify(AuthState.LOGGED_OUT);
}
void subscribe(AuthStateListener listener) {
_subscribers.add(listener);
}
void dispose(AuthStateListener listener) {
for(var l in _subscribers) {
if(l == listener)
_subscribers.remove(l);
}
}
void notify(AuthState state) {
_subscribers.forEach((AuthStateListener s) =>
s.onAuthStateChanged(state));
}
}
File: auth_provider.dart
import 'package:flutter/material.dart';
import 'package:better_login/auth.dart';
class AuthProvider extends InheritedWidget {
const AuthProvider({Key key, Widget child, this.auth}) : super(key: key, child: child);
final AuthStateListener auth;
#override
bool updateShouldNotify(InheritedWidget oldWidget) => true;
static AuthProvider of(BuildContext context) {
return context.inheritFromWidgetOfExactType(AuthProvider);
}
}
File: home_screen.dart
import 'package:flutter/material.dart';
import 'package:better_login/auth.dart';
import 'package:better_login/login_screen.dart';
import 'package:better_login/login_screen_presenter.dart';
import 'package:better_login/auth_provider.dart';
class HomeScreen extends StatelessWidget {
HomeScreen({this.authStateListener, this.authStateProvider});
final AuthStateListener authStateListener;
final AuthStateProvider authStateProvider;
void _signOut() async {
try{
authStateProvider.notify(AuthState.LOGGED_OUT);
authStateListener.onAuthStateChanged(AuthState.LOGGED_OUT);
}catch(e){
print(e);
}
}
#override
Widget build(BuildContext context) {
// TODO: implement build
return new Scaffold(
appBar: new AppBar(
title: new Text("Home"),
actions: <Widget>[
new IconButton(icon: new Icon(Icons.exit_to_app), onPressed: (){_signOut();}),
],
),
body: new Center(
child: new Text("Welcome home!"),
),
);
}
}

Flutter: How to use SharedPreferences synchronously?

I am using Shared Preferences in my Flutter app and what I would like to do is store SharedPreferences as a field on startup and then use it synchronously in the app. However I'm not sure if I'm not missing anything.
What I want to achieve is instead of:
method1() async {
SharedPreferences sp = await SharedPreferences.getInstance();
return sp.getString('someKey');
}
to
SharedPreferences sp;
//I would probably pass SharedPreferences in constructor, but the idea is the same
someInitMethod() async {
sp = await SharedPreferences.getInstance();
}
method1() {
return sp.getString('someKey');
}
method2() {
return sp.getString('someKey2');
}
method3() {
return sp.getString('someKey3');
}
In that way I would achieve synchronous access to sharedPrefs. Is it bad solution?
EDIT:
What is worth mentioning is that getInstance method will only check for instance and if there is any than it returns it, so as I see it, is that async is only needed to initialize instance. And both set and get methods are sync anyway.
static Future<SharedPreferences> getInstance() async {
if (_instance == null) {
final Map<String, Object> fromSystem =
await _kChannel.invokeMethod('getAll');
assert(fromSystem != null);
// Strip the flutter. prefix from the returned preferences.
final Map<String, Object> preferencesMap = <String, Object>{};
for (String key in fromSystem.keys) {
assert(key.startsWith(_prefix));
preferencesMap[key.substring(_prefix.length)] = fromSystem[key];
}
_instance = new SharedPreferences._(preferencesMap);
}
return _instance;
}
I use the same approach as the original poster suggests i.e. I have a global variable (actually a static field in a class that I use for all such variables) which I initialise to the shared preferences something like this:
in globals.dart:
class App {
static SharedPreferences localStorage;
static Future init() async {
localStorage = await SharedPreferences.getInstance();
}
}
in main.dart:
void main() {
start();
}
Async.Future start() async {
await App.init();
localStorage.set('userName','Bob');
print('User name is: ${localStorage.get('userName)'}'); //prints 'Bob'
}
The above worked fine but I found that if I tried to use App.localStorage from another dart file e.g. settings.dart it would not work because App.localStorage was null but I could not understand how it had become null.
Turns out the problem was that the import statement in settings.dart was import 'package:<packagename>/src/globals.dart'; when it should have been import 'globals.dart;.
#iBob101 's answer is good, but still, you have to wait before you use the SharedPreferences for the first time.
The whole point is NOT to await for your SharedPreferences and be sure that it will always be NOT NULL.
Since you'll have to wait anyway let's do it in the main() method:
class App {
static SharedPreferences localStorage;
static Future init() async {
localStorage = await SharedPreferences.getInstance();
}
}
And the main method:
void main() async{
await SharedPref.initSharedPref();
runApp(MyApp());
}
the line await SharedPref.initSharedPref(); takes ~100ms to execute. This is the only drawback as far as I can see.
But you definitely know that in every place in the app your sharedPreferenes instance in NOT NULL and ready for accessing it:
String s = App.localStorage.getString(PREF_MY_STRING_VALUE);
I think it's worthwhile
The cleanest way is to retrieve SharedPreferences in main method and pass it to MyApp as a dependency:
void main() async {
// Takes ~50ms to get in iOS Simulator.
final SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();
runApp(MyApp(sharedPreferences: sharedPreferences));
}
class MyApp extends StatefulWidget {
final SharedPreferences sharedPreferences;
const MyApp({Key key, this.sharedPreferences})
: assert(sharedPreferences != null),
super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
// You can access shared preferences via widget.sharedPreferences
return ...
}
I made a simple way to using this PrefUtil class:
import 'package:shared_preferences/shared_preferences.dart';
class PrefUtil {
static late final SharedPreferences preferences;
static bool _init = false;
static Future init() async {
if (_init) return;
preferences = await SharedPreferences.getInstance();
_init = true;
return preferences;
}
static setValue(String key, Object value) {
switch (value.runtimeType) {
case String:
preferences.setString(key, value as String);
break;
case bool:
preferences.setBool(key, value as bool);
break;
case int:
preferences.setInt(key, value as int);
break;
default:
}
}
static Object getValue(String key, Object defaultValue) {
switch (defaultValue.runtimeType) {
case String:
return preferences.getString(key) ?? "";
case bool:
return preferences.getBool(key) ?? false;
case int:
return preferences.getInt(key) ?? 0;
default:
return defaultValue;
}
}
}
In main.dart:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
PrefUtil.init();
.....
Save it like:
PrefUtil.setValue("isLogin", true);
Get the value like:
PrefUtil.getValue("isLogin", false) as bool
By this, it will initialize only once and get it where ever you need.
You can use FutureBuilder to render the loading screen while waiting for SharedPreferences to be intialized for the first time in a singleton-like class. After that, you can access it synchronously inside the children.
local_storage.dart
class LocalStorage {
static late final SharedPreferences instance;
static bool _init = false;
static Future init() async {
if (_init) return;
instance = await SharedPreferences.getInstance();
_init = true;
return instance;
}
}
app_page.dart
final Future _storageFuture = LocalStorage.init();
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: _storageFuture,
builder: (context, snapshot) {
Widget child;
if (snapshot.connectionState == ConnectionState.done) {
child = MyPage();
} else if (snapshot.hasError) {
child = Text('Error: ${snapshot.error}');
} else {
child = Text('Loading...');
}
return Scaffold(
body: Center(child: child),
);
},
);
}
my_page.dart
return Text(LocalStorage.instance.getString(kUserToken) ?? 'Empty');
call shared prefs on startup of a stateful main app (we call ours a initState() override of a StatefulWidget after super.initState())
after shared prefs inits, set the value to a field on main (ex: String _someKey)
inject this field into any child component
You can the call setState() on _someKey at you leisure and it will persist to children injected with your field