How to save to web local storage in flutter web - flutter

I have a web site built with flutter for web and currently, am trying to save to web local storage or cookie but can't seem to find any plugin or way to archive that.

You can use window.localStorage from dart:html
import 'dart:html';
class IdRepository {
final Storage _localStorage = window.localStorage;
Future save(String id) async {
_localStorage['selected_id'] = id;
}
Future<String> getId() async => _localStorage['selected_id'];
Future invalidate() async {
_localStorage.remove('selected_id');
}
}

shared_preferences dart package now supports local storage for the web from version 0.5.4.7+
Similar to shared preference on Android and iOS, the following is the code snippet for local storage on web
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart'; // rememeber to import shared_preferences: ^0.5.4+8
void main() {
runApp(MaterialApp(
home: Scaffold(
body: Center(
child: RaisedButton(
onPressed: _incrementCounter,
child: Text('Increment Counter'),
),
),
),
));
}
_incrementCounter() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int counter = (prefs.getInt('counter') ?? 0) + 1;
print('Pressed $counter times.');
await prefs.setInt('counter', counter);
}

I ran into a similar issue where my preferences weren't being persisted across runs. I thought window.localStorage was broken. I discovered that Flutter was simply launching with a new port number every time by default, so window.localStorage was getting wiped out.
This ticket talks about setting an explicit port. This fixed my issue, and now window.localStorage persists across runs:
https://github.com/Dart-Code/Dart-Code/issues/1769
In VS Code, you can set the port number in your launch.json file:
{
"name": "Flutter",
"request": "launch",
"type": "dart",
"args": ["--web-port", "8686"]
},

With flutter 1.10 we can use universal_html package:
import 'package:universal_html/html.dart';
// ...
// read preference
var myPref = window.localStorage['mypref'];
// ...
// write preference
window.localStorage['mypref'] = myPref;

I am using shared_preferences package to store data on local storage
class SessionManager {
static SessionManager manager;
static SharedPreferences _prefs;
static Future<SessionManager> getInstance() async {
if (manager == null || _prefs == null) {
manager = SessionManager();
_prefs = await SharedPreferences.getInstance();
}
return manager;
}
void putCityId(String cityId) {
_prefs.setString("KEY_CITY_ID", cityId);
}
String getCityId() {
return _prefs.getString("KEY_CITY_ID") ?? "";
}
}
shared_preferences store data for the current session only.
If you want to store data permanently then you should use cookie to store data.
import 'dart:html';
class CookieManager {
static CookieManager _manager;
static getInstance() {
if (_manager == null) {
_manager = CookieManager();
}
return _manager;
}
void _addToCookie(String key, String value) {
// 2592000 sec = 30 days.
document.cookie = "$key=$value; max-age=2592000; path=/;";
}
String _getCookie(String key) {
String cookies = document.cookie;
List<String> listValues = cookies.isNotEmpty ? cookies.split(";") : List();
String matchVal = "";
for (int i = 0; i < listValues.length; i++) {
List<String> map = listValues[i].split("=");
String _key = map[0].trim();
String _val = map[1].trim();
if (key == _key) {
matchVal = _val;
break;
}
}
return matchVal;
}
}

After upgrading to flutter 1.9, 'dart:html' is not compiled anymore as it is not part of dart SDK that shipped with Flutter.
We can use this package at the moment as it support Android, IOS and WEB:
crypted_preferences

Related

Accessing a value in multiple screens in flutter

I need to access a device id in multiple screens. To get device id from android and ios, am using package:device_info_plus. I don't want to call getId() method multiple times.
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
class DeviceInfo {
String? id;
Future<String?> getId() async {
var deviceInfo = DeviceInfoPlugin();
if (Platform.isIOS) {
var iosDeviceInfo = await deviceInfo.iosInfo;
return iosDeviceInfo.identifierForVendor; // unique ID on iOS
} else if (Platform.isAndroid) {
var androidDeviceInfo = await deviceInfo.androidInfo;
return androidDeviceInfo.androidId; // unique ID on Android
}
}
}
To access this object i registered using package:get_it and accessing the object using the get_it instance.is this correct way to access the objects in multiple screen using locator.get().id ?
// injection.dart file
import 'package:get_it/get_it.dart';
import 'package:orange/utils/device_info.dart';
final locator = GetIt.instance;
void init() async {
locator.registerSingleton<DeviceInfo>(DeviceInfo());
locator.get<DeviceInfo>().id= await locator.get<DeviceInfo>().getId();
}
Am using a flutter bloc in my app. What is the correct way to access values in multiple screens. Is my solution using get it package is correct? or shall i use shared_preferences.

How to save data of type bool in shared_preferences flutter

I created a separate calss page to working with shared preferences from all the different application pages. Save or edit data. I can save String data with ease, but I am facing a problem saving data of type bool. I try to save data of type bool to store the status of the user logged in or not. I searched for solutions for a long time, but couldn't find.
full code:
import 'package:shared_preferences/shared_preferences.dart';
class MyPreferences {
static const ID = "id";
static const STATE = "state";
static final MyPreferences instance = MyPreferences._internal();
static SharedPreferences _sharedPreferences;
String id = "";
String state = "";
MyPreferences._internal() {}
factory MyPreferences() => instance;
Future<SharedPreferences> get preferences async {
if (_sharedPreferences != null) {
return _sharedPreferences;
} else {
_sharedPreferences = await SharedPreferences.getInstance();
state = _sharedPreferences.getString(STATE);
id = _sharedPreferences.getString(ID);
return _sharedPreferences;
}
}
Future<bool> commit() async {
await _sharedPreferences.setString(STATE, state);
await _sharedPreferences.setString(ID, id);
}
Future<MyPreferences> init() async {
_sharedPreferences = await preferences;
return this;
}
}
Can somebody help me to make bool data.
thank you
Just add a couple methods to your class.
void updateLoggedIn(bool value) {
_sharedPreferences.setBool('logged_in', value);
}
bool isLoggedIn() => _sharedPreferences.getBool('logged_in') ?? false;
Then on login just run
MyPreferences.instance.updateLoggedIn(true)
And the same thing passing in false on logout.
Then whenever you want to check logged in status just run
if(MyPreferences.instance.isLoggedIn()) {
// whatever needs to happen
}

Flutter: I want to create a global variant. After close the app, it can be saved for the next time I open the app again

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Global {
static SharedPreferences _prefs;
static String key;
static Future init() async {
WidgetsFlutterBinding.ensureInitialized();
_prefs = await SharedPreferences.getInstance();
var _key = _prefs.getString("key");
if (_key != null) {
key = _key;
}
}
static saveKey(value) => _prefs.setString("key", value);
}
Here I create a global class. And I set and get value by this way.
Global.key = value; //set
Global.key; //get
But after I close the app, the value is gone. Any suggestions?
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Global {
static SharedPreferences _prefs;
static String key;
String get init => key; // adding a getter
static Future init() async {
WidgetsFlutterBinding.ensureInitialized();
_prefs = await SharedPreferences.getInstance();
var _key = _prefs.getString("key");
if (_key != null) {
key = _key;
}
}
static saveKey(value) => _prefs.setString("key", value);
}
Global().init =value; or Global.init = value; //set
Global().init; //get Global.init; //get
Are you ever calling your Init function in your Main method?
void main() async {
await Global.init();
runApp(MyApp());
}
Assuming you're doing that, the other mistake here is how you're trying to store your value. Use the saveKey function you have in place that's what it's there for. Throw this on one of your pages.
ElevatedButton(
onPressed: () {
Global.saveKey('Test');
},
child: Text('Test'),
),
Your Global class was perfectly fine, its how you were trying to use it. Try this though, only change is that I included a null check so you don't have to create an extra variable that's not used. Also added a print statement that will print the stored value when you re-start the app. It all works fine on my end.
class Global {
static SharedPreferences _prefs;
static String key;
static Future init() async {
WidgetsFlutterBinding.ensureInitialized();
_prefs = await SharedPreferences.getInstance();
key = _prefs.getString('key') ?? ''; // null check here
debugPrint(key);
}
static saveKey(value) => _prefs.setString("key", value);
}

flutter_inappwebview local https connection refused in flutter app

I'm trying to serve local content from assets through https, in order to gain access to features like webrtc which require ssl.
Since the local app server provided in flutter_inappwebview
does not handle ssl connections, I've replaced the InAppLocalHostServer class with InAppLocalHostSecureServer with the following code:
import 'dart:io';
import 'dart:async';
import 'package:flutter/services.dart' show rootBundle;
import 'package:mime/mime.dart';
class InAppLocalHostSecureServer {
HttpServer _server;
int _port = 8443;
InAppLocalHostSecureServer({int port = 8443}) {
this._port = port;
}
///Starts a server on http://localhost:[port]/.
///
///**NOTE for iOS**: For the iOS Platform, you need to add the `NSAllowsLocalNetworking` key with `true` in the `Info.plist` file (See [ATS Configuration Basics](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW35)):
///```xml
///<key>NSAppTransportSecurity</key>
///<dict>
/// <key>NSAllowsLocalNetworking</key>
/// <true/>
///</dict>
///```
///The `NSAllowsLocalNetworking` key is available since **iOS 10**.
Future<void> start() async {
if (this._server != null) {
throw Exception('Server already started on https://localhost:$_port');
}
var completer = Completer();
runZoned(() async {
SecurityContext context = new SecurityContext();
var chain = await rootBundle.load('assets/certificates/cert.pem');
var key = await rootBundle.load('assets/certificates/key.pem');
context.useCertificateChainBytes(chain.buffer.asInt8List());
context.usePrivateKeyBytes(key.buffer.asInt8List(), password: 'dartdart');
HttpServer.bindSecure('127.0.0.1', _port, context).then((server) {
print('Server running on https://localhost:' + _port.toString());
this._server = server;
server.listen((HttpRequest request) async {
print(request);
var body = List<int>();
var path = request.requestedUri.path;
path = (path.startsWith('/')) ? path.substring(1) : path;
path += (path.endsWith('/')) ? 'index.html' : '';
try {
body = (await rootBundle.load(path)).buffer.asUint8List();
} catch (e) {
print(e.toString());
request.response.close();
return;
}
var contentType = ['text', 'html'];
if (!request.requestedUri.path.endsWith('/') &&
request.requestedUri.pathSegments.isNotEmpty) {
var mimeType =
lookupMimeType(request.requestedUri.path, headerBytes: body);
if (mimeType != null) {
contentType = mimeType.split('/');
}
}
request.response.headers.contentType =
ContentType(contentType[0], contentType[1], charset: 'utf-8');
request.response.add(body);
request.response.close();
});
completer.complete();
});
}, onError: (e, stackTrace) {
print('Error: $e $stackTrace');
});
return completer.future;
}
///Closes the server.
Future<void> close() async {
if (this._server != null) {
await this._server.close(force: true);
print('Server running on http://localhost:$_port closed');
this._server = null;
}
}
}
Most of the code is a copy paste of the original class.
What I changed is that I call HttpServer.bindSecure instead of HttpServer.bind and I provide openssl certificate and key.
The server seems to start without error logged in the console, but I cannot access it.
Here is the client code that try to access a local url:
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'InAppLocalHostSecureServer.dart';
class WebAudioTest extends StatefulWidget {
#override
_WebAudioTestState createState() => _WebAudioTestState();
}
class _WebAudioTestState extends State<WebAudioTest> {
InAppWebViewController webView;
InAppLocalHostSecureServer localhostServer;
String url = "https://127.0.0.1:8443/assets/web/index.html";
#override
void initState() {
super.initState();
this.init();
}
void init() async {
this.localhostServer = new InAppLocalHostSecureServer();
await localhostServer.start();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Web Audio Test'),
),
body: InAppWebView(
initialUrl: url,
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
)),
onWebViewCreated: (InAppWebViewController c) {
webView = c;
},
onConsoleMessage: (controller, consoleMessage) {
print("CONSOLE MESSAGE: " + consoleMessage.message);
},
),
);
}
}
No error appears in the console but the flutter page display the following error message:
net::ERR_CONNECTION_REFUSED
Any help is welcome.
Ok, to answer my own questions:
the problem I had was simply that I build the InAppWebView too early, before the server has finished to launch. The solution is easy, just set a flag to true when the server is launched, and create the InAppWebView only when the flag is true.
Beside this, WebRTC works without https on localhost, I tested it on Android and iOS. So no need for local https for this use case.
But anyway if for any other reason someone needs to to serve https local content, the code in this post can serve as a basis for this.

SharedPreferences.getInstance() is always returning null

Coming from Object Oriented Programming Background, I planned on making a dedicated Settings Class to store certain basic data about the app.
I planned on starting with saving the theme of the application using SharedPreferences and LocalStorage.
However, SharedPreferences.getInstance() always seems to be returning null.
I have tried simply running, running in Debug mode, having a separate async method to load the SharedPreferences and returning a Future which is unwrapped using .then(). I can't seem to figure out why I am always getting null from SharedPreferences.getInstance() in the AppSettings.getInstance() method that I have written.
import 'package:shared_preferences/shared_preferences.dart';
import 'package:localstorage/localstorage.dart';
import 'package:flutter/material.dart';
class AppSettings {
// Singleton Instance
static AppSettings _appSettings;
// For First Launch Recognition
bool _initialize;
// Storage instances for persistent settings storage
static SharedPreferences _prefs;
static LocalStorage _dayColors = new LocalStorage('_dayColors');
static LocalStorage _nightColors = new LocalStorage('_nightColors');
// App Settings
bool _nightTheme;
Color _dayBgColor;
Color _primaryDayColor;
Color _secondaryDayColor;
Color _accentDayColor;
Color _nightBgColor;
Color _primaryNightColor;
Color _secondaryNightColor;
Color _accentNightColor;
static AppSettings getInstance() {
SharedPreferences.getInstance().then((prefs) => _prefs = prefs);
_appSettings ??= AppSettings._();
return _appSettings;
}
///
/// Initialize App Settings
///
AppSettings._() {
_checkIfFirstLaunch();
if (_initialize) {
_loadDefaultSettings();
_saveSettings();
} else {
_loadSettings();
}
}
_checkIfFirstLaunch() {
try {
_initialize = _prefs.getBool("_initialize");
} catch (e) {
_initialize = true;
}
}
_loadSettings() {
_nightTheme = _prefs.getBool("_nightTheme");
_dayColors.ready.then((_) => _loadDayColors());
_nightColors.ready.then((_) => _loadNightColors());
}
_loadDefaultSettings() {
_nightTheme = false;
_dayBgColor = Colors.white;
_primaryDayColor = Colors.blue;
_secondaryDayColor = Colors.lightBlue;
_accentDayColor = Colors.blueAccent;
_nightBgColor = Colors.black54;
_primaryNightColor = Colors.green;
_secondaryNightColor = Colors.lightGreen;
_accentNightColor = Colors.amber;
}
_saveSettings() {
_prefs.setBool("_nightTheme", _nightTheme);
_dayColors.ready.then((_) => _saveDayColors());
_nightColors.ready.then((_) => _saveNightColors());
}
}
SharedPreferences.getInstance() should return SharedPreferences singleton instance. It keeps returning null.
Your function is async and your callback (then) executes after of the return of getInstance(). You must change your function to use await and get the value of SharedPreferences.getInstance() instead use SharedPreferences.getInstance().then(...)
Look the documentation: https://pub.dev/documentation/shared_preferences/latest/shared_preferences/SharedPreferences/getInstance.html
Implementation of SharedPreferences.getInstance().
static Future<SharedPreferences> getInstance() async {
if (_instance == null) {
final Map<String, Object> preferencesMap =
await _getSharedPreferencesMap();
_instance = SharedPreferences._(preferencesMap);
}
return _instance;
}
Here is the code that worked based on Augusto's answer:
static Future<AppSettings> getInstance() async {
_prefs = await SharedPreferences.getInstance();
_appSettings ??= AppSettings._();
return _appSettings;
}