make stores easily serializable
This commit is contained in:
parent
19b2688316
commit
8cf3859ed8
|
@ -19,21 +19,14 @@ import 'util/extensions/brightness.dart';
|
|||
Future<void> main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
final configStore = ConfigStore();
|
||||
await configStore.load();
|
||||
|
||||
final accountsStore = AccountsStore();
|
||||
await accountsStore.load();
|
||||
final configStore = await ConfigStore.load();
|
||||
final accountsStore = await AccountsStore.load();
|
||||
|
||||
runApp(
|
||||
MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider.value(
|
||||
value: configStore,
|
||||
),
|
||||
ChangeNotifierProvider.value(
|
||||
value: accountsStore,
|
||||
),
|
||||
ChangeNotifierProvider.value(value: configStore),
|
||||
ChangeNotifierProvider.value(value: accountsStore),
|
||||
],
|
||||
child: const MyApp(),
|
||||
),
|
||||
|
|
|
@ -2,73 +2,57 @@ import 'dart:collection';
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:lemmy_api_client/v2.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../util/unawaited.dart';
|
||||
import 'shared_pref_keys.dart';
|
||||
|
||||
part 'accounts_store.g.dart';
|
||||
|
||||
/// Store that manages all accounts
|
||||
@JsonSerializable()
|
||||
class AccountsStore extends ChangeNotifier {
|
||||
static const prefsKey = 'v1:AccountsStore';
|
||||
static final _prefs = SharedPreferences.getInstance();
|
||||
|
||||
/// Map containing JWT tokens of specific users.
|
||||
/// If a token is in this map, the user is considered logged in
|
||||
/// for that account.
|
||||
/// `tokens['instanceHost']['username']`
|
||||
HashMap<String, HashMap<String, Jwt>> _tokens;
|
||||
@protected
|
||||
@JsonKey(defaultValue: {'lemmy.ml': {}})
|
||||
Map<String, Map<String, Jwt>> tokens;
|
||||
|
||||
/// default account for a given instance
|
||||
/// map where keys are instanceHosts and values are usernames
|
||||
HashMap<String, String> _defaultAccounts;
|
||||
@protected
|
||||
@JsonKey(defaultValue: {})
|
||||
Map<String, String> defaultAccounts;
|
||||
|
||||
/// default account for the app
|
||||
/// It is in a form of `username@instanceHost`
|
||||
String _defaultAccount;
|
||||
@protected
|
||||
String defaultAccount;
|
||||
|
||||
Future<void> load() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
static Future<AccountsStore> load() async {
|
||||
final prefs = await _prefs;
|
||||
|
||||
// I barely understand what I did. Long story short it casts a
|
||||
// raw json into a nested ObservableMap
|
||||
nestedMapsCast<T>(T f(String jwt)) => HashMap.of(
|
||||
(jsonDecode(prefs.getString(SharedPrefKeys.tokens) ??
|
||||
'{"lemmy.ml":{}}') as Map<String, dynamic>)
|
||||
?.map(
|
||||
(k, e) => MapEntry(
|
||||
k,
|
||||
HashMap.of(
|
||||
(e as Map<String, dynamic>)?.map(
|
||||
(k, e) => MapEntry(k, e == null ? null : f(e as String)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// set saved settings or create defaults
|
||||
_tokens = nestedMapsCast((json) => Jwt(json));
|
||||
_defaultAccount = prefs.getString(SharedPrefKeys.defaultAccount);
|
||||
_defaultAccounts = HashMap.of(Map.castFrom(
|
||||
jsonDecode(prefs.getString(SharedPrefKeys.defaultAccounts) ?? 'null')
|
||||
as Map<dynamic, dynamic> ??
|
||||
{},
|
||||
));
|
||||
|
||||
notifyListeners();
|
||||
return _$AccountsStoreFromJson(
|
||||
jsonDecode(prefs.getString(prefsKey) ?? '{}') as Map<String, dynamic>,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> save() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final prefs = await _prefs;
|
||||
|
||||
await prefs.setString(SharedPrefKeys.defaultAccount, _defaultAccount);
|
||||
await prefs.setString(
|
||||
SharedPrefKeys.defaultAccounts, jsonEncode(_defaultAccounts));
|
||||
await prefs.setString(SharedPrefKeys.tokens, jsonEncode(_tokens));
|
||||
await prefs.setString(prefsKey, jsonEncode(_$AccountsStoreToJson(this)));
|
||||
}
|
||||
|
||||
/// automatically sets default accounts
|
||||
void _assignDefaultAccounts() {
|
||||
// remove dangling defaults
|
||||
_defaultAccounts.entries
|
||||
defaultAccounts.entries
|
||||
.map((dft) {
|
||||
final instance = dft.key;
|
||||
final username = dft.value;
|
||||
|
@ -79,21 +63,21 @@ class AccountsStore extends ChangeNotifier {
|
|||
}
|
||||
})
|
||||
.toList()
|
||||
.forEach(_defaultAccounts.remove);
|
||||
if (_defaultAccount != null) {
|
||||
final instance = _defaultAccount.split('@')[1];
|
||||
final username = _defaultAccount.split('@')[0];
|
||||
.forEach(defaultAccounts.remove);
|
||||
if (defaultAccount != null) {
|
||||
final instance = defaultAccount.split('@')[1];
|
||||
final username = defaultAccount.split('@')[0];
|
||||
// if instance or username doesn't exist, remove
|
||||
if (!instances.contains(instance) ||
|
||||
!usernamesFor(instance).contains(username)) {
|
||||
_defaultAccount = null;
|
||||
defaultAccount = null;
|
||||
}
|
||||
}
|
||||
|
||||
// set local defaults
|
||||
for (final instanceHost in instances) {
|
||||
// if this instance is not in defaults
|
||||
if (!_defaultAccounts.containsKey(instanceHost)) {
|
||||
if (!defaultAccounts.containsKey(instanceHost)) {
|
||||
// select first account in this instance, if any
|
||||
if (!isAnonymousFor(instanceHost)) {
|
||||
setDefaultAccountFor(instanceHost, usernamesFor(instanceHost).first);
|
||||
|
@ -102,7 +86,7 @@ class AccountsStore extends ChangeNotifier {
|
|||
}
|
||||
|
||||
// set global default
|
||||
if (_defaultAccount == null) {
|
||||
if (defaultAccount == null) {
|
||||
// select first account of first instance
|
||||
for (final instanceHost in instances) {
|
||||
// select first account in this instance, if any
|
||||
|
@ -114,19 +98,19 @@ class AccountsStore extends ChangeNotifier {
|
|||
}
|
||||
|
||||
String get defaultUsername {
|
||||
if (_defaultAccount == null) {
|
||||
if (defaultAccount == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return _defaultAccount.split('@')[0];
|
||||
return defaultAccount.split('@')[0];
|
||||
}
|
||||
|
||||
String get defaultInstanceHost {
|
||||
if (_defaultAccount == null) {
|
||||
if (defaultAccount == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return _defaultAccount.split('@')[1];
|
||||
return defaultAccount.split('@')[1];
|
||||
}
|
||||
|
||||
String defaultUsernameFor(String instanceHost) {
|
||||
|
@ -134,16 +118,16 @@ class AccountsStore extends ChangeNotifier {
|
|||
return null;
|
||||
}
|
||||
|
||||
return _defaultAccounts[instanceHost];
|
||||
return defaultAccounts[instanceHost];
|
||||
}
|
||||
|
||||
Jwt get defaultToken {
|
||||
if (_defaultAccount == null) {
|
||||
if (defaultAccount == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final userTag = _defaultAccount.split('@');
|
||||
return _tokens[userTag[1]][userTag[0]];
|
||||
final userTag = defaultAccount.split('@');
|
||||
return tokens[userTag[1]][userTag[0]];
|
||||
}
|
||||
|
||||
Jwt defaultTokenFor(String instanceHost) {
|
||||
|
@ -151,13 +135,13 @@ class AccountsStore extends ChangeNotifier {
|
|||
return null;
|
||||
}
|
||||
|
||||
return _tokens[instanceHost][_defaultAccounts[instanceHost]];
|
||||
return tokens[instanceHost][defaultAccounts[instanceHost]];
|
||||
}
|
||||
|
||||
/// returns token for user of a certain id
|
||||
Jwt tokenForId(String instanceHost, int userId) =>
|
||||
_tokens.containsKey(instanceHost)
|
||||
? _tokens[instanceHost]
|
||||
tokens.containsKey(instanceHost)
|
||||
? tokens[instanceHost]
|
||||
.values
|
||||
.firstWhere((val) => val.payload.id == userId, orElse: () => null)
|
||||
: null;
|
||||
|
@ -167,12 +151,12 @@ class AccountsStore extends ChangeNotifier {
|
|||
return null;
|
||||
}
|
||||
|
||||
return _tokens[instanceHost][username];
|
||||
return tokens[instanceHost][username];
|
||||
}
|
||||
|
||||
/// sets globally default account
|
||||
void setDefaultAccount(String instanceHost, String username) {
|
||||
_defaultAccount = '$username@$instanceHost';
|
||||
defaultAccount = '$username@$instanceHost';
|
||||
|
||||
notifyListeners();
|
||||
save();
|
||||
|
@ -180,7 +164,7 @@ class AccountsStore extends ChangeNotifier {
|
|||
|
||||
/// sets default account for given instance
|
||||
void setDefaultAccountFor(String instanceHost, String username) {
|
||||
_defaultAccounts[instanceHost] = username;
|
||||
defaultAccounts[instanceHost] = username;
|
||||
|
||||
notifyListeners();
|
||||
save();
|
||||
|
@ -193,20 +177,20 @@ class AccountsStore extends ChangeNotifier {
|
|||
return true;
|
||||
}
|
||||
|
||||
return _tokens[instanceHost].isEmpty;
|
||||
return tokens[instanceHost].isEmpty;
|
||||
}
|
||||
|
||||
/// `true` if no added instance has an account assigned to it
|
||||
bool get hasNoAccount => loggedInInstances.isEmpty;
|
||||
|
||||
Iterable<String> get instances => _tokens.keys;
|
||||
Iterable<String> get instances => tokens.keys;
|
||||
|
||||
Iterable<String> get loggedInInstances =>
|
||||
instances.where((e) => !isAnonymousFor(e));
|
||||
|
||||
/// Usernames that are assigned to a given instance
|
||||
Iterable<String> usernamesFor(String instanceHost) =>
|
||||
_tokens[instanceHost].keys;
|
||||
tokens[instanceHost].keys;
|
||||
|
||||
/// adds a new account
|
||||
/// if it's the first account ever the account is
|
||||
|
@ -230,7 +214,7 @@ class AccountsStore extends ChangeNotifier {
|
|||
final userData =
|
||||
await lemmy.run(GetSite(auth: token.raw)).then((value) => value.myUser);
|
||||
|
||||
_tokens[instanceHost][userData.name] = token;
|
||||
tokens[instanceHost][userData.name] = token;
|
||||
|
||||
_assignDefaultAccounts();
|
||||
notifyListeners();
|
||||
|
@ -257,7 +241,7 @@ class AccountsStore extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
_tokens[instanceHost] = HashMap();
|
||||
tokens[instanceHost] = HashMap();
|
||||
|
||||
_assignDefaultAccounts();
|
||||
notifyListeners();
|
||||
|
@ -266,7 +250,7 @@ class AccountsStore extends ChangeNotifier {
|
|||
|
||||
/// This also removes all accounts assigned to this instance
|
||||
void removeInstance(String instanceHost) {
|
||||
_tokens.remove(instanceHost);
|
||||
tokens.remove(instanceHost);
|
||||
|
||||
_assignDefaultAccounts();
|
||||
notifyListeners();
|
||||
|
@ -274,7 +258,7 @@ class AccountsStore extends ChangeNotifier {
|
|||
}
|
||||
|
||||
void removeAccount(String instanceHost, String username) {
|
||||
_tokens[instanceHost].remove(username);
|
||||
tokens[instanceHost].remove(username);
|
||||
|
||||
_assignDefaultAccounts();
|
||||
notifyListeners();
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'accounts_store.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
AccountsStore _$AccountsStoreFromJson(Map<String, dynamic> json) {
|
||||
return AccountsStore()
|
||||
..tokens = (json['tokens'] as Map<String, dynamic>)?.map(
|
||||
(k, e) => MapEntry(
|
||||
k,
|
||||
(e as Map<String, dynamic>)?.map(
|
||||
(k, e) =>
|
||||
MapEntry(k, e == null ? null : Jwt.fromJson(e as String)),
|
||||
)),
|
||||
) ??
|
||||
{'lemmy.ml': {}}
|
||||
..defaultAccounts = (json['defaultAccounts'] as Map<String, dynamic>)?.map(
|
||||
(k, e) => MapEntry(k, e as String),
|
||||
) ??
|
||||
{}
|
||||
..defaultAccount = json['defaultAccount'] as String;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$AccountsStoreToJson(AccountsStore instance) =>
|
||||
<String, dynamic>{
|
||||
'tokens': instance.tokens,
|
||||
'defaultAccounts': instance.defaultAccounts,
|
||||
'defaultAccount': instance.defaultAccount,
|
||||
};
|
|
@ -1,12 +1,20 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import 'shared_pref_keys.dart';
|
||||
part 'config_store.g.dart';
|
||||
|
||||
/// Store managing user-level configuration such as theme or language
|
||||
@JsonSerializable()
|
||||
class ConfigStore extends ChangeNotifier {
|
||||
static const prefsKey = 'v1:ConfigStore';
|
||||
static final _prefs = SharedPreferences.getInstance();
|
||||
|
||||
ThemeMode _theme;
|
||||
@JsonKey(defaultValue: ThemeMode.system)
|
||||
ThemeMode get theme => _theme;
|
||||
set theme(ThemeMode theme) {
|
||||
_theme = theme;
|
||||
|
@ -15,6 +23,7 @@ class ConfigStore extends ChangeNotifier {
|
|||
}
|
||||
|
||||
bool _amoledDarkMode;
|
||||
@JsonKey(defaultValue: false)
|
||||
bool get amoledDarkMode => _amoledDarkMode;
|
||||
set amoledDarkMode(bool amoledDarkMode) {
|
||||
_amoledDarkMode = amoledDarkMode;
|
||||
|
@ -22,23 +31,17 @@ class ConfigStore extends ChangeNotifier {
|
|||
save();
|
||||
}
|
||||
|
||||
Future<void> load() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
// load saved settings or create defaults
|
||||
theme =
|
||||
_themeModeFromString(prefs.getString(SharedPrefKeys.theme) ?? 'system');
|
||||
amoledDarkMode = prefs.getBool(SharedPrefKeys.amoledDarkMode) ?? false;
|
||||
notifyListeners();
|
||||
static Future<ConfigStore> load() async {
|
||||
final prefs = await _prefs;
|
||||
|
||||
return _$ConfigStoreFromJson(
|
||||
jsonDecode(prefs.getString(prefsKey) ?? '{}') as Map<String, dynamic>,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> save() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final prefs = await _prefs;
|
||||
|
||||
await prefs.setString(SharedPrefKeys.theme, describeEnum(theme));
|
||||
await prefs.setBool(SharedPrefKeys.amoledDarkMode, amoledDarkMode);
|
||||
await prefs.setString(prefsKey, jsonEncode(_$ConfigStoreToJson(this)));
|
||||
}
|
||||
}
|
||||
|
||||
/// converts string to ThemeMode
|
||||
ThemeMode _themeModeFromString(String theme) =>
|
||||
ThemeMode.values.firstWhere((e) => describeEnum(e) == theme);
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'config_store.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
ConfigStore _$ConfigStoreFromJson(Map<String, dynamic> json) {
|
||||
return ConfigStore()
|
||||
..theme = _$enumDecodeNullable(_$ThemeModeEnumMap, json['theme']) ??
|
||||
ThemeMode.system
|
||||
..amoledDarkMode = json['amoledDarkMode'] as bool ?? false;
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$ConfigStoreToJson(ConfigStore instance) =>
|
||||
<String, dynamic>{
|
||||
'theme': _$ThemeModeEnumMap[instance.theme],
|
||||
'amoledDarkMode': instance.amoledDarkMode,
|
||||
};
|
||||
|
||||
T _$enumDecode<T>(
|
||||
Map<T, dynamic> enumValues,
|
||||
dynamic source, {
|
||||
T unknownValue,
|
||||
}) {
|
||||
if (source == null) {
|
||||
throw ArgumentError('A value must be provided. Supported values: '
|
||||
'${enumValues.values.join(', ')}');
|
||||
}
|
||||
|
||||
final value = enumValues.entries
|
||||
.singleWhere((e) => e.value == source, orElse: () => null)
|
||||
?.key;
|
||||
|
||||
if (value == null && unknownValue == null) {
|
||||
throw ArgumentError('`$source` is not one of the supported values: '
|
||||
'${enumValues.values.join(', ')}');
|
||||
}
|
||||
return value ?? unknownValue;
|
||||
}
|
||||
|
||||
T _$enumDecodeNullable<T>(
|
||||
Map<T, dynamic> enumValues,
|
||||
dynamic source, {
|
||||
T unknownValue,
|
||||
}) {
|
||||
if (source == null) {
|
||||
return null;
|
||||
}
|
||||
return _$enumDecode<T>(enumValues, source, unknownValue: unknownValue);
|
||||
}
|
||||
|
||||
const _$ThemeModeEnumMap = {
|
||||
ThemeMode.system: 'system',
|
||||
ThemeMode.light: 'light',
|
||||
ThemeMode.dark: 'dark',
|
||||
};
|
|
@ -1,8 +0,0 @@
|
|||
/// Collection of string constants that are keys to SharedPreferences
|
||||
class SharedPrefKeys {
|
||||
static const tokens = 'tokens';
|
||||
static const defaultAccount = 'defaultAccount';
|
||||
static const defaultAccounts = 'defaultAccounts';
|
||||
static const theme = 'theme';
|
||||
static const amoledDarkMode = 'amoledDarkMode';
|
||||
}
|
254
pubspec.lock
254
pubspec.lock
|
@ -1,6 +1,20 @@
|
|||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "14.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.41.2"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -29,6 +43,62 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.1"
|
||||
build:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.6.2"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_config
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.5"
|
||||
build_daemon:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_daemon
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.7"
|
||||
build_resolvers:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.5.3"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "6.1.7"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_collection
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.3.2"
|
||||
built_value:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "7.1.0"
|
||||
cached_network_image:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -50,6 +120,20 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0-nullsafety.1"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_util
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -57,6 +141,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0-nullsafety.1"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.6.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -85,6 +176,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.3"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.12"
|
||||
effective_dart:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -120,6 +218,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.2.1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.10.11"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -205,6 +310,20 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: graphs
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -212,6 +331,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.2"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -247,13 +373,34 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.16.1"
|
||||
json_annotation:
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.5"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.6.2"
|
||||
json_annotation:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: json_annotation
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
json_serializable:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: json_serializable
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.5.1"
|
||||
latinize:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -268,6 +415,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.12.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.11.4"
|
||||
markdown:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -296,6 +450,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.3.0-nullsafety.3"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.7"
|
||||
modal_bottom_sheet:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -310,6 +471,20 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.4"
|
||||
node_interop:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: node_interop
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
node_io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: node_io
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
octo_image:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -317,6 +492,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.3.0"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.9.3"
|
||||
package_info:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -401,6 +583,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
process:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -415,6 +604,27 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "4.3.3"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.4.4"
|
||||
pubspec_parse:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pubspec_parse
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.8"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: quiver
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
rxdart:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -464,11 +674,32 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.2+3"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.7.9"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.4+1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
source_gen:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.10+3"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -504,6 +735,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.1"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_transform
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -539,6 +777,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.29"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: timing
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.1+3"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -602,6 +847,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0-nullsafety.3"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.7+15"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -46,6 +46,7 @@ dependencies:
|
|||
fuzzy: <1.0.0
|
||||
lemmy_api_client: ^0.12.0
|
||||
matrix4_transform: ^1.1.7
|
||||
json_annotation: ^3.1.1
|
||||
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
@ -59,6 +60,8 @@ dev_dependencies:
|
|||
sdk: flutter
|
||||
effective_dart: ^1.0.0
|
||||
flutter_launcher_icons: ^0.8.1
|
||||
json_serializable: ^3.5.1
|
||||
build_runner: ^1.11.1
|
||||
|
||||
flutter_icons:
|
||||
android: true
|
||||
|
|
Loading…
Reference in New Issue