Shared_preferences (https://pub.dev/packages/shared_preferences) doesn't seem to work for Flutter for Web.
I have the following function that's called when a button is pressed.
getEmail() async {
print("reached 1st line");
SharedPreferences prefs = await SharedPreferences.getInstance();
print("reached 2nd line");
String _confirmedEmail = prefs.getString('_confirmedEmail') ?? "";
)
It prints "reached 1st line" but not "reached 2nd line", which means the program doesn't go past the await statement. Interestingly I don't get any error either. It seems to just ignore the rest of the function after the await statement.
What is the best alternative to store shared preferences in Flutter for Web?
Great news, from version 0.5.6 shared_prefs flutter supports web by default
Now it's includes shared_preferences for web
Your code should work without changes, just update dependency in pubspec.yaml
dependencies:
shared_preferences: ^0.5.6
Now the latest shared_preference package includes web support. But if you run on debug web mode you will see that the shared preference is not working on the web.
The reason is that Flutter Web runs on a random port every time and cannot recollect the shared or stored data.
The best way to test this is to run the flutter app on the web twice and check the URL it will show different ports always.
localhost:5050/#/
To solve this issue force flutter to run the app on the same port.
you can do this by adding a port while running the flutter app on the web from a terminal like this.
flutter run -d chrome --web-hostname localhost --web-port 5050
Or you can add the port in the lauch.json file of VS code like this.
"version": "0.2.0",
"configurations": [
{
"name": "appname",
"request": "launch",
"type": "dart",
"args": ["--web-port", "5050"]
},
This will help you to maintain the session and also retrieve the data.
shared_preferences are not supposed to work with flutter web, that's why value of instance never returns. For this purpose, you can use any key-value stores instead, for example, sembast
UPD: the package supports web now since version 0.5.6
I think it is supported now due to this. It depends on shared_preferences_web
image from pub dev
You probably check the tags when you search for a library in pub.dev.
For the web the best way to implement that is implementing cache for web and dcache for implement that using flutter_web.
import 'package:dcache/dcache.dart';
void main() {
Cache c = new SimpleCache(storage: new SimpleStorage(size: 20));
c.set("key", 42);
print(c.get("key")); // 42
print(c.containsKey("unknown_key")); // false
print(c.get("unknown_key")); // nil
}
As you can see, is very similar to shared_preferences for Flutter.
Hope this could help.
In latest version of shared_prefs, flutter supports web by default
SharedPreferences pref = await SharedPreferences.getInstance();
//for setting values locally:
await pref.setString("token", myJWTToken);
//for getting values:
dynamic token = pref.getString("token");
but still if you are looking for alternative then you can use flutter_session package for almost simillar purpose.
//for setting values locally:
await FlutterSession().set("token", myJWTToken);
//for getting values:
dynamic token = await FlutterSession().get("token");
Related
This seems like an issue that won't last for long, but I'm looking for a final solution for opening up a native file picker on all platforms including Desktop without installing go-flutter.
Currently I'm using file_selector plugin by Flutter.dev and it's working for web and macOS (I have yet to test on windows/linux). When I try to run it on an iOS simulator, I get an error.
MissingPluginException(No implementation found for method openFile on channel plugins.flutter.io/file_selector)
I guess this is because it doesn't support iOS and maybe this plugin is just for desktop and web.
I also saw the file_picker plugin but it requires go-flutter to be installed on the desktop to get that to work. I'd rather have nice hot-reloads/builds directly in Android studio and not need go-flutter commands to run the desktop simulations.
Is there a one stop solution that basically combines these two things? I'm pretty new to Flutter, so maybe I'm missing a really easy way to use one or the other library depending on platform with some kind of conditional? It seems kind of messy to go that route, but it could work for now.
The other answer that I'm looking for is a good solution to using file_picker plugin for desktop in an easier way, such as how to use go-flutter seamlessly.
I ended up using a conditional to detect the Platform and will use file_picker for iOS/Android and then file_selector for web/desktop.
So far it's working well. I'll probably write a quick wrapper to abstract that out of the main code.
It seems like the easiest way to do this currently is to use a package like file_picker_desktop for Windows/Mac/Linux and file_picker on Android/iOS/Web. They seem to have similar APIs, so you can make a wrapper function on your app that calls the correct library.
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart' show kIsWeb;
if (Platform.isAndroid || Platform.isIOS)) {
// use file_picker plugin
FilePickerResult? result = await FilePicker.platform.pickFiles();
if(result != null) {
File file = File(result.files.single.path);
} else {
// User canceled the picker
}
} else if(kIsWeb) {
// use file_selector plugin
final typeGroup = XTypeGroup(label: 'images', extensions: ['jpg',
'png']);
final file = await openFile(acceptedTypeGroups: [typeGroup]);
} else {
// use file_picker_desktop
try {
final result = await pickFiles(
allowMultiple: false,
);
if (result != null) {
File file = File(result.files.single.path);
} else {
// User canceled the picker
}
} catch (e) {
print(e);
}
}
The current latest version of the file_picker plugin (4.4.0) claims to work on all supported platforms, incl. desktop and Web.
I trie with Flutter 2.10.2 on macOS 12.2.1 and it works well. Also successfully tested on Win10.
final path = await FilePicker.platform.saveFile(
dialogTitle: 'Expor data',
fileName: '_${bloc.lot}_${bloc.serial}.dat',
lockParentWindow: true,
);
Setting lockParentWindow makes the file picker application modale, btw.
I want to suport both android and web.
However I use Floor to cache data and it is not supported on web. So I want to make the database calls only on android, I don't want to cache the data on web. I want to ignore these lines on web.
Can I do this somehow?
void loadNews() async {
emit(Loading());
await newsCacheRepository.init(); //because of this line on web the data doesn't appear
var articles = await newsCacheRepository.getArticles();
emit(Loaded(articles));
refreshNews();
}
Future<void> refreshNews() async {
var articles = await newsNetworkRepository.getArticles();
emit(Loaded(articles));
newsCacheRepository.cacheArticles(articles);
}
You can use Hive which supports web too. Or you can always check for platform:
if(!kIsWeb){
// Your code that doesn't support web
}
But to be safe side, you can wrap the code on a try-catch block.
But I prefer using Hive which supports web.
This is the similar question (with wrong title): Flutter Web - How to reload currently Active Page
Running the Flutter web app as PWA I need to allow users to initiate refresh and thus update the (web) app.
Unfortunately import 'dart:html' as html; is not allowed anymore when having the same codebase for web and mobile native apps. So following code is not the option:
RaisedButton(
child: Text("Update me"),
onPressed: () {
if (kIsWeb) html.window.location.reload();
},
),
What is the correct approach?
EDIT:
I have managed to use 'dart:js' in the same codebase for pwa and mobile using conditional imports. This means I can call JavaScript from within the Flutter code. Unfortunately location.reload(true); does not reload the PWA.
Ideally I would have the Flutter approach for the PWA reload / update or JavaScript workaround.
EDIT2:
The whole issue is within the PWA handling of refresh button / window reload.
Unfortunately service worker's skipWaiting() can be called only from within service worker ( https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#skip_the_waiting_phase )
The correct approach seams to be sending the skipWaiting message to the new instance of the service worker.
However skipWaiting is not yet fully supported on some browsers (iOS?) so the safer approach seams to be just unregistering the worker...
I ended up using following to stop the worker and reload.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistration().then(swr => {swr.unregister().then(
() => {location.reload(true)}
)});
} else {location.reload(true)}
Related:
Access service worker skipWaiting from within App build with Webpack+Workbox
https://medium.com/#nekrtemplar/self-destroying-serviceworker-73d62921d717
https://redfin.engineering/how-to-fix-the-refresh-button-when-using-service-workers-a8e27af6df68
https://deanhume.com/displaying-a-new-version-available-progressive-web-app/
Flutter 2.10.3
In the newest version of Flutter (2.10.3 at the time of writing this), PWAs will automatically update when the serviceWorkerVersion changes in your index.html file.
From this issue, Jonah Williams comments:
"The app will update after the new version has been downloaded and the page revisited."
You can read more about general PWA support by Flutter here. For the pull request that added server worker support, see this.
If you want to allow users to manually fetch a new version of your app, you can use the universal_html package which works on all platforms.
import 'package:universal_html/html.dart' as html;
onTap: () {
html.window.location.reload();
},
Keep an eye on this issue for an improvement to how server worker caching is handled.
A work around would be to navigate to your apps entry point route '/'
`Navigator.of(context).pushNamedAndRemoveUntil(
'/,
(Route<dynamic> route) => false
);`
I maintain a set of web versions myself. When I upgrade my web application, I will upgrade the version level. Every time I start homePage, I will request the version number of the remote configuration to match the version number of the current PWA application. When the local version number is lower than the remote version number, I will refresh
//pubspec.yaml universal_html: ***
import 'package:universal_html/html.dart' as html;
getWebVersion() async{
var res= await dio.get(web_version);
if (locaVersion < res.version) {
html.window.location.reload();
}
}
I'm currently searching for a while how I can implement the following feature with Flutter for Android and IOS apps:
When the application is opened the first time after a version update I'd like to show a dialog with the information what's new in the new version / what has been fixed etc. - some kind of release notes.
I know various ways to show this dialog on application start; but how can I ensure that this is done only one time after the app version changed?
I was thinking about storing the various version messages as texts on the remote backend and give them an unique ID; via shared preferences or other persistance the last shown message ID could be stored and compared to the last available one.
But I see this feature in so many apps that I suppose there's a "standard way" to do this or maybe a package which supports this.
Kind regards
Michael
I don't know if there is a standard way of doing it, but here is a simple way to do it.
Include "package_info" plugin in your project.
import 'package:package_info/package_info.dart';
PackageInfo.fromPlatform().then((PackageInfo packageInfo) {
String runningVersion = packageInfo.version;
//compare runningVersion to the one saved in sharedPrefs
//if they don't match show the message
//then save runningVersion to sharedPrefs
});
by package_info package, you can figure out the current version of the application.
if the user updates the application by release centers(like google play), information stored in sharedPreference, remains. so you can save the application's version in sharedPreference and every time the app opens check the current version with info saved in sharedPreference if is the same no need for showing release note but if the current version is higher than the stored version, show release note and update sharePreference with the new version.
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String currentVersion=packageInfo.version.toString();
SharedPreferences pref= await SharedPreferences.getInstance();
String latestAppVersion= pref.getString("latestAppVersion");
if(currentVersion.toInt()>latestAppVersion.toInt()){
//show release note
//update sharedPreferences with currentVersion
}
In Android, if I have the information I want to persist across sessions I know I can use SharedPreferences or create a SQLite database or even write a file to the device and read it in later.
Is there a way to save and restore data like this just using Flutter? Or would I need to write device-specific code for Android and iOS like in the services example?
There are a few options:
Read and write files: https://flutter.io/reading-writing-files/
SQLite via a Flutter plugin: https://github.com/tekartik/sqflite
SQLCipher via a Flutter plugin: https://github.com/drydart/flutter_sqlcipher
SharedPreferences via a Flutter plugin: https://github.com/flutter/plugins/tree/master/packages/shared_preferences
Localstore via a Flutter plugin: https://pub.dev/packages/localstore
If you are in a situation where you wanna save a small value that you wanna refer later. then you should store your data as key-value data using shared_preferences
Storing key-value data on disk
but if you want to store large data you should go with SQLITE
How to get Started with SQLITE in Flutter
however you can always use firebase database which is available offline
how to add firebase to your flutter project
Firebase for Flutter Codelab from google
Since we are talking about local storage you can always read and write files to the disk
Reading and Writing Files
Other solutions :
Simple Embedded Application Store database
A Flutter plugin to store data in secure storage
A late answer but I hope it will help anyone visiting here later too😁..
I will provide categories to save and their respective best methods...
Shared Preferences
Use this when storing simple values on storage e.g Color theme, app language, last scroll position(in reading apps).. these are simple settings that you would want to persist when the app restarts..
You could, however, use this to store large things(Lists, Maps, Images) but that would require serialization and deserialization.. To learn more on this deserialization and serialization go here.
Files
This helps a lot when you have data that is defined more by you for example log files, image files and maybe you want to export csv files.. I heard that this type of persistence can be washed by storage cleaners once disk runs out of space.. Am not sure as i have never seen it..
This also can store almost anything but with the help of serialization and deserialization..
Saving to a database
This is enormously helpful in data which is a bit complex. And I think this doesn't get washed up by disc cleaners as it is stored in AppData(for android)..
In this, your data is stored in an SQLite database. Its plugin is SQFLite.
Kinds of data that you might wanna put in here are like everything that can be represented by a database.
You can use shared preferences from flutter's official plugins.
https://github.com/flutter/plugins/tree/master/packages/shared_preferences
It uses Shared Preferences for Android, NSUserDefaults for iOS.
If you need to store just simple values like API token or login data (not passwords!), here is what I used:
import 'package:shared_preferences/shared_preferences.dart';
asyncFunc() async { // Async func to handle Futures easier; or use Future.then
SharedPreferences prefs = await SharedPreferences.getInstance();
}
...
// Set
prefs.setString('apiToken', token);
// Get
String token = prefs.getString('apiToken');
// Remove
prefs.remove('apiToken');
Don't forget to add shared_preferences dependency in your pubspec.yaml (preserve spacing format):
dependencies:
shared_preferences: any
You can use Localstorage
flutter pub add localstorage
1- Add dependency to pubspec.yaml (Change the version based on the last)
dependencies:
...
localstorage: ^4.0.0+1
2- Then run the following command
flutter packages get
3- import the localstorage :
import 'package:localstorage/localstorage.dart';
4- create an instance
class MainApp extends StatelessWidget {
final LocalStorage storage = new LocalStorage('localstorage_app');
...
}
Add item to lcoalstorage :
void addItemsToLocalStorage() {
storage.setItem('name', 'Abolfazl');
storage.setItem('family', 'Roshanzamir');
final info = json.encode({'name': 'Darush', 'family': 'Roshanzami'});
storage.setItem('info', info);
}
Get an item from lcoalstorage:
void getitemFromLocalStorage() {
final name = storage.getItem('name'); // Abolfazl
final family = storage.getItem('family'); // Roshanzamir
Map<String, dynamic> info = json.decode(storage.getItem('info'));
final info_name=info['name'];
final info_family=info['family'];
}
Delete an item from localstorage :
void removeItemFromLocalStorage() {
storage.deleteItem('name');
storage.deleteItem('family');
storage.deleteItem('info');
}
There are a few options:
Moor: Persistence library for Dart
https://pub.dev/packages/moor_flutter
Read and Write file
https://flutter.io/reading-writing-files/
Shared preferences plugin for flutter
https://pub.dev/packages/shared_preferences
SQlite for flutter
https://pub.dev/packages/sqflite
I was looking for the same, simple local storage but also with a reasonable level of security. The two solutions I've found that make the most sense are flutter_secure_storage (as mentioned by Raouf) for the small stuff, and hive for larger datasets.
I think If you are going to store large amount of data in local storage you can use sqflite library. It is very easy to setup and I have personally used for some test project and it works fine.
https://github.com/tekartik/sqflite
This a tutorial - https://proandroiddev.com/flutter-bookshelf-app-part-2-personal-notes-and-database-integration-a3b47a84c57
If you want to store data in cloud you can use firebase. It is solid service provide by google.
https://firebase.google.com/docs/flutter/setup
Hive (https://pub.dev/packages/hive) is very fast and flexible solution. But if you have experience with SQL; you can use SqfLite packages (https://pub.dev/packages/sqflite)