mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2024-10-01 06:55:46 -04:00
add multiple accounts menu
This commit is contained in:
parent
b0d4e35c6f
commit
87bb1657c7
@ -2,8 +2,9 @@
|
|||||||
"app": {
|
"app": {
|
||||||
"title": "VeilidChat"
|
"title": "VeilidChat"
|
||||||
},
|
},
|
||||||
"app_bar": {
|
"menu": {
|
||||||
"settings_tooltip": "Settings"
|
"settings_tooltip": "Settings",
|
||||||
|
"add_account_tooltip": "Add Account"
|
||||||
},
|
},
|
||||||
"pager": {
|
"pager": {
|
||||||
"chats": "Chats",
|
"chats": "Chats",
|
||||||
|
@ -4,7 +4,9 @@ import 'package:veilid_support/veilid_support.dart';
|
|||||||
|
|
||||||
import '../../proto/proto.dart' as proto;
|
import '../../proto/proto.dart' as proto;
|
||||||
|
|
||||||
class AccountRecordCubit extends DefaultDHTRecordCubit<proto.Account> {
|
typedef AccountRecordState = proto.Account;
|
||||||
|
|
||||||
|
class AccountRecordCubit extends DefaultDHTRecordCubit<AccountRecordState> {
|
||||||
AccountRecordCubit({
|
AccountRecordCubit({
|
||||||
required super.open,
|
required super.open,
|
||||||
}) : super(decodeState: proto.Account.fromBuffer);
|
}) : super(decodeState: proto.Account.fromBuffer);
|
||||||
|
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:async_tools/async_tools.dart';
|
||||||
|
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
|
||||||
|
import 'package:veilid_support/veilid_support.dart';
|
||||||
|
|
||||||
|
import '../../account_manager/account_manager.dart';
|
||||||
|
|
||||||
|
typedef AccountRecordsBlocMapState
|
||||||
|
= BlocMapState<TypedKey, AsyncValue<AccountRecordState>>;
|
||||||
|
|
||||||
|
// Map of the logged in user accounts to their account information
|
||||||
|
class AccountRecordsBlocMapCubit extends BlocMapCubit<TypedKey,
|
||||||
|
AsyncValue<AccountRecordState>, AccountRecordCubit>
|
||||||
|
with StateMapFollower<UserLoginsState, TypedKey, UserLogin> {
|
||||||
|
AccountRecordsBlocMapCubit(AccountRepository accountRepository)
|
||||||
|
: _accountRepository = accountRepository;
|
||||||
|
|
||||||
|
// Add account record cubit
|
||||||
|
Future<void> _addAccountRecordCubit({required UserLogin userLogin}) async =>
|
||||||
|
add(() => MapEntry(
|
||||||
|
userLogin.superIdentityRecordKey,
|
||||||
|
AccountRecordCubit(
|
||||||
|
open: () => _accountRepository.openAccountRecord(userLogin))));
|
||||||
|
|
||||||
|
/// StateFollower /////////////////////////
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> removeFromState(TypedKey key) => remove(key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> updateState(TypedKey key, UserLogin value) async {
|
||||||
|
await _addAccountRecordCubit(userLogin: value);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
final AccountRepository _accountRepository;
|
||||||
|
}
|
@ -3,7 +3,7 @@ import 'dart:async';
|
|||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
import 'package:veilid_support/veilid_support.dart';
|
import 'package:veilid_support/veilid_support.dart';
|
||||||
|
|
||||||
import '../repository/account_repository/account_repository.dart';
|
import '../repository/account_repository.dart';
|
||||||
|
|
||||||
class ActiveLocalAccountCubit extends Cubit<TypedKey?> {
|
class ActiveLocalAccountCubit extends Cubit<TypedKey?> {
|
||||||
ActiveLocalAccountCubit(AccountRepository accountRepository)
|
ActiveLocalAccountCubit(AccountRepository accountRepository)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
export 'account_record_cubit.dart';
|
export 'account_record_cubit.dart';
|
||||||
|
export 'account_records_bloc_map_cubit.dart';
|
||||||
export 'active_local_account_cubit.dart';
|
export 'active_local_account_cubit.dart';
|
||||||
export 'local_accounts_cubit.dart';
|
export 'local_accounts_cubit.dart';
|
||||||
export 'user_logins_cubit.dart';
|
export 'user_logins_cubit.dart';
|
||||||
|
@ -4,7 +4,7 @@ import 'package:bloc/bloc.dart';
|
|||||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||||
|
|
||||||
import '../models/models.dart';
|
import '../models/models.dart';
|
||||||
import '../repository/account_repository/account_repository.dart';
|
import '../repository/account_repository.dart';
|
||||||
|
|
||||||
class LocalAccountsCubit extends Cubit<IList<LocalAccount>> {
|
class LocalAccountsCubit extends Cubit<IList<LocalAccount>> {
|
||||||
LocalAccountsCubit(AccountRepository accountRepository)
|
LocalAccountsCubit(AccountRepository accountRepository)
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:bloc/bloc.dart';
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:bloc_advanced_tools/bloc_advanced_tools.dart';
|
||||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||||
|
import 'package:veilid_support/veilid_support.dart';
|
||||||
|
|
||||||
import '../models/models.dart';
|
import '../models/models.dart';
|
||||||
import '../repository/account_repository/account_repository.dart';
|
import '../repository/account_repository.dart';
|
||||||
|
|
||||||
class UserLoginsCubit extends Cubit<IList<UserLogin>> {
|
typedef UserLoginsState = IList<UserLogin>;
|
||||||
|
|
||||||
|
class UserLoginsCubit extends Cubit<UserLoginsState>
|
||||||
|
with StateMapFollowable<UserLoginsState, TypedKey, UserLogin> {
|
||||||
UserLoginsCubit(AccountRepository accountRepository)
|
UserLoginsCubit(AccountRepository accountRepository)
|
||||||
: _accountRepository = accountRepository,
|
: _accountRepository = accountRepository,
|
||||||
super(accountRepository.getUserLogins()) {
|
super(accountRepository.getUserLogins()) {
|
||||||
@ -30,6 +35,16 @@ class UserLoginsCubit extends Cubit<IList<UserLogin>> {
|
|||||||
await _accountRepositorySubscription.cancel();
|
await _accountRepositorySubscription.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// StateMapFollowable /////////////////////////
|
||||||
|
@override
|
||||||
|
IMap<TypedKey, UserLogin> getStateMap(UserLoginsState state) {
|
||||||
|
final stateValue = state;
|
||||||
|
return IMap.fromIterable(stateValue,
|
||||||
|
keyMapper: (e) => e.superIdentityRecordKey, valueMapper: (e) => e);
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
final AccountRepository _accountRepository;
|
final AccountRepository _accountRepository;
|
||||||
late final StreamSubscription<AccountRepositoryChange>
|
late final StreamSubscription<AccountRepositoryChange>
|
||||||
_accountRepositorySubscription;
|
_accountRepositorySubscription;
|
||||||
|
@ -3,9 +3,9 @@ import 'dart:async';
|
|||||||
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
|
||||||
import 'package:veilid_support/veilid_support.dart';
|
import 'package:veilid_support/veilid_support.dart';
|
||||||
|
|
||||||
import '../../../../proto/proto.dart' as proto;
|
import '../../../proto/proto.dart' as proto;
|
||||||
import '../../../tools/tools.dart';
|
import '../../tools/tools.dart';
|
||||||
import '../../models/models.dart';
|
import '../models/models.dart';
|
||||||
|
|
||||||
const String veilidChatAccountKey = 'com.veilid.veilidchat';
|
const String veilidChatAccountKey = 'com.veilid.veilidchat';
|
||||||
|
|
@ -1 +1 @@
|
|||||||
export 'account_repository/account_repository.dart';
|
export 'account_repository.dart';
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
import 'package:awesome_extensions/awesome_extensions.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import '../../proto/proto.dart' as proto;
|
|
||||||
import '../../theme/theme.dart';
|
|
||||||
import '../account_manager.dart';
|
|
||||||
|
|
||||||
class SwitchAccountWidget extends StatelessWidget {
|
|
||||||
const SwitchAccountWidget({
|
|
||||||
super.key,
|
|
||||||
});
|
|
||||||
//
|
|
||||||
|
|
||||||
@override
|
|
||||||
// ignore: prefer_expression_function_bodies
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = Theme.of(context);
|
|
||||||
final scale = theme.extension<ScaleScheme>()!;
|
|
||||||
final textTheme = theme.textTheme;
|
|
||||||
|
|
||||||
final accountRepo = AccountRepository.instance;
|
|
||||||
final localAccounts = accountRepo.getLocalAccounts();
|
|
||||||
for (final la in localAccounts) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
return DecoratedBox(
|
|
||||||
decoration: ShapeDecoration(
|
|
||||||
color: scale.primaryScale.border,
|
|
||||||
shape:
|
|
||||||
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
|
|
||||||
child: Column(children: []),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -129,7 +129,11 @@ class VeilidChatApp extends StatelessWidget {
|
|||||||
BlocProvider<PreferencesCubit>(
|
BlocProvider<PreferencesCubit>(
|
||||||
create: (context) =>
|
create: (context) =>
|
||||||
PreferencesCubit(PreferencesRepository.instance),
|
PreferencesCubit(PreferencesRepository.instance),
|
||||||
)
|
),
|
||||||
|
BlocProvider<AccountRecordsBlocMapCubit>(
|
||||||
|
create: (context) =>
|
||||||
|
AccountRecordsBlocMapCubit(AccountRepository.instance)
|
||||||
|
..follow(context.read<UserLoginsCubit>())),
|
||||||
],
|
],
|
||||||
child: BackgroundTicker(
|
child: BackgroundTicker(
|
||||||
child: _buildShortcuts(
|
child: _buildShortcuts(
|
||||||
|
@ -140,5 +140,7 @@ class ChatListCubit extends DHTShortArrayCubit<proto.Chat>
|
|||||||
valueMapper: (e) => e.value);
|
valueMapper: (e) => e.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
final ActiveChatCubit activeChatCubit;
|
final ActiveChatCubit activeChatCubit;
|
||||||
}
|
}
|
||||||
|
262
lib/layout/home/drawer_menu/drawer_menu.dart
Normal file
262
lib/layout/home/drawer_menu/drawer_menu.dart
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_svg/flutter_svg.dart';
|
||||||
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:veilid_support/veilid_support.dart';
|
||||||
|
|
||||||
|
import '../../../account_manager/account_manager.dart';
|
||||||
|
import '../../../theme/theme.dart';
|
||||||
|
import '../../../tools/tools.dart';
|
||||||
|
import 'menu_item_widget.dart';
|
||||||
|
|
||||||
|
class DrawerMenu extends StatefulWidget {
|
||||||
|
const DrawerMenu({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State createState() => _DrawerMenuState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DrawerMenuState extends State<DrawerMenu> {
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _doLoginClick(TypedKey superIdentityRecordKey) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
void _doEditClick(TypedKey superIdentityRecordKey) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _wrapInBox({required Widget child, required Color color}) =>
|
||||||
|
DecoratedBox(
|
||||||
|
decoration: ShapeDecoration(
|
||||||
|
color: color,
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(16))),
|
||||||
|
child: child);
|
||||||
|
|
||||||
|
Widget _makeAccountWidget(
|
||||||
|
{required String name,
|
||||||
|
required bool loggedIn,
|
||||||
|
required void Function() clickHandler}) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final scale = theme.extension<ScaleScheme>()!.tertiaryScale;
|
||||||
|
final abbrev = name.split(' ').map((s) => s.isEmpty ? '' : s[0]).join();
|
||||||
|
late final String shortname;
|
||||||
|
if (abbrev.length >= 3) {
|
||||||
|
shortname = abbrev[0] + abbrev[1] + abbrev[abbrev.length - 1];
|
||||||
|
} else {
|
||||||
|
shortname = abbrev;
|
||||||
|
}
|
||||||
|
|
||||||
|
final avatar = AvatarImage(
|
||||||
|
size: 32,
|
||||||
|
backgroundColor: loggedIn ? scale.primary : scale.elementBackground,
|
||||||
|
foregroundColor: loggedIn ? scale.primaryText : scale.subtleText,
|
||||||
|
child: Text(shortname, style: theme.textTheme.titleLarge));
|
||||||
|
|
||||||
|
return MenuItemWidget(
|
||||||
|
title: name,
|
||||||
|
headerWidget: avatar,
|
||||||
|
titleStyle: theme.textTheme.titleLarge!,
|
||||||
|
foregroundColor: scale.primary,
|
||||||
|
backgroundColor: scale.elementBackground,
|
||||||
|
backgroundHoverColor: scale.hoverElementBackground,
|
||||||
|
backgroundFocusColor: scale.activeElementBackground,
|
||||||
|
borderColor: scale.border,
|
||||||
|
borderHoverColor: scale.hoverBorder,
|
||||||
|
borderFocusColor: scale.primary,
|
||||||
|
footerButtonIcon: loggedIn ? Icons.edit_outlined : Icons.login_outlined,
|
||||||
|
footerCallback: clickHandler,
|
||||||
|
footerButtonIconColor: scale.border,
|
||||||
|
footerButtonIconHoverColor: scale.hoverElementBackground,
|
||||||
|
footerButtonIconFocusColor: scale.activeElementBackground,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _getAccountList(
|
||||||
|
{required TypedKey? activeLocalAccount,
|
||||||
|
required AccountRecordsBlocMapState accountRecords}) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final scale = theme.extension<ScaleScheme>()!;
|
||||||
|
|
||||||
|
final accountRepo = AccountRepository.instance;
|
||||||
|
final localAccounts = accountRepo.getLocalAccounts();
|
||||||
|
//final userLogins = accountRepo.getUserLogins();
|
||||||
|
|
||||||
|
final loggedInAccounts = <Widget>[];
|
||||||
|
final loggedOutAccounts = <Widget>[];
|
||||||
|
|
||||||
|
for (final la in localAccounts) {
|
||||||
|
final superIdentityRecordKey = la.superIdentity.recordKey;
|
||||||
|
|
||||||
|
// See if this account is logged in
|
||||||
|
final acctRecord = accountRecords.get(superIdentityRecordKey);
|
||||||
|
if (acctRecord != null) {
|
||||||
|
// Account is logged in
|
||||||
|
final loggedInAccount = acctRecord.when(
|
||||||
|
data: (value) => _makeAccountWidget(
|
||||||
|
name: value.profile.name,
|
||||||
|
loggedIn: true,
|
||||||
|
clickHandler: () {
|
||||||
|
_doEditClick(superIdentityRecordKey);
|
||||||
|
}),
|
||||||
|
loading: () => _wrapInBox(
|
||||||
|
child: buildProgressIndicator(),
|
||||||
|
color: scale.grayScale.subtleBorder),
|
||||||
|
error: (err, st) => _wrapInBox(
|
||||||
|
child: errorPage(err, st), color: scale.errorScale.subtleBorder),
|
||||||
|
);
|
||||||
|
loggedInAccounts.add(loggedInAccount);
|
||||||
|
} else {
|
||||||
|
// Account is not logged in
|
||||||
|
final loggedOutAccount = _makeAccountWidget(
|
||||||
|
name: la.name,
|
||||||
|
loggedIn: false,
|
||||||
|
clickHandler: () {
|
||||||
|
_doLoginClick(superIdentityRecordKey);
|
||||||
|
});
|
||||||
|
loggedOutAccounts.add(loggedOutAccount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assemble main menu
|
||||||
|
final mainMenu = <Widget>[...loggedInAccounts, ...loggedOutAccounts];
|
||||||
|
|
||||||
|
// Return main menu widgets
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[...mainMenu],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _getButton(
|
||||||
|
{required Icon icon,
|
||||||
|
required ScaleColor scale,
|
||||||
|
required String tooltip,
|
||||||
|
required void Function()? onPressed}) =>
|
||||||
|
IconButton(
|
||||||
|
icon: icon,
|
||||||
|
color: scale.hoverBorder,
|
||||||
|
constraints: const BoxConstraints.expand(height: 64, width: 64),
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: WidgetStateProperty.resolveWith((states) {
|
||||||
|
if (states.contains(WidgetState.hovered)) {
|
||||||
|
return scale.hoverElementBackground;
|
||||||
|
}
|
||||||
|
if (states.contains(WidgetState.focused)) {
|
||||||
|
return scale.activeElementBackground;
|
||||||
|
}
|
||||||
|
return scale.elementBackground;
|
||||||
|
}), shape: WidgetStateProperty.resolveWith((states) {
|
||||||
|
if (states.contains(WidgetState.hovered)) {
|
||||||
|
return RoundedRectangleBorder(
|
||||||
|
side: BorderSide(color: scale.hoverBorder),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(16)));
|
||||||
|
}
|
||||||
|
if (states.contains(WidgetState.focused)) {
|
||||||
|
return RoundedRectangleBorder(
|
||||||
|
side: BorderSide(color: scale.primary),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(16)));
|
||||||
|
}
|
||||||
|
return RoundedRectangleBorder(
|
||||||
|
side: BorderSide(color: scale.border),
|
||||||
|
borderRadius: const BorderRadius.all(Radius.circular(16)));
|
||||||
|
})),
|
||||||
|
tooltip: tooltip,
|
||||||
|
onPressed: onPressed);
|
||||||
|
|
||||||
|
Widget _getBottomButtons() {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final scale = theme.extension<ScaleScheme>()!;
|
||||||
|
|
||||||
|
final settingsButton = _getButton(
|
||||||
|
icon: const Icon(Icons.settings),
|
||||||
|
tooltip: translate('menu.settings_tooltip'),
|
||||||
|
scale: scale.tertiaryScale,
|
||||||
|
onPressed: () async {
|
||||||
|
await GoRouterHelper(context).push('/settings');
|
||||||
|
}).paddingLTRB(0, 0, 16, 0);
|
||||||
|
|
||||||
|
final addButton = _getButton(
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
tooltip: translate('menu.add_account_tooltip'),
|
||||||
|
scale: scale.tertiaryScale,
|
||||||
|
onPressed: () async {
|
||||||
|
await GoRouterHelper(context).push('/new_account');
|
||||||
|
}).paddingLTRB(0, 0, 16, 0);
|
||||||
|
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [settingsButton, addButton]).paddingLTRB(0, 16, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Theme.of(context);
|
||||||
|
final scale = theme.extension<ScaleScheme>()!;
|
||||||
|
//final textTheme = theme.textTheme;
|
||||||
|
final accountRecords = context.watch<AccountRecordsBlocMapCubit>().state;
|
||||||
|
final activeLocalAccount = context.watch<ActiveLocalAccountCubit>().state;
|
||||||
|
final gradient = LinearGradient(
|
||||||
|
begin: Alignment.topLeft,
|
||||||
|
end: Alignment.bottomRight,
|
||||||
|
colors: [
|
||||||
|
scale.tertiaryScale.hoverElementBackground,
|
||||||
|
scale.tertiaryScale.subtleBackground,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return DecoratedBox(
|
||||||
|
decoration: ShapeDecoration(
|
||||||
|
shadows: [
|
||||||
|
BoxShadow(
|
||||||
|
color: scale.tertiaryScale.appBackground,
|
||||||
|
blurRadius: 6,
|
||||||
|
offset: const Offset(
|
||||||
|
0,
|
||||||
|
3,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
gradient: gradient,
|
||||||
|
shape: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.only(
|
||||||
|
topRight: Radius.circular(16),
|
||||||
|
bottomRight: Radius.circular(16)))),
|
||||||
|
child: Column(children: [
|
||||||
|
FittedBox(
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
child: Row(children: [
|
||||||
|
SvgPicture.asset(
|
||||||
|
height: 48,
|
||||||
|
'assets/images/icon.svg',
|
||||||
|
).paddingLTRB(0, 0, 16, 0),
|
||||||
|
SvgPicture.asset(
|
||||||
|
height: 48,
|
||||||
|
'assets/images/title.svg',
|
||||||
|
),
|
||||||
|
])),
|
||||||
|
const Spacer(),
|
||||||
|
_getAccountList(
|
||||||
|
activeLocalAccount: activeLocalAccount,
|
||||||
|
accountRecords: accountRecords),
|
||||||
|
_getBottomButtons(),
|
||||||
|
const Spacer(),
|
||||||
|
Text('Version $packageInfoVersion',
|
||||||
|
style: theme.textTheme.labelMedium!
|
||||||
|
.copyWith(color: scale.tertiaryScale.hoverBorder))
|
||||||
|
]).paddingAll(16),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
130
lib/layout/home/drawer_menu/menu_item_widget.dart
Normal file
130
lib/layout/home/drawer_menu/menu_item_widget.dart
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
class MenuItemWidget extends StatelessWidget {
|
||||||
|
const MenuItemWidget({
|
||||||
|
required this.title,
|
||||||
|
required this.titleStyle,
|
||||||
|
required this.foregroundColor,
|
||||||
|
this.headerWidget,
|
||||||
|
this.widthBox,
|
||||||
|
this.callback,
|
||||||
|
this.backgroundColor,
|
||||||
|
this.backgroundHoverColor,
|
||||||
|
this.backgroundFocusColor,
|
||||||
|
this.borderColor,
|
||||||
|
this.borderHoverColor,
|
||||||
|
this.borderFocusColor,
|
||||||
|
this.footerButtonIcon,
|
||||||
|
this.footerButtonIconColor,
|
||||||
|
this.footerButtonIconHoverColor,
|
||||||
|
this.footerButtonIconFocusColor,
|
||||||
|
this.footerCallback,
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => TextButton(
|
||||||
|
onPressed: () => callback,
|
||||||
|
style: TextButton.styleFrom(foregroundColor: foregroundColor).copyWith(
|
||||||
|
backgroundColor: WidgetStateProperty.resolveWith((states) {
|
||||||
|
if (states.contains(WidgetState.hovered)) {
|
||||||
|
return backgroundHoverColor;
|
||||||
|
}
|
||||||
|
if (states.contains(WidgetState.focused)) {
|
||||||
|
return backgroundFocusColor;
|
||||||
|
}
|
||||||
|
return backgroundColor;
|
||||||
|
}),
|
||||||
|
side: WidgetStateBorderSide.resolveWith((states) {
|
||||||
|
if (states.contains(WidgetState.hovered)) {
|
||||||
|
return borderColor != null
|
||||||
|
? BorderSide(color: borderHoverColor!)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
if (states.contains(WidgetState.focused)) {
|
||||||
|
return borderColor != null
|
||||||
|
? BorderSide(color: borderFocusColor!)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
return borderColor != null ? BorderSide(color: borderColor!) : null;
|
||||||
|
}),
|
||||||
|
shape: WidgetStateProperty.all(
|
||||||
|
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)))),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: <Widget>[
|
||||||
|
if (headerWidget != null) headerWidget!,
|
||||||
|
if (widthBox != null) widthBox!,
|
||||||
|
Expanded(
|
||||||
|
child: FittedBox(
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
|
fit: BoxFit.scaleDown,
|
||||||
|
child: Text(
|
||||||
|
title,
|
||||||
|
style: titleStyle,
|
||||||
|
).paddingAll(8)),
|
||||||
|
),
|
||||||
|
if (footerButtonIcon != null)
|
||||||
|
IconButton.outlined(
|
||||||
|
color: footerButtonIconColor,
|
||||||
|
focusColor: footerButtonIconFocusColor,
|
||||||
|
hoverColor: footerButtonIconHoverColor,
|
||||||
|
icon: Icon(
|
||||||
|
footerButtonIcon,
|
||||||
|
size: 24,
|
||||||
|
),
|
||||||
|
onPressed: footerCallback),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
super.debugFillProperties(properties);
|
||||||
|
properties
|
||||||
|
..add(DiagnosticsProperty<TextStyle?>('textStyle', titleStyle))
|
||||||
|
..add(ObjectFlagProperty<void Function()?>.has('callback', callback))
|
||||||
|
..add(DiagnosticsProperty<Color>('foregroundColor', foregroundColor))
|
||||||
|
..add(StringProperty('title', title))
|
||||||
|
..add(
|
||||||
|
DiagnosticsProperty<IconData?>('footerButtonIcon', footerButtonIcon))
|
||||||
|
..add(ObjectFlagProperty<void Function()?>.has(
|
||||||
|
'footerCallback', footerCallback))
|
||||||
|
..add(ColorProperty('footerButtonIconColor', footerButtonIconColor))
|
||||||
|
..add(ColorProperty(
|
||||||
|
'footerButtonIconHoverColor', footerButtonIconHoverColor))
|
||||||
|
..add(ColorProperty(
|
||||||
|
'footerButtonIconFocusColor', footerButtonIconFocusColor))
|
||||||
|
..add(ColorProperty('backgroundColor', backgroundColor))
|
||||||
|
..add(ColorProperty('backgroundHoverColor', backgroundHoverColor))
|
||||||
|
..add(ColorProperty('backgroundFocusColor', backgroundFocusColor))
|
||||||
|
..add(ColorProperty('borderColor', borderColor))
|
||||||
|
..add(ColorProperty('borderHoverColor', borderHoverColor))
|
||||||
|
..add(ColorProperty('borderFocusColor', borderFocusColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final Widget? headerWidget;
|
||||||
|
final Widget? widthBox;
|
||||||
|
final TextStyle titleStyle;
|
||||||
|
final Color foregroundColor;
|
||||||
|
final void Function()? callback;
|
||||||
|
final IconData? footerButtonIcon;
|
||||||
|
final void Function()? footerCallback;
|
||||||
|
final Color? backgroundColor;
|
||||||
|
final Color? backgroundHoverColor;
|
||||||
|
final Color? backgroundFocusColor;
|
||||||
|
final Color? borderColor;
|
||||||
|
final Color? borderHoverColor;
|
||||||
|
final Color? borderFocusColor;
|
||||||
|
final Color? footerButtonIconColor;
|
||||||
|
final Color? footerButtonIconHoverColor;
|
||||||
|
final Color? footerButtonIconFocusColor;
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
export 'drawer_menu/drawer_menu.dart';
|
||||||
export 'home_account_invalid.dart';
|
export 'home_account_invalid.dart';
|
||||||
export 'home_account_locked.dart';
|
export 'home_account_locked.dart';
|
||||||
export 'home_account_missing.dart';
|
export 'home_account_missing.dart';
|
||||||
|
@ -2,7 +2,7 @@ import 'package:awesome_extensions/awesome_extensions.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_translate/flutter_translate.dart';
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart';
|
||||||
|
|
||||||
import '../../../account_manager/account_manager.dart';
|
import '../../../account_manager/account_manager.dart';
|
||||||
import '../../../chat/chat.dart';
|
import '../../../chat/chat.dart';
|
||||||
@ -36,7 +36,7 @@ class _HomeAccountReadyMainState extends State<HomeAccountReadyMain> {
|
|||||||
return Column(children: <Widget>[
|
return Column(children: <Widget>[
|
||||||
Row(children: [
|
Row(children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(Icons.settings),
|
icon: const Icon(Icons.menu),
|
||||||
color: scale.secondaryScale.borderText,
|
color: scale.secondaryScale.borderText,
|
||||||
constraints: const BoxConstraints.expand(height: 64, width: 64),
|
constraints: const BoxConstraints.expand(height: 64, width: 64),
|
||||||
style: ButtonStyle(
|
style: ButtonStyle(
|
||||||
@ -46,7 +46,9 @@ class _HomeAccountReadyMainState extends State<HomeAccountReadyMain> {
|
|||||||
borderRadius: BorderRadius.all(Radius.circular(16))))),
|
borderRadius: BorderRadius.all(Radius.circular(16))))),
|
||||||
tooltip: translate('app_bar.settings_tooltip'),
|
tooltip: translate('app_bar.settings_tooltip'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
await GoRouterHelper(context).push('/settings');
|
final ctrl = context.read<ZoomDrawerController>();
|
||||||
|
await ctrl.toggle?.call();
|
||||||
|
//await GoRouterHelper(context).push('/settings');
|
||||||
}).paddingLTRB(0, 0, 8, 0),
|
}).paddingLTRB(0, 0, 8, 0),
|
||||||
asyncValueBuilder(account,
|
asyncValueBuilder(account,
|
||||||
(_, account) => ProfileWidget(profile: account.profile))
|
(_, account) => ProfileWidget(profile: account.profile))
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:awesome_extensions/awesome_extensions.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../../account_manager/account_manager.dart';
|
import '../../account_manager/account_manager.dart';
|
||||||
import '../../theme/theme.dart';
|
import '../../theme/theme.dart';
|
||||||
|
import 'drawer_menu/drawer_menu.dart';
|
||||||
import 'home_account_invalid.dart';
|
import 'home_account_invalid.dart';
|
||||||
import 'home_account_locked.dart';
|
import 'home_account_locked.dart';
|
||||||
import 'home_account_missing.dart';
|
import 'home_account_missing.dart';
|
||||||
@ -31,7 +36,7 @@ class HomeShellState extends State<HomeShell> {
|
|||||||
|
|
||||||
Widget buildWithLogin(BuildContext context) {
|
Widget buildWithLogin(BuildContext context) {
|
||||||
final activeLocalAccount = context.watch<ActiveLocalAccountCubit>().state;
|
final activeLocalAccount = context.watch<ActiveLocalAccountCubit>().state;
|
||||||
|
final accountRecordsCubit = context.watch<AccountRecordsBlocMapCubit>();
|
||||||
if (activeLocalAccount == null) {
|
if (activeLocalAccount == null) {
|
||||||
// If no logged in user is active, show the loading panel
|
// If no logged in user is active, show the loading panel
|
||||||
return const HomeNoActive();
|
return const HomeNoActive();
|
||||||
@ -39,6 +44,11 @@ class HomeShellState extends State<HomeShell> {
|
|||||||
|
|
||||||
final accountInfo =
|
final accountInfo =
|
||||||
AccountRepository.instance.getAccountInfo(activeLocalAccount);
|
AccountRepository.instance.getAccountInfo(activeLocalAccount);
|
||||||
|
final activeCubit =
|
||||||
|
accountRecordsCubit.tryOperate(activeLocalAccount, closure: (c) => c);
|
||||||
|
if (activeCubit == null) {
|
||||||
|
return waitingPage();
|
||||||
|
}
|
||||||
|
|
||||||
switch (accountInfo.status) {
|
switch (accountInfo.status) {
|
||||||
case AccountInfoStatus.noAccount:
|
case AccountInfoStatus.noAccount:
|
||||||
@ -48,14 +58,13 @@ class HomeShellState extends State<HomeShell> {
|
|||||||
case AccountInfoStatus.accountLocked:
|
case AccountInfoStatus.accountLocked:
|
||||||
return const HomeAccountLocked();
|
return const HomeAccountLocked();
|
||||||
case AccountInfoStatus.accountReady:
|
case AccountInfoStatus.accountReady:
|
||||||
return Provider<ActiveAccountInfo>.value(
|
return MultiProvider(providers: [
|
||||||
|
Provider<ActiveAccountInfo>.value(
|
||||||
value: accountInfo.activeAccountInfo!,
|
value: accountInfo.activeAccountInfo!,
|
||||||
child: BlocProvider(
|
),
|
||||||
create: (context) => AccountRecordCubit(
|
Provider<AccountRecordCubit>.value(value: activeCubit),
|
||||||
open: () async => AccountRepository.instance
|
Provider<ZoomDrawerController>.value(value: _zoomDrawerController),
|
||||||
.openAccountRecord(
|
], child: widget.accountReadyBuilder);
|
||||||
accountInfo.activeAccountInfo!.userLogin)),
|
|
||||||
child: widget.accountReadyBuilder));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,11 +73,38 @@ class HomeShellState extends State<HomeShell> {
|
|||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final scale = theme.extension<ScaleScheme>()!;
|
final scale = theme.extension<ScaleScheme>()!;
|
||||||
|
|
||||||
// XXX: eventually write account switcher here
|
final gradient = LinearGradient(
|
||||||
|
begin: Alignment.topCenter,
|
||||||
|
end: Alignment.bottomCenter,
|
||||||
|
colors: [
|
||||||
|
scale.tertiaryScale.subtleBackground,
|
||||||
|
scale.tertiaryScale.appBackground,
|
||||||
|
]);
|
||||||
|
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
child: DecoratedBox(
|
child: DecoratedBox(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(gradient: gradient),
|
||||||
color: scale.primaryScale.activeElementBackground),
|
child: ZoomDrawer(
|
||||||
child: buildWithLogin(context)));
|
controller: _zoomDrawerController,
|
||||||
|
//menuBackgroundColor: Colors.transparent,
|
||||||
|
menuScreen: const DrawerMenu(),
|
||||||
|
mainScreen: DecoratedBox(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: scale.primaryScale.activeElementBackground),
|
||||||
|
child: buildWithLogin(context)),
|
||||||
|
borderRadius: 24,
|
||||||
|
showShadow: true,
|
||||||
|
angle: 0,
|
||||||
|
drawerShadowsBackgroundColor: theme.shadowColor,
|
||||||
|
mainScreenOverlayColor: theme.shadowColor.withAlpha(0x3F),
|
||||||
|
openCurve: Curves.fastEaseInToSlowEaseOut,
|
||||||
|
// duration: const Duration(milliseconds: 250),
|
||||||
|
// reverseDuration: const Duration(milliseconds: 250),
|
||||||
|
menuScreenTapClose: true,
|
||||||
|
mainScreenScale: .25,
|
||||||
|
slideWidth: min(360, MediaQuery.of(context).size.width * 0.9),
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final ZoomDrawerController _zoomDrawerController = ZoomDrawerController();
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_translate/flutter_translate.dart';
|
import 'package:flutter_translate/flutter_translate.dart';
|
||||||
import 'package:intl/date_symbol_data_local.dart';
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
|
|
||||||
import 'package:stack_trace/stack_trace.dart';
|
import 'package:stack_trace/stack_trace.dart';
|
||||||
|
|
||||||
import 'app.dart';
|
import 'app.dart';
|
||||||
@ -45,6 +46,9 @@ void main() async {
|
|||||||
fallbackLocale: 'en_US', supportedLocales: ['en_US']);
|
fallbackLocale: 'en_US', supportedLocales: ['en_US']);
|
||||||
await initializeDateFormatting();
|
await initializeDateFormatting();
|
||||||
|
|
||||||
|
// Get package info
|
||||||
|
await initPackageInfo();
|
||||||
|
|
||||||
// Run the app
|
// Run the app
|
||||||
// Hot reloads will only restart this part, not Veilid
|
// Hot reloads will only restart this part, not Veilid
|
||||||
runApp(LocalizedApp(localizationDelegate,
|
runApp(LocalizedApp(localizationDelegate,
|
||||||
|
14
lib/tools/package_info.dart
Normal file
14
lib/tools/package_info.dart
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
|
||||||
|
String packageInfoAppName = '';
|
||||||
|
String packageInfoPackageName = '';
|
||||||
|
String packageInfoVersion = '';
|
||||||
|
String packageInfoBuildNumber = '';
|
||||||
|
|
||||||
|
Future<void> initPackageInfo() async {
|
||||||
|
final packageInfo = await PackageInfo.fromPlatform();
|
||||||
|
packageInfoAppName = packageInfo.appName;
|
||||||
|
packageInfoPackageName = packageInfo.packageName;
|
||||||
|
packageInfoVersion = packageInfo.version;
|
||||||
|
packageInfoBuildNumber = packageInfo.buildNumber;
|
||||||
|
}
|
@ -1,8 +1,10 @@
|
|||||||
|
|
||||||
export 'animations.dart';
|
export 'animations.dart';
|
||||||
export 'enter_password.dart';
|
export 'enter_password.dart';
|
||||||
export 'enter_pin.dart';
|
export 'enter_pin.dart';
|
||||||
export 'loggy.dart';
|
export 'loggy.dart';
|
||||||
export 'misc.dart';
|
export 'misc.dart';
|
||||||
|
export 'package_info.dart';
|
||||||
export 'phono_byte.dart';
|
export 'phono_byte.dart';
|
||||||
export 'pop_control.dart';
|
export 'pop_control.dart';
|
||||||
export 'responsive.dart';
|
export 'responsive.dart';
|
||||||
|
@ -6,6 +6,7 @@ import FlutterMacOS
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
import mobile_scanner
|
import mobile_scanner
|
||||||
|
import package_info_plus
|
||||||
import pasteboard
|
import pasteboard
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import screen_retriever
|
import screen_retriever
|
||||||
@ -19,6 +20,7 @@ import window_manager
|
|||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
MobileScannerPlugin.register(with: registry.registrar(forPlugin: "MobileScannerPlugin"))
|
||||||
|
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||||
PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
|
PasteboardPlugin.register(with: registry.registrar(forPlugin: "PasteboardPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
|
||||||
|
@ -2,6 +2,8 @@ PODS:
|
|||||||
- FlutterMacOS (1.0.0)
|
- FlutterMacOS (1.0.0)
|
||||||
- mobile_scanner (5.1.1):
|
- mobile_scanner (5.1.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
|
- package_info_plus (0.0.1):
|
||||||
|
- FlutterMacOS
|
||||||
- pasteboard (0.0.1):
|
- pasteboard (0.0.1):
|
||||||
- FlutterMacOS
|
- FlutterMacOS
|
||||||
- path_provider_foundation (0.0.1):
|
- path_provider_foundation (0.0.1):
|
||||||
@ -29,6 +31,7 @@ PODS:
|
|||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||||
- mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos`)
|
- mobile_scanner (from `Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos`)
|
||||||
|
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
|
||||||
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
|
- pasteboard (from `Flutter/ephemeral/.symlinks/plugins/pasteboard/macos`)
|
||||||
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
- path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
|
||||||
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
|
- screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
|
||||||
@ -45,6 +48,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: Flutter/ephemeral
|
:path: Flutter/ephemeral
|
||||||
mobile_scanner:
|
mobile_scanner:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/mobile_scanner/macos
|
||||||
|
package_info_plus:
|
||||||
|
:path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos
|
||||||
pasteboard:
|
pasteboard:
|
||||||
:path: Flutter/ephemeral/.symlinks/plugins/pasteboard/macos
|
:path: Flutter/ephemeral/.symlinks/plugins/pasteboard/macos
|
||||||
path_provider_foundation:
|
path_provider_foundation:
|
||||||
@ -69,6 +74,7 @@ EXTERNAL SOURCES:
|
|||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
|
||||||
mobile_scanner: 1efac1e53c294b24e3bb55bcc7f4deee0233a86b
|
mobile_scanner: 1efac1e53c294b24e3bb55bcc7f4deee0233a86b
|
||||||
|
package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c
|
||||||
pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99
|
pasteboard: 9b69dba6fedbb04866be632205d532fe2f6b1d99
|
||||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||||
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
|
screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
|
||||||
|
@ -36,10 +36,11 @@ packages:
|
|||||||
async_tools:
|
async_tools:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "../../../dart_async_tools"
|
name: async_tools
|
||||||
relative: true
|
sha256: "72590010ed6c6f5cbd5d40e33834abc08a43da6a73ac3c3645517d53899b8684"
|
||||||
source: path
|
url: "https://pub.dev"
|
||||||
version: "0.1.1"
|
source: hosted
|
||||||
|
version: "0.1.2"
|
||||||
bloc:
|
bloc:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -51,10 +52,11 @@ packages:
|
|||||||
bloc_advanced_tools:
|
bloc_advanced_tools:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "../../../bloc_advanced_tools"
|
name: bloc_advanced_tools
|
||||||
relative: true
|
sha256: "0cf9b3a73a67addfe22ec3f97a1ac240f6ad53870d6b21a980260f390d7901cd"
|
||||||
source: path
|
url: "https://pub.dev"
|
||||||
version: "0.1.1"
|
source: hosted
|
||||||
|
version: "0.1.2"
|
||||||
boolean_selector:
|
boolean_selector:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
24
pubspec.lock
24
pubspec.lock
@ -585,6 +585,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_zoom_drawer:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_zoom_drawer
|
||||||
|
sha256: "5a3708548868463fb36e0e3171761ab7cb513df88d2f14053802812d2e855060"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.0"
|
||||||
form_builder_validators:
|
form_builder_validators:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -857,6 +865,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
|
package_info_plus:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: package_info_plus
|
||||||
|
sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.0.0"
|
||||||
|
package_info_plus_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: package_info_plus_platform_interface
|
||||||
|
sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.0.0"
|
||||||
pasteboard:
|
pasteboard:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -45,6 +45,7 @@ dependencies:
|
|||||||
flutter_spinkit: ^5.2.1
|
flutter_spinkit: ^5.2.1
|
||||||
flutter_svg: ^2.0.10+1
|
flutter_svg: ^2.0.10+1
|
||||||
flutter_translate: ^4.1.0
|
flutter_translate: ^4.1.0
|
||||||
|
flutter_zoom_drawer: ^3.2.0
|
||||||
form_builder_validators: ^10.0.1
|
form_builder_validators: ^10.0.1
|
||||||
freezed_annotation: ^2.4.1
|
freezed_annotation: ^2.4.1
|
||||||
go_router: ^14.1.4
|
go_router: ^14.1.4
|
||||||
@ -56,6 +57,7 @@ dependencies:
|
|||||||
meta: ^1.12.0
|
meta: ^1.12.0
|
||||||
mobile_scanner: ^5.1.1
|
mobile_scanner: ^5.1.1
|
||||||
motion_toast: ^2.10.0
|
motion_toast: ^2.10.0
|
||||||
|
package_info_plus: ^8.0.0
|
||||||
pasteboard: ^0.2.0
|
pasteboard: ^0.2.0
|
||||||
path: ^1.9.0
|
path: ^1.9.0
|
||||||
path_provider: ^2.1.3
|
path_provider: ^2.1.3
|
||||||
|
Loading…
Reference in New Issue
Block a user