ui cleanup

This commit is contained in:
Christien Rioux 2024-04-10 16:13:08 -04:00
parent 1f99279cd2
commit 23ec185324
26 changed files with 419 additions and 244 deletions

View File

@ -58,12 +58,15 @@
"account_page": {
"contact_invitations": "Contact Invitations"
},
"accounts_menu": {
"invite_contact": "Invite Contact",
"add_contact_sheet": {
"new_contact": "New Contact",
"create_invite": "Create Invitation",
"scan_invite": "Scan Invitation",
"paste_invite": "Paste Invitation"
},
"add_chat_sheet": {
"new_chat": "New Chat"
},
"create_invitation_dialog": {
"title": "Create Contact Invitation",
"connect_with_me": "Connect with me on VeilidChat!",

View File

@ -113,7 +113,9 @@ class NewAccountPageState extends State<NewAccountPage> {
body: _newAccountForm(
context,
onSubmit: (formKey) async {
// dismiss the keyboard by unfocusing the textfield
FocusScope.of(context).unfocus();
try {
final name =
_formKey.currentState!.fields[formFieldName]!.value as String;

View File

@ -1,6 +1,7 @@
import 'package:animated_theme_switcher/animated_theme_switcher.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_translate/flutter_translate.dart';
@ -12,9 +13,15 @@ import 'init.dart';
import 'layout/splash.dart';
import 'router/router.dart';
import 'settings/settings.dart';
import 'theme/models/theme_preference.dart';
import 'tick.dart';
import 'tools/loggy.dart';
import 'veilid_processor/veilid_processor.dart';
class ReloadThemeIntent extends Intent {
const ReloadThemeIntent();
}
class VeilidChatApp extends StatelessWidget {
const VeilidChatApp({
required this.initialThemeData,
@ -25,6 +32,28 @@ class VeilidChatApp extends StatelessWidget {
final ThemeData initialThemeData;
void _reloadTheme(BuildContext context) {
log.info('Reloading theme');
final theme =
PreferencesRepository.instance.value.themePreferences.themeData();
ThemeSwitcher.of(context).changeTheme(theme: theme);
}
Widget _buildShortcuts(
{required BuildContext context,
required Widget Function(BuildContext) builder}) =>
ThemeSwitcher(
builder: (context) => Shortcuts(
shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(
LogicalKeyboardKey.alt, LogicalKeyboardKey.keyR):
const ReloadThemeIntent(),
},
child: Actions(actions: <Type, Action<Intent>>{
ReloadThemeIntent: CallbackAction<ReloadThemeIntent>(
onInvoke: (intent) => _reloadTheme(context)),
}, child: Focus(autofocus: true, child: builder(context)))));
@override
Widget build(BuildContext context) => FutureProvider<VeilidChatGlobalInit?>(
initialData: null,
@ -68,21 +97,24 @@ class VeilidChatApp extends StatelessWidget {
)
],
child: BackgroundTicker(
builder: (context) => MaterialApp.router(
debugShowCheckedModeBanner: false,
routerConfig: context.watch<RouterCubit>().router(),
title: translate('app.title'),
theme: theme,
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
FormBuilderLocalizations.delegate,
localizationDelegate
],
supportedLocales:
localizationDelegate.supportedLocales,
locale: localizationDelegate.currentLocale,
)),
child: _buildShortcuts(
context: context,
builder: (context) => MaterialApp.router(
debugShowCheckedModeBanner: false,
routerConfig:
context.watch<RouterCubit>().router(),
title: translate('app.title'),
theme: theme,
localizationsDelegates: [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
FormBuilderLocalizations.delegate,
localizationDelegate
],
supportedLocales:
localizationDelegate.supportedLocales,
locale: localizationDelegate.currentLocale,
))),
)),
);
});

View File

@ -0,0 +1,71 @@
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';
import '../../tools/tools.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: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Text(
'Group and custom chat functionality is not available yet')
// Column(children: [
// IconButton(
// onPressed: () async {
// Navigator.pop(sheetContext);
// await CreateInvitationDialog.show(context);
// },
// iconSize: 64,
// icon: const Icon(Icons.contact_page),
// color: scale.primaryScale.background),
// Text(
// translate('accounts_menu.create_invite'),
// )
// ]),
// Column(children: [
// IconButton(
// onPressed: () async {
// Navigator.pop(sheetContext);
// await ScanInvitationDialog.show(context);
// },
// iconSize: 64,
// icon: const Icon(Icons.qr_code_scanner),
// color: scale.primaryScale.background),
// Text(
// translate('accounts_menu.scan_invite'),
// )
// ]),
// Column(children: [
// IconButton(
// onPressed: () async {
// Navigator.pop(sheetContext);
// await PasteInvitationDialog.show(context);
// },
// iconSize: 64,
// icon: const Icon(Icons.paste),
// color: scale.primaryScale.background),
// Text(
// translate('accounts_menu.paste_invite'),
// )
// ])
]).paddingAll(16))));
}

View File

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

View File

@ -7,7 +7,6 @@ import 'package:searchable_listview/searchable_listview.dart';
import '../../contacts/contacts.dart';
import '../../proto/proto.dart' as proto;
import '../../theme/theme.dart';
import '../../tools/tools.dart';
import '../chat_list.dart';
@ -17,10 +16,6 @@ class ChatSingleContactListWidget extends StatelessWidget {
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
final theme = Theme.of(context);
//final textTheme = theme.textTheme;
final scale = theme.extension<ScaleScheme>()!;
final contactListV = context.watch<ContactListCubit>().state;
return contactListV.builder((context, contactList) {
@ -29,55 +24,49 @@ class ChatSingleContactListWidget extends StatelessWidget {
valueMapper: (c) => c);
final chatListV = context.watch<ChatListCubit>().state;
return chatListV.builder((context, chatList) => SizedBox.expand(
return chatListV
.builder((context, chatList) => SizedBox.expand(
child: styledTitleContainer(
context: context,
title: translate('chat_list.chats'),
child: SizedBox.expand(
child: (chatList.isEmpty)
? const EmptyChatListWidget()
: SearchableList<proto.Chat>(
initialList: chatList.toList(),
builder: (l, i, c) {
child: (chatList.isEmpty)
? const EmptyChatListWidget()
: SearchableList<proto.Chat>(
initialList: chatList.toList(),
builder: (l, i, c) {
final contact =
contactMap[c.remoteConversationRecordKey];
if (contact == null) {
return const Text('...');
}
return ChatSingleContactItemWidget(
contact: contact,
disabled: contactListV.busy);
},
filter: (value) {
final lowerValue = value.toLowerCase();
return chatList.where((c) {
final contact =
contactMap[c.remoteConversationRecordKey];
if (contact == null) {
return const Text('...');
return false;
}
return ChatSingleContactItemWidget(
contact: contact,
disabled: contactListV.busy);
},
filter: (value) {
final lowerValue = value.toLowerCase();
return chatList.where((c) {
final contact =
contactMap[c.remoteConversationRecordKey];
if (contact == null) {
return false;
}
return contact.editedProfile.name
.toLowerCase()
.contains(lowerValue) ||
contact.editedProfile.pronouns
.toLowerCase()
.contains(lowerValue);
}).toList();
},
spaceBetweenSearchAndList: 4,
inputDecoration: InputDecoration(
labelText: translate('chat_list.search'),
contentPadding: const EdgeInsets.all(2),
fillColor: scale.primaryScale.elementBackground,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: scale.primaryScale.hoverBorder,
),
borderRadius: BorderRadius.circular(8),
),
),
).paddingAll(8))))
.paddingLTRB(8, 0, 8, 8));
return contact.editedProfile.name
.toLowerCase()
.contains(lowerValue) ||
contact.editedProfile.pronouns
.toLowerCase()
.contains(lowerValue);
}).toList();
},
spaceBetweenSearchAndList: 4,
inputDecoration: InputDecoration(
labelText: translate('chat_list.search'),
),
),
).paddingAll(8))))
.paddingLTRB(8, 0, 8, 8);
});
}
}

View File

@ -182,7 +182,7 @@ class CreateInvitationDialogState extends State<CreateInvitationDialog> {
LengthLimitingTextInputFormatter(128),
],
decoration: InputDecoration(
border: const OutlineInputBorder(),
//border: const OutlineInputBorder(),
hintText:
translate('create_invitation_dialog.enter_message_hint'),
labelText: translate('create_invitation_dialog.message')),

View File

@ -0,0 +1,72 @@
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';
import '../../tools/tools.dart';
import 'create_invitation_dialog.dart';
import 'paste_invitation_dialog.dart';
import 'scan_invitation_dialog.dart';
Widget newContactBottomSheetBuilder(
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_contact_sheet.new_contact'),
child: SizedBox(
height: 160,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Column(children: [
IconButton(
onPressed: () async {
Navigator.pop(sheetContext);
await CreateInvitationDialog.show(context);
},
iconSize: 64,
icon: const Icon(Icons.contact_page),
color: scale.primaryScale.background),
Text(
translate('add_contact_sheet.create_invite'),
)
]),
Column(children: [
IconButton(
onPressed: () async {
Navigator.pop(sheetContext);
await ScanInvitationDialog.show(context);
},
iconSize: 64,
icon: const Icon(Icons.qr_code_scanner),
color: scale.primaryScale.background),
Text(
translate('add_contact_sheet.scan_invite'),
)
]),
Column(children: [
IconButton(
onPressed: () async {
Navigator.pop(sheetContext);
await PasteInvitationDialog.show(context);
},
iconSize: 64,
icon: const Icon(Icons.paste),
color: scale.primaryScale.background),
Text(
translate('add_contact_sheet.paste_invite'),
)
])
]).paddingAll(16))));
}

View File

@ -1,66 +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';
import 'paste_invitation_dialog.dart';
import 'scan_invitation_dialog.dart';
import 'create_invitation_dialog.dart';
Widget newContactInvitationBottomSheetBuilder(
BuildContext sheetContext, BuildContext context) {
final theme = Theme.of(sheetContext);
final textTheme = theme.textTheme;
final scale = theme.extension<ScaleScheme>()!;
return KeyboardListener(
focusNode: FocusNode(),
onKeyEvent: (ke) {
if (ke.logicalKey == LogicalKeyboardKey.escape) {
Navigator.pop(sheetContext);
}
},
child: SizedBox(
height: 200,
child: Column(children: [
Text(translate('accounts_menu.invite_contact'),
style: textTheme.titleMedium)
.paddingAll(8),
Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
Column(children: [
IconButton(
onPressed: () async {
Navigator.pop(sheetContext);
await CreateInvitationDialog.show(context);
},
iconSize: 64,
icon: const Icon(Icons.contact_page),
color: scale.primaryScale.background),
Text(translate('accounts_menu.create_invite'))
]),
Column(children: [
IconButton(
onPressed: () async {
Navigator.pop(sheetContext);
await ScanInvitationDialog.show(context);
},
iconSize: 64,
icon: const Icon(Icons.qr_code_scanner),
color: scale.primaryScale.background),
Text(translate('accounts_menu.scan_invite'))
]),
Column(children: [
IconButton(
onPressed: () async {
Navigator.pop(sheetContext);
await PasteInvitationDialog.show(context);
},
iconSize: 64,
icon: const Icon(Icons.paste),
color: scale.primaryScale.background),
Text(translate('accounts_menu.paste_invite'))
])
]).expanded()
])));
}

View File

@ -125,7 +125,6 @@ class PasteInvitationDialogState extends State<PasteInvitationDialog> {
maxLines: null,
controller: _pasteTextController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: '--- BEGIN VEILIDCHAT CONTACT INVITE ----\n'
'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\n'
'---- END VEILIDCHAT CONTACT INVITE -----\n',

View File

@ -3,6 +3,6 @@ export 'contact_invitation_item_widget.dart';
export 'contact_invitation_list_widget.dart';
export 'create_invitation_dialog.dart';
export 'invitation_dialog.dart';
export 'new_contact_invitation_bottom_sheet.dart';
export 'new_contact_bottom_sheet.dart';
export 'paste_invitation_dialog.dart';
export 'scan_invitation_dialog.dart';

View File

@ -29,14 +29,15 @@ class ContactItemWidget extends StatelessWidget {
final remoteConversationKey =
contact.remoteConversationRecordKey.toVeilid();
const selected =
false; // xxx: eventually when we have selectable contacts: activeContactCubit.state == remoteConversationRecordKey;
const selected = false; // xxx: eventually when we have selectable contacts:
// activeContactCubit.state == remoteConversationRecordKey;
return Container(
margin: const EdgeInsets.fromLTRB(0, 4, 0, 0),
clipBehavior: Clip.antiAlias,
decoration: ShapeDecoration(
color: selected
// ignore: dead_code
? scale.primaryScale.activeElementBackground
: scale.primaryScale.hoverElementBackground,
shape: RoundedRectangleBorder(
@ -102,9 +103,11 @@ class ContactItemWidget extends StatelessWidget {
? Text(contact.editedProfile.pronouns)
: null,
iconColor: selected
// ignore: dead_code
? scale.primaryScale.appText
: scale.primaryScale.subtleText,
textColor: selected
// ignore: dead_code
? scale.primaryScale.appText
: scale.primaryScale.subtleText,
selectedColor: scale.primaryScale.appText,

View File

@ -6,7 +6,6 @@ import 'package:flutter_translate/flutter_translate.dart';
import 'package:searchable_listview/searchable_listview.dart';
import '../../proto/proto.dart' as proto;
import '../../theme/theme.dart';
import '../../tools/tools.dart';
import 'contact_item_widget.dart';
import 'empty_contact_list_widget.dart';
@ -26,47 +25,33 @@ class ContactListWidget extends StatelessWidget {
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
//final textTheme = theme.textTheme;
final scale = theme.extension<ScaleScheme>()!;
return SizedBox.expand(
child: styledTitleContainer(
context: context,
title: translate('contact_list.title'),
child: SizedBox.expand(
child: (contactList.isEmpty)
? const EmptyContactListWidget()
: SearchableList<proto.Contact>(
initialList: contactList.toList(),
builder: (l, i, c) =>
ContactItemWidget(contact: c, disabled: disabled),
filter: (value) {
final lowerValue = value.toLowerCase();
return contactList
.where((element) =>
element.editedProfile.name
.toLowerCase()
.contains(lowerValue) ||
element.editedProfile.pronouns
.toLowerCase()
.contains(lowerValue))
.toList();
},
spaceBetweenSearchAndList: 4,
inputDecoration: InputDecoration(
labelText: translate('contact_list.search'),
contentPadding: const EdgeInsets.all(2),
fillColor: scale.primaryScale.appText,
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: scale.primaryScale.hoverBorder,
),
borderRadius: BorderRadius.circular(8),
),
),
).paddingAll(8),
))).paddingLTRB(8, 0, 8, 8);
}
Widget build(BuildContext context) => SizedBox.expand(
child: styledTitleContainer(
context: context,
title: translate('contact_list.title'),
child: SizedBox.expand(
child: (contactList.isEmpty)
? const EmptyContactListWidget()
: SearchableList<proto.Contact>(
initialList: contactList.toList(),
builder: (l, i, c) =>
ContactItemWidget(contact: c, disabled: disabled),
filter: (value) {
final lowerValue = value.toLowerCase();
return contactList
.where((element) =>
element.editedProfile.name
.toLowerCase()
.contains(lowerValue) ||
element.editedProfile.pronouns
.toLowerCase()
.contains(lowerValue))
.toList();
},
spaceBetweenSearchAndList: 4,
inputDecoration: InputDecoration(
labelText: translate('contact_list.search'),
),
).paddingAll(8),
))).paddingLTRB(8, 0, 8, 8);
}

View File

@ -12,8 +12,6 @@ class HomeAccountReadyChat extends StatefulWidget {
}
class HomeAccountReadyChatState extends State<HomeAccountReadyChat> {
final _unfocusNode = FocusNode();
@override
void initState() {
super.initState();
@ -26,7 +24,6 @@ class HomeAccountReadyChatState extends State<HomeAccountReadyChat> {
@override
void dispose() {
_unfocusNode.dispose();
super.dispose();
}
@ -42,8 +39,6 @@ class HomeAccountReadyChatState extends State<HomeAccountReadyChat> {
@override
Widget build(BuildContext context) => SafeArea(
child: GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(_unfocusNode),
child: buildChatComponent(context),
));
);
}

View File

@ -18,8 +18,6 @@ class AccountPage extends StatefulWidget {
}
class AccountPageState extends State<AccountPage> {
final _unfocusNode = FocusNode();
@override
void initState() {
super.initState();
@ -27,7 +25,6 @@ class AccountPageState extends State<AccountPage> {
@override
void dispose() {
_unfocusNode.dispose();
super.dispose();
}

View File

@ -11,8 +11,6 @@ class ChatsPage extends StatefulWidget {
}
class ChatsPageState extends State<ChatsPage> {
final _unfocusNode = FocusNode();
@override
void initState() {
super.initState();
@ -20,7 +18,6 @@ class ChatsPageState extends State<ChatsPage> {
@override
void dispose() {
_unfocusNode.dispose();
super.dispose();
}

View File

@ -5,9 +5,9 @@ import 'package:flutter/rendering.dart';
import 'package:flutter_animate/flutter_animate.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:preload_page_view/preload_page_view.dart';
import 'package:stylish_bottom_bar/model/bar_items.dart';
import 'package:stylish_bottom_bar/stylish_bottom_bar.dart';
import '../../../../chat/chat.dart';
import '../../../../contact_invitation/contact_invitation.dart';
import '../../../../theme/theme.dart';
import '../../../../tools/tools.dart';
@ -28,8 +28,6 @@ class MainPager extends StatefulWidget {
class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
//////////////////////////////////////////////////////////////////
final _unfocusNode = FocusNode();
var _currentPage = 0;
final pageController = PreloadPageController();
@ -56,7 +54,6 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
@override
void dispose() {
_unfocusNode.dispose();
pageController.dispose();
super.dispose();
}
@ -127,21 +124,13 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
});
}
Widget _onNewChatBottomSheetBuilder(
BuildContext sheetContext, BuildContext context) =>
const SizedBox(
height: 200,
child: Center(
child: Text(
'Group and custom chat functionality is not available yet')));
Widget _bottomSheetBuilder(BuildContext sheetContext, BuildContext context) {
if (_currentPage == 0) {
// New contact invitation
return newContactInvitationBottomSheetBuilder(sheetContext, context);
return newContactBottomSheetBuilder(sheetContext, context);
} else if (_currentPage == 1) {
// New chat
return _onNewChatBottomSheetBuilder(sheetContext, context);
return newChatBottomSheetBuilder(sheetContext, context);
} else {
// Unknown error
return debugPage('unknown page');

View File

@ -19,8 +19,6 @@ class HomeShell extends StatefulWidget {
}
class HomeShellState extends State<HomeShell> {
final _unfocusNode = FocusNode();
@override
void initState() {
super.initState();
@ -28,7 +26,6 @@ class HomeShellState extends State<HomeShell> {
@override
void dispose() {
_unfocusNode.dispose();
super.dispose();
}
@ -69,11 +66,9 @@ class HomeShellState extends State<HomeShell> {
// XXX: eventually write account switcher here
return SafeArea(
child: GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(_unfocusNode),
child: DecoratedBox(
decoration: BoxDecoration(
color: scale.primaryScale.activeElementBackground),
child: buildWithLogin(context))));
child: DecoratedBox(
decoration: BoxDecoration(
color: scale.primaryScale.activeElementBackground),
child: buildWithLogin(context)));
}
}

View File

@ -53,11 +53,11 @@ class SettingsPageState extends State<SettingsPage> {
child: ListView(
children: [
buildSettingsPageColorPreferences(
onChanged: () => setState(() {})),
context: context, onChanged: () => setState(() {})),
buildSettingsPageBrightnessPreferences(
onChanged: () => setState(() {})),
],
context: context, onChanged: () => setState(() {})),
].map((x) => x.paddingLTRB(0, 0, 0, 8)).toList(),
),
).paddingSymmetric(horizontal: 24, vertical: 8),
).paddingSymmetric(horizontal: 24, vertical: 16),
)));
}

View File

@ -1,7 +1,10 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
import 'package:radix_colors/radix_colors.dart';
import '../../tools/tools.dart';
import 'scale_color.dart';
import 'scale_scheme.dart';
@ -571,26 +574,27 @@ ColorScheme _scaleToColorScheme(Brightness brightness, ScaleScheme scale) =>
Colors.red, // scale.primaryScale.hoverElementBackground,
onPrimaryContainer: Colors.green, //scale.primaryScale.subtleText,
secondary: scale.secondaryScale.background,
onSecondary: scale.secondaryScale.appText,
onSecondary: scale.secondaryScale.foregroundText,
secondaryContainer: scale.secondaryScale.hoverElementBackground,
onSecondaryContainer: scale.secondaryScale.subtleText,
tertiary: scale.tertiaryScale.background,
onTertiary: scale.tertiaryScale.appText,
onTertiary: scale.tertiaryScale.foregroundText,
tertiaryContainer: scale.tertiaryScale.hoverElementBackground,
onTertiaryContainer: scale.tertiaryScale.subtleText,
error: scale.errorScale.background,
onError: scale.errorScale.appText,
onError: scale.errorScale.foregroundText,
errorContainer: scale.errorScale.hoverElementBackground,
onErrorContainer: scale.errorScale.subtleText,
background: scale.grayScale.appBackground, // reviewed
onBackground: scale.grayScale.appText, // reviewed
surface: scale.primaryScale.activeElementBackground, // reviewed
onSurface: scale.primaryScale.subtleText, // reviewed
surface: scale.primaryScale.background, // reviewed
onSurface: scale.primaryScale.foregroundText, // reviewed
surfaceVariant: scale.primaryScale.elementBackground,
onSurfaceVariant: scale.primaryScale.subtleText, // ?? reviewed a little
onSurfaceVariant:
scale.primaryScale.foregroundText, // ?? reviewed a little
outline: scale.primaryScale.border,
outlineVariant: scale.primaryScale.subtleBorder,
shadow: RadixColors.dark.gray.step1,
shadow: const Color(0xFF000000),
scrim: scale.primaryScale.background,
inverseSurface: scale.primaryScale.subtleText,
onInverseSurface: scale.primaryScale.subtleBackground,
@ -612,7 +616,7 @@ ChatTheme makeChatTheme(ScaleScheme scale, TextTheme textTheme) =>
contentPadding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
border: const OutlineInputBorder(
borderSide: BorderSide.none,
borderRadius: BorderRadius.all(Radius.circular(16))),
borderRadius: BorderRadius.all(Radius.circular(8))),
),
inputContainerDecoration:
BoxDecoration(color: scale.primaryScale.border),
@ -641,10 +645,39 @@ ChatTheme makeChatTheme(ScaleScheme scale, TextTheme textTheme) =>
fontSize: 64,
));
TextTheme _makeTextTheme(Brightness brightness) {
late final TextTheme textTheme;
if (Platform.isIOS) {
textTheme = (brightness == Brightness.light)
? Typography.blackCupertino
: Typography.whiteCupertino;
} else if (Platform.isMacOS) {
textTheme = (brightness == Brightness.light)
? Typography.blackRedwoodCity
: Typography.whiteRedwoodCity;
} else if (Platform.isAndroid || Platform.isFuchsia) {
textTheme = (brightness == Brightness.light)
? Typography.blackMountainView
: Typography.whiteMountainView;
} else if (Platform.isLinux) {
textTheme = (brightness == Brightness.light)
? Typography.blackHelsinki
: Typography.whiteHelsinki;
} else if (Platform.isWindows) {
textTheme = (brightness == Brightness.light)
? Typography.blackRedmond
: Typography.whiteRedmond;
} else {
log.warning('unknown platform');
textTheme = (brightness == Brightness.light)
? Typography.blackHelsinki
: Typography.whiteHelsinki;
}
return textTheme;
}
ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) {
final textTheme = (brightness == Brightness.light)
? Typography.blackCupertino
: Typography.whiteCupertino;
final textTheme = _makeTextTheme(brightness);
final radix = _radixScheme(brightness, themeColor);
final scaleScheme = radix.toScale();
final colorScheme = _scaleToColorScheme(brightness, scaleScheme);
@ -655,8 +688,42 @@ ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) {
bottomSheetTheme: themeData.bottomSheetTheme.copyWith(
elevation: 0,
modalElevation: 0,
shape:
RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16)))),
canvasColor: scaleScheme.primaryScale.subtleBackground,
chipTheme: themeData.chipTheme.copyWith(
backgroundColor: scaleScheme.primaryScale.elementBackground,
selectedColor: scaleScheme.primaryScale.activeElementBackground,
surfaceTintColor: scaleScheme.primaryScale.hoverElementBackground,
checkmarkColor: scaleScheme.primaryScale.background,
side: BorderSide(color: scaleScheme.primaryScale.border)),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: scaleScheme.primaryScale.elementBackground,
foregroundColor: scaleScheme.primaryScale.appText,
disabledBackgroundColor: scaleScheme.grayScale.elementBackground,
disabledForegroundColor: scaleScheme.grayScale.appText,
shape: RoundedRectangleBorder(
side: BorderSide(color: scaleScheme.primaryScale.border),
borderRadius: BorderRadius.circular(8))),
),
focusColor: scaleScheme.primaryScale.activeElementBackground,
hoverColor: scaleScheme.primaryScale.hoverElementBackground,
inputDecorationTheme: themeData.inputDecorationTheme.copyWith(
border: OutlineInputBorder(
borderSide: BorderSide(color: scaleScheme.primaryScale.border),
borderRadius: BorderRadius.circular(8)),
contentPadding: const EdgeInsets.all(8),
labelStyle: TextStyle(
color: scaleScheme.primaryScale.subtleText.withAlpha(127)),
floatingLabelStyle:
TextStyle(color: scaleScheme.primaryScale.subtleText),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: scaleScheme.primaryScale.hoverBorder, width: 2),
borderRadius: BorderRadius.circular(8))),
extensions: <ThemeExtension<dynamic>>[
scaleScheme,
]);

View File

@ -22,7 +22,7 @@ List<DropdownMenuItem<dynamic>> _getBrightnessDropdownItems() {
}
Widget buildSettingsPageBrightnessPreferences(
{required void Function() onChanged}) {
{required BuildContext context, required void Function() onChanged}) {
final preferencesRepository = PreferencesRepository.instance;
final themePreferences = preferencesRepository.value.themePreferences;
return ThemeSwitcher.withTheme(

View File

@ -30,7 +30,8 @@ List<DropdownMenuItem<dynamic>> _getThemeDropdownItems() {
.toList();
}
Widget buildSettingsPageColorPreferences({required void Function() onChanged}) {
Widget buildSettingsPageColorPreferences(
{required BuildContext context, required void Function() onChanged}) {
final preferencesRepository = PreferencesRepository.instance;
final themePreferences = preferencesRepository.value.themePreferences;
return ThemeSwitcher.withTheme(

View File

@ -1,24 +1,17 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:veilid_support/veilid_support.dart';
import 'veilid_processor/veilid_processor.dart';
class BackgroundTicker extends StatefulWidget {
const BackgroundTicker({required this.builder, super.key});
const BackgroundTicker({required this.child, super.key});
final Widget Function(BuildContext) builder;
final Widget child;
@override
BackgroundTickerState createState() => BackgroundTickerState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(ObjectFlagProperty<Widget Function(BuildContext p1)>.has(
'builder', builder));
}
}
class BackgroundTickerState extends State<BackgroundTicker> {
@ -48,7 +41,7 @@ class BackgroundTickerState extends State<BackgroundTicker> {
@override
// ignore: prefer_expression_function_bodies
Widget build(BuildContext context) {
return widget.builder(context);
return widget.child;
}
Future<void> _onTick() async {

View File

@ -154,7 +154,7 @@ Widget styledTitleContainer({
title,
style: textTheme.titleMedium!
.copyWith(color: scale.primaryScale.subtleText),
).paddingLTRB(8, 8, 8, 8),
).paddingLTRB(8, 8, 8, 4),
DecoratedBox(
decoration: ShapeDecoration(
color:
@ -168,6 +168,43 @@ Widget styledTitleContainer({
]));
}
Widget styledBottomSheet({
required BuildContext context,
required String title,
required Widget child,
Color? borderColor,
Color? backgroundColor,
}) {
final theme = Theme.of(context);
final scale = theme.extension<ScaleScheme>()!;
final textTheme = theme.textTheme;
return DecoratedBox(
decoration: ShapeDecoration(
color: borderColor ?? scale.primaryScale.border,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16)))),
child: Column(mainAxisSize: MainAxisSize.min, children: [
Text(
title,
style: textTheme.titleMedium!
.copyWith(color: scale.primaryScale.subtleText),
).paddingLTRB(8, 8, 8, 4),
DecoratedBox(
decoration: ShapeDecoration(
color:
backgroundColor ?? scale.primaryScale.subtleBackground,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(16),
topRight: Radius.circular(16)))),
child: child)
.paddingLTRB(4, 4, 4, 0)
]));
}
bool get isPlatformDark =>
WidgetsBinding.instance.platformDispatcher.platformBrightness ==
Brightness.dark;

View File

@ -80,6 +80,18 @@ class _DeveloperPageState extends State<DeveloperPage> {
return;
}
if (debugCommand.startsWith('change_log_ignore ')) {
final args = debugCommand.split(' ');
if (args.length < 3) {
_debugOut('Incorrect number of arguments');
return;
}
final layer = args[1];
final changes = args[2].split(',');
Veilid.instance.changeLogIgnore(layer, changes);
return;
}
if (debugCommand == 'ellet') {
setState(() {
_showEllet = !_showEllet;

View File

@ -1,6 +1,7 @@
import 'package:veilid/veilid.dart';
import 'dart:io' show Platform;
import 'package:veilid/veilid.dart';
Map<String, dynamic> getDefaultVeilidPlatformConfig(
bool isWeb, String appName) {
final ignoreLogTargetsStr =