Read data using from the device as soon as an app opens in flutter - flutter

I am saving the app's theme to the device using SharedPreferences, in this way:
void _serializeTheme() async {
SharedPreferences _preferences = await SharedPreferences.getInstance();
_preferences.setString("theme", "dark");
}
Upon opening the app I can use _preferences.getString("theme") to detect the theme, but as SharedPreferences.getInstance() is async, I need to wrap the home of my application in a FutureBuilder which waits for the theme to load before displaying the page and renders a container while waiting. My problem is that for the brief time that the app spends loading the theme the screen flashes with the color of the container which, as I have no idea of which theme the user has saved before loading it, has to be a fixed color which may be in discordance with the background color of the home page. To prevent this is there a way I can load the theme before displaying anything at all?
So, is there a way to run an asyncronous function before opening the application? Or if this isn't fiesable, is there a way to read data syncronously from the device so that I could read it inside initState() so that I can know the theme that the user saved before rendering anyting?

In your case, I would get await data in before runApp(),
Example:
main()async{
// Here
SharedPreferences _preferences = await SharedPreferences.getInstance();
runApp(YourApp());
}
or
void main() {
runZonedGuarded(() async {
// Here
SharedPreferences _preferences = await SharedPreferences.getInstance();
runApp(YourApp());
}, (_, s) {});
}

You have to show something. You can set a splash screen of your choice using https://pub.dev/packages/flutter_native_splash, and then use that same screen to show when the FutureBuilder doesn't yet have the completed future, before transitioning to your main theme when it's ready.

Related

Access Variable form another class in top level function Flutter

I have a background notification handler in my flutter app, which is a top-level function like so:
Future<void> _onBackgroundMessage(RemoteMessage message) async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
final chatClient = StreamChatClient(STREAM_API_KEY);
print(Utils.user?.uid);
print(Utils.user?.streamToken);
chatClient.connectUser(
su.User(id: Utils.user?.uid ?? ''),
Utils.user?.streamToken ?? '',
connectWebSocket: false,
);
NotificationUtils.handleGetStreamNotification(message, chatClient);
SharedPreferences prefs = await SharedPreferences.getInstance();
int appBadgeCounter = prefs.getInt(appBadgePrefsKey) ?? 0;
FlutterAppBadger.updateBadgeCount(appBadgeCounter + 1);
}
In the notification handler, I need to connect to a separate server using a uid and a token. I have a Utils singleton class where I store an app user as a variable. I initialize this Utils app variable in my home page stateful widget class and persist it throughout the app. This way, I can minimize my number of database calls for user data.
However, in this top-level notification handler, the Utils.user is always null. This top level function exists in my home page stateful widget class, but it is a top level function still.
I want to avoid making database calls for every single notification that the app receives. Is there a way to get this Utils. user data without getting null??
you have to setUser before using it in chatClient.connectUser and along with you can check if user is null or not if null initialize it then it stops does not make extra calls.
Future<void> _onBackgroundMessage(RemoteMessage message) async {
...
await Firebase.initializeApp();
final chatClient = StreamChatClient(STREAM_API_KEY);
if(Utils.user != null) {
Utils.setUser();
}
print(Utils.user?.uid);
print(Utils.user?.streamToken);
...
}

Flutter: pause code execution to initialize SharedPreferences on startup

I read other answers about initializing SharedPreferences in Flutter app (e.g. this one), but I want to take another route: initialize SharedPreferences object once at the very beginning, and then just pass it around to specific clients as required (a.k.a. dependency injection).
I tried to make my main method async and then use await:
void main() async {
var prefs = await SharedPreferences.getInstance();
runApp(MyApp(prefs));
}
Intuitively, I expected that the execution will halt until prefs is initialized, and then proceeds to runApp. Unfortunately, I get a white screen instead of my UI, so I guess I'm missing something here.
I also tried to use then:
void main() async {
SharedPreferences.getInstance().then((prefs) => runApp(MyApp(prefs)));
}
Same result: white screen.
Is there a way to initialize SharedPreference in this manner in Flutter?
add this before you preference instance
WidgetsFlutterBinding.ensureInitialized();

How to save data in my settings menu in Flutter?

I was wondering how to save data locally in Flutter. I am creating a Settings menu in my app, and I have a draft made of that UI, but I want to save the user's preferences when they close that menu, but I have no idea how to accomplish that.
Do you know some tutorial to do so? I have searched in Youtube, but I have no idea how to search for it. I have only found UI tutorials and I don't want that.
A picture of my UI is (I want to save that boolean option).
I would appreciate any help you could give to me!
You should use shared_preferences package. It's very simple and it's for non-critical data like this! Here is an example how to use it:
class SettingsRepository {
final SharedPreferences preferences;
SettingsRepositoryImpl(this.preferences);
Future<int> getDifficulty() {
return preferences.getInt(Keys.difficulty) ?? 1;
// this 1 in the end is a default value
}
Future setDifficulty(int difficulty) {
return preferences.setInt(Keys.difficulty, difficulty);
}
}
To learn more go to https://pub.dev/packages/shared_preferences. I assume you want to call preferences.setBool
You can save your boolean option using shared preferences.
Future<Null> saveOption(bool isSelected) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool('option', isSelected);
}
Then you can get the option from there.
Future<bool> getOption() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
return prefs.getBool('option');
}
You can read more from here.

Flutter : Strange behavior of Shared Preferences

I have a problem with inconsistent shared preferences value. I will try to describe it as simple as possible.
I'm using Firebase Cloud Messaging for push notifications. When app is in background and notification came in, background handler bellow is invoked.
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int counter = (prefs.getInt('badge') ?? 0) + 1;
prefs.setInt('badge', counter).then((bool success) {
print(counter);
});
}
My widget uses WidgetsBindingObserver to determine lifecycle state. When I enter the app, state of that widget is onResume and there I want to read that badge value from shared preferences like this.
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final int counter = (prefs.getInt('badge') ?? 0);
print(counter);
}
}
Scenario 1:
App opened, notification came in - set badge field to 1.
App in background, notification came in - background handler set badge field to 2.
App resumed, read that badge field, it's still 1.
Scenario 2:
App opened, notification came in - set badge field to 1.
App in background, notification came in - background handler set badge field to 2.
App in background, notification came in - background handler set badge field to 3.
App resumed, read that badge field, it's still 1.
Question: Any idea why field isn't updated?
SharedPreferences can be used on background events handlers. The problem is that the background handler run in a different isolate so, when you try to get a data, the shared preferences instance is empty. To avoid this you simply have to force a refresh:
SharedPreferences prefs= await SharedPreferences.getInstance();
await prefs.reload();
final int counter = (prefs.getInt('badge') ?? 0);
In the same mode, if the shared preferences can be modified in a background hadler, be sure you call this "reload" function in the main isolate when you try to read from theirs.
SharedPreferences or any other local storage won't work in the _firebaseMessagingBackgroundHandler.
You should capture it on getInitialMessage or onMessageOpenedApp.
https://firebase.flutter.dev/docs/messaging/notifications/
TL;DR:
getInitialMessage gets triggered when the application is opened from a terminated state. While onMessageOpenedApp gets triggered when the application is opened from background state.
FirebaseMessaging.instance.getInitialMessage().then((RemoteMessage message) {
if (message != null) {
Navigator.of(context).pushNamed('/messages', arguments: message.data);
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
if (message != null) {
Navigator.of(context).pushNamed('/messages', arguments: message.data);
}
});

Flutter SharedPreferences resetting the data

I have this code in flutter using SharedPreferences to store data:
Future<bool> setUserStatus(String userStatus) async{
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('userStatus', 'active');
return true;
}
Is it possible to use this same setUserStatus in another file, which will get this main.dart imported to it, and change the SharedPreferences data to something else based on the actions taken in the other file
Do this,
await setUserStatus( status );
if you don't want to wait for the future to complete just remove await from the begining.
Calling prefs.clear()will erase all the preferences set on the device. so I would suggest not to use that here. If you want to clear a particular preference just use
prefs.remove(key) or prefs.setString(key,null)