theming work, revamp contact invitation

This commit is contained in:
Christien Rioux 2025-03-19 23:28:09 -04:00
parent 3c95c9d1a3
commit ae841ec42a
26 changed files with 504 additions and 507 deletions

View file

@ -158,9 +158,8 @@
"away": "Away" "away": "Away"
}, },
"add_contact_sheet": { "add_contact_sheet": {
"new_contact": "New Contact", "add_contact": "Add Contact",
"create_invite": "Create\nInvitation", "create_invite": "Create\nInvitation",
"receive_invite": "Receive\nInvitation",
"scan_invite": "Scan\nInvitation", "scan_invite": "Scan\nInvitation",
"paste_invite": "Paste\nInvitation" "paste_invite": "Paste\nInvitation"
}, },

View file

@ -14,7 +14,6 @@ class ActiveLocalAccountCubit extends Cubit<TypedKey?> {
switch (change) { switch (change) {
case AccountRepositoryChange.activeLocalAccount: case AccountRepositoryChange.activeLocalAccount:
emit(_accountRepository.getActiveLocalAccount()); emit(_accountRepository.getActiveLocalAccount());
break;
// Ignore these // Ignore these
case AccountRepositoryChange.localAccounts: case AccountRepositoryChange.localAccounts:
case AccountRepositoryChange.userLogins: case AccountRepositoryChange.userLogins:

View file

@ -32,6 +32,7 @@ class PerAccountCollectionState with _$PerAccountCollectionState {
} }
extension PerAccountCollectionStateExt on PerAccountCollectionState { extension PerAccountCollectionStateExt on PerAccountCollectionState {
// Returns if the account is ready and logged in
bool get isReady => bool get isReady =>
avAccountRecordState != null && avAccountRecordState != null &&
avAccountRecordState!.isData && avAccountRecordState!.isData &&
@ -45,7 +46,11 @@ extension PerAccountCollectionStateExt on PerAccountCollectionState {
activeConversationsBlocMapCubit != null && activeConversationsBlocMapCubit != null &&
activeSingleContactChatBlocMapCubit != null; activeSingleContactChatBlocMapCubit != null;
Widget provide({required Widget child}) => MultiBlocProvider(providers: [ /// If we have a selected account and it is ready and not locked,
/// this will provide the unlocked account's cubits to the context
Widget provideReady({required Widget child}) {
if (isReady) {
return MultiBlocProvider(providers: [
BlocProvider.value(value: accountInfoCubit!), BlocProvider.value(value: accountInfoCubit!),
BlocProvider.value(value: accountRecordCubit!), BlocProvider.value(value: accountRecordCubit!),
BlocProvider.value(value: contactInvitationListCubit!), BlocProvider.value(value: contactInvitationListCubit!),
@ -56,4 +61,9 @@ extension PerAccountCollectionStateExt on PerAccountCollectionState {
BlocProvider.value(value: activeConversationsBlocMapCubit!), BlocProvider.value(value: activeConversationsBlocMapCubit!),
BlocProvider.value(value: activeSingleContactChatBlocMapCubit!), BlocProvider.value(value: activeSingleContactChatBlocMapCubit!),
], child: child); ], child: child);
} else {
// Otherwise we just provide the child
return child;
}
}
} }

View file

@ -33,11 +33,6 @@ class ChatComponentWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
// final theme = Theme.of(context);
// final scale = theme.extension<ScaleScheme>()!;
// final scaleConfig = theme.extension<ScaleConfig>()!;
// final textTheme = theme.textTheme;
// Get the account info // Get the account info
final accountInfo = context.watch<AccountInfoCubit>().state; final accountInfo = context.watch<AccountInfoCubit>().state;
@ -125,7 +120,7 @@ class ChatComponentWidget extends StatelessWidget {
return Column( return Column(
children: [ children: [
Container( Container(
height: 48, height: 40,
decoration: BoxDecoration( decoration: BoxDecoration(
color: scale.border, color: scale.border,
), ),
@ -141,9 +136,10 @@ class ChatComponentWidget extends StatelessWidget {
)), )),
const Spacer(), const Spacer(),
IconButton( IconButton(
iconSize: 24,
icon: Icon(Icons.close, color: scale.borderText), icon: Icon(Icons.close, color: scale.borderText),
onPressed: _onClose) onPressed: _onClose)
.paddingLTRB(16, 0, 16, 0) .paddingLTRB(0, 0, 8, 0)
]), ]),
), ),
DecoratedBox( DecoratedBox(

View file

@ -1,31 +0,0 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_translate/flutter_translate.dart';
import '../../theme/theme.dart';
Widget newChatBottomSheetBuilder(
BuildContext sheetContext, BuildContext context) {
//final theme = Theme.of(sheetContext);
//final scale = theme.extension<ScaleScheme>()!;
return KeyboardListener(
focusNode: FocusNode(),
onKeyEvent: (ke) {
if (ke.logicalKey == LogicalKeyboardKey.escape) {
Navigator.pop(sheetContext);
}
},
child: styledBottomSheet(
context: context,
title: translate('add_chat_sheet.new_chat'),
child: SizedBox(
height: 160,
child: const Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
'Group and custom chat functionality is not available yet')
]).paddingAll(16))));
}

View file

@ -1,8 +1,7 @@
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 '../../theme/models/scale_theme/scale_scheme.dart'; import '../../theme/theme.dart';
import '../../theme/views/views.dart';
class NoConversationWidget extends StatelessWidget { class NoConversationWidget extends StatelessWidget {
const NoConversationWidget({super.key}); const NoConversationWidget({super.key});

View file

@ -1,4 +1,3 @@
export 'chat_component_widget.dart'; export 'chat_component_widget.dart';
export 'empty_chat_widget.dart'; export 'empty_chat_widget.dart';
export 'new_chat_bottom_sheet.dart';
export 'no_conversation_widget.dart'; export 'no_conversation_widget.dart';

View file

@ -48,7 +48,7 @@ class ContactInvitationItemWidget extends StatelessWidget {
key: ObjectKey(contactInvitationRecord), key: ObjectKey(contactInvitationRecord),
disabled: tileDisabled, disabled: tileDisabled,
selected: selected, selected: selected,
tileScale: ScaleKind.primary, tileScale: ScaleKind.secondary,
title: title, title: title,
leading: const Icon(Icons.person_add), leading: const Icon(Icons.person_add),
onTap: () async { onTap: () async {

View file

@ -17,19 +17,25 @@ import 'contact_item_widget.dart';
import 'empty_contact_list_widget.dart'; import 'empty_contact_list_widget.dart';
enum ContactsBrowserElementKind { enum ContactsBrowserElementKind {
invitation,
contact, contact,
invitation,
} }
class ContactsBrowserElement { class ContactsBrowserElement {
ContactsBrowserElement.invitation(proto.ContactInvitationRecord i)
: kind = ContactsBrowserElementKind.invitation,
contact = null,
invitation = i;
ContactsBrowserElement.contact(proto.Contact c) ContactsBrowserElement.contact(proto.Contact c)
: kind = ContactsBrowserElementKind.contact, : kind = ContactsBrowserElementKind.contact,
invitation = null, invitation = null,
contact = c; contact = c;
ContactsBrowserElement.invitation(proto.ContactInvitationRecord i)
: kind = ContactsBrowserElementKind.invitation,
contact = null,
invitation = i;
String get sortKey => switch (kind) {
ContactsBrowserElementKind.contact => contact!.displayName,
ContactsBrowserElementKind.invitation =>
invitation!.recipient + invitation!.message
};
final ContactsBrowserElementKind kind; final ContactsBrowserElementKind kind;
final proto.ContactInvitationRecord? invitation; final proto.ContactInvitationRecord? invitation;
@ -66,27 +72,25 @@ class ContactsBrowser extends StatefulWidget {
class _ContactsBrowserState extends State<ContactsBrowser> class _ContactsBrowserState extends State<ContactsBrowser>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
Widget buildInvitationBar(BuildContext context) { Widget buildInvitationButton(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
final textTheme = theme.textTheme; final scaleScheme = theme.extension<ScaleScheme>()!;
final scale = theme.extension<ScaleScheme>()!;
final scaleConfig = theme.extension<ScaleConfig>()!; final scaleConfig = theme.extension<ScaleConfig>()!;
final menuIconColor = scaleConfig.preferBorders final menuIconColor = scaleConfig.preferBorders
? scale.primaryScale.hoverBorder ? scaleScheme.primaryScale.hoverBorder
: scale.primaryScale.hoverBorder; : scaleScheme.primaryScale.hoverBorder;
final menuBackgroundColor = scaleConfig.preferBorders final menuBackgroundColor = scaleConfig.preferBorders
? scale.primaryScale.elementBackground ? scaleScheme.primaryScale.activeElementBackground
: scale.primaryScale.elementBackground; : scaleScheme.primaryScale.activeElementBackground;
final menuBorderColor = scale.primaryScale.hoverBorder; final menuBorderColor = scaleScheme.primaryScale.hoverBorder;
final menuParams = StarMenuParameters( final menuParams = StarMenuParameters(
shape: MenuShape.grid, shape: MenuShape.linear,
checkItemsScreenBoundaries: true,
centerOffset: const Offset(0, 64), centerOffset: const Offset(0, 64),
backgroundParams: // backgroundParams:
BackgroundParams(backgroundColor: theme.shadowColor.withAlpha(128)), // BackgroundParams(backgroundColor: theme.shadowColor.withAlpha(128)),
boundaryBackground: BoundaryBackground( boundaryBackground: BoundaryBackground(
color: menuBackgroundColor, color: menuBackgroundColor,
decoration: ShapeDecoration( decoration: ShapeDecoration(
@ -99,89 +103,64 @@ class _ContactsBrowserState extends State<ContactsBrowser>
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
8 * scaleConfig.borderRadiusScale))))); 8 * scaleConfig.borderRadiusScale)))));
final receiveInviteMenuItems = [ ElevatedButton makeMenuButton(
Column(mainAxisSize: MainAxisSize.min, children: [ {required IconData iconData,
IconButton( required String text,
onPressed: () async { required void Function()? onPressed}) =>
_receiveInviteMenuController.closeMenu!(); ElevatedButton.icon(
await ScanInvitationDialog.show(context); onPressed: onPressed,
},
iconSize: 32,
icon: Icon( icon: Icon(
Icons.qr_code_scanner, iconData,
size: 32, size: 32,
color: menuIconColor, ).paddingSTEB(0, 8, 0, 8),
), label: Text(
), text,
Text(translate('add_contact_sheet.scan_invite'),
maxLines: 2, maxLines: 2,
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: textTheme.labelSmall!.copyWith(color: menuIconColor)) ).paddingSTEB(0, 8, 0, 8));
]).paddingAll(4),
Column(mainAxisSize: MainAxisSize.min, children: [ final inviteMenuItems = [
IconButton( makeMenuButton(
iconData: Icons.paste,
text: translate('add_contact_sheet.paste_invite'),
onPressed: () async { onPressed: () async {
_receiveInviteMenuController.closeMenu!(); _invitationMenuController.closeMenu!();
await PasteInvitationDialog.show(context); await PasteInvitationDialog.show(context);
}, }),
iconSize: 32, makeMenuButton(
icon: Icon( iconData: Icons.qr_code_scanner,
Icons.paste, text: translate('add_contact_sheet.scan_invite'),
size: 32, onPressed: () async {
color: menuIconColor, _invitationMenuController.closeMenu!();
), await ScanInvitationDialog.show(context);
), }).paddingLTRB(0, 0, 0, 8),
Text(translate('add_contact_sheet.paste_invite'), makeMenuButton(
maxLines: 2, iconData: Icons.contact_page,
textAlign: TextAlign.center, text: translate('add_contact_sheet.create_invite'),
style: textTheme.labelSmall!.copyWith(color: menuIconColor)) onPressed: () async {
]).paddingAll(4) _invitationMenuController.closeMenu!();
await CreateInvitationDialog.show(context);
}).paddingLTRB(0, 0, 0, 8),
]; ];
return Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ return StarMenu(
Column(mainAxisSize: MainAxisSize.min, children: [ items: inviteMenuItems,
IconButton(
onPressed: () async {
await CreateInvitationDialog.show(context);
},
iconSize: 32,
icon: const Icon(Icons.contact_page),
color: menuIconColor,
),
Text(translate('add_contact_sheet.create_invite'),
maxLines: 2,
textAlign: TextAlign.center,
style: textTheme.labelSmall!.copyWith(color: menuIconColor))
]),
StarMenu(
items: receiveInviteMenuItems,
onItemTapped: (_index, controller) { onItemTapped: (_index, controller) {
controller.closeMenu!(); controller.closeMenu!();
}, },
controller: _receiveInviteMenuController, controller: _invitationMenuController,
params: menuParams, params: menuParams,
child: Column(mainAxisSize: MainAxisSize.min, children: [ child: IconButton(
IconButton(
onPressed: () {}, onPressed: () {},
iconSize: 32, iconSize: 24,
icon: ImageIcon( icon: Icon(Icons.person_add, color: menuIconColor),
const AssetImage('assets/images/handshake.png'), tooltip: translate('add_contact_sheet.add_contact')),
size: 32, );
color: menuIconColor,
)),
Text(translate('add_contact_sheet.receive_invite'),
maxLines: 2,
textAlign: TextAlign.center,
style: textTheme.labelSmall!.copyWith(color: menuIconColor))
]),
),
]).paddingAll(16);
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Theme.of(context); final theme = Theme.of(context);
final textTheme = theme.textTheme;
final scale = theme.extension<ScaleScheme>()!; final scale = theme.extension<ScaleScheme>()!;
//final scaleConfig = theme.extension<ScaleConfig>()!; //final scaleConfig = theme.extension<ScaleConfig>()!;
@ -196,40 +175,23 @@ class _ContactsBrowserState extends State<ContactsBrowser>
final contactList = final contactList =
ciState.state.asData?.value.map((x) => x.value).toIList(); ciState.state.asData?.value.map((x) => x.value).toIList();
final expansionListData = final initialList = <ContactsBrowserElement>[];
<ContactsBrowserElementKind, List<ContactsBrowserElement>>{};
if (contactInvitationRecordList.isNotEmpty) {
expansionListData[ContactsBrowserElementKind.invitation] =
contactInvitationRecordList
.toList()
.map(ContactsBrowserElement.invitation)
.toList();
}
if (contactList != null) { if (contactList != null) {
expansionListData[ContactsBrowserElementKind.contact] = initialList
contactList.toList().map(ContactsBrowserElement.contact).toList(); .addAll(contactList.toList().map(ContactsBrowserElement.contact));
} }
if (contactInvitationRecordList.isNotEmpty) {
initialList.addAll(contactInvitationRecordList
.toList()
.map(ContactsBrowserElement.invitation));
}
initialList.sort((a, b) => a.sortKey.compareTo(b.sortKey));
return Column(children: [ return Column(children: [
buildInvitationBar(context), SearchableList<ContactsBrowserElement>(
SearchableList<ContactsBrowserElement>.expansion( initialList: initialList,
expansionListData: expansionListData, itemBuilder: (element) {
expansionTitleBuilder: (k) {
final kind = k as ContactsBrowserElementKind;
late final String title;
switch (kind) {
case ContactsBrowserElementKind.contact:
title = translate('contacts_dialog.contacts');
case ContactsBrowserElementKind.invitation:
title = translate('contacts_dialog.invitations');
}
return Center(
child: Text(title, style: textTheme.titleSmall),
);
},
expansionInitiallyExpanded: (k) => true,
expansionListBuilder: (_index, element) {
switch (element.kind) { switch (element.kind) {
case ContactsBrowserElementKind.contact: case ContactsBrowserElementKind.contact:
final contact = element.contact!; final contact = element.contact!;
@ -245,51 +207,57 @@ class _ContactsBrowserState extends State<ContactsBrowser>
case ContactsBrowserElementKind.invitation: case ContactsBrowserElementKind.invitation:
final invitation = element.invitation!; final invitation = element.invitation!;
return ContactInvitationItemWidget( return ContactInvitationItemWidget(
contactInvitationRecord: invitation, disabled: false) contactInvitationRecord: invitation,
disabled: false)
.paddingLTRB(0, 4, 0, 0); .paddingLTRB(0, 4, 0, 0);
} }
}, },
filterExpansionData: (value) { filter: (value) {
final lowerValue = value.toLowerCase(); final lowerValue = value.toLowerCase();
final filteredMap = {
for (final entry in expansionListData.entries) final filtered = <ContactsBrowserElement>[];
entry.key: (expansionListData[entry.key] ?? []).where((element) { for (final element in initialList) {
switch (element.kind) { switch (element.kind) {
case ContactsBrowserElementKind.contact: case ContactsBrowserElementKind.contact:
final contact = element.contact!; final contact = element.contact!;
return contact.nickname if (contact.nickname.toLowerCase().contains(lowerValue) ||
.toLowerCase()
.contains(lowerValue) ||
contact.profile.name contact.profile.name
.toLowerCase() .toLowerCase()
.contains(lowerValue) || .contains(lowerValue) ||
contact.profile.pronouns contact.profile.pronouns
.toLowerCase() .toLowerCase()
.contains(lowerValue); .contains(lowerValue)) {
filtered.add(element);
}
case ContactsBrowserElementKind.invitation: case ContactsBrowserElementKind.invitation:
final invitation = element.invitation!; final invitation = element.invitation!;
return invitation.message if (invitation.message
.toLowerCase() .toLowerCase()
.contains(lowerValue) || .contains(lowerValue) ||
invitation.recipient.toLowerCase().contains(lowerValue); invitation.recipient
.toLowerCase()
.contains(lowerValue)) {
filtered.add(element);
} }
}).toList() }
}; }
return filteredMap; return filtered;
}, },
hideEmptyExpansionItems: true,
searchFieldHeight: 40, searchFieldHeight: 40,
listViewPadding: const EdgeInsets.all(4), listViewPadding: const EdgeInsets.fromLTRB(4, 0, 4, 4),
spaceBetweenSearchAndList: 4, searchFieldPadding: const EdgeInsets.fromLTRB(4, 8, 4, 4),
emptyWidget: contactList == null emptyWidget: contactList == null
? waitingPage(text: translate('contact_list.loading_contacts')) ? waitingPage(
text: translate('contact_list.loading_contacts'))
: const EmptyContactListWidget(), : const EmptyContactListWidget(),
defaultSuffixIconColor: scale.primaryScale.border, defaultSuffixIconColor: scale.primaryScale.border,
closeKeyboardWhenScrolling: true, closeKeyboardWhenScrolling: true,
searchFieldEnabled: contactList != null, searchFieldEnabled: contactList != null,
inputDecoration: inputDecoration:
InputDecoration(labelText: translate('contact_list.search')), InputDecoration(labelText: translate('contact_list.search')),
).expanded() secondaryWidget:
buildInvitationButton(context).paddingLTRB(4, 0, 0, 0))
.expanded()
]); ]);
} }
@ -318,5 +286,5 @@ class _ContactsBrowserState extends State<ContactsBrowser>
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
final _receiveInviteMenuController = StarMenuController(); final _invitationMenuController = StarMenuController();
} }

View file

@ -1,196 +0,0 @@
import 'package:async_tools/async_tools.dart';
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:provider/provider.dart';
import '../../chat/chat.dart';
import '../../chat_list/chat_list.dart';
import '../../layout/layout.dart';
import '../../proto/proto.dart' as proto;
import '../../theme/theme.dart';
import '../contacts.dart';
const _kDoBackArrow = 'doBackArrow';
class ContactsDialog extends StatefulWidget {
const ContactsDialog._({required this.modalContext});
@override
State<ContactsDialog> createState() => _ContactsDialogState();
static Future<void> show(BuildContext modalContext) async {
await showDialog<void>(
context: modalContext,
barrierDismissible: false,
useRootNavigator: false,
builder: (context) => ContactsDialog._(modalContext: modalContext));
}
final BuildContext modalContext;
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
.add(DiagnosticsProperty<BuildContext>('modalContext', modalContext));
}
}
class _ContactsDialogState extends State<ContactsDialog> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final appBarIconColor = scale.primaryScale.borderText;
final enableSplit = !isMobileWidth(context);
final enableLeft = enableSplit || _selectedContact == null;
final enableRight = enableSplit || _selectedContact != null;
return SizedBox(
width: MediaQuery.of(context).size.width,
child: StyledScaffold(
appBar: DefaultAppBar(
title: Text(!enableSplit && enableRight
? translate('contacts_dialog.edit_contact')
: translate('contacts_dialog.contacts')),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
singleFuture((this, _kDoBackArrow), () async {
final confirmed = await _onContactSelected(null);
if (!enableSplit && enableRight) {
} else {
if (confirmed) {
if (context.mounted) {
Navigator.pop(context);
}
}
}
});
},
),
actions: [
if (_selectedContact != null)
FittedBox(
fit: BoxFit.scaleDown,
child:
Column(mainAxisSize: MainAxisSize.min, children: [
IconButton(
icon: const Icon(Icons.chat_bubble),
color: appBarIconColor,
tooltip: translate('contacts_dialog.new_chat'),
onPressed: () async {
await _onChatStarted(_selectedContact!);
}),
Text(translate('contacts_dialog.new_chat'),
style: theme.textTheme.labelSmall!
.copyWith(color: appBarIconColor)),
])).paddingLTRB(8, 0, 8, 0),
if (enableSplit && _selectedContact != null)
FittedBox(
fit: BoxFit.scaleDown,
child:
Column(mainAxisSize: MainAxisSize.min, children: [
IconButton(
icon: const Icon(Icons.close),
color: appBarIconColor,
tooltip:
translate('contacts_dialog.close_contact'),
onPressed: () async {
await _onContactSelected(null);
}),
Text(translate('contacts_dialog.close_contact'),
style: theme.textTheme.labelSmall!
.copyWith(color: appBarIconColor)),
])).paddingLTRB(8, 0, 8, 0),
]),
body: LayoutBuilder(builder: (context, constraint) {
final maxWidth = constraint.maxWidth;
return ColoredBox(
color: scale.primaryScale.appBackground,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Offstage(
offstage: !enableLeft,
child: SizedBox(
width: enableLeft && !enableRight
? maxWidth
: (maxWidth / 3).clamp(200, 500),
child: DecoratedBox(
decoration: BoxDecoration(
color: scale
.primaryScale.subtleBackground),
child: ContactsBrowser(
selectedContactRecordKey: _selectedContact
?.localConversationRecordKey
.toVeilid(),
onContactSelected: _onContactSelected,
onStartChat: _onChatStarted,
).paddingLTRB(8, 0, 8, 8)))),
if (enableRight && enableLeft)
Container(
constraints: const BoxConstraints(
minWidth: 1, maxWidth: 1),
color: scale.primaryScale.subtleBorder),
if (enableRight)
if (_selectedContact == null)
const NoContactWidget().expanded()
else
ContactDetailsWidget(
contact: _selectedContact!,
onModifiedState: _onModifiedState)
.paddingLTRB(16, 16, 16, 16)
.expanded(),
]));
})));
}
void _onModifiedState(bool isModified) {
setState(() {
_isModified = isModified;
});
}
Future<bool> _onContactSelected(proto.Contact? contact) async {
if (contact != _selectedContact && _isModified) {
final ok = await showConfirmModal(
context: context,
title: translate('confirmation.discard_changes'),
text: translate('confirmation.are_you_sure_discard'));
if (!ok) {
return false;
}
}
setState(() {
_selectedContact = contact;
_isModified = false;
});
return true;
}
Future<void> _onChatStarted(proto.Contact contact) async {
final chatListCubit = context.read<ChatListCubit>();
await chatListCubit.getOrCreateChatSingleContact(contact: contact);
if (mounted) {
context
.read<ActiveChatCubit>()
.setActiveChat(contact.localConversationRecordKey.toVeilid());
Navigator.pop(context);
}
}
proto.Contact? _selectedContact;
bool _isModified = false;
}

View file

@ -0,0 +1,171 @@
import 'package:async_tools/async_tools.dart';
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:provider/provider.dart';
import '../../chat/chat.dart';
import '../../chat_list/chat_list.dart';
import '../../layout/layout.dart';
import '../../proto/proto.dart' as proto;
import '../../theme/theme.dart';
import '../contacts.dart';
const _kDoBackArrow = 'doBackArrow';
class ContactsPage extends StatefulWidget {
const ContactsPage({super.key});
@override
State<ContactsPage> createState() => _ContactsPageState();
}
class _ContactsPageState extends State<ContactsPage> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final appBarIconColor = scale.primaryScale.borderText;
final enableSplit = !isMobileSize(context);
final enableLeft = enableSplit || _selectedContact == null;
final enableRight = enableSplit || _selectedContact != null;
return StyledScaffold(
appBar: DefaultAppBar(
title: Text(!enableSplit && enableRight
? translate('contacts_dialog.edit_contact')
: translate('contacts_dialog.contacts')),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
singleFuture((this, _kDoBackArrow), () async {
final confirmed = await _onContactSelected(null);
if (!enableSplit && enableRight) {
} else {
if (confirmed) {
if (context.mounted) {
Navigator.pop(context);
}
}
}
});
},
),
actions: [
if (_selectedContact != null)
FittedBox(
fit: BoxFit.scaleDown,
child: Column(mainAxisSize: MainAxisSize.min, children: [
IconButton(
icon: const Icon(Icons.chat_bubble),
color: appBarIconColor,
tooltip: translate('contacts_dialog.new_chat'),
onPressed: () async {
await _onChatStarted(_selectedContact!);
}),
Text(translate('contacts_dialog.new_chat'),
style: theme.textTheme.labelSmall!
.copyWith(color: appBarIconColor)),
])).paddingLTRB(8, 0, 8, 0),
if (enableSplit && _selectedContact != null)
FittedBox(
fit: BoxFit.scaleDown,
child: Column(mainAxisSize: MainAxisSize.min, children: [
IconButton(
icon: const Icon(Icons.close),
color: appBarIconColor,
tooltip: translate('contacts_dialog.close_contact'),
onPressed: () async {
await _onContactSelected(null);
}),
Text(translate('contacts_dialog.close_contact'),
style: theme.textTheme.labelSmall!
.copyWith(color: appBarIconColor)),
])).paddingLTRB(8, 0, 8, 0),
]),
body: LayoutBuilder(builder: (context, constraint) {
final maxWidth = constraint.maxWidth;
return ColoredBox(
color: scale.primaryScale.appBackground,
child:
Row(crossAxisAlignment: CrossAxisAlignment.start, children: [
Offstage(
offstage: !enableLeft,
child: SizedBox(
width: enableLeft && !enableRight
? maxWidth
: (maxWidth / 3).clamp(200, 500),
child: DecoratedBox(
decoration: BoxDecoration(
color: scale.primaryScale.subtleBackground),
child: ContactsBrowser(
selectedContactRecordKey: _selectedContact
?.localConversationRecordKey
.toVeilid(),
onContactSelected: _onContactSelected,
onStartChat: _onChatStarted,
).paddingLTRB(8, 0, 8, 8)))),
if (enableRight && enableLeft)
Container(
constraints:
const BoxConstraints(minWidth: 1, maxWidth: 1),
color: scale.primaryScale.subtleBorder),
if (enableRight)
if (_selectedContact == null)
const NoContactWidget().expanded()
else
ContactDetailsWidget(
contact: _selectedContact!,
onModifiedState: _onModifiedState)
.paddingLTRB(16, 16, 16, 16)
.expanded(),
]));
}));
}
void _onModifiedState(bool isModified) {
setState(() {
_isModified = isModified;
});
}
Future<bool> _onContactSelected(proto.Contact? contact) async {
if (contact != _selectedContact && _isModified) {
final ok = await showConfirmModal(
context: context,
title: translate('confirmation.discard_changes'),
text: translate('confirmation.are_you_sure_discard'));
if (!ok) {
return false;
}
}
setState(() {
_selectedContact = contact;
_isModified = false;
});
return true;
}
Future<void> _onChatStarted(proto.Contact contact) async {
final chatListCubit = context.read<ChatListCubit>();
await chatListCubit.getOrCreateChatSingleContact(contact: contact);
if (mounted) {
context
.read<ActiveChatCubit>()
.setActiveChat(contact.localConversationRecordKey.toVeilid());
Navigator.pop(context);
}
}
proto.Contact? _selectedContact;
bool _isModified = false;
}

View file

@ -2,7 +2,7 @@ export 'availability_widget.dart';
export 'contact_details_widget.dart'; export 'contact_details_widget.dart';
export 'contact_item_widget.dart'; export 'contact_item_widget.dart';
export 'contacts_browser.dart'; export 'contacts_browser.dart';
export 'contacts_dialog.dart'; export 'contacts_page.dart';
export 'edit_contact_form.dart'; export 'edit_contact_form.dart';
export 'empty_contact_list_widget.dart'; export 'empty_contact_list_widget.dart';
export 'no_contact_widget.dart'; export 'no_contact_widget.dart';

View file

@ -84,8 +84,9 @@ class _DrawerMenuState extends State<DrawerMenu> {
hoverBorder = border; hoverBorder = border;
activeBorder = border; activeBorder = border;
} else { } else {
background = background = selected
selected ? scale.activeElementBackground : scale.elementBackground; ? scale.elementBackground
: scale.elementBackground.withAlpha(128);
hoverBackground = scale.hoverElementBackground; hoverBackground = scale.hoverElementBackground;
activeBackground = scale.activeElementBackground; activeBackground = scale.activeElementBackground;
border = loggedIn ? scale.border : scale.subtleBorder; border = loggedIn ? scale.border : scale.subtleBorder;
@ -132,9 +133,16 @@ class _DrawerMenuState extends State<DrawerMenu> {
callback: callback, callback: callback,
footerButtonIcon: loggedIn ? Icons.edit_outlined : null, footerButtonIcon: loggedIn ? Icons.edit_outlined : null,
footerCallback: footerCallback, footerCallback: footerCallback,
footerButtonIconColor: border, footerButtonIconColor:
footerButtonIconHoverColor: hoverBackground, scaleConfig.preferBorders ? scale.border : scale.borderText,
footerButtonIconFocusColor: activeBackground, footerButtonIconHoverColor:
(scaleConfig.preferBorders || scaleConfig.useVisualIndicators)
? null
: hoverBorder,
footerButtonIconFocusColor:
(scaleConfig.preferBorders || scaleConfig.useVisualIndicators)
? null
: activeBorder,
minHeight: 48, minHeight: 48,
)); ));
} }

View file

@ -39,6 +39,8 @@ class MenuItemWidget extends StatelessWidget {
} }
return backgroundColor; return backgroundColor;
}), }),
overlayColor:
WidgetStateProperty.resolveWith((states) => backgroundHoverColor),
side: WidgetStateBorderSide.resolveWith((states) { side: WidgetStateBorderSide.resolveWith((states) {
if (states.contains(WidgetState.hovered)) { if (states.contains(WidgetState.hovered)) {
return borderColor != null return borderColor != null

View file

@ -89,7 +89,11 @@ class _HomeAccountReadyState extends State<HomeAccountReady> {
)), )),
tooltip: translate('menu.contacts_tooltip'), tooltip: translate('menu.contacts_tooltip'),
onPressed: () async { onPressed: () async {
await ContactsDialog.show(context); await Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (_) => const ContactsPage(),
),
);
}); });
}); });
@ -139,10 +143,7 @@ class _HomeAccountReadyState extends State<HomeAccountReady> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final isLarge = responsiveVisibility( final isSmallScreen = isMobileSize(context);
context: context,
phone: false,
);
final theme = Theme.of(context); final theme = Theme.of(context);
final scaleScheme = theme.extension<ScaleScheme>()!; final scaleScheme = theme.extension<ScaleScheme>()!;
@ -159,14 +160,7 @@ class _HomeAccountReadyState extends State<HomeAccountReady> {
late final bool visibleRight; late final bool visibleRight;
late final double leftWidth; late final double leftWidth;
late final double rightWidth; late final double rightWidth;
if (isLarge) { if (isSmallScreen) {
visibleLeft = true;
visibleRight = true;
leftWidth = leftColumnSize;
rightWidth = constraints.maxWidth -
leftColumnSize -
(scaleConfig.useVisualIndicators ? 2 : 0);
} else {
if (hasActiveChat) { if (hasActiveChat) {
visibleLeft = false; visibleLeft = false;
visibleRight = true; visibleRight = true;
@ -178,6 +172,13 @@ class _HomeAccountReadyState extends State<HomeAccountReady> {
leftWidth = constraints.maxWidth; leftWidth = constraints.maxWidth;
rightWidth = 400; // whatever rightWidth = 400; // whatever
} }
} else {
visibleLeft = true;
visibleRight = true;
leftWidth = leftColumnSize;
rightWidth = constraints.maxWidth -
leftColumnSize -
(scaleConfig.useVisualIndicators ? 2 : 0);
} }
return Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: [ return Row(crossAxisAlignment: CrossAxisAlignment.stretch, children: [

View file

@ -136,15 +136,11 @@ class HomeScreenState extends State<HomeScreen>
} }
// Re-export all ready blocs to the account display subtree // Re-export all ready blocs to the account display subtree
return perAccountCollectionState.provide( final pages = <MaterialPage<void>>[
child: Navigator( const MaterialPage<void>(child: HomeAccountReady())
onPopPage: (route, result) { ];
if (!route.didPop(result)) { return perAccountCollectionState.provideReady(
return false; child: Navigator(onDidRemovePage: pages.remove, pages: pages));
}
return true;
},
pages: const [MaterialPage(child: HomeAccountReady())]));
} }
} }

View file

@ -65,29 +65,49 @@ class RouterCubit extends Cubit<RouterState> {
), ),
GoRoute( GoRoute(
path: '/edit_account', path: '/edit_account',
redirect: (_, state) {
final extra = state.extra;
if (extra == null ||
extra is! List<Object> ||
extra[0] is! TypedKey) {
return '/';
}
return null;
},
builder: (context, state) { builder: (context, state) {
final extra = state.extra! as List<Object?>; final extra = state.extra! as List<Object>;
return EditAccountPage( return EditAccountPage(
superIdentityRecordKey: extra[0]! as TypedKey, superIdentityRecordKey: extra[0] as TypedKey,
initialValue: extra[1]! as AccountSpec, initialValue: extra[1] as AccountSpec,
accountRecord: extra[2]! as OwnedDHTRecordPointer, accountRecord: extra[2] as OwnedDHTRecordPointer,
); );
}, },
), ),
GoRoute( GoRoute(
path: '/new_account', path: '/new_account',
builder: (context, state) => const NewAccountPage(), builder: (context, state) => const NewAccountPage(),
), routes: [
GoRoute( GoRoute(
path: '/new_account/recovery_key', path: 'recovery_key',
redirect: (_, state) {
final extra = state.extra;
if (extra == null ||
extra is! List<Object> ||
extra[0] is! WritableSuperIdentity ||
extra[1] is! String) {
return '/';
}
return null;
},
builder: (context, state) { builder: (context, state) {
final extra = state.extra! as List<Object?>; final extra = state.extra! as List<Object>;
return ShowRecoveryKeyPage( return ShowRecoveryKeyPage(
writableSuperIdentity: writableSuperIdentity:
extra[0]! as WritableSuperIdentity, extra[0] as WritableSuperIdentity,
name: extra[1]! as String); name: extra[1] as String);
}), }),
]),
GoRoute( GoRoute(
path: '/settings', path: '/settings',
builder: (context, state) => const SettingsPage(), builder: (context, state) => const SettingsPage(),

View file

@ -16,7 +16,7 @@ ChatTheme makeChatTheme(
: scale.secondaryScale.calloutBackground, : scale.secondaryScale.calloutBackground,
backgroundColor: backgroundColor:
scale.grayScale.appBackground.withAlpha(scaleConfig.wallpaperAlpha), scale.grayScale.appBackground.withAlpha(scaleConfig.wallpaperAlpha),
messageBorderRadius: scaleConfig.borderRadiusScale * 16, messageBorderRadius: scaleConfig.borderRadiusScale * 12,
bubbleBorderSide: scaleConfig.preferBorders bubbleBorderSide: scaleConfig.preferBorders
? BorderSide( ? BorderSide(
color: scale.primaryScale.calloutBackground, color: scale.primaryScale.calloutBackground,
@ -37,7 +37,7 @@ ChatTheme makeChatTheme(
filled: !scaleConfig.preferBorders, filled: !scaleConfig.preferBorders,
fillColor: scale.primaryScale.subtleBackground, fillColor: scale.primaryScale.subtleBackground,
isDense: true, isDense: true,
contentPadding: const EdgeInsets.fromLTRB(8, 12, 8, 12), contentPadding: const EdgeInsets.fromLTRB(8, 8, 8, 8),
disabledBorder: OutlineInputBorder( disabledBorder: OutlineInputBorder(
borderSide: scaleConfig.preferBorders borderSide: scaleConfig.preferBorders
? BorderSide(color: scale.grayScale.border, width: 2) ? BorderSide(color: scale.grayScale.border, width: 2)
@ -65,10 +65,12 @@ ChatTheme makeChatTheme(
color: scaleConfig.preferBorders color: scaleConfig.preferBorders
? scale.primaryScale.elementBackground ? scale.primaryScale.elementBackground
: scale.primaryScale.border), : scale.primaryScale.border),
inputPadding: const EdgeInsets.all(12), inputPadding: const EdgeInsets.all(6),
inputTextColor: !scaleConfig.preferBorders inputTextColor: !scaleConfig.preferBorders
? scale.primaryScale.appText ? scale.primaryScale.appText
: scale.primaryScale.border, : scale.primaryScale.border,
messageInsetsHorizontal: 12,
messageInsetsVertical: 8,
attachmentButtonIcon: const Icon(Icons.attach_file), attachmentButtonIcon: const Icon(Icons.attach_file),
sentMessageBodyTextStyle: textTheme.bodyLarge!.copyWith( sentMessageBodyTextStyle: textTheme.bodyLarge!.copyWith(
color: scaleConfig.preferBorders color: scaleConfig.preferBorders

View file

@ -263,6 +263,36 @@ ThemeData contrastGenerator({
final baseThemeData = scaleTheme.toThemeData(brightness); final baseThemeData = scaleTheme.toThemeData(brightness);
WidgetStateProperty<BorderSide?> elementBorderWidgetStateProperty() =>
WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.disabled)) {
return BorderSide(color: scheme.grayScale.border.withAlpha(0x7F));
} else if (states.contains(WidgetState.pressed)) {
return BorderSide(
color: scheme.primaryScale.border,
strokeAlign: BorderSide.strokeAlignOutside);
} else if (states.contains(WidgetState.hovered)) {
return BorderSide(color: scheme.primaryScale.hoverBorder, width: 2);
} else if (states.contains(WidgetState.focused)) {
return BorderSide(color: scheme.primaryScale.hoverBorder, width: 2);
}
return BorderSide(color: scheme.primaryScale.border);
});
WidgetStateProperty<Color?> elementBackgroundWidgetStateProperty() =>
WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.disabled)) {
return scheme.grayScale.elementBackground;
} else if (states.contains(WidgetState.pressed)) {
return scheme.primaryScale.activeElementBackground;
} else if (states.contains(WidgetState.hovered)) {
return scheme.primaryScale.hoverElementBackground;
} else if (states.contains(WidgetState.focused)) {
return scheme.primaryScale.activeElementBackground;
}
return scheme.primaryScale.elementBackground;
});
final elevatedButtonTheme = ElevatedButtonThemeData( final elevatedButtonTheme = ElevatedButtonThemeData(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: scheme.primaryScale.elementBackground, backgroundColor: scheme.primaryScale.elementBackground,
@ -274,20 +304,9 @@ ThemeData contrastGenerator({
side: BorderSide(color: scheme.primaryScale.border), side: BorderSide(color: scheme.primaryScale.border),
borderRadius: borderRadius:
BorderRadius.circular(8 * scaleConfig.borderRadiusScale))) BorderRadius.circular(8 * scaleConfig.borderRadiusScale)))
.copyWith(side: WidgetStateProperty.resolveWith((states) { .copyWith(
if (states.contains(WidgetState.disabled)) { side: elementBorderWidgetStateProperty(),
return BorderSide(color: scheme.grayScale.border.withAlpha(0x7F)); backgroundColor: elementBackgroundWidgetStateProperty()));
} else if (states.contains(WidgetState.pressed)) {
return BorderSide(
color: scheme.primaryScale.border,
strokeAlign: BorderSide.strokeAlignOutside);
} else if (states.contains(WidgetState.hovered)) {
return BorderSide(color: scheme.primaryScale.hoverBorder);
} else if (states.contains(WidgetState.focused)) {
return BorderSide(color: scheme.primaryScale.hoverBorder, width: 2);
}
return BorderSide(color: scheme.primaryScale.border);
})));
final themeData = baseThemeData.copyWith( final themeData = baseThemeData.copyWith(
// chipTheme: baseThemeData.chipTheme.copyWith( // chipTheme: baseThemeData.chipTheme.copyWith(

View file

@ -97,7 +97,7 @@ class ScaleScheme extends ThemeExtension<ScaleScheme> {
onSurfaceVariant: secondaryScale.appText, onSurfaceVariant: secondaryScale.appText,
outline: primaryScale.border, outline: primaryScale.border,
outlineVariant: secondaryScale.border, outlineVariant: secondaryScale.border,
shadow: primaryScale.appBackground.darken(60), shadow: primaryScale.primary.darken(60),
//scrim: primaryScale.background, //scrim: primaryScale.background,
// inverseSurface: primaryScale.subtleText, // inverseSurface: primaryScale.subtleText,
// onInverseSurface: primaryScale.subtleBackground, // onInverseSurface: primaryScale.subtleBackground,

View file

@ -43,6 +43,50 @@ class ScaleTheme extends ThemeExtension<ScaleTheme> {
config: config.lerp(other.config, t)); config: config.lerp(other.config, t));
} }
WidgetStateProperty<BorderSide?> elementBorderWidgetStateProperty() =>
WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.disabled)) {
return BorderSide(
color: scheme.grayScale.border.withAlpha(0x7F),
strokeAlign: BorderSide.strokeAlignOutside);
} else if (states.contains(WidgetState.pressed)) {
return BorderSide(
color: scheme.primaryScale.border,
);
} else if (states.contains(WidgetState.hovered)) {
return BorderSide(
color: scheme.primaryScale.hoverBorder,
strokeAlign: BorderSide.strokeAlignOutside);
} else if (states.contains(WidgetState.focused)) {
return BorderSide(
color: scheme.primaryScale.hoverBorder,
width: 2,
strokeAlign: BorderSide.strokeAlignOutside);
}
return BorderSide(
color: scheme.primaryScale.border,
strokeAlign: BorderSide.strokeAlignOutside);
});
WidgetStateProperty<Color?> elementColorWidgetStateProperty() =>
WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.disabled)) {
return scheme.grayScale.primary.withAlpha(0x7F);
} else if (states.contains(WidgetState.pressed)) {
return scheme.primaryScale.borderText;
} else if (states.contains(WidgetState.hovered)) {
return scheme.primaryScale.borderText;
} else if (states.contains(WidgetState.focused)) {
return scheme.primaryScale.borderText;
}
return Color.lerp(
scheme.primaryScale.borderText, scheme.primaryScale.primary, 0.25);
});
// WidgetStateProperty<Color?> elementBackgroundWidgetStateProperty() {
// return null;
// }
ThemeData toThemeData(Brightness brightness) { ThemeData toThemeData(Brightness brightness) {
final colorScheme = scheme.toColorScheme(brightness); final colorScheme = scheme.toColorScheme(brightness);
@ -51,8 +95,9 @@ class ScaleTheme extends ThemeExtension<ScaleTheme> {
final elevatedButtonTheme = ElevatedButtonThemeData( final elevatedButtonTheme = ElevatedButtonThemeData(
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
elevation: 0,
textStyle: textTheme.labelSmall,
backgroundColor: scheme.primaryScale.elementBackground, backgroundColor: scheme.primaryScale.elementBackground,
foregroundColor: scheme.primaryScale.appText,
disabledBackgroundColor: disabledBackgroundColor:
scheme.grayScale.elementBackground.withAlpha(0x7F), scheme.grayScale.elementBackground.withAlpha(0x7F),
disabledForegroundColor: disabledForegroundColor:
@ -61,20 +106,11 @@ class ScaleTheme extends ThemeExtension<ScaleTheme> {
side: BorderSide(color: scheme.primaryScale.border), side: BorderSide(color: scheme.primaryScale.border),
borderRadius: borderRadius:
BorderRadius.circular(8 * config.borderRadiusScale))) BorderRadius.circular(8 * config.borderRadiusScale)))
.copyWith(side: WidgetStateProperty.resolveWith((states) { .copyWith(
if (states.contains(WidgetState.disabled)) { foregroundColor: elementColorWidgetStateProperty(),
return BorderSide(color: scheme.grayScale.border.withAlpha(0x7F)); side: elementBorderWidgetStateProperty(),
} else if (states.contains(WidgetState.pressed)) { iconColor: elementColorWidgetStateProperty(),
return BorderSide( ));
color: scheme.primaryScale.border,
strokeAlign: BorderSide.strokeAlignOutside);
} else if (states.contains(WidgetState.hovered)) {
return BorderSide(color: scheme.primaryScale.hoverBorder);
} else if (states.contains(WidgetState.focused)) {
return BorderSide(color: scheme.primaryScale.hoverBorder, width: 2);
}
return BorderSide(color: scheme.primaryScale.border);
})));
final themeData = baseThemeData.copyWith( final themeData = baseThemeData.copyWith(
scrollbarTheme: baseThemeData.scrollbarTheme.copyWith( scrollbarTheme: baseThemeData.scrollbarTheme.copyWith(

View file

@ -29,11 +29,12 @@ class PopControl extends StatelessWidget {
return PopScope( return PopScope(
canPop: false, canPop: false,
onPopInvoked: (didPop) { onPopInvokedWithResult: (didPop, _) {
if (didPop) { if (didPop) {
return; return;
} }
_doDismiss(navigator); _doDismiss(navigator);
return;
}, },
child: child); child: child);
} }

View file

@ -1,4 +1,3 @@
import 'package:awesome_extensions/awesome_extensions.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../theme.dart'; import '../theme.dart';
@ -13,7 +12,7 @@ class StyledScaffold extends StatelessWidget {
final scaleConfig = theme.extension<ScaleConfig>()!; final scaleConfig = theme.extension<ScaleConfig>()!;
final scale = scaleScheme.scale(ScaleKind.primary); final scale = scaleScheme.scale(ScaleKind.primary);
final enableBorder = !isMobileSize(context); const enableBorder = false; //!isMobileSize(context);
var scaffold = clipBorder( var scaffold = clipBorder(
clipEnabled: enableBorder, clipEnabled: enableBorder,
@ -28,7 +27,7 @@ class StyledScaffold extends StatelessWidget {
return GestureDetector( return GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(), onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: scaffold.paddingAll(enableBorder ? 32 : 0)); child: scaffold /*.paddingAll(enableBorder ? 32 : 0) */);
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////

View file

@ -7,7 +7,6 @@ import 'package:flutter/services.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import '../theme/views/responsive.dart'; import '../theme/views/responsive.dart';
import 'tools.dart';
export 'package:window_manager/window_manager.dart' show TitleBarStyle; export 'package:window_manager/window_manager.dart' show TitleBarStyle;

View file

@ -1310,10 +1310,10 @@ packages:
description: description:
path: "." path: "."
ref: main ref: main
resolved-ref: db0f7b6f1baec0250ecba82f3d161bac1cf23d7d resolved-ref: f367c2f713dcc0c965a4f7af5952d94b2f699998
url: "https://gitlab.com/veilid/Searchable-Listview.git" url: "https://gitlab.com/veilid/Searchable-Listview.git"
source: git source: git
version: "2.14.1" version: "2.16.0"
share_plus: share_plus:
dependency: "direct main" dependency: "direct main"
description: description:

View file

@ -111,7 +111,7 @@ dependencies:
xterm: ^4.0.0 xterm: ^4.0.0
zxing2: ^0.2.3 zxing2: ^0.2.3
#dependency_overrides: # dependency_overrides:
# async_tools: # async_tools:
# path: ../dart_async_tools # path: ../dart_async_tools
# bloc_advanced_tools: # bloc_advanced_tools: