More UI Cleanup

This commit is contained in:
Christien Rioux 2025-05-27 16:43:38 -04:00
parent 3b1cb53b8a
commit 68e8d7fd39
17 changed files with 281 additions and 301 deletions

View file

@ -20,7 +20,6 @@ class ProfileWidget extends StatelessWidget {
//
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
@ -54,7 +53,7 @@ class ProfileWidget extends StatelessWidget {
? scale.primaryScale.border
: scale.primaryScale.borderText),
textAlign: TextAlign.left,
).paddingAll(8),
).paddingAll(8.scaled(context)),
if (_profile.pronouns.isNotEmpty && _showPronouns)
Text('(${_profile.pronouns})',
textAlign: TextAlign.right,
@ -62,7 +61,7 @@ class ProfileWidget extends StatelessWidget {
color: scaleConfig.preferBorders
? scale.primaryScale.border
: scale.primaryScale.primary))
.paddingAll(8),
.paddingAll(8.scaled(context)),
const Spacer()
]),
);

View file

@ -161,7 +161,7 @@ class _ChatComponentWidgetState extends State<ChatComponentWidget> {
return Column(
children: [
Container(
height: 48,
height: 40.scaledNoShrink(context),
decoration: BoxDecoration(
color: scale.border,
),
@ -177,7 +177,7 @@ class _ChatComponentWidgetState extends State<ChatComponentWidget> {
)),
const Spacer(),
IconButton(
iconSize: 24,
iconSize: 24.scaledNoShrink(context),
icon: Icon(Icons.close, color: scale.borderText),
onPressed: widget._onClose)
.paddingLTRB(0, 0, 8, 0)

View file

@ -63,7 +63,11 @@ class ChatListWidget extends StatelessWidget {
title: translate('chat_list.chats'),
child: (chatList.isEmpty)
? const SizedBox.expand(child: EmptyChatListWidget())
: SearchableList<proto.Chat>(
: TapRegion(
onTapOutside: (_) {
FocusScope.of(context).unfocus();
},
child: SearchableList<proto.Chat>(
initialList: chatList.map((x) => x.value).toList(),
itemBuilder: (c) {
switch (c.whichKind()) {
@ -86,7 +90,7 @@ class ChatListWidget extends StatelessWidget {
inputDecoration: InputDecoration(
labelText: translate('chat_list.search'),
),
).paddingAll(8),
)).paddingAll(8),
)))
.paddingLTRB(8, 0, 8, 8);
});

View file

@ -5,7 +5,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:searchable_listview/searchable_listview.dart';
import 'package:star_menu/star_menu.dart';
import 'package:veilid_support/veilid_support.dart';
import '../../contact_invitation/contact_invitation.dart';
@ -90,75 +89,62 @@ class _ContactsBrowserState extends State<ContactsBrowser>
final menuBorderColor = scaleScheme.primaryScale.hoverBorder;
final menuParams = StarMenuParameters(
shape: MenuShape.linear,
centerOffset: Offset(0, 64.scaled(context)),
boundaryBackground: BoundaryBackground(
color: menuBackgroundColor,
PopupMenuEntry<void> makeMenuButton(
{required IconData iconData,
required String text,
void Function()? onTap}) =>
PopupMenuItem(
onTap: onTap,
child: DecoratedBox(
decoration: ShapeDecoration(
color: menuBackgroundColor,
shape: RoundedRectangleBorder(
side: scaleConfig.useVisualIndicators
? BorderSide(
width: 2, color: menuBorderColor, strokeAlign: 0)
width: 2,
color: menuBorderColor,
strokeAlign: 0)
: BorderSide.none,
borderRadius: BorderRadius.circular(
8 * scaleConfig.borderRadiusScale)))));
ElevatedButton makeMenuButton(
{required IconData iconData,
required String text,
required void Function()? onPressed}) =>
ElevatedButton.icon(
onPressed: onPressed,
icon: Icon(
iconData,
size: 32.scaled(context),
).paddingSTEB(0, 8.scaled(context), 0, 8.scaled(context)),
label: Text(
8 * scaleConfig.borderRadiusScale))),
child: Row(spacing: 4.scaled(context), children: [
Icon(iconData, size: 32.scaled(context)),
Text(
text,
textScaler: MediaQuery.of(context).textScaler,
maxLines: 2,
textAlign: TextAlign.center,
).paddingSTEB(0, 8.scaled(context), 0, 8.scaled(context)));
)
]).paddingAll(4.scaled(context)))
.paddingLTRB(0, 2.scaled(context), 0, 2.scaled(context)));
final inviteMenuItems = [
makeMenuButton(
iconData: Icons.paste,
text: translate('add_contact_sheet.paste_invite'),
onPressed: () async {
_invitationMenuController.closeMenu!();
await PasteInvitationDialog.show(context);
iconData: Icons.contact_page,
text: translate('add_contact_sheet.create_invite'),
onTap: () async {
await CreateInvitationDialog.show(context);
}),
makeMenuButton(
iconData: Icons.qr_code_scanner,
text: translate('add_contact_sheet.scan_invite'),
onPressed: () async {
_invitationMenuController.closeMenu!();
onTap: () async {
await ScanInvitationDialog.show(context);
}).paddingLTRB(0, 0, 0, 8.scaled(context)),
}),
makeMenuButton(
iconData: Icons.contact_page,
text: translate('add_contact_sheet.create_invite'),
onPressed: () async {
_invitationMenuController.closeMenu!();
await CreateInvitationDialog.show(context);
}).paddingLTRB(0, 0, 0, 8.scaled(context)),
iconData: Icons.paste,
text: translate('add_contact_sheet.paste_invite'),
onTap: () async {
await PasteInvitationDialog.show(context);
}),
];
return StarMenu(
items: inviteMenuItems,
onItemTapped: (_, controller) {
controller.closeMenu!();
},
controller: _invitationMenuController,
params: menuParams,
child: IconButton(
onPressed: () {},
iconSize: 24.scaled(context),
icon: Icon(Icons.person_add, color: menuIconColor),
tooltip: translate('add_contact_sheet.add_contact')),
);
return PopupMenuButton(
itemBuilder: (_) => inviteMenuItems,
menuPadding: const EdgeInsets.symmetric(vertical: 4).scaled(context),
tooltip: translate('add_contact_sheet.add_contact'),
child: Icon(
size: 32.scaled(context), Icons.person_add, color: menuIconColor));
}
@override
@ -253,12 +239,11 @@ class _ContactsBrowserState extends State<ContactsBrowser>
text: translate('contact_list.loading_contacts'))
: const EmptyContactListWidget(),
defaultSuffixIconColor: scale.primaryScale.border,
closeKeyboardWhenScrolling: true,
searchFieldEnabled: contactList != null,
inputDecoration:
InputDecoration(labelText: translate('contact_list.search')),
secondaryWidget: buildInvitationButton(context)
.paddingLTRB(4.scaled(context), 0, 0, 0))
.paddingLTRB(8.scaled(context), 0, 0, 0))
.expanded()
]);
}
@ -276,5 +261,4 @@ class _ContactsBrowserState extends State<ContactsBrowser>
}
////////////////////////////////////////////////////////////////////////////
final _invitationMenuController = StarMenuController();
}

View file

@ -13,7 +13,7 @@ class DefaultAppBar extends AppBar {
Widget? leading,
super.actions})
: super(
toolbarHeight: 40.scaled(context),
toolbarHeight: 48.scaled(context),
leadingWidth: 40.scaled(context),
leading: leading ??
Container(

View file

@ -31,9 +31,9 @@ class _HomeAccountReadyState extends State<HomeAccountReady> {
return AspectRatio(
aspectRatio: 1,
child: IconButton(
icon: const Icon(
icon: Icon(
size: 32.scaled(context),
Icons.menu,
applyTextScaling: true,
),
color: scaleConfig.preferBorders
? scale.primaryScale.border
@ -70,9 +70,9 @@ class _HomeAccountReadyState extends State<HomeAccountReady> {
return AspectRatio(
aspectRatio: 1,
child: IconButton(
icon: const Icon(
icon: Icon(
size: 32.scaled(context),
Icons.contacts,
applyTextScaling: true,
),
color: scaleConfig.preferBorders
? scale.primaryScale.border

View file

@ -5,7 +5,6 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:flutter_zoom_drawer/flutter_zoom_drawer.dart';
import 'package:keyboard_avoider/keyboard_avoider.dart';
import 'package:provider/provider.dart';
import 'package:transitioned_indexed_stack/transitioned_indexed_stack.dart';
import 'package:url_launcher/url_launcher_string.dart';
@ -207,7 +206,9 @@ class HomeScreenState extends State<HomeScreen>
.indexWhere((x) => x.superIdentity.recordKey == activeLocalAccount);
final canClose = activeIndex != -1;
final drawer = ZoomDrawer(
final drawer = Scaffold(
backgroundColor: Colors.transparent,
body: ZoomDrawer(
controller: _zoomDrawerController,
menuScreen: Builder(builder: (context) {
final zoomDrawer = ZoomDrawer.of(context);
@ -230,13 +231,9 @@ class HomeScreenState extends State<HomeScreen>
disableDragGesture: !canClose,
mainScreenScale: .25,
slideWidth: min(360, MediaQuery.of(context).size.width * 0.9),
);
));
final drawerWithAvoider =
isWeb ? drawer : KeyboardAvoider(curve: Curves.ease, child: drawer);
return DefaultTextStyle(
style: theme.textTheme.bodySmall!, child: drawerWithAvoider);
return DefaultTextStyle(style: theme.textTheme.bodySmall!, child: drawer);
}
////////////////////////////////////////////////////////////////////////////

View file

@ -69,7 +69,7 @@ Widget buildSettingsPageNotificationPreferences(
softWrap: false,
style: textTheme.labelMedium,
textAlign: TextAlign.center,
)));
).fit(fit: BoxFit.scaleDown)));
}
return out;
}
@ -108,22 +108,64 @@ Widget buildSettingsPageNotificationPreferences(
return out;
}
// Invitation accepted
Widget notificationSettingsItem(
{required String title,
required bool notificationsEnabled,
NotificationMode? deliveryValue,
SoundEffect? soundValue,
Future<void> Function(NotificationMode)? onNotificationModeChanged,
Future<void> Function(SoundEffect)? onSoundChanged}) =>
Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.scaled(context),
children: [
Text('$title:', style: textTheme.titleMedium),
Wrap(
spacing: 8.scaled(context), // gap between adjacent chips
runSpacing: 8.scaled(context), // gap between lines
children: [
if (deliveryValue != null)
IntrinsicWidth(
child: StyledDropdown<NotificationMode>(
decoratorLabel: translate('settings_page.delivery'),
items: notificationModeItems(),
value: deliveryValue,
onChanged: !notificationsEnabled
? null
: onNotificationModeChanged,
)),
if (soundValue != null)
IntrinsicWidth(
child: StyledDropdown<SoundEffect>(
decoratorLabel: translate('settings_page.sound'),
items: soundEffectItems(),
value: soundValue,
onChanged: !notificationsEnabled ? null : onSoundChanged,
))
])
]).paddingAll(4.scaled(context));
return InputDecorator(
decoration: InputDecoration(
labelText: translate('settings_page.notifications'),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8 * scaleConfig.borderRadiusScale),
borderRadius:
BorderRadius.circular(8 * scaleConfig.borderRadiusScale),
borderSide: BorderSide(width: 2, color: scale.primaryScale.border),
),
),
child: Column(mainAxisSize: MainAxisSize.min, children: [
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 8.scaled(context),
children: [
// Display Beta Warning
StyledCheckbox(
label: translate('settings_page.display_beta_warning'),
value: notificationsPreference.displayBetaWarning,
onChanged: (value) async {
final newNotificationsPreference =
notificationsPreference.copyWith(displayBetaWarning: value);
final newNotificationsPreference = notificationsPreference
.copyWith(displayBetaWarning: value);
await updatePreferences(newNotificationsPreference);
}),
@ -141,14 +183,15 @@ Widget buildSettingsPageNotificationPreferences(
label: translate('settings_page.enable_notifications'),
value: notificationsPreference.enableNotifications,
onChanged: (value) async {
final newNotificationsPreference =
notificationsPreference.copyWith(enableNotifications: value);
final newNotificationsPreference = notificationsPreference
.copyWith(enableNotifications: value);
await updatePreferences(newNotificationsPreference);
}),
StyledDropdown<MessageNotificationContent>(
items: messageNotificationContentItems(),
value: notificationsPreference.messageNotificationContent,
decoratorLabel: translate('settings_page.message_notification_content'),
decoratorLabel:
translate('settings_page.message_notification_content'),
onChanged: !notificationsPreference.enableNotifications
? null
: (value) async {
@ -156,122 +199,56 @@ Widget buildSettingsPageNotificationPreferences(
.copyWith(messageNotificationContent: value);
await updatePreferences(newNotificationsPreference);
},
).paddingLTRB(0, 4.scaled(context), 0, 4.scaled(context)),
).paddingAll(4.scaled(context)),
// Notifications
Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
children: [
TableRow(children: [
Text(translate('settings_page.event'),
textAlign: TextAlign.center,
style: textTheme.titleMedium!.copyWith(
color: scale.primaryScale.border,
decorationColor: scale.primaryScale.border,
decoration: TextDecoration.underline))
.paddingAll(8.scaled(context)),
Text(translate('settings_page.delivery'),
textAlign: TextAlign.center,
style: textTheme.titleMedium!.copyWith(
color: scale.primaryScale.border,
decorationColor: scale.primaryScale.border,
decoration: TextDecoration.underline))
.paddingAll(8.scaled(context)),
Text(translate('settings_page.sound'),
textAlign: TextAlign.center,
style: textTheme.titleMedium!.copyWith(
color: scale.primaryScale.border,
decorationColor: scale.primaryScale.border,
decoration: TextDecoration.underline))
.paddingAll(8.scaled(context)),
]),
TableRow(children: [
// Invitation accepted
Text(
textAlign: TextAlign.right,
translate('settings_page.invitation_accepted'))
.paddingAll(4.scaled(context)),
StyledDropdown<NotificationMode>(
items: notificationModeItems(),
value: notificationsPreference.onInvitationAcceptedMode,
onChanged: !notificationsPreference.enableNotifications
? null
: (value) async {
final newNotificationsPreference =
notificationsPreference.copyWith(
onInvitationAcceptedMode: value);
notificationSettingsItem(
title: translate('settings_page.invitation_accepted'),
notificationsEnabled:
notificationsPreference.enableNotifications,
deliveryValue: notificationsPreference.onInvitationAcceptedMode,
soundValue: notificationsPreference.onInvitationAcceptedSound,
onNotificationModeChanged: (value) async {
final newNotificationsPreference = notificationsPreference
.copyWith(onInvitationAcceptedMode: value);
await updatePreferences(newNotificationsPreference);
},
).paddingAll(4.scaled(context)),
StyledDropdown<SoundEffect>(
items: soundEffectItems(),
value: notificationsPreference.onInvitationAcceptedSound,
onChanged: !notificationsPreference.enableNotifications
? null
: (value) async {
final newNotificationsPreference =
notificationsPreference.copyWith(
onInvitationAcceptedSound: value);
onSoundChanged: (value) async {
final newNotificationsPreference = notificationsPreference
.copyWith(onInvitationAcceptedSound: value);
await updatePreferences(newNotificationsPreference);
},
).paddingLTRB(
4.scaled(context), 4.scaled(context), 0, 4.scaled(context))
]),
}),
// Message received
TableRow(children: [
Text(
textAlign: TextAlign.right,
translate('settings_page.message_received'))
.paddingAll(4.scaled(context)),
StyledDropdown<NotificationMode>(
items: notificationModeItems(),
value: notificationsPreference.onMessageReceivedMode,
onChanged: !notificationsPreference.enableNotifications
? null
: (value) async {
final newNotificationsPreference =
notificationsPreference.copyWith(
onMessageReceivedMode: value);
notificationSettingsItem(
title: translate('settings_page.message_received'),
notificationsEnabled:
notificationsPreference.enableNotifications,
deliveryValue: notificationsPreference.onMessageReceivedMode,
soundValue: notificationsPreference.onMessageReceivedSound,
onNotificationModeChanged: (value) async {
final newNotificationsPreference = notificationsPreference
.copyWith(onMessageReceivedMode: value);
await updatePreferences(newNotificationsPreference);
},
).paddingAll(4),
StyledDropdown<SoundEffect>(
items: soundEffectItems(),
value: notificationsPreference.onMessageReceivedSound,
onChanged: !notificationsPreference.enableNotifications
? null
: (value) async {
final newNotificationsPreference =
notificationsPreference.copyWith(
onMessageReceivedSound: value);
onSoundChanged: (value) async {
final newNotificationsPreference = notificationsPreference
.copyWith(onMessageReceivedSound: value);
await updatePreferences(newNotificationsPreference);
},
).paddingLTRB(
4.scaled(context), 4.scaled(context), 0, 4.scaled(context))
]),
}),
// Message sent
TableRow(children: [
Text(
textAlign: TextAlign.right,
translate('settings_page.message_sent'))
.paddingAll(4.scaled(context)),
const SizedBox.shrink(),
StyledDropdown<SoundEffect>(
items: soundEffectItems(),
value: notificationsPreference.onMessageSentSound,
onChanged: !notificationsPreference.enableNotifications
? null
: (value) async {
final newNotificationsPreference =
notificationsPreference.copyWith(
onMessageSentSound: value);
notificationSettingsItem(
title: translate('settings_page.message_sent'),
notificationsEnabled:
notificationsPreference.enableNotifications,
soundValue: notificationsPreference.onMessageSentSound,
onSoundChanged: (value) async {
final newNotificationsPreference = notificationsPreference
.copyWith(onMessageSentSound: value);
await updatePreferences(newNotificationsPreference);
},
).paddingLTRB(
4.scaled(context), 4.scaled(context), 0, 4.scaled(context))
]),
])
]).paddingAll(8.scaled(context)),
);
}),
]).paddingAll(4.scaled(context)));
}

View file

@ -23,7 +23,7 @@ class SettingsPage extends StatelessWidget {
title: Text(translate('settings_page.titlebar')),
leading: IconButton(
iconSize: 24.scaled(context),
icon: Icon(Icons.arrow_back),
icon: const Icon(Icons.arrow_back),
onPressed: () => GoRouterHelper(context).pop(),
),
actions: <Widget>[
@ -56,7 +56,6 @@ class SettingsPage extends StatelessWidget {
.map((x) => x.paddingLTRB(0, 0, 0, 8.scaled(context)))
.toList(),
).paddingSymmetric(vertical: 4.scaled(context)),
).paddingSymmetric(
horizontal: 8.scaled(context), vertical: 8.scaled(context)),
),
));
}

View file

@ -191,6 +191,12 @@ class ScaleTheme extends ThemeExtension<ScaleTheme> {
inputDecorationTheme:
ScaleInputDecoratorTheme(scheme, config, textTheme),
sliderTheme: sliderTheme,
popupMenuTheme: PopupMenuThemeData(
color: scheme.primaryScale.subtleBackground,
shadowColor: Colors.transparent,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(8 * config.borderRadiusScale))),
extensions: <ThemeExtension<dynamic>>[scheme, config, this]);
return themeData;

View file

@ -38,6 +38,16 @@ const _scaleNumMult = <double>[
1 + 1 / 1,
];
const _scaleNumMultNoShrink = <double>[
1,
1,
1,
1,
1 + 1 / 4,
1 + 1 / 2,
1 + 1 / 1,
];
int displayScaleToIndex(double displayScale) {
final idx = _scales.indexWhere((elem) => elem > displayScale);
final currentScaleIdx = idx == -1 ? _scales.length - 1 : max(0, idx - 1);
@ -92,6 +102,14 @@ extension DisplayScaledNum on num {
displayScaleToIndex(prefs.themePreference.displayScale);
return this * _scaleNumMult[currentScaleIdx];
}
double scaledNoShrink(BuildContext context) {
final prefs = context.watch<PreferencesCubit>().state.asData?.value ??
PreferencesRepository.instance.value;
final currentScaleIdx =
displayScaleToIndex(prefs.themePreference.displayScale);
return this * _scaleNumMultNoShrink[currentScaleIdx];
}
}
extension DisplayScaledEdgeInsets on EdgeInsets {

View file

@ -43,7 +43,11 @@ class StyledCheckbox extends StatelessWidget {
await _onChanged(value);
});
})),
Text(_label, style: textStyle).paddingAll(4.scaled(context)),
Text(
_label,
style: textStyle,
overflow: TextOverflow.clip,
).paddingLTRB(4.scaled(context), 0, 0, 0).flexible(),
]);
if (_decoratorLabel != null) {

View file

@ -27,7 +27,7 @@ class StyledScaffold extends StatelessWidget {
return GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: scaffold /*.paddingAll(enableBorder ? 32 : 0) */);
child: scaffold);
}
////////////////////////////////////////////////////////////////////////////

View file

@ -132,7 +132,7 @@ class StyledSlideTile extends StatelessWidget {
softWrap: false,
),
subtitle: subtitle.isNotEmpty ? Text(subtitle) : null,
contentPadding: const EdgeInsets.fromLTRB(8, 4, 8, 4)
contentPadding: const EdgeInsets.fromLTRB(8, 2, 8, 2)
.scaled(context),
iconColor: scaleTileTheme.textColor,
textColor: scaleTileTheme.textColor,

View file

@ -571,6 +571,7 @@ Container clipBorder({
required Color borderColor,
required Widget child,
}) =>
// We want to return a container here
// ignore: avoid_unnecessary_containers, use_decorated_box
Container(
decoration: ShapeDecoration(

View file

@ -848,14 +848,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.9.5"
keyboard_avoider:
dependency: "direct main"
description:
name: keyboard_avoider
sha256: d2917bd52c6612bf8d1ff97f74049ddf3592a81d44e814f0e7b07dcfd245b75c
url: "https://pub.dev"
source: hosted
version: "0.2.0"
lint_hard:
dependency: "direct dev"
description:

View file

@ -56,7 +56,6 @@ dependencies:
image: ^4.5.3
intl: ^0.19.0
json_annotation: ^4.9.0
keyboard_avoider: ^0.2.0
loggy: ^2.0.3
meta: ^1.16.0
mobile_scanner: ^7.0.0