Migrated from localstore to shared preferences
(only for user, server and theme) This was done, because localstore is somewhat inconsistent in terms of events on different platforms. Also storing user, server and theme using shared-preferences should fit into flutters ecosystem a little better
This commit is contained in:
parent
e492a3f8ce
commit
1af8d6f068
9 changed files with 183 additions and 127 deletions
|
@ -1,7 +1,8 @@
|
||||||
import 'package:localstore/localstore.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
import 'package:http/http.dart' as http;
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
const String wellKnownPath = "/.well-known/outbag/server";
|
const String wellKnownPath = "/.well-known/outbag/server";
|
||||||
const int defaultPort = 7223;
|
const int defaultPort = 7223;
|
||||||
|
|
||||||
|
@ -48,15 +49,32 @@ class OutbagServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> toDisk() async {
|
Future<void> toDisk() async {
|
||||||
final db = Localstore.instance;
|
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
await db.collection('meta').doc('server').set(toMap());
|
|
||||||
|
await prefs.setString('server-host', host);
|
||||||
|
await prefs.setInt('server-port', port);
|
||||||
|
await prefs.setString('server-path', path);
|
||||||
|
await prefs.setString('server-tag', tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<OutbagServer> fromDisk() async {
|
static Future<OutbagServer> fromDisk() async {
|
||||||
final db = Localstore.instance;
|
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
final data = await db.collection('meta').doc('server').get();
|
|
||||||
|
|
||||||
return OutbagServer.fromMap(data!);
|
return OutbagServer(
|
||||||
|
path: prefs.getString('server-path')!,
|
||||||
|
port: prefs.getInt('server-port')!,
|
||||||
|
tag: prefs.getString('server-tag')!,
|
||||||
|
host: prefs.getString('server-host')!
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> removeDisk() async {
|
||||||
|
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
|
||||||
|
await prefs.remove('server-host');
|
||||||
|
await prefs.remove('server-port');
|
||||||
|
await prefs.remove('server-path');
|
||||||
|
await prefs.remove('server-tag');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:localstore/localstore.dart';
|
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
class AppTheme {
|
class AppTheme {
|
||||||
ThemeMode mode;
|
ThemeMode mode;
|
||||||
|
@ -47,27 +47,14 @@ class AppTheme {
|
||||||
return [AppTheme.auto, AppTheme.light, AppTheme.dark];
|
return [AppTheme.auto, AppTheme.light, AppTheme.dark];
|
||||||
}
|
}
|
||||||
|
|
||||||
static listen(Function(Map<String, dynamic>) cb) {
|
|
||||||
final db = Localstore.instance;
|
|
||||||
final stream = db.collection('settings').stream;
|
|
||||||
stream.listen(cb);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> toDisk() async {
|
Future<void> toDisk() async {
|
||||||
final db = Localstore.instance;
|
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
await db.collection('settings').doc('ui').set({'theme': mode.index});
|
await prefs.setInt('theme', mode.index);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<AppTheme> fromDisk() async {
|
static Future<AppTheme> fromDisk() async {
|
||||||
final db = Localstore.instance;
|
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
final doc = await db.collection('settings').doc('ui').get();
|
return AppTheme(ThemeMode.values[(prefs.getInt('theme')) ?? 0]);
|
||||||
try {
|
|
||||||
final index = doc?['theme'];
|
|
||||||
final mode = ThemeMode.values[index];
|
|
||||||
return AppTheme(mode);
|
|
||||||
} catch (_) {
|
|
||||||
return AppTheme(ThemeMode.system);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import 'package:localstore/localstore.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import './resolve_url.dart';
|
import './resolve_url.dart';
|
||||||
|
|
||||||
class User {
|
class User {
|
||||||
|
@ -10,36 +10,30 @@ class User {
|
||||||
final OutbagServer server;
|
final OutbagServer server;
|
||||||
|
|
||||||
Future<void> toDisk() async {
|
Future<void> toDisk() async {
|
||||||
final db = Localstore.instance;
|
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
await db
|
|
||||||
.collection('meta')
|
await prefs.setString('username', username);
|
||||||
.doc('auth')
|
await prefs.setString('password', password);
|
||||||
.set({'username': username, 'password': password});
|
|
||||||
await server.toDisk();
|
await server.toDisk();
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<User> fromDisk() async {
|
static Future<User> fromDisk() async {
|
||||||
final db = Localstore.instance;
|
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
final data = await db.collection('meta').doc('auth').get();
|
|
||||||
final server = await OutbagServer.fromDisk();
|
final server = await OutbagServer.fromDisk();
|
||||||
|
|
||||||
return User(
|
return User(
|
||||||
username: data?['username'],
|
username: prefs.getString('username')!,
|
||||||
password: data?['password'],
|
password: prefs.getString('password')!,
|
||||||
server: server,
|
server: server);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<void> removeDisk() async {
|
static Future<void> removeDisk() async {
|
||||||
final db = Localstore.instance;
|
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
await db.collection('meta').doc('auth').delete();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static listen(Function(Map<String, dynamic>) cb) async {
|
await prefs.remove('username');
|
||||||
final db = Localstore.instance;
|
await prefs.remove('password');
|
||||||
final stream = db.collection('meta').stream;
|
await OutbagServer.removeDisk();
|
||||||
stream.listen(cb);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String get humanReadable {
|
String get humanReadable {
|
||||||
|
|
|
@ -44,34 +44,7 @@ class _OutbagAppState extends State {
|
||||||
|
|
||||||
AppTheme theme = AppTheme.auto;
|
AppTheme theme = AppTheme.auto;
|
||||||
|
|
||||||
@override
|
void loadTheme() async {
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
|
||||||
// wait for user to be authorized
|
|
||||||
User.listen((_) async {
|
|
||||||
try {
|
|
||||||
final user = await User.fromDisk();
|
|
||||||
setState(() {
|
|
||||||
this.user = user;
|
|
||||||
});
|
|
||||||
fetchInfo(user);
|
|
||||||
} catch (_) {
|
|
||||||
// no userdata found
|
|
||||||
setState(() {
|
|
||||||
user = null;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
AppTheme.listen((_) async {
|
|
||||||
final theme = await AppTheme.fromDisk();
|
|
||||||
setState(() {
|
|
||||||
this.theme = theme;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// load theme
|
// load theme
|
||||||
try {
|
try {
|
||||||
final theme = await AppTheme.fromDisk();
|
final theme = await AppTheme.fromDisk();
|
||||||
|
@ -79,7 +52,9 @@ class _OutbagAppState extends State {
|
||||||
this.theme = theme;
|
this.theme = theme;
|
||||||
});
|
});
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadUser() async {
|
||||||
// load user
|
// load user
|
||||||
try {
|
try {
|
||||||
final user = await User.fromDisk();
|
final user = await User.fromDisk();
|
||||||
|
@ -95,6 +70,15 @@ class _OutbagAppState extends State {
|
||||||
user = null;
|
user = null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
loadTheme();
|
||||||
|
loadUser();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,20 +168,17 @@ class _OutbagAppState extends State {
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'signin',
|
name: 'signin',
|
||||||
path: 'signin',
|
path: 'signin',
|
||||||
builder: (context, state) =>
|
builder: (context, state) => AuthPage(mode: Mode.signin, refresh: loadUser),
|
||||||
const AuthPage(mode: Mode.signin),
|
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'signup',
|
name: 'signup',
|
||||||
path: 'signup',
|
path: 'signup',
|
||||||
builder: (context, state) =>
|
builder: (context, state) => AuthPage(mode: Mode.signup, refresh: loadUser),
|
||||||
const AuthPage(mode: Mode.signup),
|
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'signup-ota',
|
name: 'signup-ota',
|
||||||
path: 'signup-ota',
|
path: 'signup-ota',
|
||||||
builder: (context, state) =>
|
builder: (context, state) => AuthPage(mode: Mode.signupOTA, refresh: loadUser),
|
||||||
const AuthPage(mode: Mode.signupOTA),
|
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
|
@ -215,8 +196,7 @@ class _OutbagAppState extends State {
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'settings',
|
name: 'settings',
|
||||||
path: 'settings',
|
path: 'settings',
|
||||||
builder: (context, state) =>
|
builder: (context, state) => SettingsPage(refreshTheme: loadTheme)),
|
||||||
const SettingsPage()),
|
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: 'join-room',
|
path: 'join-room',
|
||||||
name: 'add-room',
|
name: 'add-room',
|
||||||
|
@ -239,26 +219,22 @@ class _OutbagAppState extends State {
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'edit-room',
|
name: 'edit-room',
|
||||||
path: 'edit',
|
path: 'edit',
|
||||||
builder: (context, state) =>
|
builder: (context, state) => EditRoomPage(
|
||||||
EditRoomPage(
|
state.params['server'] ?? '',
|
||||||
state.params['server'] ??
|
|
||||||
'',
|
|
||||||
state.params['id'] ?? '')),
|
state.params['id'] ?? '')),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'room-members',
|
name: 'room-members',
|
||||||
path: 'members',
|
path: 'members',
|
||||||
builder: (context, state) =>
|
builder: (context, state) =>
|
||||||
ManageRoomMembersPage(
|
ManageRoomMembersPage(
|
||||||
state.params['server'] ??
|
state.params['server'] ?? '',
|
||||||
'',
|
|
||||||
state.params['id'] ?? '')),
|
state.params['id'] ?? '')),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
name: 'room-permissions',
|
name: 'room-permissions',
|
||||||
path: 'roles',
|
path: 'roles',
|
||||||
builder: (context, state) =>
|
builder: (context, state) =>
|
||||||
EditRoomPermissionSetPage(
|
EditRoomPermissionSetPage(
|
||||||
state.params['server'] ??
|
state.params['server'] ?? '',
|
||||||
'',
|
|
||||||
state.params['id'] ?? '')),
|
state.params['id'] ?? '')),
|
||||||
])
|
])
|
||||||
]),
|
]),
|
||||||
|
|
|
@ -15,7 +15,8 @@ enum Mode {
|
||||||
|
|
||||||
class AuthPage extends StatefulWidget {
|
class AuthPage extends StatefulWidget {
|
||||||
final Mode mode;
|
final Mode mode;
|
||||||
const AuthPage({super.key, this.mode = Mode.signin});
|
Function refresh;
|
||||||
|
AuthPage({super.key, required this.mode, required this.refresh});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _AuthPageState();
|
State<StatefulWidget> createState() => _AuthPageState();
|
||||||
|
@ -53,7 +54,8 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const CircularProgressIndicator(),
|
const CircularProgressIndicator(),
|
||||||
Text(AppLocalizations.of(context)!.loading, style: textTheme.titleLarge),
|
Text(AppLocalizations.of(context)!.loading,
|
||||||
|
style: textTheme.titleLarge),
|
||||||
])))
|
])))
|
||||||
: Scaffold(
|
: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
@ -73,9 +75,12 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
keyboardType: TextInputType.url,
|
keyboardType: TextInputType.url,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.dns),
|
prefixIcon: const Icon(Icons.dns),
|
||||||
labelText: AppLocalizations.of(context)!.inputServerLabel,
|
labelText: AppLocalizations.of(context)!
|
||||||
hintText: AppLocalizations.of(context)!.inputServerHint,
|
.inputServerLabel,
|
||||||
helperText:AppLocalizations.of(context)!.inputServerHelp,
|
hintText:
|
||||||
|
AppLocalizations.of(context)!.inputServerHint,
|
||||||
|
helperText:
|
||||||
|
AppLocalizations.of(context)!.inputServerHelp,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -87,9 +92,12 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
keyboardType: TextInputType.emailAddress,
|
keyboardType: TextInputType.emailAddress,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.person),
|
prefixIcon: const Icon(Icons.person),
|
||||||
labelText: AppLocalizations.of(context)!.inputUsernameLabel,
|
labelText: AppLocalizations.of(context)!
|
||||||
hintText: AppLocalizations.of(context)!.inputUsernameHint,
|
.inputUsernameLabel,
|
||||||
helperText:AppLocalizations.of(context)!.inputUsernameHelp,
|
hintText: AppLocalizations.of(context)!
|
||||||
|
.inputUsernameHint,
|
||||||
|
helperText: AppLocalizations.of(context)!
|
||||||
|
.inputUsernameHelp,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -102,9 +110,12 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.lock),
|
prefixIcon: const Icon(Icons.lock),
|
||||||
labelText: AppLocalizations.of(context)!.inputPasswordLabel,
|
labelText: AppLocalizations.of(context)!
|
||||||
hintText: AppLocalizations.of(context)!.inputPasswordHint,
|
.inputPasswordLabel,
|
||||||
helperText:AppLocalizations.of(context)!.inputPasswordHelp,
|
hintText: AppLocalizations.of(context)!
|
||||||
|
.inputPasswordHint,
|
||||||
|
helperText: AppLocalizations.of(context)!
|
||||||
|
.inputPasswordHelp,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -120,9 +131,12 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.lock),
|
prefixIcon: const Icon(Icons.lock),
|
||||||
labelText: AppLocalizations.of(context)!.inputPasswordRepeatLabel,
|
labelText: AppLocalizations.of(context)!
|
||||||
hintText: AppLocalizations.of(context)!.inputPasswordRepeatHint,
|
.inputPasswordRepeatLabel,
|
||||||
helperText:AppLocalizations.of(context)!.inputPasswordRepeatHelp,
|
hintText: AppLocalizations.of(context)!
|
||||||
|
.inputPasswordRepeatHint,
|
||||||
|
helperText: AppLocalizations.of(context)!
|
||||||
|
.inputPasswordRepeatHelp,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -139,9 +153,12 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
keyboardType: TextInputType.visiblePassword,
|
keyboardType: TextInputType.visiblePassword,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.key),
|
prefixIcon: const Icon(Icons.key),
|
||||||
labelText: AppLocalizations.of(context)!.inputOTALabel,
|
labelText: AppLocalizations.of(context)!
|
||||||
hintText: AppLocalizations.of(context)!.inputOTAHint,
|
.inputOTALabel,
|
||||||
helperText:AppLocalizations.of(context)!.inputOTAHelp,
|
hintText: AppLocalizations.of(context)!
|
||||||
|
.inputOTAHint,
|
||||||
|
helperText: AppLocalizations.of(context)!
|
||||||
|
.inputOTAHelp,
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -166,7 +183,8 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
});
|
});
|
||||||
|
|
||||||
showSimpleSnackbar(scaffMgr,
|
showSimpleSnackbar(scaffMgr,
|
||||||
text: AppLocalizations.of(context)!.errorPasswordsDoNotMatch,
|
text: AppLocalizations.of(context)!
|
||||||
|
.errorPasswordsDoNotMatch,
|
||||||
action: AppLocalizations.of(context)!.dismiss);
|
action: AppLocalizations.of(context)!.dismiss);
|
||||||
|
|
||||||
_ctrPasswordRpt.clear();
|
_ctrPasswordRpt.clear();
|
||||||
|
@ -201,7 +219,8 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
});
|
});
|
||||||
|
|
||||||
showSimpleSnackbar(scaffMgr,
|
showSimpleSnackbar(scaffMgr,
|
||||||
text: AppLocalizations.of(context)!.errorInvalidServer(_ctrServer.text),
|
text: AppLocalizations.of(context)!
|
||||||
|
.errorInvalidServer(_ctrServer.text),
|
||||||
action: AppLocalizations.of(context)!.dismiss);
|
action: AppLocalizations.of(context)!.dismiss);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -248,6 +267,7 @@ class _AuthPageState extends State<AuthPage> {
|
||||||
password: password,
|
password: password,
|
||||||
server: server)
|
server: server)
|
||||||
.toDisk();
|
.toDisk();
|
||||||
|
widget.refresh();
|
||||||
}, after: () {
|
}, after: () {
|
||||||
setState(() {
|
setState(() {
|
||||||
showSpinner = false;
|
showSpinner = false;
|
||||||
|
|
|
@ -9,7 +9,8 @@ import 'package:provider/provider.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class SettingsPage extends StatefulWidget {
|
class SettingsPage extends StatefulWidget {
|
||||||
const SettingsPage({super.key});
|
Function refreshTheme;
|
||||||
|
SettingsPage({super.key, required this.refreshTheme});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() => _SettingsPageState();
|
State<StatefulWidget> createState() => _SettingsPageState();
|
||||||
|
@ -113,6 +114,7 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||||
onSelectionChanged: (item) async {
|
onSelectionChanged: (item) async {
|
||||||
try {
|
try {
|
||||||
await item.first.toDisk();
|
await item.first.toDisk();
|
||||||
|
widget.refreshTheme();
|
||||||
} catch (_) {}
|
} catch (_) {}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
@ -6,7 +6,9 @@ import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
|
import shared_preferences_foundation
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
}
|
}
|
||||||
|
|
56
pubspec.lock
56
pubspec.lock
|
@ -381,6 +381,62 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.5"
|
version: "6.0.5"
|
||||||
|
shared_preferences:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: shared_preferences
|
||||||
|
sha256: "78528fd87d0d08ffd3e69551173c026e8eacc7b7079c82eb6a77413957b7e394"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.20"
|
||||||
|
shared_preferences_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_android
|
||||||
|
sha256: ad423a80fe7b4e48b50d6111b3ea1027af0e959e49d485712e134863d9c1c521
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.17"
|
||||||
|
shared_preferences_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_foundation
|
||||||
|
sha256: "1e755f8583229f185cfca61b1d80fb2344c9d660e1c69ede5450d8f478fa5310"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.5"
|
||||||
|
shared_preferences_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_linux
|
||||||
|
sha256: "3a59ed10890a8409ad0faad7bb2957dab4b92b8fbe553257b05d30ed8af2c707"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.5"
|
||||||
|
shared_preferences_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_platform_interface
|
||||||
|
sha256: "824bfd02713e37603b2bdade0842e47d56e7db32b1dcdd1cae533fb88e2913fc"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.1"
|
||||||
|
shared_preferences_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_web
|
||||||
|
sha256: "0dc2633f215a3d4aa3184c9b2c5766f4711e4e5a6b256e62aafee41f89f1bfb8"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.6"
|
||||||
|
shared_preferences_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: shared_preferences_windows
|
||||||
|
sha256: "71bcd669bb9cdb6b39f22c4a7728b6d49e934f6cba73157ffa5a54f1eed67436"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.5"
|
||||||
sky_engine:
|
sky_engine:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description: flutter
|
description: flutter
|
||||||
|
|
|
@ -39,6 +39,7 @@ dependencies:
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
intl: any
|
intl: any
|
||||||
|
shared_preferences: ^2.0.20
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
Loading…
Reference in a new issue