diff --git a/assets/i18n/en.json b/assets/i18n/en.json index 3ae7b8a..7c47707 100644 --- a/assets/i18n/en.json +++ b/assets/i18n/en.json @@ -4,7 +4,9 @@ }, "menu": { "settings_tooltip": "Settings", - "add_account_tooltip": "Add Account" + "add_account_tooltip": "Add Account", + "accounts": "Accounts", + "version": "Version" }, "pager": { "chats": "Chats", @@ -192,6 +194,7 @@ "eggplant": "Eggplant", "lime": "Lime", "grim": "Grim", + "elite": "31337", "contrast": "Contrast" }, "brightness": { diff --git a/lib/account_manager/views/edit_account_page.dart b/lib/account_manager/views/edit_account_page.dart index 36a9c28..0cf0264 100644 --- a/lib/account_manager/views/edit_account_page.dart +++ b/lib/account_manager/views/edit_account_page.dart @@ -261,7 +261,7 @@ class _EditAccountPageState extends State { Widget build(BuildContext context) { final displayModalHUD = _isInAsyncCall; - return Scaffold( + return StyledScaffold( // resizeToAvoidBottomInset: false, appBar: DefaultAppBar( title: Text(translate('edit_account_page.titlebar')), diff --git a/lib/account_manager/views/new_account_page.dart b/lib/account_manager/views/new_account_page.dart index f38bd3a..6b98467 100644 --- a/lib/account_manager/views/new_account_page.dart +++ b/lib/account_manager/views/new_account_page.dart @@ -93,7 +93,7 @@ class _NewAccountPageState extends State { Widget build(BuildContext context) { final displayModalHUD = _isInAsyncCall; - return Scaffold( + return StyledScaffold( // resizeToAvoidBottomInset: false, appBar: DefaultAppBar( title: Text(translate('new_account_page.titlebar')), diff --git a/lib/account_manager/views/profile_widget.dart b/lib/account_manager/views/profile_widget.dart index ecb7c3d..1f06476 100644 --- a/lib/account_manager/views/profile_widget.dart +++ b/lib/account_manager/views/profile_widget.dart @@ -21,24 +21,42 @@ class ProfileWidget extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final scale = theme.extension()!; + final scaleConfig = theme.extension()!; + final textTheme = theme.textTheme; return DecoratedBox( decoration: ShapeDecoration( - color: scale.primaryScale.border, - shape: - RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))), + color: scaleConfig.preferBorders + ? scale.primaryScale.elementBackground + : scale.primaryScale.border, + shape: RoundedRectangleBorder( + side: !scaleConfig.useVisualIndicators + ? BorderSide.none + : BorderSide( + strokeAlign: BorderSide.strokeAlignCenter, + color: scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText, + width: 2), + borderRadius: BorderRadius.all( + Radius.circular(16 * scaleConfig.borderRadiusScale))), + ), child: Column(children: [ Text( _profile.name, - style: textTheme.headlineSmall! - .copyWith(color: scale.primaryScale.borderText), + style: textTheme.headlineSmall!.copyWith( + color: scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText), textAlign: TextAlign.left, ).paddingAll(4), if (_profile.pronouns.isNotEmpty) Text(_profile.pronouns, - style: textTheme.bodyMedium! - .copyWith(color: scale.primaryScale.borderText)) + style: textTheme.bodyMedium!.copyWith( + color: scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText)) .paddingLTRB(4, 0, 4, 4), ]), ); diff --git a/lib/app.dart b/lib/app.dart index 01d2a6d..3dd2d57 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -11,12 +11,12 @@ import 'package:provider/provider.dart'; import 'package:veilid_support/veilid_support.dart'; import 'account_manager/account_manager.dart'; -import 'account_manager/cubits/active_local_account_cubit.dart'; import 'init.dart'; import 'layout/splash.dart'; import 'router/router.dart'; import 'settings/settings.dart'; import 'theme/models/theme_preference.dart'; +import 'theme/theme.dart'; import 'tick.dart'; import 'tools/loggy.dart'; import 'veilid_processor/veilid_processor.dart'; @@ -69,9 +69,7 @@ class VeilidChatApp extends StatelessWidget { }); } - Widget _buildShortcuts( - {required BuildContext context, - required Widget Function(BuildContext) builder}) => + Widget _buildShortcuts({required Widget Function(BuildContext) builder}) => ThemeSwitcher( builder: (context) => Shortcuts( shortcuts: { @@ -136,25 +134,42 @@ class VeilidChatApp extends StatelessWidget { accountRepository: AccountRepository.instance, locator: context.read)), ], - child: BackgroundTicker( - child: _buildShortcuts( - context: context, - builder: (context) => MaterialApp.router( - debugShowCheckedModeBanner: false, - routerConfig: - context.read().router(), - title: translate('app.title'), - theme: theme, - localizationsDelegates: [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - FormBuilderLocalizations.delegate, - localizationDelegate - ], - supportedLocales: - localizationDelegate.supportedLocales, - locale: localizationDelegate.currentLocale, - ))), + child: + BackgroundTicker(child: _buildShortcuts(builder: (context) { + final scale = theme.extension()!; + final scaleConfig = theme.extension()!; + + final gradient = LinearGradient( + begin: Alignment.topLeft, + end: Alignment.bottomRight, + colors: scaleConfig.preferBorders && + theme.brightness == Brightness.light + ? [ + scale.grayScale.hoverElementBackground, + scale.grayScale.subtleBackground, + ] + : [ + scale.tertiaryScale.hoverElementBackground, + scale.tertiaryScale.subtleBackground, + ]); + + return DecoratedBox( + decoration: BoxDecoration(gradient: gradient), + child: MaterialApp.router( + debugShowCheckedModeBanner: false, + routerConfig: context.read().router(), + title: translate('app.title'), + theme: theme, + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + FormBuilderLocalizations.delegate, + localizationDelegate + ], + supportedLocales: localizationDelegate.supportedLocales, + locale: localizationDelegate.currentLocale, + )); + })), )), ); }); diff --git a/lib/chat/views/chat_component_widget.dart b/lib/chat/views/chat_component_widget.dart index 2d38b3c..f95cebf 100644 --- a/lib/chat/views/chat_component_widget.dart +++ b/lib/chat/views/chat_component_widget.dart @@ -154,8 +154,9 @@ class ChatComponentWidget extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final scale = theme.extension()!; - final textTheme = Theme.of(context).textTheme; - final chatTheme = makeChatTheme(scale, textTheme); + final scaleConfig = theme.extension()!; + final textTheme = theme.textTheme; + final chatTheme = makeChatTheme(scale, scaleConfig, textTheme); final errorChatTheme = (ChatThemeEditor(chatTheme) ..inputTextColor = scale.errorScale.primary ..sendButtonIcon = Image.asset( @@ -191,104 +192,99 @@ class ChatComponentWidget extends StatelessWidget { chatComponentCubit.scrollOffset = 0; } - return DefaultTextStyle( - style: textTheme.bodySmall!, - child: Align( - alignment: AlignmentDirectional.centerEnd, - child: Stack( + return Align( + alignment: AlignmentDirectional.centerEnd, + child: Stack( + children: [ + Column( children: [ - Column( - children: [ - Container( - height: 48, + Container( + height: 48, + decoration: BoxDecoration( + color: scale.primaryScale.subtleBorder, + ), + child: Row(children: [ + Align( + alignment: AlignmentDirectional.centerStart, + child: Padding( + padding: + const EdgeInsetsDirectional.fromSTEB(16, 0, 16, 0), + child: Text(title, + textAlign: TextAlign.start, + style: textTheme.titleMedium!.copyWith( + color: scale.primaryScale.borderText)), + )), + const Spacer(), + IconButton( + icon: Icon(Icons.close, + color: scale.primaryScale.borderText), + onPressed: () async { + context.read().setActiveChat(null); + }).paddingLTRB(16, 0, 16, 0) + ]), + ), + Expanded( + child: DecoratedBox( decoration: BoxDecoration( - color: scale.primaryScale.subtleBorder, - ), - child: Row(children: [ - Align( - alignment: AlignmentDirectional.centerStart, - child: Padding( - padding: const EdgeInsetsDirectional.fromSTEB( - 16, 0, 16, 0), - child: Text(title, - textAlign: TextAlign.start, - style: textTheme.titleMedium!.copyWith( - color: scale.primaryScale.borderText)), - )), - const Spacer(), - IconButton( - icon: Icon(Icons.close, - color: scale.primaryScale.borderText), - onPressed: () async { - context.read().setActiveChat(null); - }).paddingLTRB(16, 0, 16, 0) - ]), - ), - Expanded( - child: DecoratedBox( - decoration: const BoxDecoration(), - child: NotificationListener( - onNotification: (notification) { - if (chatComponentCubit.scrollOffset != 0) { - return false; - } + color: scale.primaryScale.subtleBackground), + child: NotificationListener( + onNotification: (notification) { + if (chatComponentCubit.scrollOffset != 0) { + return false; + } - if (!isFirstPage && - notification.metrics.pixels <= - ((notification.metrics.maxScrollExtent - - notification.metrics - .minScrollExtent) * - (1.0 - onEndReachedThreshold) + - notification - .metrics.minScrollExtent)) { - // - final scrollOffset = (notification - .metrics.maxScrollExtent - + if (!isFirstPage && + notification.metrics.pixels <= + ((notification.metrics.maxScrollExtent - + notification + .metrics.minScrollExtent) * + (1.0 - onEndReachedThreshold) + + notification.metrics.minScrollExtent)) { + // + final scrollOffset = + (notification.metrics.maxScrollExtent - notification.metrics.minScrollExtent) * (1.0 - onEndReachedThreshold); - chatComponentCubit.scrollOffset = scrollOffset; + chatComponentCubit.scrollOffset = scrollOffset; - // - singleFuture(chatComponentState.chatKey, - () async { - await _handlePageForward(chatComponentCubit, - messageWindow, notification); - }); - } else if (!isLastPage && - notification.metrics.pixels >= - ((notification.metrics.maxScrollExtent - - notification.metrics - .minScrollExtent) * - onEndReachedThreshold + - notification - .metrics.minScrollExtent)) { - // - final scrollOffset = -(notification - .metrics.maxScrollExtent - + // + singleFuture(chatComponentState.chatKey, () async { + await _handlePageForward(chatComponentCubit, + messageWindow, notification); + }); + } else if (!isLastPage && + notification.metrics.pixels >= + ((notification.metrics.maxScrollExtent - + notification + .metrics.minScrollExtent) * + onEndReachedThreshold + + notification.metrics.minScrollExtent)) { + // + final scrollOffset = + -(notification.metrics.maxScrollExtent - notification.metrics.minScrollExtent) * (1.0 - onEndReachedThreshold); - chatComponentCubit.scrollOffset = scrollOffset; - // - singleFuture(chatComponentState.chatKey, - () async { - await _handlePageBackward(chatComponentCubit, - messageWindow, notification); - }); - } - return false; - }, - child: ValueListenableBuilder( - valueListenable: - chatComponentState.textEditingController, - builder: (context, textEditingValue, __) { - final messageIsValid = utf8 - .encode(textEditingValue.text) - .lengthInBytes < - 2048; + chatComponentCubit.scrollOffset = scrollOffset; + // + singleFuture(chatComponentState.chatKey, () async { + await _handlePageBackward(chatComponentCubit, + messageWindow, notification); + }); + } + return false; + }, + child: ValueListenableBuilder( + valueListenable: + chatComponentState.textEditingController, + builder: (context, textEditingValue, __) { + final messageIsValid = utf8 + .encode(textEditingValue.text) + .lengthInBytes < + 2048; - return Chat( + return Chat( key: chatComponentState.chatKey, theme: messageIsValid ? chatTheme @@ -343,13 +339,14 @@ class ChatComponentWidget extends StatelessWidget { //showUserAvatars: false, //showUserNames: true, user: localUser, - emptyState: const EmptyChatWidget()); - }))), - ), - ], + emptyState: const EmptyChatWidget()) + .paddingLTRB(0, 2, 0, 0); + }))), ), ], ), - )); + ], + ), + ); } } diff --git a/lib/contact_invitation/views/contact_invitation_display.dart b/lib/contact_invitation/views/contact_invitation_display.dart index 2e4acad..25ca4e5 100644 --- a/lib/contact_invitation/views/contact_invitation_display.dart +++ b/lib/contact_invitation/views/contact_invitation_display.dart @@ -47,6 +47,7 @@ class ContactInvitationDisplayDialog extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final textTheme = theme.textTheme; + final scaleConfig = theme.extension()!; final signedContactInvitationBytesV = context.watch().state; @@ -59,7 +60,8 @@ class ContactInvitationDisplayDialog extends StatelessWidget { child: Dialog( shape: RoundedRectangleBorder( side: const BorderSide(width: 2), - borderRadius: BorderRadius.circular(16)), + borderRadius: + BorderRadius.circular(16 * scaleConfig.borderRadiusScale)), backgroundColor: Colors.white, child: ConstrainedBox( constraints: BoxConstraints( diff --git a/lib/contact_invitation/views/contact_invitation_list_widget.dart b/lib/contact_invitation/views/contact_invitation_list_widget.dart index 19243b7..4ea44a3 100644 --- a/lib/contact_invitation/views/contact_invitation_list_widget.dart +++ b/lib/contact_invitation/views/contact_invitation_list_widget.dart @@ -40,13 +40,14 @@ class ContactInvitationListWidgetState final theme = Theme.of(context); //final textTheme = theme.textTheme; final scale = theme.extension()!; + final scaleConfig = theme.extension()!; return Container( width: double.infinity, margin: const EdgeInsets.fromLTRB(4, 0, 4, 4), decoration: ShapeDecoration( shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), + borderRadius: BorderRadius.circular(16 * scaleConfig.borderRadiusScale), )), constraints: const BoxConstraints(maxHeight: 200), child: Container( @@ -54,7 +55,8 @@ class ContactInvitationListWidgetState decoration: ShapeDecoration( color: scale.primaryScale.subtleBackground, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), + borderRadius: + BorderRadius.circular(16 * scaleConfig.borderRadiusScale), )), child: ListView.builder( controller: _scrollController, diff --git a/lib/layout/home/drawer_menu/drawer_menu.dart b/lib/layout/home/drawer_menu/drawer_menu.dart index b8df2ea..855edcc 100644 --- a/lib/layout/home/drawer_menu/drawer_menu.dart +++ b/lib/layout/home/drawer_menu/drawer_menu.dart @@ -1,6 +1,7 @@ import 'package:async_tools/async_tools.dart'; import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:fast_immutable_collections/fast_immutable_collections.dart'; +import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -47,18 +48,22 @@ class _DrawerMenuState extends State { }); } - Widget _wrapInBox({required Widget child, required Color color}) => + Widget _wrapInBox( + {required Widget child, + required Color color, + required double borderRadius}) => DecoratedBox( decoration: ShapeDecoration( color: color, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16))), + borderRadius: BorderRadius.circular(borderRadius))), child: child); Widget _makeAccountWidget( {required String name, required bool selected, required ScaleColor scale, + required ScaleConfig scaleConfig, required bool loggedIn, required void Function()? callback, required void Function()? footerCallback}) { @@ -71,13 +76,37 @@ class _DrawerMenuState extends State { shortname = abbrev; } + late final Color background; + late final Color hoverBackground; + late final Color activeBackground; + late final Color border; + late final Color hoverBorder; + late final Color activeBorder; + if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders) { + background = loggedIn ? scale.border : scale.subtleBorder; + hoverBackground = background; + activeBackground = background; + border = + selected ? scale.activeElementBackground : scale.elementBackground; + hoverBorder = border; + activeBorder = border; + } else { + background = + selected ? scale.activeElementBackground : scale.elementBackground; + hoverBackground = scale.hoverElementBackground; + activeBackground = scale.activeElementBackground; + border = loggedIn ? scale.border : scale.subtleBorder; + hoverBorder = scale.hoverBorder; + activeBorder = scale.primary; + } + final avatar = Container( height: 34, width: 34, decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all( - color: loggedIn ? scale.border : scale.subtleBorder, + color: border, width: 2, strokeAlign: BorderSide.strokeAlignOutside), color: Colors.blue, @@ -89,27 +118,28 @@ class _DrawerMenuState extends State { child: Text(shortname, style: theme.textTheme.titleLarge))); return AnimatedPadding( - padding: EdgeInsets.fromLTRB(selected ? 0 : 0, 0, selected ? 0 : 8, 0), + padding: EdgeInsets.fromLTRB(selected ? 0 : 8, selected ? 0 : 2, + selected ? 0 : 8, selected ? 0 : 2), duration: const Duration(milliseconds: 50), child: MenuItemWidget( title: name, headerWidget: avatar, - titleStyle: theme.textTheme.titleLarge!, + titleStyle: theme.textTheme.titleSmall! + .copyWith(color: scaleConfig.useVisualIndicators ? border : null), foregroundColor: scale.primary, - backgroundColor: selected - ? scale.activeElementBackground - : scale.elementBackground, - backgroundHoverColor: scale.hoverElementBackground, - backgroundFocusColor: scale.activeElementBackground, - borderColor: scale.border, - borderHoverColor: scale.hoverBorder, - borderFocusColor: scale.primary, + backgroundColor: background, + backgroundHoverColor: hoverBackground, + backgroundFocusColor: activeBackground, + borderColor: border, + borderHoverColor: hoverBorder, + borderFocusColor: activeBorder, + borderRadius: 16 * scaleConfig.borderRadiusScale, callback: callback, footerButtonIcon: loggedIn ? Icons.edit_outlined : null, footerCallback: footerCallback, - footerButtonIconColor: scale.border, - footerButtonIconHoverColor: scale.hoverElementBackground, - footerButtonIconFocusColor: scale.activeElementBackground, + footerButtonIconColor: border, + footerButtonIconHoverColor: hoverBackground, + footerButtonIconFocusColor: activeBackground, )); } @@ -120,6 +150,7 @@ class _DrawerMenuState extends State { perAccountCollectionBlocMapState}) { final theme = Theme.of(context); final scaleScheme = theme.extension()!; + final scaleConfig = theme.extension()!; final loggedInAccounts = []; final loggedOutAccounts = []; @@ -133,11 +164,12 @@ class _DrawerMenuState extends State { final avAccountRecordState = perAccountState?.avAccountRecordState; if (perAccountState != null && avAccountRecordState != null) { // Account is logged in - final scale = theme.extension()!.tertiaryScale; + final scale = theme.extension()!.primaryScale; final loggedInAccount = avAccountRecordState.when( data: (value) => _makeAccountWidget( name: value.profile.name, scale: scale, + scaleConfig: scaleConfig, selected: superIdentityRecordKey == activeLocalAccount, loggedIn: true, callback: () { @@ -152,10 +184,12 @@ class _DrawerMenuState extends State { }), loading: () => _wrapInBox( child: buildProgressIndicator(), - color: scaleScheme.grayScale.subtleBorder), + color: scaleScheme.grayScale.subtleBorder, + borderRadius: 16 * scaleConfig.borderRadiusScale), error: (err, st) => _wrapInBox( child: errorPage(err, st), - color: scaleScheme.errorScale.subtleBorder), + color: scaleScheme.errorScale.subtleBorder, + borderRadius: 16 * scaleConfig.borderRadiusScale), ); loggedInAccounts.add(loggedInAccount.paddingLTRB(0, 0, 0, 8)); } else { @@ -164,6 +198,7 @@ class _DrawerMenuState extends State { final loggedOutAccount = _makeAccountWidget( name: la.name, scale: scale, + scaleConfig: scaleConfig, selected: superIdentityRecordKey == activeLocalAccount, loggedIn: false, callback: () => {_doSwitchClick(superIdentityRecordKey)}, @@ -185,49 +220,77 @@ class _DrawerMenuState extends State { } Widget _getButton( - {required Icon icon, - required ScaleColor scale, - required String tooltip, - required void Function()? onPressed}) => - IconButton( - icon: icon, - color: scale.hoverBorder, - constraints: const BoxConstraints.expand(height: 64, width: 64), - style: ButtonStyle( - backgroundColor: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.hovered)) { - return scale.hoverElementBackground; - } - if (states.contains(WidgetState.focused)) { - return scale.activeElementBackground; - } - return scale.elementBackground; - }), shape: WidgetStateProperty.resolveWith((states) { - if (states.contains(WidgetState.hovered)) { - return RoundedRectangleBorder( - side: BorderSide(color: scale.hoverBorder), - borderRadius: const BorderRadius.all(Radius.circular(16))); - } - if (states.contains(WidgetState.focused)) { - return RoundedRectangleBorder( - side: BorderSide(color: scale.primary), - borderRadius: const BorderRadius.all(Radius.circular(16))); - } + {required Icon icon, + required ScaleColor scale, + required ScaleConfig scaleConfig, + required String tooltip, + required void Function()? onPressed}) { + late final Color background; + late final Color hoverBackground; + late final Color activeBackground; + late final Color border; + late final Color hoverBorder; + late final Color activeBorder; + if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders) { + background = scale.border; + hoverBackground = scale.hoverBorder; + activeBackground = scale.primary; + border = scale.elementBackground; + hoverBorder = scale.hoverElementBackground; + activeBorder = scale.activeElementBackground; + } else { + background = scale.elementBackground; + hoverBackground = scale.hoverElementBackground; + activeBackground = scale.activeElementBackground; + border = scale.border; + hoverBorder = scale.hoverBorder; + activeBorder = scale.primary; + } + return IconButton( + icon: icon, + color: border, + constraints: const BoxConstraints.expand(height: 64, width: 64), + style: ButtonStyle( + backgroundColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.hovered)) { + return hoverBackground; + } + if (states.contains(WidgetState.focused)) { + return activeBackground; + } + return background; + }), shape: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.hovered)) { return RoundedRectangleBorder( - side: BorderSide(color: scale.border), - borderRadius: const BorderRadius.all(Radius.circular(16))); - })), - tooltip: tooltip, - onPressed: onPressed); + side: BorderSide(color: hoverBorder), + borderRadius: BorderRadius.all( + Radius.circular(16 * scaleConfig.borderRadiusScale))); + } + if (states.contains(WidgetState.focused)) { + return RoundedRectangleBorder( + side: BorderSide(color: activeBorder), + borderRadius: BorderRadius.all( + Radius.circular(16 * scaleConfig.borderRadiusScale))); + } + return RoundedRectangleBorder( + side: BorderSide(color: border), + borderRadius: BorderRadius.all( + Radius.circular(16 * scaleConfig.borderRadiusScale))); + })), + tooltip: tooltip, + onPressed: onPressed); + } Widget _getBottomButtons() { final theme = Theme.of(context); final scale = theme.extension()!; + final scaleConfig = theme.extension()!; final settingsButton = _getButton( icon: const Icon(Icons.settings), tooltip: translate('menu.settings_tooltip'), scale: scale.tertiaryScale, + scaleConfig: scaleConfig, onPressed: () async { await GoRouterHelper(context).push('/settings'); }).paddingLTRB(0, 0, 16, 0); @@ -236,6 +299,7 @@ class _DrawerMenuState extends State { icon: const Icon(Icons.add), tooltip: translate('menu.add_account_tooltip'), scale: scale.tertiaryScale, + scaleConfig: scaleConfig, onPressed: () async { await GoRouterHelper(context).push('/new_account'); }).paddingLTRB(0, 0, 16, 0); @@ -259,53 +323,105 @@ class _DrawerMenuState extends State { begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ - scale.tertiaryScale.hoverElementBackground, + scale.tertiaryScale.subtleBorder, scale.tertiaryScale.subtleBackground, ]); return DecoratedBox( decoration: ShapeDecoration( shadows: [ - BoxShadow( - color: scale.tertiaryScale.appBackground, - blurRadius: 6, - offset: const Offset( - 0, - 3, + if (scaleConfig.useVisualIndicators && !scaleConfig.preferBorders) + BoxShadow( + color: scale.tertiaryScale.primary.darken(80), + spreadRadius: 2, + ) + else if (scaleConfig.useVisualIndicators && + scaleConfig.preferBorders) + BoxShadow( + color: scale.tertiaryScale.border, + spreadRadius: 2, + ) + else + BoxShadow( + color: scale.tertiaryScale.primary.darken(40), + blurRadius: 6, + offset: const Offset( + 0, + 4, + ), ), - ), ], - gradient: gradient, - shape: const RoundedRectangleBorder( + gradient: scaleConfig.useVisualIndicators ? null : gradient, + color: scaleConfig.useVisualIndicators + ? (scaleConfig.preferBorders + ? scale.tertiaryScale.appBackground + : scale.tertiaryScale.subtleBorder) + : null, + shape: RoundedRectangleBorder( + side: scaleConfig.preferBorders + ? BorderSide(color: scale.tertiaryScale.primary, width: 2) + : BorderSide.none, borderRadius: BorderRadius.only( - topRight: Radius.circular(16), - bottomRight: Radius.circular(16)))), + topRight: Radius.circular(16 * scaleConfig.borderRadiusScale), + bottomRight: + Radius.circular(16 * scaleConfig.borderRadiusScale)))), child: Column(children: [ FittedBox( fit: BoxFit.scaleDown, - child: Row(children: [ - SvgPicture.asset( + child: ColorFiltered( + colorFilter: ColorFilter.mode( + theme.brightness == Brightness.light + ? scale.tertiaryScale.primary + : scale.tertiaryScale.border, + scaleConfig.preferBorders + ? BlendMode.modulate + : BlendMode.dst), + child: Row(children: [ + SvgPicture.asset( + height: 48, + 'assets/images/icon.svg', + colorFilter: scaleConfig.useVisualIndicators + ? grayColorFilter + : null) + .paddingLTRB(0, 0, 16, 0), + SvgPicture.asset( height: 48, - 'assets/images/icon.svg', + 'assets/images/title.svg', colorFilter: scaleConfig.useVisualIndicators ? grayColorFilter - : null) - .paddingLTRB(0, 0, 16, 0), - SvgPicture.asset( - height: 48, - 'assets/images/title.svg', - colorFilter: - scaleConfig.useVisualIndicators ? grayColorFilter : null), - ])), + : null), + ]))), const Spacer(), - _getAccountList( - localAccounts: localAccounts, - activeLocalAccount: activeLocalAccount, - perAccountCollectionBlocMapState: perAccountCollectionBlocMapState), + DecoratedBox( + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + side: !scaleConfig.useVisualIndicators + ? BorderSide.none + : scaleConfig.preferBorders + ? BorderSide(color: scale.tertiaryScale.border) + : BorderSide(color: scale.tertiaryScale.primary), + borderRadius: BorderRadius.circular( + 16 * scaleConfig.borderRadiusScale)), + color: scaleConfig.preferBorders + ? Colors.transparent + : scale.tertiaryScale.border.withAlpha(0x5F)), + child: Column(children: [ + Text(translate('menu.accounts'), + style: theme.textTheme.titleMedium!.copyWith( + color: scaleConfig.preferBorders + ? scale.tertiaryScale.border + : scale.tertiaryScale.primary)) + .paddingLTRB(0, 0, 0, 16), + _getAccountList( + localAccounts: localAccounts, + activeLocalAccount: activeLocalAccount, + perAccountCollectionBlocMapState: + perAccountCollectionBlocMapState) + ]).paddingAll(16)), _getBottomButtons(), const Spacer(), Row(children: [ - Text('Version $packageInfoVersion', + Text('${translate('menu.version')} $packageInfoVersion', style: theme.textTheme.labelMedium! .copyWith(color: scale.tertiaryScale.hoverBorder)), const Spacer(), @@ -315,6 +431,6 @@ class _DrawerMenuState extends State { ), ]) ]).paddingAll(16), - ); + ).paddingLTRB(0, 2, 2, 2); } } diff --git a/lib/layout/home/drawer_menu/menu_item_widget.dart b/lib/layout/home/drawer_menu/menu_item_widget.dart index 260646f..b94a243 100644 --- a/lib/layout/home/drawer_menu/menu_item_widget.dart +++ b/lib/layout/home/drawer_menu/menu_item_widget.dart @@ -1,7 +1,6 @@ import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; class MenuItemWidget extends StatelessWidget { const MenuItemWidget({ @@ -17,6 +16,7 @@ class MenuItemWidget extends StatelessWidget { this.borderColor, this.borderHoverColor, this.borderFocusColor, + this.borderRadius, this.footerButtonIcon, this.footerButtonIconColor, this.footerButtonIconHoverColor, @@ -41,18 +41,20 @@ class MenuItemWidget extends StatelessWidget { side: WidgetStateBorderSide.resolveWith((states) { if (states.contains(WidgetState.hovered)) { return borderColor != null - ? BorderSide(color: borderHoverColor!) + ? BorderSide(width: 2, color: borderHoverColor!) : null; } if (states.contains(WidgetState.focused)) { return borderColor != null - ? BorderSide(color: borderFocusColor!) + ? BorderSide(width: 2, color: borderFocusColor!) : null; } - return borderColor != null ? BorderSide(color: borderColor!) : null; + return borderColor != null + ? BorderSide(width: 2, color: borderColor!) + : null; }), - shape: WidgetStateProperty.all( - RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)))), + shape: WidgetStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(borderRadius ?? 0)))), child: Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( @@ -104,6 +106,7 @@ class MenuItemWidget extends StatelessWidget { ..add(ColorProperty('backgroundHoverColor', backgroundHoverColor)) ..add(ColorProperty('backgroundFocusColor', backgroundFocusColor)) ..add(ColorProperty('borderColor', borderColor)) + ..add(DoubleProperty('borderRadius', borderRadius)) ..add(ColorProperty('borderHoverColor', borderHoverColor)) ..add(ColorProperty('borderFocusColor', borderFocusColor)); } @@ -122,6 +125,7 @@ class MenuItemWidget extends StatelessWidget { final Color? backgroundHoverColor; final Color? backgroundFocusColor; final Color? borderColor; + final double? borderRadius; final Color? borderHoverColor; final Color? borderFocusColor; final Color? footerButtonIconColor; diff --git a/lib/layout/home/home_account_ready/home_account_ready_main.dart b/lib/layout/home/home_account_ready/home_account_ready_main.dart index f682929..0c50bbd 100644 --- a/lib/layout/home/home_account_ready/home_account_ready_main.dart +++ b/lib/layout/home/home_account_ready/home_account_ready_main.dart @@ -30,23 +30,39 @@ class _HomeAccountReadyMainState extends State { (c) => c.state.asData!.value.profile); final theme = Theme.of(context); final scale = theme.extension()!; + final scaleConfig = theme.extension()!; return ColoredBox( - color: scale.primaryScale.subtleBorder, + color: scaleConfig.preferBorders + ? scale.primaryScale.subtleBackground + : scale.primaryScale.subtleBorder, child: Column(children: [ Row(children: [ IconButton( icon: const Icon(Icons.menu), - color: scale.secondaryScale.borderText, + color: scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText, constraints: const BoxConstraints.expand(height: 64, width: 64), style: ButtonStyle( backgroundColor: WidgetStateProperty.all( - scale.primaryScale.hoverBorder), + scaleConfig.preferBorders + ? scale.primaryScale.hoverElementBackground + : scale.primaryScale.hoverBorder), shape: WidgetStateProperty.all( - const RoundedRectangleBorder( - borderRadius: - BorderRadius.all(Radius.circular(16))))), + RoundedRectangleBorder( + side: !scaleConfig.useVisualIndicators + ? BorderSide.none + : BorderSide( + strokeAlign: BorderSide.strokeAlignCenter, + color: scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText, + width: 2), + borderRadius: BorderRadius.all(Radius.circular( + 16 * scaleConfig.borderRadiusScale))), + )), tooltip: translate('menu.settings_tooltip'), onPressed: () async { final ctrl = context.read(); @@ -82,6 +98,7 @@ class _HomeAccountReadyMainState extends State { final w = MediaQuery.of(context).size.width; final theme = Theme.of(context); final scale = theme.extension()!; + final scaleConfig = theme.extension()!; final children = [ ConstrainedBox( @@ -92,7 +109,10 @@ class _HomeAccountReadyMainState extends State { SizedBox( width: 2, height: double.infinity, - child: ColoredBox(color: scale.primaryScale.hoverBorder)), + child: ColoredBox( + color: scaleConfig.preferBorders + ? scale.primaryScale.subtleBorder + : scale.primaryScale.subtleBackground)), Expanded(child: buildTabletRightPane(context)), ]; diff --git a/lib/layout/home/home_account_ready/main_pager/account_page.dart b/lib/layout/home/home_account_ready/main_pager/account_page.dart index 0d8650e..f80a8fe 100644 --- a/lib/layout/home/home_account_ready/main_pager/account_page.dart +++ b/lib/layout/home/home_account_ready/main_pager/account_page.dart @@ -34,6 +34,7 @@ class AccountPageState extends State { final theme = Theme.of(context); final textTheme = theme.textTheme; final scale = theme.extension()!; + final scaleConfig = theme.extension()!; final cilState = context.watch().state; final cilBusy = cilState.busy; @@ -55,10 +56,12 @@ class AccountPageState extends State { backgroundColor: scale.primaryScale.border, collapsedBackgroundColor: scale.primaryScale.border, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), + borderRadius: + BorderRadius.circular(16 * scaleConfig.borderRadiusScale), ), collapsedShape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), + borderRadius: + BorderRadius.circular(16 * scaleConfig.borderRadiusScale), ), title: Text( translate('account_page.contact_invitations'), diff --git a/lib/layout/home/home_account_ready/main_pager/main_pager.dart b/lib/layout/home/home_account_ready/main_pager/main_pager.dart index 1e79344..d043433 100644 --- a/lib/layout/home/home_account_ready/main_pager/main_pager.dart +++ b/lib/layout/home/home_account_ready/main_pager/main_pager.dart @@ -1,12 +1,14 @@ import 'dart:async'; +import 'package:animated_bottom_navigation_bar/' + 'animated_bottom_navigation_bar.dart'; +import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/material.dart'; 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:provider/provider.dart'; -import 'package:stylish_bottom_bar/stylish_bottom_bar.dart'; import '../../../../chat/chat.dart'; import '../../../../contact_invitation/contact_invitation.dart'; @@ -39,7 +41,7 @@ class MainPagerState extends State with TickerProviderStateMixin { super.dispose(); } - bool onScrollNotification(ScrollNotification notification) { + bool _onScrollNotification(ScrollNotification notification) { if (notification is UserScrollNotification && notification.metrics.axis == Axis.vertical) { switch (notification.direction) { @@ -58,30 +60,6 @@ class MainPagerState extends State with TickerProviderStateMixin { return false; } - BottomBarItem buildBottomBarItem(int index) { - final theme = Theme.of(context); - final scale = theme.extension()!; - return BottomBarItem( - title: Text(_bottomLabelList[index]), - icon: - Icon(_selectedIconList[index], color: scale.primaryScale.borderText), - selectedIcon: - Icon(_selectedIconList[index], color: scale.primaryScale.borderText), - backgroundColor: scale.primaryScale.borderText, - //badge: const Text('9+'), - //showBadge: true, - ); - } - - List _buildBottomBarItems() { - final bottomBarItems = List.empty(growable: true); - for (var index = 0; index < _bottomLabelList.length; index++) { - final item = buildBottomBarItem(index); - bottomBarItems.add(item); - } - return bottomBarItems; - } - Future scanContactInvitationDialog(BuildContext context) async { await showDialog( context: context, @@ -104,6 +82,63 @@ class MainPagerState extends State with TickerProviderStateMixin { }); } + Widget _buildBottomBarItem(int index, bool isActive) { + final theme = Theme.of(context); + final scale = theme.extension()!; + final scaleConfig = theme.extension()!; + + final color = scaleConfig.useVisualIndicators + ? (scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText) + : (isActive + ? (scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText) + : (scaleConfig.preferBorders + ? scale.primaryScale.subtleBorder + : scale.primaryScale.borderText.withAlpha(0x80))); + + final item = Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + _selectedIconList[index], + size: 24, + color: color, + ), + const SizedBox(height: 4), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: Text( + _bottomLabelList[index], + style: theme.textTheme.labelLarge!.copyWith( + fontWeight: isActive ? FontWeight.bold : FontWeight.normal, + color: color), + ), + ) + ], + ); + + if (scaleConfig.useVisualIndicators && isActive) { + return DecoratedBox( + decoration: ShapeDecoration( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular( + 14 * scaleConfig.borderRadiusScale), + side: BorderSide( + width: 2, + color: scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText))), + child: item) + .paddingLTRB(8, 0, 8, 6); + } + + return item; + } + Widget _bottomSheetBuilder(BuildContext sheetContext, BuildContext context) { if (currentPage == 0) { // New contact invitation @@ -122,12 +157,13 @@ class MainPagerState extends State with TickerProviderStateMixin { Widget build(BuildContext context) { final theme = Theme.of(context); final scale = theme.extension()!; + final scaleConfig = theme.extension()!; return Scaffold( //extendBody: true, backgroundColor: Colors.transparent, body: NotificationListener( - onNotification: onScrollNotification, + onNotification: _onScrollNotification, child: PreloadPageView( key: _pageViewKey, controller: pageController, @@ -148,31 +184,46 @@ class MainPagerState extends State with TickerProviderStateMixin { // style: Theme.of(context).textTheme.headlineSmall, // ), // ), - bottomNavigationBar: StylishBottomBar( - backgroundColor: scale.primaryScale.hoverBorder, - option: AnimatedBarOptions( - inkEffect: true, - inkColor: scale.primaryScale.hoverPrimary, - opacity: 0.3, - ), - items: _buildBottomBarItems(), - hasNotch: true, - fabLocation: StylishBarFabLocation.end, - currentIndex: currentPage, + bottomNavigationBar: AnimatedBottomNavigationBar.builder( + itemCount: 2, + height: 64, + tabBuilder: _buildBottomBarItem, + activeIndex: currentPage, + gapLocation: GapLocation.end, + gapWidth: 90, + notchSmoothness: NotchSmoothness.defaultEdge, + notchMargin: 4, + backgroundColor: scaleConfig.preferBorders + ? scale.primaryScale.hoverElementBackground + : scale.primaryScale.hoverBorder, + elevation: 0, onTap: (index) async { await pageController.animateToPage(index, duration: 250.ms, curve: Curves.easeInOut); }, ), - floatingActionButton: BottomSheetActionButton( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(14))), - foregroundColor: scale.secondaryScale.borderText, - backgroundColor: scale.secondaryScale.hoverBorder, + shape: CircleBorder( + side: !scaleConfig.useVisualIndicators + ? BorderSide.none + : BorderSide( + strokeAlign: BorderSide.strokeAlignCenter, + color: scaleConfig.preferBorders + ? scale.secondaryScale.border + : scale.secondaryScale.borderText, + width: 2), + ), + foregroundColor: scaleConfig.preferBorders + ? scale.secondaryScale.border + : scale.secondaryScale.borderText, + backgroundColor: scaleConfig.preferBorders + ? scale.secondaryScale.hoverElementBackground + : scale.secondaryScale.hoverBorder, builder: (context) => Icon( _fabIconList[currentPage], - color: scale.secondaryScale.borderText, + color: scaleConfig.preferBorders + ? scale.secondaryScale.border + : scale.secondaryScale.borderText, ), bottomSheetBuilder: (sheetContext) => _bottomSheetBuilder(sheetContext, context)), diff --git a/lib/layout/home/home_screen.dart b/lib/layout/home/home_screen.dart index 47d3d64..1e10d27 100644 --- a/lib/layout/home/home_screen.dart +++ b/lib/layout/home/home_screen.dart @@ -118,6 +118,23 @@ class HomeScreenState extends State } } + Widget _applyPageBorder(Widget child) { + final theme = Theme.of(context); + final scale = theme.extension()!; + final scaleConfig = theme.extension()!; + + return ValueListenableBuilder( + valueListenable: _zoomDrawerController.stateNotifier!, + child: child, + builder: (context, drawerState, staticChild) => clipBorder( + clipEnabled: drawerState != DrawerState.closed, + borderEnabled: + scaleConfig.preferBorders && scaleConfig.useVisualIndicators, + borderRadius: 16 * scaleConfig.borderRadiusScale, + borderColor: scale.primaryScale.border, + child: staticChild!)); + } + Widget _buildAccountPageView(BuildContext context) { final localAccounts = context.watch().state; final activeLocalAccount = context.watch().state; @@ -127,7 +144,7 @@ class HomeScreenState extends State final activeIndex = localAccounts .indexWhere((x) => x.superIdentity.recordKey == activeLocalAccount); if (activeIndex == -1) { - return const HomeNoActive(); + return _applyPageBorder(const HomeNoActive()); } final accountPages = []; @@ -141,7 +158,7 @@ class HomeScreenState extends State } final accountPage = _buildAccountPage( context, superIdentityRecordKey, perAccountCollectionState); - accountPages.add(KeyedSubtree.wrap(accountPage, i)); + accountPages.add(_applyPageBorder(accountPage)); } return SlideIndexedStack( @@ -154,15 +171,6 @@ class HomeScreenState extends State @override Widget build(BuildContext context) { final theme = Theme.of(context); - final scale = theme.extension()!; - - final gradient = LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - scale.tertiaryScale.subtleBackground, - scale.tertiaryScale.appBackground, - ]); final localAccounts = context.watch().state; final activeLocalAccount = context.watch().state; @@ -171,8 +179,8 @@ class HomeScreenState extends State final canClose = activeIndex != -1; return SafeArea( - child: DecoratedBox( - decoration: BoxDecoration(gradient: gradient), + child: DefaultTextStyle( + style: theme.textTheme.bodySmall!, child: ZoomDrawer( controller: _zoomDrawerController, //menuBackgroundColor: Colors.transparent, @@ -188,18 +196,16 @@ class HomeScreenState extends State mainScreen: Provider.value( value: _zoomDrawerController, child: Builder(builder: _buildAccountPageView)), - borderRadius: 24, - //showShadow: false, + borderRadius: 0, angle: 0, - drawerShadowsBackgroundColor: theme.shadowColor, - mainScreenOverlayColor: theme.shadowColor.withAlpha(0x3F), + mainScreenOverlayColor: theme.shadowColor.withAlpha(0x2F), openCurve: Curves.fastEaseInToSlowEaseOut, // duration: const Duration(milliseconds: 250), // reverseDuration: const Duration(milliseconds: 250), menuScreenTapClose: canClose, mainScreenTapClose: canClose, disableDragGesture: !canClose, - mainScreenScale: .25, + mainScreenScale: .15, slideWidth: min(360, MediaQuery.of(context).size.width * 0.9), ))); } diff --git a/lib/settings/settings_page.dart b/lib/settings/settings_page.dart index 5eb89fb..eefbbd2 100644 --- a/lib/settings/settings_page.dart +++ b/lib/settings/settings_page.dart @@ -36,19 +36,18 @@ class SettingsPageState extends State { @override Widget build(BuildContext context) => AsyncBlocBuilder( - builder: (context, state) => ThemeSwitchingArea( - child: Scaffold( - appBar: DefaultAppBar( - title: Text(translate('settings_page.titlebar')), - leading: IconButton( - icon: const Icon(Icons.arrow_back), - onPressed: () => GoRouterHelper(context).pop(), - ), - actions: [ - const SignalStrengthMeterWidget() - .paddingLTRB(16, 0, 16, 0), - ]), - body: FormBuilder( + builder: (context, state) => StyledScaffold( + appBar: DefaultAppBar( + title: Text(translate('settings_page.titlebar')), + leading: IconButton( + icon: const Icon(Icons.arrow_back), + onPressed: () => GoRouterHelper(context).pop(), + ), + actions: [ + const SignalStrengthMeterWidget().paddingLTRB(16, 0, 16, 0), + ]), + body: ThemeSwitchingArea( + child: FormBuilder( key: _formKey, child: ListView( children: [ diff --git a/lib/theme/models/chat_theme.dart b/lib/theme/models/chat_theme.dart index 15fcdc3..2d52eae 100644 --- a/lib/theme/models/chat_theme.dart +++ b/lib/theme/models/chat_theme.dart @@ -5,47 +5,82 @@ import 'package:flutter_chat_ui/flutter_chat_ui.dart'; import 'scale_scheme.dart'; -ChatTheme makeChatTheme(ScaleScheme scale, TextTheme textTheme) => +ChatTheme makeChatTheme( + ScaleScheme scale, ScaleConfig scaleConfig, TextTheme textTheme) => DefaultChatTheme( - primaryColor: scale.primaryScale.calloutBackground, - secondaryColor: scale.secondaryScale.calloutBackground, + primaryColor: scaleConfig.preferBorders + ? scale.primaryScale.calloutText + : scale.primaryScale.calloutBackground, + secondaryColor: scaleConfig.preferBorders + ? scale.secondaryScale.calloutText + : scale.secondaryScale.calloutBackground, backgroundColor: scale.grayScale.appBackground, + messageBorderRadius: scaleConfig.borderRadiusScale * 16, + bubbleBorderSide: scaleConfig.preferBorders + ? BorderSide( + color: scale.primaryScale.calloutBackground, + width: 2, + ) + : null, sendButtonIcon: Image.asset( 'assets/icon-send.png', - color: scale.primaryScale.borderText, + color: scaleConfig.preferBorders + ? scale.primaryScale.border + : scale.primaryScale.borderText, package: 'flutter_chat_ui', ), inputBackgroundColor: Colors.blue, inputBorderRadius: BorderRadius.zero, inputTextDecoration: InputDecoration( - filled: true, - fillColor: scale.primaryScale.elementBackground, + filled: !scaleConfig.preferBorders, + fillColor: scale.primaryScale.subtleBackground, isDense: true, contentPadding: const EdgeInsets.fromLTRB(8, 12, 8, 12), - border: const OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.all(Radius.circular(8))), - focusedBorder: const OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.all(Radius.circular(8))), + disabledBorder: OutlineInputBorder( + borderSide: scaleConfig.preferBorders + ? BorderSide(color: scale.grayScale.border, width: 2) + : BorderSide.none, + borderRadius: BorderRadius.all( + Radius.circular(8 * scaleConfig.borderRadiusScale))), + enabledBorder: OutlineInputBorder( + borderSide: scaleConfig.preferBorders + ? BorderSide(color: scale.primaryScale.border, width: 2) + : BorderSide.none, + borderRadius: BorderRadius.all( + Radius.circular(8 * scaleConfig.borderRadiusScale))), + focusedBorder: OutlineInputBorder( + borderSide: scaleConfig.preferBorders + ? BorderSide(color: scale.primaryScale.border, width: 2) + : BorderSide.none, + borderRadius: BorderRadius.all( + Radius.circular(8 * scaleConfig.borderRadiusScale))), ), - inputContainerDecoration: - BoxDecoration(color: scale.primaryScale.border), - inputPadding: const EdgeInsets.all(9), - inputTextColor: scale.primaryScale.appText, + inputContainerDecoration: BoxDecoration( + border: scaleConfig.preferBorders + ? Border( + top: BorderSide(color: scale.primaryScale.border, width: 2)) + : null, + color: scaleConfig.preferBorders + ? scale.primaryScale.elementBackground + : scale.primaryScale.border), + inputPadding: const EdgeInsets.all(12), + inputTextColor: !scaleConfig.preferBorders + ? scale.primaryScale.appText + : scale.primaryScale.border, attachmentButtonIcon: const Icon(Icons.attach_file), - sentMessageBodyTextStyle: TextStyle( - color: scale.primaryScale.calloutText, - fontSize: 16, - fontWeight: FontWeight.w500, - height: 1.5, + sentMessageBodyTextStyle: textTheme.bodyLarge!.copyWith( + color: scaleConfig.preferBorders + ? scale.primaryScale.calloutBackground + : scale.primaryScale.calloutText, ), sentEmojiMessageTextStyle: const TextStyle( color: Colors.white, fontSize: 64, ), receivedMessageBodyTextStyle: TextStyle( - color: scale.secondaryScale.calloutText, + color: scaleConfig.preferBorders + ? scale.secondaryScale.calloutBackground + : scale.secondaryScale.calloutText, fontSize: 16, fontWeight: FontWeight.w500, height: 1.5, diff --git a/lib/theme/models/contrast_generator.dart b/lib/theme/models/contrast_generator.dart index 52b32c9..09cef2b 100644 --- a/lib/theme/models/contrast_generator.dart +++ b/lib/theme/models/contrast_generator.dart @@ -5,11 +5,14 @@ import 'scale_color.dart'; import 'scale_input_decorator_theme.dart'; import 'scale_scheme.dart'; -ScaleScheme _contrastScale(Brightness brightness) { - final back = brightness == Brightness.light ? Colors.white : Colors.black; - final front = brightness == Brightness.light ? Colors.black : Colors.white; +ScaleColor _contrastScaleColor( + {required Brightness brightness, + required Color frontColor, + required Color backColor}) { + final back = brightness == Brightness.light ? backColor : frontColor; + final front = brightness == Brightness.light ? frontColor : backColor; - final primaryScale = ScaleColor( + return ScaleColor( appBackground: back, subtleBackground: back, elementBackground: back, @@ -28,21 +31,236 @@ ScaleScheme _contrastScale(Brightness brightness) { calloutBackground: front, calloutText: back, ); - - return ScaleScheme( - primaryScale: primaryScale, - primaryAlphaScale: primaryScale, - secondaryScale: primaryScale, - tertiaryScale: primaryScale, - grayScale: primaryScale, - errorScale: primaryScale); } -ThemeData contrastGenerator(Brightness brightness) { - final textTheme = makeRadixTextTheme(brightness); - final scaleScheme = _contrastScale(brightness); - final colorScheme = scaleScheme.toColorScheme(brightness); - final scaleConfig = ScaleConfig(useVisualIndicators: true); +const kMonoSpaceFontDisplay = 'Source Code Pro'; +const kMonoSpaceFontText = 'Source Code Pro'; + +TextTheme makeMonoSpaceTextTheme(Brightness brightness) => + (brightness == Brightness.light) + ? const TextTheme( + displayLarge: TextStyle( + debugLabel: 'blackMonoSpace displayLarge', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.black54, + decoration: TextDecoration.none), + displayMedium: TextStyle( + debugLabel: 'blackMonoSpace displayMedium', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.black54, + decoration: TextDecoration.none), + displaySmall: TextStyle( + debugLabel: 'blackMonoSpace displaySmall', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.black54, + decoration: TextDecoration.none), + headlineLarge: TextStyle( + debugLabel: 'blackMonoSpace headlineLarge', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.black54, + decoration: TextDecoration.none), + headlineMedium: TextStyle( + debugLabel: 'blackMonoSpace headlineMedium', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.black54, + decoration: TextDecoration.none), + headlineSmall: TextStyle( + debugLabel: 'blackMonoSpace headlineSmall', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.black87, + decoration: TextDecoration.none), + titleLarge: TextStyle( + debugLabel: 'blackMonoSpace titleLarge', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.black87, + decoration: TextDecoration.none), + titleMedium: TextStyle( + debugLabel: 'blackMonoSpace titleMedium', + fontFamily: kMonoSpaceFontText, + color: Colors.black87, + decoration: TextDecoration.none), + titleSmall: TextStyle( + debugLabel: 'blackMonoSpace titleSmall', + fontFamily: kMonoSpaceFontText, + color: Colors.black, + decoration: TextDecoration.none), + bodyLarge: TextStyle( + debugLabel: 'blackMonoSpace bodyLarge', + fontFamily: kMonoSpaceFontText, + color: Colors.black87, + decoration: TextDecoration.none), + bodyMedium: TextStyle( + debugLabel: 'blackMonoSpace bodyMedium', + fontFamily: kMonoSpaceFontText, + color: Colors.black87, + decoration: TextDecoration.none), + bodySmall: TextStyle( + debugLabel: 'blackMonoSpace bodySmall', + fontFamily: kMonoSpaceFontText, + color: Colors.black54, + decoration: TextDecoration.none), + labelLarge: TextStyle( + debugLabel: 'blackMonoSpace labelLarge', + fontFamily: kMonoSpaceFontText, + color: Colors.black87, + decoration: TextDecoration.none), + labelMedium: TextStyle( + debugLabel: 'blackMonoSpace labelMedium', + fontFamily: kMonoSpaceFontText, + color: Colors.black, + decoration: TextDecoration.none), + labelSmall: TextStyle( + debugLabel: 'blackMonoSpace labelSmall', + fontFamily: kMonoSpaceFontText, + color: Colors.black, + decoration: TextDecoration.none), + ) + : const TextTheme( + displayLarge: TextStyle( + debugLabel: 'whiteMonoSpace displayLarge', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.white70, + decoration: TextDecoration.none), + displayMedium: TextStyle( + debugLabel: 'whiteMonoSpace displayMedium', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.white70, + decoration: TextDecoration.none), + displaySmall: TextStyle( + debugLabel: 'whiteMonoSpace displaySmall', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.white70, + decoration: TextDecoration.none), + headlineLarge: TextStyle( + debugLabel: 'whiteMonoSpace headlineLarge', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.white70, + decoration: TextDecoration.none), + headlineMedium: TextStyle( + debugLabel: 'whiteMonoSpace headlineMedium', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.white70, + decoration: TextDecoration.none), + headlineSmall: TextStyle( + debugLabel: 'whiteMonoSpace headlineSmall', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.white, + decoration: TextDecoration.none), + titleLarge: TextStyle( + debugLabel: 'whiteMonoSpace titleLarge', + fontFamily: kMonoSpaceFontDisplay, + color: Colors.white, + decoration: TextDecoration.none), + titleMedium: TextStyle( + debugLabel: 'whiteMonoSpace titleMedium', + fontFamily: kMonoSpaceFontText, + color: Colors.white, + decoration: TextDecoration.none), + titleSmall: TextStyle( + debugLabel: 'whiteMonoSpace titleSmall', + fontFamily: kMonoSpaceFontText, + color: Colors.white, + decoration: TextDecoration.none), + bodyLarge: TextStyle( + debugLabel: 'whiteMonoSpace bodyLarge', + fontFamily: kMonoSpaceFontText, + color: Colors.white, + decoration: TextDecoration.none), + bodyMedium: TextStyle( + debugLabel: 'whiteMonoSpace bodyMedium', + fontFamily: kMonoSpaceFontText, + color: Colors.white, + decoration: TextDecoration.none), + bodySmall: TextStyle( + debugLabel: 'whiteMonoSpace bodySmall', + fontFamily: kMonoSpaceFontText, + color: Colors.white70, + decoration: TextDecoration.none), + labelLarge: TextStyle( + debugLabel: 'whiteMonoSpace labelLarge', + fontFamily: kMonoSpaceFontText, + color: Colors.white, + decoration: TextDecoration.none), + labelMedium: TextStyle( + debugLabel: 'whiteMonoSpace labelMedium', + fontFamily: kMonoSpaceFontText, + color: Colors.white, + decoration: TextDecoration.none), + labelSmall: TextStyle( + debugLabel: 'whiteMonoSpace labelSmall', + fontFamily: kMonoSpaceFontText, + color: Colors.white, + decoration: TextDecoration.none), + ); + +ScaleScheme _contrastScaleScheme( + {required Brightness brightness, + required Color primaryFront, + required Color primaryBack, + required Color secondaryFront, + required Color secondaryBack, + required Color tertiaryFront, + required Color tertiaryBack, + required Color grayFront, + required Color grayBack, + required Color errorFront, + required Color errorBack}) => + ScaleScheme( + primaryScale: _contrastScaleColor( + brightness: brightness, + frontColor: primaryFront, + backColor: primaryBack), + primaryAlphaScale: _contrastScaleColor( + brightness: brightness, + frontColor: primaryFront, + backColor: primaryBack), + secondaryScale: _contrastScaleColor( + brightness: brightness, + frontColor: secondaryFront, + backColor: secondaryBack), + tertiaryScale: _contrastScaleColor( + brightness: brightness, + frontColor: tertiaryFront, + backColor: tertiaryBack), + grayScale: _contrastScaleColor( + brightness: brightness, frontColor: grayFront, backColor: grayBack), + errorScale: _contrastScaleColor( + brightness: brightness, + frontColor: errorFront, + backColor: errorBack)); + +ThemeData contrastGenerator({ + required Brightness brightness, + required ScaleConfig scaleConfig, + required Color primaryFront, + required Color primaryBack, + required Color secondaryFront, + required Color secondaryBack, + required Color tertiaryFront, + required Color tertiaryBack, + required Color grayFront, + required Color grayBack, + required Color errorFront, + required Color errorBack, + TextTheme? customTextTheme, +}) { + final textTheme = customTextTheme ?? makeRadixTextTheme(brightness); + final scaleScheme = _contrastScaleScheme( + brightness: brightness, + primaryFront: primaryFront, + primaryBack: primaryBack, + secondaryFront: secondaryFront, + secondaryBack: secondaryBack, + tertiaryFront: tertiaryFront, + tertiaryBack: tertiaryBack, + grayFront: grayFront, + grayBack: grayBack, + errorFront: errorFront, + errorBack: errorBack, + ); + final colorScheme = scaleScheme.toColorScheme( + brightness, + ); final themeData = ThemeData.from( colorScheme: colorScheme, textTheme: textTheme, useMaterial3: true); @@ -50,10 +268,11 @@ ThemeData contrastGenerator(Brightness brightness) { bottomSheetTheme: themeData.bottomSheetTheme.copyWith( elevation: 0, modalElevation: 0, - shape: const RoundedRectangleBorder( + shape: RoundedRectangleBorder( borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16)))), + topLeft: Radius.circular(16 * scaleConfig.borderRadiusScale), + topRight: + Radius.circular(16 * scaleConfig.borderRadiusScale)))), canvasColor: scaleScheme.primaryScale.subtleBackground, chipTheme: themeData.chipTheme.copyWith( backgroundColor: scaleScheme.primaryScale.elementBackground, @@ -69,13 +288,15 @@ ThemeData contrastGenerator(Brightness brightness) { disabledForegroundColor: scaleScheme.grayScale.appText, shape: RoundedRectangleBorder( side: BorderSide(color: scaleScheme.primaryScale.border), - borderRadius: BorderRadius.circular(8))), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale))), ), textSelectionTheme: TextSelectionThemeData( cursorColor: scaleScheme.primaryScale.appText, selectionColor: scaleScheme.primaryScale.appText.withAlpha(0x7F), selectionHandleColor: scaleScheme.primaryScale.appText), - inputDecorationTheme: ScaleInputDecoratorTheme(scaleScheme, textTheme), + inputDecorationTheme: + ScaleInputDecoratorTheme(scaleScheme, scaleConfig, textTheme), extensions: >[ scaleScheme, scaleConfig, diff --git a/lib/theme/models/radix_generator.dart b/lib/theme/models/radix_generator.dart index 4bd593f..92d52c8 100644 --- a/lib/theme/models/radix_generator.dart +++ b/lib/theme/models/radix_generator.dart @@ -604,7 +604,11 @@ ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) { final radix = _radixScheme(brightness, themeColor); final scaleScheme = radix.toScale(); final colorScheme = scaleScheme.toColorScheme(brightness); - final scaleConfig = ScaleConfig(useVisualIndicators: false); + final scaleConfig = ScaleConfig( + useVisualIndicators: false, + preferBorders: false, + borderRadiusScale: 1, + ); final themeData = ThemeData.from( colorScheme: colorScheme, textTheme: textTheme, useMaterial3: true); @@ -654,8 +658,10 @@ ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) { disabledForegroundColor: scaleScheme.grayScale.primary, shape: RoundedRectangleBorder( side: BorderSide(color: scaleScheme.primaryScale.border), - borderRadius: BorderRadius.circular(8))), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale))), ), - inputDecorationTheme: ScaleInputDecoratorTheme(scaleScheme, textTheme), + inputDecorationTheme: + ScaleInputDecoratorTheme(scaleScheme, scaleConfig, textTheme), extensions: >[scaleScheme, scaleConfig]); } diff --git a/lib/theme/models/scale_input_decorator_theme.dart b/lib/theme/models/scale_input_decorator_theme.dart index f6865cd..265670e 100644 --- a/lib/theme/models/scale_input_decorator_theme.dart +++ b/lib/theme/models/scale_input_decorator_theme.dart @@ -3,11 +3,13 @@ import 'package:flutter/material.dart'; import 'scale_scheme.dart'; class ScaleInputDecoratorTheme extends InputDecorationTheme { - ScaleInputDecoratorTheme(this._scaleScheme, this._textTheme) + ScaleInputDecoratorTheme( + this._scaleScheme, ScaleConfig scaleConfig, this._textTheme) : super( border: OutlineInputBorder( borderSide: BorderSide(color: _scaleScheme.primaryScale.border), - borderRadius: BorderRadius.circular(8)), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale)), contentPadding: const EdgeInsets.all(8), labelStyle: TextStyle( color: _scaleScheme.primaryScale.subtleText.withAlpha(127)), @@ -16,7 +18,8 @@ class ScaleInputDecoratorTheme extends InputDecorationTheme { focusedBorder: OutlineInputBorder( borderSide: BorderSide( color: _scaleScheme.primaryScale.hoverBorder, width: 2), - borderRadius: BorderRadius.circular(8))); + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale))); final ScaleScheme _scaleScheme; final TextTheme _textTheme; diff --git a/lib/theme/models/scale_scheme.dart b/lib/theme/models/scale_scheme.dart index 990fe1e..512fda6 100644 --- a/lib/theme/models/scale_scheme.dart +++ b/lib/theme/models/scale_scheme.dart @@ -1,3 +1,6 @@ +import 'dart:ui'; + +import 'package:awesome_extensions/awesome_extensions.dart'; import 'package:flutter/material.dart'; import 'scale_color.dart'; @@ -97,7 +100,7 @@ class ScaleScheme extends ThemeExtension { onSurfaceVariant: secondaryScale.primaryText, // ?? reviewed a little outline: primaryScale.border, outlineVariant: secondaryScale.border, - shadow: const Color(0xFF000000), + shadow: primaryScale.primary.darken(80), //scrim: primaryScale.background, // inverseSurface: primaryScale.subtleText, // onInverseSurface: primaryScale.subtleBackground, @@ -109,16 +112,24 @@ class ScaleScheme extends ThemeExtension { class ScaleConfig extends ThemeExtension { ScaleConfig({ required this.useVisualIndicators, + required this.preferBorders, + required this.borderRadiusScale, }); final bool useVisualIndicators; + final bool preferBorders; + final double borderRadiusScale; @override ScaleConfig copyWith({ bool? useVisualIndicators, + bool? preferBorders, + double? borderRadiusScale, }) => ScaleConfig( useVisualIndicators: useVisualIndicators ?? this.useVisualIndicators, + preferBorders: preferBorders ?? this.preferBorders, + borderRadiusScale: borderRadiusScale ?? this.borderRadiusScale, ); @override @@ -127,8 +138,10 @@ class ScaleConfig extends ThemeExtension { return this; } return ScaleConfig( - useVisualIndicators: - t < .5 ? useVisualIndicators : other.useVisualIndicators, - ); + useVisualIndicators: + t < .5 ? useVisualIndicators : other.useVisualIndicators, + preferBorders: t < .5 ? preferBorders : other.preferBorders, + borderRadiusScale: + lerpDouble(borderRadiusScale, other.borderRadiusScale, t) ?? 1); } } diff --git a/lib/theme/models/slider_tile.dart b/lib/theme/models/slider_tile.dart index 251581e..11b4199 100644 --- a/lib/theme/models/slider_tile.dart +++ b/lib/theme/models/slider_tile.dart @@ -64,13 +64,13 @@ class SliderTile extends StatelessWidget { final theme = Theme.of(context); final scale = theme.extension()!; final tileColor = scale.scale(!disabled ? tileScale : ScaleKind.gray); - final scalecfg = theme.extension()!; + final scaleConfig = theme.extension()!; final borderColor = selected ? tileColor.hoverBorder : tileColor.border; - final backgroundColor = scalecfg.useVisualIndicators && !selected + final backgroundColor = scaleConfig.useVisualIndicators && !selected ? tileColor.borderText : borderColor; - final textColor = scalecfg.useVisualIndicators && !selected + final textColor = scaleConfig.useVisualIndicators && !selected ? borderColor : tileColor.borderText; @@ -79,10 +79,11 @@ class SliderTile extends StatelessWidget { decoration: ShapeDecoration( color: backgroundColor, shape: RoundedRectangleBorder( - side: scalecfg.useVisualIndicators + side: scaleConfig.useVisualIndicators ? BorderSide(width: 2, color: borderColor, strokeAlign: 0) : BorderSide.none, - borderRadius: BorderRadius.circular(8), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale), )), child: Slidable( // Specify a key if the Slidable is dismissible. @@ -95,12 +96,12 @@ class SliderTile extends StatelessWidget { .map( (a) => SlidableAction( onPressed: disabled ? null : a.onPressed, - backgroundColor: scalecfg.useVisualIndicators + backgroundColor: scaleConfig.useVisualIndicators ? (selected ? tileColor.borderText : tileColor.border) : scale.scale(a.actionScale).primary, - foregroundColor: scalecfg.useVisualIndicators + foregroundColor: scaleConfig.useVisualIndicators ? (selected ? tileColor.border : tileColor.borderText) @@ -118,12 +119,12 @@ class SliderTile extends StatelessWidget { .map( (a) => SlidableAction( onPressed: disabled ? null : a.onPressed, - backgroundColor: scalecfg.useVisualIndicators + backgroundColor: scaleConfig.useVisualIndicators ? (selected ? tileColor.borderText : tileColor.border) : scale.scale(a.actionScale).primary, - foregroundColor: scalecfg.useVisualIndicators + foregroundColor: scaleConfig.useVisualIndicators ? (selected ? tileColor.border : tileColor.borderText) @@ -134,7 +135,7 @@ class SliderTile extends StatelessWidget { ) .toList()), child: Padding( - padding: scalecfg.useVisualIndicators + padding: scaleConfig.useVisualIndicators ? EdgeInsets.zero : const EdgeInsets.fromLTRB(0, 2, 0, 2), child: ListTile( diff --git a/lib/theme/models/theme_preference.dart b/lib/theme/models/theme_preference.dart index 334bbba..0accd8f 100644 --- a/lib/theme/models/theme_preference.dart +++ b/lib/theme/models/theme_preference.dart @@ -5,6 +5,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import '../views/widget_helpers.dart'; import 'contrast_generator.dart'; import 'radix_generator.dart'; +import 'scale_scheme.dart'; part 'theme_preference.freezed.dart'; part 'theme_preference.g.dart'; @@ -37,6 +38,7 @@ enum ColorPreference { lime, grim, // Accessible Colors + elite, contrast; factory ColorPreference.fromJson(dynamic j) => @@ -63,7 +65,7 @@ class ThemePreferences with _$ThemePreferences { } extension ThemePreferencesExt on ThemePreferences { - /// Get material 'ThemeData' for existinb + /// Get material 'ThemeData' for existing theme ThemeData themeData() { late final Brightness brightness; switch (brightnessPreference) { @@ -83,8 +85,60 @@ extension ThemePreferencesExt on ThemePreferences { switch (colorPreference) { // Special cases case ColorPreference.contrast: - // xxx do contrastGenerator - themeData = contrastGenerator(brightness); + themeData = contrastGenerator( + brightness: brightness, + scaleConfig: ScaleConfig( + useVisualIndicators: true, + preferBorders: false, + borderRadiusScale: 1), + primaryFront: Colors.black, + primaryBack: Colors.white, + secondaryFront: Colors.black, + secondaryBack: Colors.white, + tertiaryFront: Colors.black, + tertiaryBack: Colors.white, + grayFront: Colors.black, + grayBack: Colors.white, + errorFront: Colors.black, + errorBack: Colors.white, + ); + case ColorPreference.elite: + themeData = brightness == Brightness.light + ? contrastGenerator( + brightness: Brightness.light, + scaleConfig: ScaleConfig( + useVisualIndicators: true, + preferBorders: true, + borderRadiusScale: 0.2), + primaryFront: const Color(0xFF000000), + primaryBack: const Color(0xFF00FF00), + secondaryFront: const Color(0xFF000000), + secondaryBack: const Color(0xFF00FFFF), + tertiaryFront: const Color(0xFF000000), + tertiaryBack: const Color(0xFFFF00FF), + grayFront: const Color(0xFF000000), + grayBack: const Color(0xFFFFFFFF), + errorFront: const Color(0xFFC0C0C0), + errorBack: const Color(0xFF0000FF), + customTextTheme: makeMonoSpaceTextTheme(Brightness.light)) + : contrastGenerator( + brightness: Brightness.dark, + scaleConfig: ScaleConfig( + useVisualIndicators: true, + preferBorders: true, + borderRadiusScale: 0.5), + primaryFront: const Color(0xFF000000), + primaryBack: const Color(0xFF00FF00), + secondaryFront: const Color(0xFF000000), + secondaryBack: const Color(0xFF00FFFF), + tertiaryFront: const Color(0xFF000000), + tertiaryBack: const Color(0xFFFF00FF), + grayFront: const Color(0xFF000000), + grayBack: const Color(0xFFFFFFFF), + errorFront: const Color(0xFF0000FF), + errorBack: const Color(0xFFC0C0C0), + customTextTheme: makeMonoSpaceTextTheme(Brightness.dark), + ); // Generate from Radix case ColorPreference.scarlet: themeData = radixGenerator(brightness, RadixThemeColor.scarlet); diff --git a/lib/theme/views/color_preferences.dart b/lib/theme/views/color_preferences.dart index 4a5219d..228c2fb 100644 --- a/lib/theme/views/color_preferences.dart +++ b/lib/theme/views/color_preferences.dart @@ -22,6 +22,7 @@ List> _getThemeDropdownItems() { ColorPreference.eggplant: translate('themes.eggplant'), ColorPreference.lime: translate('themes.lime'), ColorPreference.grim: translate('themes.grim'), + ColorPreference.elite: translate('themes.elite'), ColorPreference.contrast: translate('themes.contrast') }; diff --git a/lib/theme/views/styled_dialog.dart b/lib/theme/views/styled_dialog.dart index 0fd079c..b48a8fb 100644 --- a/lib/theme/views/styled_dialog.dart +++ b/lib/theme/views/styled_dialog.dart @@ -11,12 +11,14 @@ class StyledDialog extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final scale = theme.extension()!; + final scaleConfig = theme.extension()!; final textTheme = theme.textTheme; return AlertDialog( elevation: 0, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(16)), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.all( + Radius.circular(16 * scaleConfig.borderRadiusScale)), ), contentPadding: const EdgeInsets.all(4), backgroundColor: scale.primaryScale.dialogBorder, @@ -31,12 +33,14 @@ class StyledDialog extends StatelessWidget { decoration: ShapeDecoration( color: scale.primaryScale.border, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16))), + borderRadius: BorderRadius.circular( + 16 * scaleConfig.borderRadiusScale))), child: DecoratedBox( decoration: ShapeDecoration( color: scale.primaryScale.appBackground, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12))), + borderRadius: BorderRadius.circular( + 12 * scaleConfig.borderRadiusScale))), child: child.paddingAll(0)))); } diff --git a/lib/theme/views/styled_scaffold.dart b/lib/theme/views/styled_scaffold.dart new file mode 100644 index 0000000..6be898c --- /dev/null +++ b/lib/theme/views/styled_scaffold.dart @@ -0,0 +1,27 @@ +import 'package:awesome_extensions/awesome_extensions.dart'; +import 'package:flutter/material.dart'; + +import '../theme.dart'; + +class StyledScaffold extends StatelessWidget { + const StyledScaffold({required this.appBar, required this.body, super.key}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final scale = theme.extension()!; + final scaleConfig = theme.extension()!; + + return clipBorder( + clipEnabled: true, + borderEnabled: scaleConfig.useVisualIndicators, + borderRadius: 16 * scaleConfig.borderRadiusScale, + borderColor: scale.primaryScale.border, + child: Scaffold(appBar: appBar, body: body, key: key)) + .paddingAll(32); + } + + //////////////////////////////////////////////////////////////////////////// + final PreferredSizeWidget? appBar; + final Widget? body; +} diff --git a/lib/theme/views/views.dart b/lib/theme/views/views.dart index 6f6d7ac..0bdf87b 100644 --- a/lib/theme/views/views.dart +++ b/lib/theme/views/views.dart @@ -2,4 +2,5 @@ export 'brightness_preferences.dart'; export 'color_preferences.dart'; export 'scanner_error_widget.dart'; export 'styled_dialog.dart'; +export 'styled_scaffold.dart'; export 'widget_helpers.dart'; diff --git a/lib/theme/views/widget_helpers.dart b/lib/theme/views/widget_helpers.dart index e3dfd94..3577d66 100644 --- a/lib/theme/views/widget_helpers.dart +++ b/lib/theme/views/widget_helpers.dart @@ -132,7 +132,7 @@ void showErrorToast(BuildContext context, String message) { contentPadding: const EdgeInsets.all(16), primaryColor: scale.errorScale.elementBackground, secondaryColor: scale.errorScale.calloutBackground, - borderRadius: 16, + borderRadius: 16 * scaleConfig.borderRadiusScale, toastDuration: const Duration(seconds: 4), animationDuration: const Duration(milliseconds: 1000), displayBorder: scaleConfig.useVisualIndicators, @@ -152,7 +152,7 @@ void showInfoToast(BuildContext context, String message) { contentPadding: const EdgeInsets.all(16), primaryColor: scale.tertiaryScale.elementBackground, secondaryColor: scale.tertiaryScale.calloutBackground, - borderRadius: 16, + borderRadius: 16 * scaleConfig.borderRadiusScale, toastDuration: const Duration(seconds: 2), animationDuration: const Duration(milliseconds: 500), displayBorder: scaleConfig.useVisualIndicators, @@ -170,13 +170,15 @@ Widget styledTitleContainer({ }) { final theme = Theme.of(context); final scale = theme.extension()!; + final scaleConfig = theme.extension()!; final textTheme = theme.textTheme; return DecoratedBox( decoration: ShapeDecoration( color: borderColor ?? scale.primaryScale.border, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), + borderRadius: + BorderRadius.circular(16 * scaleConfig.borderRadiusScale), )), child: Column(children: [ Text( @@ -189,7 +191,8 @@ Widget styledTitleContainer({ color: backgroundColor ?? scale.primaryScale.subtleBackground, shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), + borderRadius: BorderRadius.circular( + 16 * scaleConfig.borderRadiusScale), )), child: child) .paddingAll(4) @@ -207,15 +210,17 @@ Widget styledBottomSheet({ }) { final theme = Theme.of(context); final scale = theme.extension()!; + final scaleConfig = theme.extension()!; final textTheme = theme.textTheme; return DecoratedBox( decoration: ShapeDecoration( color: borderColor ?? scale.primaryScale.dialogBorder, - shape: const RoundedRectangleBorder( + shape: RoundedRectangleBorder( borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16)))), + topLeft: Radius.circular(16 * scaleConfig.borderRadiusScale), + topRight: + Radius.circular(16 * scaleConfig.borderRadiusScale)))), child: Column(mainAxisSize: MainAxisSize.min, children: [ Text( title, @@ -226,10 +231,12 @@ Widget styledBottomSheet({ decoration: ShapeDecoration( color: backgroundColor ?? scale.primaryScale.subtleBackground, - shape: const RoundedRectangleBorder( + shape: RoundedRectangleBorder( borderRadius: BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16)))), + topLeft: Radius.circular( + 16 * scaleConfig.borderRadiusScale), + topRight: Radius.circular( + 16 * scaleConfig.borderRadiusScale)))), child: child) .paddingLTRB(4, 4, 4, 0) ])); @@ -261,3 +268,25 @@ const grayColorFilter = ColorFilter.matrix([ 1, 0, ]); + +Widget clipBorder({ + required bool clipEnabled, + required bool borderEnabled, + required double borderRadius, + required Color borderColor, + required Widget child, +}) => + ClipRRect( + borderRadius: clipEnabled + ? BorderRadius.circular(borderRadius) + : BorderRadius.zero, + child: DecoratedBox( + decoration: BoxDecoration(boxShadow: [ + if (borderEnabled) BoxShadow(color: borderColor, spreadRadius: 2) + ]), + child: ClipRRect( + borderRadius: clipEnabled + ? BorderRadius.circular(borderRadius) + : BorderRadius.zero, + child: child, + )).paddingAll(clipEnabled && borderEnabled ? 2 : 0)); diff --git a/lib/tools/enter_pin.dart b/lib/tools/enter_pin.dart index d0a21ec..5d476fb 100644 --- a/lib/tools/enter_pin.dart +++ b/lib/tools/enter_pin.dart @@ -51,6 +51,7 @@ class _EnterPinDialogState extends State { Widget build(BuildContext context) { final theme = Theme.of(context); final scale = theme.extension()!; + final scaleConfig = theme.extension()!; final focusedBorderColor = scale.primaryScale.hoverBorder; final fillColor = scale.primaryScale.elementBackground; final borderColor = scale.primaryScale.border; @@ -61,7 +62,7 @@ class _EnterPinDialogState extends State { textStyle: TextStyle(fontSize: 22, color: scale.primaryScale.appText), decoration: BoxDecoration( color: fillColor, - borderRadius: BorderRadius.circular(8), + borderRadius: BorderRadius.circular(8 * scaleConfig.borderRadiusScale), border: Border.all(color: borderColor), ), ); diff --git a/lib/veilid_processor/views/developer.dart b/lib/veilid_processor/views/developer.dart index bfcaa35..85914ab 100644 --- a/lib/veilid_processor/views/developer.dart +++ b/lib/veilid_processor/views/developer.dart @@ -140,6 +140,7 @@ class _DeveloperPageState extends State { final theme = Theme.of(context); final textTheme = theme.textTheme; final scale = theme.extension()!; + final scaleConfig = theme.extension()!; // WidgetsBinding.instance.addPostFrameCallback((_) { // if (!_isScrolling && _wantsBottom) { @@ -225,12 +226,22 @@ class _DeveloperPageState extends State { .copyWith(color: scale.primaryScale.primaryText), padding: const EdgeInsets.fromLTRB(8, 4, 8, 4), openBoxDecoration: BoxDecoration( - color: scale.primaryScale.border, - borderRadius: BorderRadius.circular(8), + //color: scale.primaryScale.border, + border: Border.all( + color: scaleConfig.useVisualIndicators + ? scale.primaryScale.hoverBorder + : scale.primaryScale.borderText), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale), ), boxDecoration: BoxDecoration( - color: scale.primaryScale.hoverBorder, - borderRadius: BorderRadius.circular(8), + //color: scale.primaryScale.hoverBorder, + border: Border.all( + color: scaleConfig.useVisualIndicators + ? scale.primaryScale.hoverBorder + : scale.primaryScale.borderText), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale), ), ), dropdownOptions: DropdownOptions( @@ -239,7 +250,8 @@ class _DeveloperPageState extends State { duration: 150.ms, color: scale.primaryScale.elementBackground, borderSide: BorderSide(color: scale.primaryScale.border), - borderRadius: BorderRadius.circular(8), + borderRadius: + BorderRadius.circular(8 * scaleConfig.borderRadiusScale), padding: const EdgeInsets.fromLTRB(8, 4, 8, 4), ), dropdownTriangleOptions: const DropdownTriangleOptions( @@ -283,10 +295,12 @@ class _DeveloperPageState extends State { filled: true, contentPadding: const EdgeInsets.fromLTRB(8, 2, 8, 2), enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), + borderRadius: BorderRadius.circular( + 8 * scaleConfig.borderRadiusScale), borderSide: BorderSide.none), border: OutlineInputBorder( - borderRadius: BorderRadius.circular(8), + borderRadius: BorderRadius.circular( + 8 * scaleConfig.borderRadiusScale), ), fillColor: scale.primaryScale.subtleBackground, hintText: translate('developer.command'), diff --git a/pubspec.lock b/pubspec.lock index 2e743b1..736e2bc 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.4.1" + animated_bottom_navigation_bar: + dependency: "direct main" + description: + name: animated_bottom_navigation_bar + sha256: "2b04a2ae4b0742669e60ddf309467d6a354cefd2d0cd20f4737b1efaf9834cda" + url: "https://pub.dev" + source: hosted + version: "1.3.3" animated_switcher_transitions: dependency: "direct main" description: @@ -481,11 +489,9 @@ packages: flutter_chat_ui: dependency: "direct main" description: - path: "." - ref: main - resolved-ref: "0d8ac2fcafe24eba1adff9290a9ccd41f7718480" - url: "https://gitlab.com/veilid/flutter-chat-ui.git" - source: git + path: "../flutter_chat_ui" + relative: true + source: path version: "1.6.14" flutter_form_builder: dependency: "direct main" @@ -1351,14 +1357,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" - stylish_bottom_bar: - dependency: "direct main" - description: - name: stylish_bottom_bar - sha256: ca72557a5bd8f44caae9017eb3a73002e9189d7a9d2fac598fa55be13724f32b - url: "https://pub.dev" - source: hosted - version: "1.1.0" synchronized: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a1f097b..c5c6a3f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -8,6 +8,7 @@ environment: flutter: '>=3.22.1' dependencies: + animated_bottom_navigation_bar: ^1.3.3 animated_switcher_transitions: ^1.0.0 animated_theme_switcher: ^2.0.10 ansicolor: ^2.0.2 @@ -83,7 +84,6 @@ dependencies: split_view: ^3.2.1 stack_trace: ^1.11.1 stream_transform: ^2.1.0 - stylish_bottom_bar: ^1.1.0 transitioned_indexed_stack: ^1.0.2 uuid: ^4.4.0 veilid: @@ -95,13 +95,13 @@ dependencies: xterm: ^4.0.0 zxing2: ^0.2.3 -# dependency_overrides: +dependency_overrides: # async_tools: # path: ../dart_async_tools # bloc_advanced_tools: # path: ../bloc_advanced_tools -# flutter_chat_ui: -# path: ../flutter_chat_ui + flutter_chat_ui: + path: ../flutter_chat_ui dev_dependencies: build_runner: ^2.4.11