mirror of
https://gitlab.com/veilid/veilidchat.git
synced 2024-10-01 06:55:46 -04:00
unify handling of themes
accessible theming/high contrast support
This commit is contained in:
parent
23ec185324
commit
4f02435964
@ -6,7 +6,6 @@
|
||||
"settings_tooltip": "Settings"
|
||||
},
|
||||
"pager": {
|
||||
"account": "Account",
|
||||
"chats": "Chats",
|
||||
"contacts": "Contacts"
|
||||
},
|
||||
@ -67,6 +66,9 @@
|
||||
"add_chat_sheet": {
|
||||
"new_chat": "New Chat"
|
||||
},
|
||||
"chat": {
|
||||
"say_something": "Say Something"
|
||||
},
|
||||
"create_invitation_dialog": {
|
||||
"title": "Create Contact Invitation",
|
||||
"connect_with_me": "Connect with me on VeilidChat!",
|
||||
|
@ -7,6 +7,7 @@ import 'package:form_builder_validators/form_builder_validators.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../../../layout/default_app_bar.dart';
|
||||
import '../../../theme/theme.dart';
|
||||
import '../../../tools/tools.dart';
|
||||
import '../../../veilid_processor/veilid_processor.dart';
|
||||
import '../../account_manager.dart';
|
||||
|
@ -31,11 +31,14 @@ class ProfileWidget extends StatelessWidget {
|
||||
child: Column(children: [
|
||||
Text(
|
||||
_profile.name,
|
||||
style: textTheme.headlineSmall,
|
||||
style: textTheme.headlineSmall!
|
||||
.copyWith(color: scale.primaryScale.borderText),
|
||||
textAlign: TextAlign.left,
|
||||
).paddingAll(4),
|
||||
if (_profile.pronouns.isNotEmpty)
|
||||
Text(_profile.pronouns, style: textTheme.bodyMedium)
|
||||
Text(_profile.pronouns,
|
||||
style: textTheme.bodyMedium!
|
||||
.copyWith(color: scale.primaryScale.borderText))
|
||||
.paddingLTRB(4, 0, 4, 4),
|
||||
]),
|
||||
);
|
||||
|
@ -13,7 +13,6 @@ import '../../chat_list/chat_list.dart';
|
||||
import '../../contacts/contacts.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../theme/theme.dart';
|
||||
import '../../tools/tools.dart';
|
||||
import '../chat.dart';
|
||||
|
||||
class ChatComponent extends StatelessWidget {
|
||||
@ -173,11 +172,13 @@ class ChatComponent extends StatelessWidget {
|
||||
16, 0, 16, 0),
|
||||
child: Text(_remoteUser.firstName!,
|
||||
textAlign: TextAlign.start,
|
||||
style: textTheme.titleMedium),
|
||||
style: textTheme.titleMedium!.copyWith(
|
||||
color: scale.primaryScale.borderText)),
|
||||
)),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
icon: Icon(Icons.close,
|
||||
color: scale.primaryScale.borderText),
|
||||
onPressed: () async {
|
||||
context.read<ActiveChatCubit>().setActiveChat(null);
|
||||
}).paddingLTRB(16, 0, 16, 0)
|
||||
|
@ -1,4 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
|
||||
import '../../theme/theme.dart';
|
||||
|
||||
class EmptyChatWidget extends StatelessWidget {
|
||||
const EmptyChatWidget({super.key});
|
||||
@ -7,28 +10,32 @@ class EmptyChatWidget extends StatelessWidget {
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(
|
||||
BuildContext context,
|
||||
) =>
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.chat,
|
||||
color: Theme.of(context).disabledColor,
|
||||
size: 48,
|
||||
),
|
||||
Text(
|
||||
'Say Something',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).disabledColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
) {
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.chat,
|
||||
color: scale.primaryScale.subtleBorder,
|
||||
size: 48,
|
||||
),
|
||||
Text(
|
||||
translate('chat.say_something'),
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: scale.primaryScale.subtleBorder,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,11 @@ 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>()!;
|
||||
//final theme = Theme.of(sheetContext);
|
||||
//final scale = theme.extension<ScaleScheme>()!;
|
||||
|
||||
return KeyboardListener(
|
||||
focusNode: FocusNode(),
|
||||
@ -23,49 +22,10 @@ Widget newChatBottomSheetBuilder(
|
||||
title: translate('add_chat_sheet.new_chat'),
|
||||
child: SizedBox(
|
||||
height: 160,
|
||||
child: Row(
|
||||
child: const 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))));
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
import 'package:async_tools/async_tools.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import '../../chat/cubits/active_chat_cubit.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
@ -25,85 +23,35 @@ class ChatSingleContactItemWidget extends StatelessWidget {
|
||||
Widget build(
|
||||
BuildContext context,
|
||||
) {
|
||||
final theme = Theme.of(context);
|
||||
//final textTheme = theme.textTheme;
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
|
||||
final activeChatCubit = context.watch<ActiveChatCubit>();
|
||||
final remoteConversationRecordKey =
|
||||
_contact.remoteConversationRecordKey.toVeilid();
|
||||
final selected = activeChatCubit.state == remoteConversationRecordKey;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.fromLTRB(0, 4, 0, 0),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: ShapeDecoration(
|
||||
color: selected
|
||||
? scale.primaryScale.activeElementBackground
|
||||
: scale.primaryScale.hoverElementBackground,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
)),
|
||||
child: Slidable(
|
||||
key: ObjectKey(_contact),
|
||||
endActionPane: ActionPane(
|
||||
motion: const DrawerMotion(),
|
||||
children: [
|
||||
SlidableAction(
|
||||
onPressed: _disabled
|
||||
? null
|
||||
: (context) async {
|
||||
final chatListCubit = context.read<ChatListCubit>();
|
||||
await chatListCubit.deleteChat(
|
||||
remoteConversationRecordKey:
|
||||
remoteConversationRecordKey);
|
||||
},
|
||||
backgroundColor: scale.tertiaryScale.background,
|
||||
foregroundColor: scale.tertiaryScale.foregroundText,
|
||||
icon: Icons.delete,
|
||||
label: translate('button.delete'),
|
||||
padding: const EdgeInsets.all(2)),
|
||||
// SlidableAction(
|
||||
// onPressed: (context) => (),
|
||||
// backgroundColor: scale.secondaryScale.background,
|
||||
// foregroundColor: scale.secondaryScale.text,
|
||||
// icon: Icons.edit,
|
||||
// label: 'Edit',
|
||||
// ),
|
||||
],
|
||||
),
|
||||
|
||||
// The child of the Slidable is what the user sees when the
|
||||
// component is not dragged.
|
||||
child: ListTile(
|
||||
onTap: _disabled
|
||||
? null
|
||||
: () {
|
||||
singleFuture(activeChatCubit, () async {
|
||||
activeChatCubit
|
||||
.setActiveChat(remoteConversationRecordKey);
|
||||
});
|
||||
},
|
||||
title: Text(_contact.editedProfile.name),
|
||||
|
||||
/// xxx show last message here
|
||||
subtitle: (_contact.editedProfile.pronouns.isNotEmpty)
|
||||
? Text(_contact.editedProfile.pronouns)
|
||||
: null,
|
||||
iconColor: selected
|
||||
? scale.primaryScale.appText
|
||||
: scale.primaryScale.subtleText,
|
||||
textColor: selected
|
||||
? scale.primaryScale.appText
|
||||
: scale.primaryScale.subtleText,
|
||||
selectedColor: scale.primaryScale.appText,
|
||||
//Text(Timestamp.fromInt64(contactInvitationRecord.expiration) / ),
|
||||
leading: const Icon(Icons.chat))));
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(DiagnosticsProperty<proto.Contact>('contact', _contact));
|
||||
return SliderTile(
|
||||
key: ObjectKey(_contact),
|
||||
disabled: _disabled,
|
||||
selected: selected,
|
||||
tileScale: ScaleKind.secondary,
|
||||
title: _contact.editedProfile.name,
|
||||
subtitle: _contact.editedProfile.pronouns,
|
||||
icon: Icons.chat,
|
||||
onTap: () {
|
||||
singleFuture(activeChatCubit, () async {
|
||||
activeChatCubit.setActiveChat(remoteConversationRecordKey);
|
||||
});
|
||||
},
|
||||
endActions: [
|
||||
SliderTileAction(
|
||||
icon: Icons.delete,
|
||||
label: translate('button.delete'),
|
||||
actionScale: ScaleKind.tertiary,
|
||||
onPressed: (context) async {
|
||||
final chatListCubit = context.read<ChatListCubit>();
|
||||
await chatListCubit.deleteChat(
|
||||
remoteConversationRecordKey: remoteConversationRecordKey);
|
||||
})
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import 'package:searchable_listview/searchable_listview.dart';
|
||||
|
||||
import '../../contacts/contacts.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../tools/tools.dart';
|
||||
import '../../theme/theme.dart';
|
||||
import '../chat_list.dart';
|
||||
|
||||
class ChatSingleContactListWidget extends StatelessWidget {
|
||||
@ -41,8 +41,9 @@ class ChatSingleContactListWidget extends StatelessWidget {
|
||||
return const Text('...');
|
||||
}
|
||||
return ChatSingleContactItemWidget(
|
||||
contact: contact,
|
||||
disabled: contactListV.busy);
|
||||
contact: contact,
|
||||
disabled: contactListV.busy)
|
||||
.paddingLTRB(0, 4, 0, 0);
|
||||
},
|
||||
filter: (value) {
|
||||
final lowerValue = value.toLowerCase();
|
||||
|
@ -10,6 +10,7 @@ import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:qr_flutter/qr_flutter.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../theme/theme.dart';
|
||||
import '../../tools/tools.dart';
|
||||
import '../contact_invitation.dart';
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../theme/theme.dart';
|
||||
@ -28,99 +27,51 @@ class ContactInvitationItemWidget 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 remoteConversationKey =
|
||||
// contact.remoteConversationRecordKey.toVeilid();
|
||||
|
||||
return Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: ShapeDecoration(
|
||||
color: scale.tertiaryScale.subtleBorder,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
)),
|
||||
child: Slidable(
|
||||
// Specify a key if the Slidable is dismissible.
|
||||
key: ObjectKey(contactInvitationRecord),
|
||||
endActionPane: ActionPane(
|
||||
// A motion is a widget used to control how the pane animates.
|
||||
motion: const DrawerMotion(),
|
||||
const selected =
|
||||
false; // xxx: eventually when we have selectable invitations:
|
||||
// activeContactCubit.state == remoteConversationRecordKey;
|
||||
|
||||
// A pane can dismiss the Slidable.
|
||||
//dismissible: DismissiblePane(onDismissed: () {}),
|
||||
final tileDisabled =
|
||||
disabled || context.watch<ContactInvitationListCubit>().isBusy;
|
||||
|
||||
// All actions are defined in the children parameter.
|
||||
children: [
|
||||
// A SlidableAction can have an icon and/or a label.
|
||||
SlidableAction(
|
||||
onPressed: disabled
|
||||
? null
|
||||
: (context) async {
|
||||
final contactInvitationListCubit =
|
||||
context.read<ContactInvitationListCubit>();
|
||||
await contactInvitationListCubit.deleteInvitation(
|
||||
accepted: false,
|
||||
contactRequestInboxRecordKey:
|
||||
contactInvitationRecord
|
||||
.contactRequestInbox.recordKey
|
||||
.toVeilid());
|
||||
},
|
||||
backgroundColor: scale.tertiaryScale.background,
|
||||
foregroundColor: scale.tertiaryScale.appText,
|
||||
icon: Icons.delete,
|
||||
label: translate('button.delete'),
|
||||
padding: const EdgeInsets.all(2)),
|
||||
],
|
||||
),
|
||||
|
||||
// startActionPane: ActionPane(
|
||||
// motion: const DrawerMotion(),
|
||||
// children: [
|
||||
// SlidableAction(
|
||||
// // An action can be bigger than the others.
|
||||
// flex: 2,
|
||||
// onPressed: (context) => (),
|
||||
// backgroundColor: Color(0xFF7BC043),
|
||||
// foregroundColor: Colors.white,
|
||||
// icon: Icons.archive,
|
||||
// label: 'Archive',
|
||||
// ),
|
||||
// SlidableAction(
|
||||
// onPressed: (context) => (),
|
||||
// backgroundColor: Color(0xFF0392CF),
|
||||
// foregroundColor: Colors.white,
|
||||
// icon: Icons.save,
|
||||
// label: 'Save',
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
|
||||
// The child of the Slidable is what the user sees when the
|
||||
// component is not dragged.
|
||||
child: ListTile(
|
||||
//title: Text(translate('contact_list.invitation')),
|
||||
onTap: disabled
|
||||
? null
|
||||
: () async {
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
await ContactInvitationDisplayDialog.show(
|
||||
context: context,
|
||||
message: contactInvitationRecord.message,
|
||||
create: (context) => InvitationGeneratorCubit.value(
|
||||
Uint8List.fromList(
|
||||
contactInvitationRecord.invitation)));
|
||||
},
|
||||
title: Text(
|
||||
contactInvitationRecord.message.isEmpty
|
||||
? translate('contact_list.invitation')
|
||||
: contactInvitationRecord.message,
|
||||
softWrap: true,
|
||||
),
|
||||
iconColor: scale.tertiaryScale.background,
|
||||
textColor: scale.tertiaryScale.appText,
|
||||
//Text(Timestamp.fromInt64(contactInvitationRecord.expiration) / ),
|
||||
leading: const Icon(Icons.person_add))));
|
||||
return SliderTile(
|
||||
key: ObjectKey(contactInvitationRecord),
|
||||
disabled: tileDisabled,
|
||||
selected: selected,
|
||||
tileScale: ScaleKind.primary,
|
||||
title: contactInvitationRecord.message.isEmpty
|
||||
? translate('contact_list.invitation')
|
||||
: contactInvitationRecord.message,
|
||||
icon: Icons.person_add,
|
||||
onTap: () async {
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
await ContactInvitationDisplayDialog.show(
|
||||
context: context,
|
||||
message: contactInvitationRecord.message,
|
||||
create: (context) => InvitationGeneratorCubit.value(
|
||||
Uint8List.fromList(contactInvitationRecord.invitation)));
|
||||
},
|
||||
endActions: [
|
||||
SliderTileAction(
|
||||
icon: Icons.delete,
|
||||
label: translate('button.delete'),
|
||||
actionScale: ScaleKind.tertiary,
|
||||
onPressed: (context) async {
|
||||
final contactInvitationListCubit =
|
||||
context.read<ContactInvitationListCubit>();
|
||||
await contactInvitationListCubit.deleteInvitation(
|
||||
accepted: false,
|
||||
contactRequestInboxRecordKey: contactInvitationRecord
|
||||
.contactRequestInbox.recordKey
|
||||
.toVeilid());
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../theme/theme.dart';
|
||||
import '../../tools/tools.dart';
|
||||
import '../contact_invitation.dart';
|
||||
|
||||
|
@ -9,6 +9,7 @@ import 'package:veilid_support/veilid_support.dart';
|
||||
|
||||
import '../../account_manager/account_manager.dart';
|
||||
import '../../contacts/contacts.dart';
|
||||
import '../../theme/theme.dart';
|
||||
import '../../tools/tools.dart';
|
||||
import '../contact_invitation.dart';
|
||||
|
||||
|
@ -4,7 +4,6 @@ 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';
|
||||
@ -37,7 +36,7 @@ Widget newContactBottomSheetBuilder(
|
||||
},
|
||||
iconSize: 64,
|
||||
icon: const Icon(Icons.contact_page),
|
||||
color: scale.primaryScale.background),
|
||||
color: scale.primaryScale.hoverBorder),
|
||||
Text(
|
||||
translate('add_contact_sheet.create_invite'),
|
||||
)
|
||||
@ -50,7 +49,7 @@ Widget newContactBottomSheetBuilder(
|
||||
},
|
||||
iconSize: 64,
|
||||
icon: const Icon(Icons.qr_code_scanner),
|
||||
color: scale.primaryScale.background),
|
||||
color: scale.primaryScale.hoverBorder),
|
||||
Text(
|
||||
translate('add_contact_sheet.scan_invite'),
|
||||
)
|
||||
@ -63,7 +62,7 @@ Widget newContactBottomSheetBuilder(
|
||||
},
|
||||
iconSize: 64,
|
||||
icon: const Icon(Icons.paste),
|
||||
color: scale.primaryScale.background),
|
||||
color: scale.primaryScale.hoverBorder),
|
||||
Text(
|
||||
translate('add_contact_sheet.paste_invite'),
|
||||
)
|
||||
|
@ -211,7 +211,7 @@ class ScanInvitationDialogState extends State<ScanInvitationDialog> {
|
||||
scale.grayScale.subtleBackground);
|
||||
case TorchState.on:
|
||||
return Icon(Icons.flash_on,
|
||||
color: scale.primaryScale.background);
|
||||
color: scale.primaryScale.primary);
|
||||
}
|
||||
},
|
||||
),
|
||||
@ -258,8 +258,8 @@ class ScanInvitationDialogState extends State<ScanInvitationDialog> {
|
||||
alignment: Alignment.topRight,
|
||||
child: IconButton(
|
||||
color: Colors.white,
|
||||
icon: Icon(Icons.close,
|
||||
color: scale.grayScale.background),
|
||||
icon:
|
||||
Icon(Icons.close, color: scale.grayScale.primary),
|
||||
iconSize: 32,
|
||||
onPressed: () => {
|
||||
SchedulerBinding.instance
|
||||
|
@ -2,7 +2,6 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_animate/flutter_animate.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
import 'package:flutter_translate/flutter_translate.dart';
|
||||
import '../../chat_list/chat_list.dart';
|
||||
import '../../layout/layout.dart';
|
||||
@ -17,106 +16,65 @@ class ContactItemWidget extends StatelessWidget {
|
||||
final proto.Contact contact;
|
||||
final bool disabled;
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(DiagnosticsProperty<proto.Contact>('contact', contact))
|
||||
..add(DiagnosticsProperty<bool>('disabled', disabled));
|
||||
}
|
||||
|
||||
@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 remoteConversationKey =
|
||||
contact.remoteConversationRecordKey.toVeilid();
|
||||
|
||||
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(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
)),
|
||||
child: Slidable(
|
||||
key: ObjectKey(contact),
|
||||
endActionPane: ActionPane(
|
||||
motion: const DrawerMotion(),
|
||||
children: [
|
||||
SlidableAction(
|
||||
onPressed: disabled || context.watch<ChatListCubit>().isBusy
|
||||
? null
|
||||
: (context) async {
|
||||
final contactListCubit =
|
||||
context.read<ContactListCubit>();
|
||||
final chatListCubit = context.read<ChatListCubit>();
|
||||
final tileDisabled = disabled || context.watch<ContactListCubit>().isBusy;
|
||||
|
||||
// Remove any chats for this contact
|
||||
await chatListCubit.deleteChat(
|
||||
remoteConversationRecordKey:
|
||||
remoteConversationKey);
|
||||
return SliderTile(
|
||||
key: ObjectKey(contact),
|
||||
disabled: tileDisabled,
|
||||
selected: selected,
|
||||
tileScale: ScaleKind.primary,
|
||||
title: contact.editedProfile.name,
|
||||
subtitle: contact.editedProfile.pronouns,
|
||||
icon: Icons.person,
|
||||
onTap: () async {
|
||||
// Start a chat
|
||||
final chatListCubit = context.read<ChatListCubit>();
|
||||
|
||||
// Delete the contact itself
|
||||
await contactListCubit.deleteContact(
|
||||
contact: contact);
|
||||
},
|
||||
backgroundColor: scale.tertiaryScale.background,
|
||||
foregroundColor: scale.tertiaryScale.appText,
|
||||
icon: Icons.delete,
|
||||
label: translate('button.delete'),
|
||||
padding: const EdgeInsets.all(2)),
|
||||
// SlidableAction(
|
||||
// onPressed: (context) => (),
|
||||
// backgroundColor: scale.secondaryScale.background,
|
||||
// foregroundColor: scale.secondaryScale.text,
|
||||
// icon: Icons.edit,
|
||||
// label: 'Edit',
|
||||
// ),
|
||||
],
|
||||
),
|
||||
await chatListCubit.getOrCreateChatSingleContact(
|
||||
remoteConversationRecordKey: remoteConversationKey);
|
||||
// Click over to chats
|
||||
if (context.mounted) {
|
||||
await MainPager.of(context)
|
||||
?.pageController
|
||||
.animateToPage(1, duration: 250.ms, curve: Curves.easeInOut);
|
||||
}
|
||||
},
|
||||
endActions: [
|
||||
SliderTileAction(
|
||||
icon: Icons.delete,
|
||||
label: translate('button.delete'),
|
||||
actionScale: ScaleKind.tertiary,
|
||||
onPressed: (context) async {
|
||||
final contactListCubit = context.read<ContactListCubit>();
|
||||
final chatListCubit = context.read<ChatListCubit>();
|
||||
|
||||
// The child of the Slidable is what the user sees when the
|
||||
// component is not dragged.
|
||||
child: ListTile(
|
||||
onTap: disabled || context.watch<ChatListCubit>().isBusy
|
||||
? null
|
||||
: () async {
|
||||
// Start a chat
|
||||
final chatListCubit = context.read<ChatListCubit>();
|
||||
await chatListCubit.getOrCreateChatSingleContact(
|
||||
remoteConversationRecordKey: remoteConversationKey);
|
||||
// Click over to chats
|
||||
if (context.mounted) {
|
||||
await MainPager.of(context)
|
||||
?.pageController
|
||||
.animateToPage(1,
|
||||
duration: 250.ms, curve: Curves.easeInOut);
|
||||
}
|
||||
},
|
||||
title: Text(contact.editedProfile.name),
|
||||
subtitle: (contact.editedProfile.pronouns.isNotEmpty)
|
||||
? 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,
|
||||
leading: const Icon(Icons.person))));
|
||||
}
|
||||
// Remove any chats for this contact
|
||||
await chatListCubit.deleteChat(
|
||||
remoteConversationRecordKey: remoteConversationKey);
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties.add(DiagnosticsProperty<proto.Contact>('contact', contact));
|
||||
// Delete the contact itself
|
||||
await contactListCubit.deleteContact(contact: contact);
|
||||
})
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:searchable_listview/searchable_listview.dart';
|
||||
|
||||
import '../../proto/proto.dart' as proto;
|
||||
import '../../tools/tools.dart';
|
||||
import '../../theme/theme.dart';
|
||||
import 'contact_item_widget.dart';
|
||||
import 'empty_contact_list_widget.dart';
|
||||
|
||||
@ -25,33 +25,40 @@ class ContactListWidget extends StatelessWidget {
|
||||
}
|
||||
|
||||
@override
|
||||
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);
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
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)
|
||||
.paddingLTRB(0, 4, 0, 0),
|
||||
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,
|
||||
defaultSuffixIconColor: scale.primaryScale.border,
|
||||
inputDecoration: InputDecoration(
|
||||
labelText: translate('contact_list.search'),
|
||||
),
|
||||
).paddingAll(8),
|
||||
))).paddingLTRB(8, 0, 8, 8);
|
||||
}
|
||||
}
|
||||
|
@ -37,11 +37,11 @@ class _HomeAccountReadyMainState extends State<HomeAccountReadyMain> {
|
||||
Row(children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.settings),
|
||||
color: scale.secondaryScale.appText,
|
||||
color: scale.secondaryScale.borderText,
|
||||
constraints: const BoxConstraints.expand(height: 64, width: 64),
|
||||
style: ButtonStyle(
|
||||
backgroundColor:
|
||||
MaterialStateProperty.all(scale.secondaryScale.border),
|
||||
backgroundColor: MaterialStateProperty.all(
|
||||
scale.primaryScale.hoverBorder),
|
||||
shape: MaterialStateProperty.all(
|
||||
const RoundedRectangleBorder(
|
||||
borderRadius:
|
||||
|
@ -11,7 +11,7 @@ import '../../../chat_list/chat_list.dart';
|
||||
import '../../../contact_invitation/contact_invitation.dart';
|
||||
import '../../../contacts/contacts.dart';
|
||||
import '../../../router/router.dart';
|
||||
import '../../../tools/tools.dart';
|
||||
import '../../../theme/theme.dart';
|
||||
|
||||
class HomeAccountReadyShell extends StatefulWidget {
|
||||
factory HomeAccountReadyShell(
|
||||
|
@ -61,8 +61,9 @@ class AccountPageState extends State<AccountPage> {
|
||||
translate('account_page.contact_invitations'),
|
||||
textAlign: TextAlign.center,
|
||||
style: textTheme.titleMedium!
|
||||
.copyWith(color: scale.primaryScale.subtleText),
|
||||
.copyWith(color: scale.primaryScale.borderText),
|
||||
),
|
||||
iconColor: scale.primaryScale.borderText,
|
||||
initiallyExpanded: true,
|
||||
children: [
|
||||
ContactInvitationListWidget(
|
||||
|
@ -10,7 +10,6 @@ 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';
|
||||
import 'account_page.dart';
|
||||
import 'bottom_sheet_action_button.dart';
|
||||
import 'chats_page.dart';
|
||||
@ -41,7 +40,7 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
|
||||
Icons.add_comment_sharp,
|
||||
];
|
||||
final _bottomLabelList = <String>[
|
||||
translate('pager.account'),
|
||||
translate('pager.contacts'),
|
||||
translate('pager.chats'),
|
||||
];
|
||||
|
||||
@ -82,12 +81,11 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
return BottomBarItem(
|
||||
title: Text(_bottomLabelList[index]),
|
||||
icon: Icon(_selectedIconList[index], color: scale.primaryScale.appText),
|
||||
icon:
|
||||
Icon(_selectedIconList[index], color: scale.primaryScale.borderText),
|
||||
selectedIcon:
|
||||
Icon(_selectedIconList[index], color: scale.primaryScale.appText),
|
||||
backgroundColor: scale.primaryScale.appText,
|
||||
//unSelectedColor: theme.colorScheme.primaryContainer,
|
||||
//selectedColor: theme.colorScheme.primary,
|
||||
Icon(_selectedIconList[index], color: scale.primaryScale.borderText),
|
||||
backgroundColor: scale.primaryScale.borderText,
|
||||
//badge: const Text('9+'),
|
||||
//showBadge: true,
|
||||
);
|
||||
@ -169,21 +167,10 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
|
||||
// ),
|
||||
bottomNavigationBar: StylishBottomBar(
|
||||
backgroundColor: scale.primaryScale.hoverBorder,
|
||||
// gradient: LinearGradient(
|
||||
// begin: Alignment.topCenter,
|
||||
// end: Alignment.bottomCenter,
|
||||
// colors: <Color>[
|
||||
// theme.colorScheme.primary,
|
||||
// theme.colorScheme.primaryContainer,
|
||||
// ]),
|
||||
//borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
option: AnimatedBarOptions(
|
||||
// iconSize: 32,
|
||||
//barAnimation: BarAnimation.fade,
|
||||
iconStyle: IconStyle.animated,
|
||||
inkEffect: true,
|
||||
inkColor: scale.primaryScale.hoverBackground,
|
||||
//opacity: 0.3,
|
||||
inkColor: scale.primaryScale.hoverPrimary,
|
||||
opacity: 0.3,
|
||||
),
|
||||
items: _buildBottomBarItems(),
|
||||
hasNotch: true,
|
||||
@ -198,11 +185,11 @@ class MainPagerState extends State<MainPager> with TickerProviderStateMixin {
|
||||
floatingActionButton: BottomSheetActionButton(
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(14))),
|
||||
foregroundColor: scale.secondaryScale.appText,
|
||||
foregroundColor: scale.secondaryScale.borderText,
|
||||
backgroundColor: scale.secondaryScale.hoverBorder,
|
||||
builder: (context) => Icon(
|
||||
_fabIconList[_currentPage],
|
||||
color: scale.secondaryScale.appText,
|
||||
color: scale.secondaryScale.borderText,
|
||||
),
|
||||
bottomSheetBuilder: (sheetContext) =>
|
||||
_bottomSheetBuilder(sheetContext, context)),
|
||||
|
@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../tools/tools.dart';
|
||||
import '../../theme/theme.dart';
|
||||
|
||||
class HomeNoActive extends StatefulWidget {
|
||||
const HomeNoActive({super.key});
|
||||
|
54
lib/theme/models/chat_theme.dart
Normal file
54
lib/theme/models/chat_theme.dart
Normal file
@ -0,0 +1,54 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_chat_ui/flutter_chat_ui.dart';
|
||||
|
||||
import 'scale_scheme.dart';
|
||||
|
||||
ChatTheme makeChatTheme(ScaleScheme scale, TextTheme textTheme) =>
|
||||
DefaultChatTheme(
|
||||
primaryColor: scale.primaryScale.calloutBackground,
|
||||
secondaryColor: scale.secondaryScale.calloutBackground,
|
||||
backgroundColor: scale.grayScale.appBackground,
|
||||
sendButtonIcon: Image.asset(
|
||||
'assets/icon-send.png',
|
||||
color: scale.primaryScale.borderText,
|
||||
package: 'flutter_chat_ui',
|
||||
),
|
||||
inputBackgroundColor: Colors.blue,
|
||||
inputBorderRadius: BorderRadius.zero,
|
||||
inputTextDecoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: scale.primaryScale.elementBackground,
|
||||
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))),
|
||||
),
|
||||
inputContainerDecoration:
|
||||
BoxDecoration(color: scale.primaryScale.border),
|
||||
inputPadding: const EdgeInsets.all(9),
|
||||
inputTextColor: scale.primaryScale.appText,
|
||||
attachmentButtonIcon: const Icon(Icons.attach_file),
|
||||
sentMessageBodyTextStyle: TextStyle(
|
||||
color: scale.primaryScale.calloutText,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.5,
|
||||
),
|
||||
sentEmojiMessageTextStyle: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 64,
|
||||
),
|
||||
receivedMessageBodyTextStyle: TextStyle(
|
||||
color: scale.secondaryScale.calloutText,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.5,
|
||||
),
|
||||
receivedEmojiMessageTextStyle: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 64,
|
||||
));
|
83
lib/theme/models/contrast_generator.dart
Normal file
83
lib/theme/models/contrast_generator.dart
Normal file
@ -0,0 +1,83 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'radix_generator.dart';
|
||||
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;
|
||||
|
||||
final primaryScale = ScaleColor(
|
||||
appBackground: back,
|
||||
subtleBackground: back,
|
||||
elementBackground: back,
|
||||
hoverElementBackground: back,
|
||||
activeElementBackground: back,
|
||||
subtleBorder: front,
|
||||
border: front,
|
||||
hoverBorder: front,
|
||||
primary: back,
|
||||
hoverPrimary: back,
|
||||
subtleText: front,
|
||||
appText: front,
|
||||
primaryText: front,
|
||||
borderText: back,
|
||||
dialogBorder: front,
|
||||
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);
|
||||
|
||||
final themeData = ThemeData.from(
|
||||
colorScheme: colorScheme, textTheme: textTheme, useMaterial3: true);
|
||||
return themeData.copyWith(
|
||||
bottomSheetTheme: themeData.bottomSheetTheme.copyWith(
|
||||
elevation: 0,
|
||||
modalElevation: 0,
|
||||
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.border,
|
||||
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))),
|
||||
),
|
||||
textSelectionTheme: TextSelectionThemeData(
|
||||
cursorColor: scaleScheme.primaryScale.appText,
|
||||
selectionColor: scaleScheme.primaryScale.appText.withAlpha(0x7F),
|
||||
selectionHandleColor: scaleScheme.primaryScale.appText),
|
||||
inputDecorationTheme: ScaleInputDecoratorTheme(scaleScheme, textTheme),
|
||||
extensions: <ThemeExtension<dynamic>>[
|
||||
scaleScheme,
|
||||
scaleConfig,
|
||||
]);
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
export 'chat_theme.dart';
|
||||
export 'radix_generator.dart';
|
||||
export 'scale_color.dart';
|
||||
export 'scale_scheme.dart';
|
||||
export 'slider_tile.dart';
|
||||
export 'theme_preference.dart';
|
||||
|
@ -1,16 +1,16 @@
|
||||
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_input_decorator_theme.dart';
|
||||
import 'scale_scheme.dart';
|
||||
|
||||
enum RadixThemeColor {
|
||||
scarlet, // tomato + red + violet
|
||||
babydoll, // crimson + purple + pink
|
||||
scarlet, // red + violet + tomato
|
||||
babydoll, // crimson + pink + purple
|
||||
vapor, // pink + cyan + plum
|
||||
gold, // yellow + amber + orange
|
||||
garden, // grass + orange + brown
|
||||
@ -19,7 +19,7 @@ enum RadixThemeColor {
|
||||
lapis, // blue + indigo + mint
|
||||
eggplant, // violet + purple + indigo
|
||||
lime, // lime + yellow + orange
|
||||
grim, // mauve + slate + sage
|
||||
grim, // grey + purple + brown
|
||||
}
|
||||
|
||||
enum _RadixBaseColor {
|
||||
@ -282,11 +282,15 @@ extension ToScaleColor on RadixColor {
|
||||
subtleBorder: step6,
|
||||
border: step7,
|
||||
hoverBorder: step8,
|
||||
background: step9,
|
||||
hoverBackground: step10,
|
||||
primary: step9,
|
||||
hoverPrimary: step10,
|
||||
subtleText: step11,
|
||||
appText: step12,
|
||||
foregroundText: scaleExtra.foregroundText,
|
||||
primaryText: scaleExtra.foregroundText,
|
||||
borderText: step12,
|
||||
dialogBorder: step9,
|
||||
calloutBackground: step9,
|
||||
calloutText: scaleExtra.foregroundText,
|
||||
);
|
||||
}
|
||||
|
||||
@ -338,28 +342,27 @@ class RadixScheme {
|
||||
RadixScheme _radixScheme(Brightness brightness, RadixThemeColor themeColor) {
|
||||
late RadixScheme radixScheme;
|
||||
switch (themeColor) {
|
||||
// tomato + red + violet
|
||||
// red + violet + tomato
|
||||
case RadixThemeColor.scarlet:
|
||||
radixScheme = RadixScheme(
|
||||
primaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.tomato),
|
||||
primaryScale: _radixColorSteps(brightness, false, _RadixBaseColor.red),
|
||||
primaryExtra: RadixScaleExtra(foregroundText: Colors.white),
|
||||
primaryAlphaScale:
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.tomato),
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.red),
|
||||
primaryAlphaExtra: RadixScaleExtra(foregroundText: Colors.white),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.red),
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.violet),
|
||||
secondaryExtra: RadixScaleExtra(foregroundText: Colors.white),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.violet),
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.tomato),
|
||||
tertiaryExtra: RadixScaleExtra(foregroundText: Colors.white),
|
||||
grayScale: _radixGraySteps(brightness, false, _RadixBaseColor.tomato),
|
||||
grayScale: _radixGraySteps(brightness, false, _RadixBaseColor.red),
|
||||
grayExtra: RadixScaleExtra(foregroundText: Colors.white),
|
||||
errorScale: _radixColorSteps(brightness, false, _RadixBaseColor.yellow),
|
||||
errorExtra: RadixScaleExtra(foregroundText: Colors.black),
|
||||
);
|
||||
|
||||
// crimson + purple + pink
|
||||
// crimson + pink + purple
|
||||
case RadixThemeColor.babydoll:
|
||||
radixScheme = RadixScheme(
|
||||
primaryScale:
|
||||
@ -369,10 +372,10 @@ RadixScheme _radixScheme(Brightness brightness, RadixThemeColor themeColor) {
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.crimson),
|
||||
primaryAlphaExtra: RadixScaleExtra(foregroundText: Colors.white),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.purple),
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.pink),
|
||||
secondaryExtra: RadixScaleExtra(foregroundText: Colors.white),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.pink),
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.purple),
|
||||
tertiaryExtra: RadixScaleExtra(foregroundText: Colors.white),
|
||||
grayScale:
|
||||
_radixGraySteps(brightness, false, _RadixBaseColor.crimson),
|
||||
@ -546,13 +549,13 @@ RadixScheme _radixScheme(Brightness brightness, RadixThemeColor themeColor) {
|
||||
_radixGraySteps(brightness, false, _RadixBaseColor.tomato),
|
||||
primaryExtra: RadixScaleExtra(foregroundText: Colors.white),
|
||||
primaryAlphaScale:
|
||||
_radixColorSteps(brightness, true, _RadixBaseColor.tomato),
|
||||
_radixGraySteps(brightness, true, _RadixBaseColor.tomato),
|
||||
primaryAlphaExtra: RadixScaleExtra(foregroundText: Colors.white),
|
||||
secondaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.indigo),
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.purple),
|
||||
secondaryExtra: RadixScaleExtra(foregroundText: Colors.white),
|
||||
tertiaryScale:
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.teal),
|
||||
_radixColorSteps(brightness, false, _RadixBaseColor.brown),
|
||||
tertiaryExtra: RadixScaleExtra(foregroundText: Colors.white),
|
||||
grayScale: brightness == Brightness.dark
|
||||
? RadixColors.dark.gray
|
||||
@ -565,87 +568,7 @@ RadixScheme _radixScheme(Brightness brightness, RadixThemeColor themeColor) {
|
||||
return radixScheme;
|
||||
}
|
||||
|
||||
ColorScheme _scaleToColorScheme(Brightness brightness, ScaleScheme scale) =>
|
||||
ColorScheme(
|
||||
brightness: brightness,
|
||||
primary: scale.primaryScale.background, // reviewed
|
||||
onPrimary: scale.primaryScale.foregroundText, // reviewed
|
||||
primaryContainer:
|
||||
Colors.red, // scale.primaryScale.hoverElementBackground,
|
||||
onPrimaryContainer: Colors.green, //scale.primaryScale.subtleText,
|
||||
secondary: scale.secondaryScale.background,
|
||||
onSecondary: scale.secondaryScale.foregroundText,
|
||||
secondaryContainer: scale.secondaryScale.hoverElementBackground,
|
||||
onSecondaryContainer: scale.secondaryScale.subtleText,
|
||||
tertiary: scale.tertiaryScale.background,
|
||||
onTertiary: scale.tertiaryScale.foregroundText,
|
||||
tertiaryContainer: scale.tertiaryScale.hoverElementBackground,
|
||||
onTertiaryContainer: scale.tertiaryScale.subtleText,
|
||||
error: scale.errorScale.background,
|
||||
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.background, // reviewed
|
||||
onSurface: scale.primaryScale.foregroundText, // reviewed
|
||||
surfaceVariant: scale.primaryScale.elementBackground,
|
||||
onSurfaceVariant:
|
||||
scale.primaryScale.foregroundText, // ?? reviewed a little
|
||||
outline: scale.primaryScale.border,
|
||||
outlineVariant: scale.primaryScale.subtleBorder,
|
||||
shadow: const Color(0xFF000000),
|
||||
scrim: scale.primaryScale.background,
|
||||
inverseSurface: scale.primaryScale.subtleText,
|
||||
onInverseSurface: scale.primaryScale.subtleBackground,
|
||||
inversePrimary: scale.primaryScale.hoverBackground,
|
||||
surfaceTint: scale.primaryAlphaScale.hoverElementBackground,
|
||||
);
|
||||
|
||||
ChatTheme makeChatTheme(ScaleScheme scale, TextTheme textTheme) =>
|
||||
DefaultChatTheme(
|
||||
primaryColor: scale.primaryScale.background,
|
||||
secondaryColor: scale.secondaryScale.background,
|
||||
backgroundColor: scale.grayScale.appBackground,
|
||||
inputBackgroundColor: Colors.blue,
|
||||
inputBorderRadius: BorderRadius.zero,
|
||||
inputTextDecoration: InputDecoration(
|
||||
filled: true,
|
||||
fillColor: scale.primaryScale.elementBackground,
|
||||
isDense: true,
|
||||
contentPadding: const EdgeInsets.fromLTRB(8, 12, 8, 12),
|
||||
border: const OutlineInputBorder(
|
||||
borderSide: BorderSide.none,
|
||||
borderRadius: BorderRadius.all(Radius.circular(8))),
|
||||
),
|
||||
inputContainerDecoration:
|
||||
BoxDecoration(color: scale.primaryScale.border),
|
||||
inputPadding: const EdgeInsets.all(9),
|
||||
inputTextColor: scale.primaryScale.appText,
|
||||
attachmentButtonIcon: const Icon(Icons.attach_file),
|
||||
sentMessageBodyTextStyle: TextStyle(
|
||||
color: scale.primaryScale.foregroundText,
|
||||
decorationColor: Colors.red,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.5,
|
||||
),
|
||||
sentEmojiMessageTextStyle: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 64,
|
||||
),
|
||||
receivedMessageBodyTextStyle: TextStyle(
|
||||
color: scale.primaryScale.foregroundText,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.5,
|
||||
),
|
||||
receivedEmojiMessageTextStyle: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 64,
|
||||
));
|
||||
|
||||
TextTheme _makeTextTheme(Brightness brightness) {
|
||||
TextTheme makeRadixTextTheme(Brightness brightness) {
|
||||
late final TextTheme textTheme;
|
||||
if (Platform.isIOS) {
|
||||
textTheme = (brightness == Brightness.light)
|
||||
@ -677,10 +600,11 @@ TextTheme _makeTextTheme(Brightness brightness) {
|
||||
}
|
||||
|
||||
ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) {
|
||||
final textTheme = _makeTextTheme(brightness);
|
||||
final textTheme = makeRadixTextTheme(brightness);
|
||||
final radix = _radixScheme(brightness, themeColor);
|
||||
final scaleScheme = radix.toScale();
|
||||
final colorScheme = _scaleToColorScheme(brightness, scaleScheme);
|
||||
final colorScheme = scaleScheme.toColorScheme(brightness);
|
||||
final scaleConfig = ScaleConfig(useVisualIndicators: false);
|
||||
|
||||
final themeData = ThemeData.from(
|
||||
colorScheme: colorScheme, textTheme: textTheme, useMaterial3: true);
|
||||
@ -697,34 +621,18 @@ ThemeData radixGenerator(Brightness brightness, RadixThemeColor themeColor) {
|
||||
backgroundColor: scaleScheme.primaryScale.elementBackground,
|
||||
selectedColor: scaleScheme.primaryScale.activeElementBackground,
|
||||
surfaceTintColor: scaleScheme.primaryScale.hoverElementBackground,
|
||||
checkmarkColor: scaleScheme.primaryScale.background,
|
||||
checkmarkColor: scaleScheme.primaryScale.primary,
|
||||
side: BorderSide(color: scaleScheme.primaryScale.border)),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: scaleScheme.primaryScale.elementBackground,
|
||||
foregroundColor: scaleScheme.primaryScale.appText,
|
||||
foregroundColor: scaleScheme.primaryScale.primary,
|
||||
disabledBackgroundColor: scaleScheme.grayScale.elementBackground,
|
||||
disabledForegroundColor: scaleScheme.grayScale.appText,
|
||||
disabledForegroundColor: scaleScheme.grayScale.primary,
|
||||
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,
|
||||
]);
|
||||
inputDecorationTheme: ScaleInputDecoratorTheme(scaleScheme, textTheme),
|
||||
extensions: <ThemeExtension<dynamic>>[scaleScheme, scaleConfig]);
|
||||
}
|
||||
|
@ -10,11 +10,15 @@ class ScaleColor {
|
||||
required this.subtleBorder,
|
||||
required this.border,
|
||||
required this.hoverBorder,
|
||||
required this.background,
|
||||
required this.hoverBackground,
|
||||
required this.primary,
|
||||
required this.hoverPrimary,
|
||||
required this.subtleText,
|
||||
required this.appText,
|
||||
required this.foregroundText,
|
||||
required this.primaryText,
|
||||
required this.borderText,
|
||||
required this.dialogBorder,
|
||||
required this.calloutBackground,
|
||||
required this.calloutText,
|
||||
});
|
||||
|
||||
Color appBackground;
|
||||
@ -25,11 +29,15 @@ class ScaleColor {
|
||||
Color subtleBorder;
|
||||
Color border;
|
||||
Color hoverBorder;
|
||||
Color background;
|
||||
Color hoverBackground;
|
||||
Color primary;
|
||||
Color hoverPrimary;
|
||||
Color subtleText;
|
||||
Color appText;
|
||||
Color foregroundText;
|
||||
Color primaryText;
|
||||
Color borderText;
|
||||
Color dialogBorder;
|
||||
Color calloutBackground;
|
||||
Color calloutText;
|
||||
|
||||
ScaleColor copyWith({
|
||||
Color? appBackground,
|
||||
@ -45,24 +53,31 @@ class ScaleColor {
|
||||
Color? subtleText,
|
||||
Color? appText,
|
||||
Color? foregroundText,
|
||||
Color? borderText,
|
||||
Color? dialogBorder,
|
||||
Color? calloutBackground,
|
||||
Color? calloutText,
|
||||
}) =>
|
||||
ScaleColor(
|
||||
appBackground: appBackground ?? this.appBackground,
|
||||
subtleBackground: subtleBackground ?? this.subtleBackground,
|
||||
elementBackground: elementBackground ?? this.elementBackground,
|
||||
hoverElementBackground:
|
||||
hoverElementBackground ?? this.hoverElementBackground,
|
||||
activeElementBackground:
|
||||
activeElementBackground ?? this.activeElementBackground,
|
||||
subtleBorder: subtleBorder ?? this.subtleBorder,
|
||||
border: border ?? this.border,
|
||||
hoverBorder: hoverBorder ?? this.hoverBorder,
|
||||
background: background ?? this.background,
|
||||
hoverBackground: hoverBackground ?? this.hoverBackground,
|
||||
subtleText: subtleText ?? this.subtleText,
|
||||
appText: appText ?? this.appText,
|
||||
foregroundText: foregroundText ?? this.foregroundText,
|
||||
);
|
||||
appBackground: appBackground ?? this.appBackground,
|
||||
subtleBackground: subtleBackground ?? this.subtleBackground,
|
||||
elementBackground: elementBackground ?? this.elementBackground,
|
||||
hoverElementBackground:
|
||||
hoverElementBackground ?? this.hoverElementBackground,
|
||||
activeElementBackground:
|
||||
activeElementBackground ?? this.activeElementBackground,
|
||||
subtleBorder: subtleBorder ?? this.subtleBorder,
|
||||
border: border ?? this.border,
|
||||
hoverBorder: hoverBorder ?? this.hoverBorder,
|
||||
primary: background ?? this.primary,
|
||||
hoverPrimary: hoverBackground ?? this.hoverPrimary,
|
||||
subtleText: subtleText ?? this.subtleText,
|
||||
appText: appText ?? this.appText,
|
||||
primaryText: foregroundText ?? this.primaryText,
|
||||
borderText: borderText ?? this.borderText,
|
||||
dialogBorder: dialogBorder ?? this.dialogBorder,
|
||||
calloutBackground: calloutBackground ?? this.calloutBackground,
|
||||
calloutText: calloutText ?? this.calloutText);
|
||||
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static ScaleColor lerp(ScaleColor a, ScaleColor b, double t) => ScaleColor(
|
||||
@ -85,14 +100,22 @@ class ScaleColor {
|
||||
border: Color.lerp(a.border, b.border, t) ?? const Color(0x00000000),
|
||||
hoverBorder: Color.lerp(a.hoverBorder, b.hoverBorder, t) ??
|
||||
const Color(0x00000000),
|
||||
background: Color.lerp(a.background, b.background, t) ??
|
||||
const Color(0x00000000),
|
||||
hoverBackground: Color.lerp(a.hoverBackground, b.hoverBackground, t) ??
|
||||
primary: Color.lerp(a.primary, b.primary, t) ?? const Color(0x00000000),
|
||||
hoverPrimary: Color.lerp(a.hoverPrimary, b.hoverPrimary, t) ??
|
||||
const Color(0x00000000),
|
||||
subtleText: Color.lerp(a.subtleText, b.subtleText, t) ??
|
||||
const Color(0x00000000),
|
||||
appText: Color.lerp(a.appText, b.appText, t) ?? const Color(0x00000000),
|
||||
foregroundText: Color.lerp(a.foregroundText, b.foregroundText, t) ??
|
||||
primaryText: Color.lerp(a.primaryText, b.primaryText, t) ??
|
||||
const Color(0x00000000),
|
||||
borderText: Color.lerp(a.borderText, b.borderText, t) ??
|
||||
const Color(0x00000000),
|
||||
dialogBorder: Color.lerp(a.dialogBorder, b.dialogBorder, t) ??
|
||||
const Color(0x00000000),
|
||||
calloutBackground:
|
||||
Color.lerp(a.calloutBackground, b.calloutBackground, t) ??
|
||||
const Color(0x00000000),
|
||||
calloutText: Color.lerp(a.calloutText, b.calloutText, t) ??
|
||||
const Color(0x00000000),
|
||||
);
|
||||
}
|
||||
|
165
lib/theme/models/scale_input_decorator_theme.dart
Normal file
165
lib/theme/models/scale_input_decorator_theme.dart
Normal file
@ -0,0 +1,165 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'scale_scheme.dart';
|
||||
|
||||
class ScaleInputDecoratorTheme extends InputDecorationTheme {
|
||||
ScaleInputDecoratorTheme(this._scaleScheme, this._textTheme)
|
||||
: super(
|
||||
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)));
|
||||
|
||||
final ScaleScheme _scaleScheme;
|
||||
final TextTheme _textTheme;
|
||||
|
||||
@override
|
||||
TextStyle? get hintStyle => MaterialStateTextStyle.resolveWith((states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return TextStyle(color: _scaleScheme.grayScale.border);
|
||||
}
|
||||
return TextStyle(color: _scaleScheme.primaryScale.border);
|
||||
});
|
||||
|
||||
@override
|
||||
Color? get fillColor => MaterialStateColor.resolveWith((states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return _scaleScheme.grayScale.primary.withOpacity(0.04);
|
||||
}
|
||||
return _scaleScheme.primaryScale.primary.withOpacity(0.04);
|
||||
});
|
||||
|
||||
@override
|
||||
BorderSide? get activeIndicatorBorder =>
|
||||
MaterialStateBorderSide.resolveWith((states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return BorderSide(
|
||||
color: _scaleScheme.grayScale.border.withAlpha(0x7F));
|
||||
}
|
||||
if (states.contains(MaterialState.error)) {
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return BorderSide(color: _scaleScheme.errorScale.hoverBorder);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return BorderSide(color: _scaleScheme.errorScale.border, width: 2);
|
||||
}
|
||||
return BorderSide(color: _scaleScheme.errorScale.subtleBorder);
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return BorderSide(color: _scaleScheme.secondaryScale.hoverBorder);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return BorderSide(
|
||||
color: _scaleScheme.secondaryScale.border, width: 2);
|
||||
}
|
||||
return BorderSide(color: _scaleScheme.secondaryScale.subtleBorder);
|
||||
});
|
||||
|
||||
@override
|
||||
BorderSide? get outlineBorder =>
|
||||
MaterialStateBorderSide.resolveWith((states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return BorderSide(
|
||||
color: _scaleScheme.grayScale.border.withAlpha(0x7F));
|
||||
}
|
||||
if (states.contains(MaterialState.error)) {
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return BorderSide(color: _scaleScheme.errorScale.hoverBorder);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return BorderSide(color: _scaleScheme.errorScale.border, width: 2);
|
||||
}
|
||||
return BorderSide(color: _scaleScheme.errorScale.subtleBorder);
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return BorderSide(color: _scaleScheme.primaryScale.hoverBorder);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return BorderSide(color: _scaleScheme.primaryScale.border, width: 2);
|
||||
}
|
||||
return BorderSide(color: _scaleScheme.primaryScale.subtleBorder);
|
||||
});
|
||||
|
||||
@override
|
||||
Color? get iconColor => _scaleScheme.primaryScale.primary;
|
||||
|
||||
@override
|
||||
Color? get prefixIconColor => MaterialStateColor.resolveWith((states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return _scaleScheme.primaryScale.primary.withAlpha(0x3F);
|
||||
}
|
||||
if (states.contains(MaterialState.error)) {
|
||||
return _scaleScheme.errorScale.primary;
|
||||
}
|
||||
return _scaleScheme.primaryScale.primary;
|
||||
});
|
||||
|
||||
@override
|
||||
Color? get suffixIconColor => MaterialStateColor.resolveWith((states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return _scaleScheme.primaryScale.primary.withAlpha(0x3F);
|
||||
}
|
||||
if (states.contains(MaterialState.error)) {
|
||||
return _scaleScheme.errorScale.primary;
|
||||
}
|
||||
return _scaleScheme.primaryScale.primary;
|
||||
});
|
||||
|
||||
@override
|
||||
TextStyle? get labelStyle => MaterialStateTextStyle.resolveWith((states) {
|
||||
final textStyle = _textTheme.bodyLarge ?? const TextStyle();
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return textStyle.copyWith(
|
||||
color: _scaleScheme.grayScale.border.withAlpha(0x7F));
|
||||
}
|
||||
if (states.contains(MaterialState.error)) {
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return textStyle.copyWith(
|
||||
color: _scaleScheme.errorScale.hoverBorder);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return textStyle.copyWith(
|
||||
color: _scaleScheme.errorScale.hoverBorder);
|
||||
}
|
||||
return textStyle.copyWith(
|
||||
color: _scaleScheme.errorScale.subtleBorder);
|
||||
}
|
||||
if (states.contains(MaterialState.hovered)) {
|
||||
return textStyle.copyWith(
|
||||
color: _scaleScheme.primaryScale.hoverBorder);
|
||||
}
|
||||
if (states.contains(MaterialState.focused)) {
|
||||
return textStyle.copyWith(
|
||||
color: _scaleScheme.primaryScale.hoverBorder);
|
||||
}
|
||||
return textStyle.copyWith(color: _scaleScheme.primaryScale.border);
|
||||
});
|
||||
|
||||
@override
|
||||
TextStyle? get floatingLabelStyle => labelStyle;
|
||||
|
||||
@override
|
||||
TextStyle? get helperStyle => MaterialStateTextStyle.resolveWith((states) {
|
||||
final textStyle = _textTheme.bodySmall ?? const TextStyle();
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return textStyle.copyWith(
|
||||
color: _scaleScheme.grayScale.border.withAlpha(0x7F));
|
||||
}
|
||||
return textStyle.copyWith(
|
||||
color: _scaleScheme.secondaryScale.border.withAlpha(0x7F));
|
||||
});
|
||||
|
||||
@override
|
||||
TextStyle? get errorStyle => MaterialStateTextStyle.resolveWith((states) {
|
||||
final textStyle = _textTheme.bodySmall ?? const TextStyle();
|
||||
return textStyle.copyWith(color: _scaleScheme.errorScale.primary);
|
||||
});
|
||||
}
|
@ -2,14 +2,17 @@ import 'package:flutter/material.dart';
|
||||
|
||||
import 'scale_color.dart';
|
||||
|
||||
enum ScaleKind { primary, primaryAlpha, secondary, tertiary, gray, error }
|
||||
|
||||
class ScaleScheme extends ThemeExtension<ScaleScheme> {
|
||||
ScaleScheme(
|
||||
{required this.primaryScale,
|
||||
required this.primaryAlphaScale,
|
||||
required this.secondaryScale,
|
||||
required this.tertiaryScale,
|
||||
required this.grayScale,
|
||||
required this.errorScale});
|
||||
ScaleScheme({
|
||||
required this.primaryScale,
|
||||
required this.primaryAlphaScale,
|
||||
required this.secondaryScale,
|
||||
required this.tertiaryScale,
|
||||
required this.grayScale,
|
||||
required this.errorScale,
|
||||
});
|
||||
|
||||
final ScaleColor primaryScale;
|
||||
final ScaleColor primaryAlphaScale;
|
||||
@ -18,6 +21,23 @@ class ScaleScheme extends ThemeExtension<ScaleScheme> {
|
||||
final ScaleColor grayScale;
|
||||
final ScaleColor errorScale;
|
||||
|
||||
ScaleColor scale(ScaleKind kind) {
|
||||
switch (kind) {
|
||||
case ScaleKind.primary:
|
||||
return primaryScale;
|
||||
case ScaleKind.primaryAlpha:
|
||||
return primaryAlphaScale;
|
||||
case ScaleKind.secondary:
|
||||
return secondaryScale;
|
||||
case ScaleKind.tertiary:
|
||||
return tertiaryScale;
|
||||
case ScaleKind.gray:
|
||||
return grayScale;
|
||||
case ScaleKind.error:
|
||||
return errorScale;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
ScaleScheme copyWith(
|
||||
{ScaleColor? primaryScale,
|
||||
@ -50,4 +70,65 @@ class ScaleScheme extends ThemeExtension<ScaleScheme> {
|
||||
errorScale: ScaleColor.lerp(errorScale, other.errorScale, t),
|
||||
);
|
||||
}
|
||||
|
||||
ColorScheme toColorScheme(Brightness brightness) => ColorScheme(
|
||||
brightness: brightness,
|
||||
primary: primaryScale.primary, // reviewed
|
||||
onPrimary: primaryScale.primaryText, // reviewed
|
||||
// primaryContainer: primaryScale.hoverElementBackground,
|
||||
// onPrimaryContainer: primaryScale.subtleText,
|
||||
secondary: secondaryScale.primary,
|
||||
onSecondary: secondaryScale.primaryText,
|
||||
// secondaryContainer: secondaryScale.hoverElementBackground,
|
||||
// onSecondaryContainer: secondaryScale.subtleText,
|
||||
tertiary: tertiaryScale.primary,
|
||||
onTertiary: tertiaryScale.primaryText,
|
||||
// tertiaryContainer: tertiaryScale.hoverElementBackground,
|
||||
// onTertiaryContainer: tertiaryScale.subtleText,
|
||||
error: errorScale.primary,
|
||||
onError: errorScale.primaryText,
|
||||
// errorContainer: errorScale.hoverElementBackground,
|
||||
// onErrorContainer: errorScale.subtleText,
|
||||
background: grayScale.appBackground, // reviewed
|
||||
onBackground: grayScale.appText, // reviewed
|
||||
surface: primaryScale.primary, // reviewed
|
||||
onSurface: primaryScale.primaryText, // reviewed
|
||||
surfaceVariant: secondaryScale.primary,
|
||||
onSurfaceVariant: secondaryScale.primaryText, // ?? reviewed a little
|
||||
outline: primaryScale.border,
|
||||
outlineVariant: secondaryScale.border,
|
||||
shadow: const Color(0xFF000000),
|
||||
//scrim: primaryScale.background,
|
||||
// inverseSurface: primaryScale.subtleText,
|
||||
// onInverseSurface: primaryScale.subtleBackground,
|
||||
// inversePrimary: primaryScale.hoverBackground,
|
||||
// surfaceTint: primaryAlphaScale.hoverElementBackground,
|
||||
);
|
||||
}
|
||||
|
||||
class ScaleConfig extends ThemeExtension<ScaleConfig> {
|
||||
ScaleConfig({
|
||||
required this.useVisualIndicators,
|
||||
});
|
||||
|
||||
final bool useVisualIndicators;
|
||||
|
||||
@override
|
||||
ScaleConfig copyWith({
|
||||
bool? useVisualIndicators,
|
||||
}) =>
|
||||
ScaleConfig(
|
||||
useVisualIndicators: useVisualIndicators ?? this.useVisualIndicators,
|
||||
);
|
||||
|
||||
@override
|
||||
ScaleConfig lerp(ScaleConfig? other, double t) {
|
||||
if (other is! ScaleConfig) {
|
||||
return this;
|
||||
}
|
||||
return ScaleConfig(
|
||||
useVisualIndicators:
|
||||
t < .5 ? useVisualIndicators : other.useVisualIndicators,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
151
lib/theme/models/slider_tile.dart
Normal file
151
lib/theme/models/slider_tile.dart
Normal file
@ -0,0 +1,151 @@
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||
|
||||
import '../theme.dart';
|
||||
|
||||
class SliderTileAction {
|
||||
const SliderTileAction({
|
||||
required this.actionScale,
|
||||
required this.onPressed,
|
||||
this.key,
|
||||
this.icon,
|
||||
this.label,
|
||||
});
|
||||
|
||||
final Key? key;
|
||||
final ScaleKind actionScale;
|
||||
final String? label;
|
||||
final IconData? icon;
|
||||
final SlidableActionCallback? onPressed;
|
||||
}
|
||||
|
||||
class SliderTile extends StatelessWidget {
|
||||
const SliderTile(
|
||||
{required this.disabled,
|
||||
required this.selected,
|
||||
required this.tileScale,
|
||||
required this.title,
|
||||
this.subtitle = '',
|
||||
this.endActions = const [],
|
||||
this.startActions = const [],
|
||||
this.onTap,
|
||||
this.icon,
|
||||
super.key});
|
||||
|
||||
final bool disabled;
|
||||
final bool selected;
|
||||
final ScaleKind tileScale;
|
||||
final List<SliderTileAction> endActions;
|
||||
final List<SliderTileAction> startActions;
|
||||
final GestureTapCallback? onTap;
|
||||
final IconData? icon;
|
||||
final String title;
|
||||
final String subtitle;
|
||||
|
||||
@override
|
||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||
super.debugFillProperties(properties);
|
||||
properties
|
||||
..add(DiagnosticsProperty<bool>('disabled', disabled))
|
||||
..add(DiagnosticsProperty<bool>('selected', selected))
|
||||
..add(DiagnosticsProperty<ScaleKind>('tileScale', tileScale))
|
||||
..add(IterableProperty<SliderTileAction>('endActions', endActions))
|
||||
..add(IterableProperty<SliderTileAction>('startActions', startActions))
|
||||
..add(ObjectFlagProperty<GestureTapCallback?>.has('onTap', onTap))
|
||||
..add(DiagnosticsProperty<IconData?>('icon', icon))
|
||||
..add(StringProperty('title', title))
|
||||
..add(StringProperty('subtitle', subtitle));
|
||||
}
|
||||
|
||||
@override
|
||||
// ignore: prefer_expression_function_bodies
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
final tileColor = scale.scale(!disabled ? tileScale : ScaleKind.gray);
|
||||
final scalecfg = theme.extension<ScaleConfig>()!;
|
||||
|
||||
final borderColor = selected ? tileColor.hoverBorder : tileColor.border;
|
||||
final backgroundColor = scalecfg.useVisualIndicators && !selected
|
||||
? tileColor.borderText
|
||||
: borderColor;
|
||||
final textColor = scalecfg.useVisualIndicators && !selected
|
||||
? borderColor
|
||||
: tileColor.borderText;
|
||||
|
||||
return Container(
|
||||
clipBehavior: Clip.antiAlias,
|
||||
decoration: ShapeDecoration(
|
||||
color: backgroundColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: scalecfg.useVisualIndicators
|
||||
? BorderSide(width: 2, color: borderColor, strokeAlign: 0)
|
||||
: BorderSide.none,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
)),
|
||||
child: Slidable(
|
||||
// Specify a key if the Slidable is dismissible.
|
||||
key: key,
|
||||
endActionPane: endActions.isEmpty
|
||||
? null
|
||||
: ActionPane(
|
||||
motion: const DrawerMotion(),
|
||||
children: endActions
|
||||
.map(
|
||||
(a) => SlidableAction(
|
||||
onPressed: disabled ? null : a.onPressed,
|
||||
backgroundColor: scalecfg.useVisualIndicators
|
||||
? (selected
|
||||
? tileColor.borderText
|
||||
: tileColor.border)
|
||||
: scale.scale(a.actionScale).primary,
|
||||
foregroundColor: scalecfg.useVisualIndicators
|
||||
? (selected
|
||||
? tileColor.border
|
||||
: tileColor.borderText)
|
||||
: scale.scale(a.actionScale).primaryText,
|
||||
icon: a.icon,
|
||||
label: a.label,
|
||||
padding: const EdgeInsets.all(2)),
|
||||
)
|
||||
.toList()),
|
||||
startActionPane: startActions.isEmpty
|
||||
? null
|
||||
: ActionPane(
|
||||
motion: const DrawerMotion(),
|
||||
children: startActions
|
||||
.map(
|
||||
(a) => SlidableAction(
|
||||
onPressed: disabled ? null : a.onPressed,
|
||||
backgroundColor: scalecfg.useVisualIndicators
|
||||
? (selected
|
||||
? tileColor.borderText
|
||||
: tileColor.border)
|
||||
: scale.scale(a.actionScale).primary,
|
||||
foregroundColor: scalecfg.useVisualIndicators
|
||||
? (selected
|
||||
? tileColor.border
|
||||
: tileColor.borderText)
|
||||
: scale.scale(a.actionScale).primaryText,
|
||||
icon: a.icon,
|
||||
label: a.label,
|
||||
padding: const EdgeInsets.all(2)),
|
||||
)
|
||||
.toList()),
|
||||
child: Padding(
|
||||
padding: scalecfg.useVisualIndicators
|
||||
? EdgeInsets.zero
|
||||
: const EdgeInsets.fromLTRB(0, 2, 0, 2),
|
||||
child: ListTile(
|
||||
onTap: onTap,
|
||||
title: Text(
|
||||
title,
|
||||
softWrap: true,
|
||||
),
|
||||
subtitle: subtitle.isNotEmpty ? Text(subtitle) : null,
|
||||
iconColor: textColor,
|
||||
textColor: textColor,
|
||||
leading: icon == null ? null : Icon(icon)))));
|
||||
}
|
||||
}
|
@ -2,7 +2,8 @@ import 'package:change_case/change_case.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../../tools/tools.dart';
|
||||
import '../views/widget_helpers.dart';
|
||||
import 'contrast_generator.dart';
|
||||
import 'radix_generator.dart';
|
||||
|
||||
part 'theme_preference.freezed.dart';
|
||||
@ -83,7 +84,7 @@ extension ThemePreferencesExt on ThemePreferences {
|
||||
// Special cases
|
||||
case ColorPreference.contrast:
|
||||
// xxx do contrastGenerator
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.grim);
|
||||
themeData = contrastGenerator(brightness);
|
||||
// Generate from Radix
|
||||
case ColorPreference.scarlet:
|
||||
themeData = radixGenerator(brightness, RadixThemeColor.scarlet);
|
||||
|
@ -2,7 +2,7 @@ import 'package:awesome_extensions/awesome_extensions.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../theme/theme.dart';
|
||||
import '../theme.dart';
|
||||
|
||||
class StyledDialog extends StatelessWidget {
|
||||
const StyledDialog({required this.title, required this.child, super.key});
|
||||
@ -19,10 +19,11 @@ class StyledDialog extends StatelessWidget {
|
||||
borderRadius: BorderRadius.all(Radius.circular(16)),
|
||||
),
|
||||
contentPadding: const EdgeInsets.all(4),
|
||||
backgroundColor: scale.primaryScale.border,
|
||||
backgroundColor: scale.primaryScale.dialogBorder,
|
||||
title: Text(
|
||||
title,
|
||||
style: textTheme.titleMedium,
|
||||
style: textTheme.titleMedium!
|
||||
.copyWith(color: scale.primaryScale.borderText),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
titlePadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
|
@ -1,2 +1,5 @@
|
||||
export 'brightness_preferences.dart';
|
||||
export 'color_preferences.dart';
|
||||
export 'scanner_error_widget.dart';
|
||||
export 'styled_dialog.dart';
|
||||
export 'widget_helpers.dart';
|
||||
|
@ -9,7 +9,7 @@ import 'package:flutter_translate/flutter_translate.dart';
|
||||
import 'package:motion_toast/motion_toast.dart';
|
||||
import 'package:quickalert/quickalert.dart';
|
||||
|
||||
import '../theme/theme.dart';
|
||||
import '../theme.dart';
|
||||
|
||||
extension BorderExt on Widget {
|
||||
DecoratedBox debugBorder() => DecoratedBox(
|
||||
@ -35,19 +35,22 @@ Widget buildProgressIndicator() => Builder(builder: (context) {
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
return SpinKitFoldingCube(
|
||||
color: scale.tertiaryScale.background,
|
||||
color: scale.tertiaryScale.primary,
|
||||
size: 80,
|
||||
);
|
||||
});
|
||||
|
||||
Widget waitingPage({String? text}) => Builder(
|
||||
builder: (context) => ColoredBox(
|
||||
color: Theme.of(context).scaffoldBackgroundColor,
|
||||
child: Center(
|
||||
child: Column(children: [
|
||||
buildProgressIndicator().expanded(),
|
||||
if (text != null) Text(text)
|
||||
]))));
|
||||
Widget waitingPage({String? text}) => Builder(builder: (context) {
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
return ColoredBox(
|
||||
color: scale.tertiaryScale.primaryText,
|
||||
child: Center(
|
||||
child: Column(children: [
|
||||
buildProgressIndicator().expanded(),
|
||||
if (text != null) Text(text)
|
||||
])));
|
||||
});
|
||||
|
||||
Widget debugPage(String text) => Builder(
|
||||
builder: (context) => ColoredBox(
|
||||
@ -132,12 +135,30 @@ void showInfoToast(BuildContext context, String message) {
|
||||
).show(context);
|
||||
}
|
||||
|
||||
// Widget insetBorder(
|
||||
// {required BuildContext context,
|
||||
// required bool enabled,
|
||||
// required Color color,
|
||||
// required Widget child}) {
|
||||
// if (!enabled) {
|
||||
// return child;
|
||||
// }
|
||||
|
||||
// return Stack({
|
||||
// children: [] {
|
||||
// DecoratedBox(decoration: BoxDecoration()
|
||||
// child,
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
Widget styledTitleContainer({
|
||||
required BuildContext context,
|
||||
required String title,
|
||||
required Widget child,
|
||||
Color? borderColor,
|
||||
Color? backgroundColor,
|
||||
Color? titleColor,
|
||||
}) {
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
@ -153,7 +174,7 @@ Widget styledTitleContainer({
|
||||
Text(
|
||||
title,
|
||||
style: textTheme.titleMedium!
|
||||
.copyWith(color: scale.primaryScale.subtleText),
|
||||
.copyWith(color: titleColor ?? scale.primaryScale.borderText),
|
||||
).paddingLTRB(8, 8, 8, 4),
|
||||
DecoratedBox(
|
||||
decoration: ShapeDecoration(
|
||||
@ -174,6 +195,7 @@ Widget styledBottomSheet({
|
||||
required Widget child,
|
||||
Color? borderColor,
|
||||
Color? backgroundColor,
|
||||
Color? titleColor,
|
||||
}) {
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
@ -181,7 +203,7 @@ Widget styledBottomSheet({
|
||||
|
||||
return DecoratedBox(
|
||||
decoration: ShapeDecoration(
|
||||
color: borderColor ?? scale.primaryScale.border,
|
||||
color: borderColor ?? scale.primaryScale.dialogBorder,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(16),
|
||||
@ -190,7 +212,7 @@ Widget styledBottomSheet({
|
||||
Text(
|
||||
title,
|
||||
style: textTheme.titleMedium!
|
||||
.copyWith(color: scale.primaryScale.subtleText),
|
||||
.copyWith(color: titleColor ?? scale.primaryScale.borderText),
|
||||
).paddingLTRB(8, 8, 8, 4),
|
||||
DecoratedBox(
|
||||
decoration: ShapeDecoration(
|
@ -52,27 +52,23 @@ class _EnterPasswordDialogState extends State<EnterPasswordDialog> {
|
||||
final theme = Theme.of(context);
|
||||
final scale = theme.extension<ScaleScheme>()!;
|
||||
|
||||
return Dialog(
|
||||
backgroundColor: scale.grayScale.subtleBackground,
|
||||
return StyledDialog(
|
||||
title: widget.matchPass == null
|
||||
? translate('enter_password_dialog.enter_password')
|
||||
: translate('enter_password_dialog.reenter_password'),
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
widget.matchPass == null
|
||||
? translate('enter_password_dialog.enter_password')
|
||||
: translate('enter_password_dialog.reenter_password'),
|
||||
style: theme.textTheme.titleLarge,
|
||||
).paddingAll(16),
|
||||
TextField(
|
||||
controller: passwordController,
|
||||
focusNode: focusNode,
|
||||
autofocus: true,
|
||||
enableSuggestions: false,
|
||||
obscureText:
|
||||
!_passwordVisible, //This will obscure text dynamically
|
||||
obscureText: !_passwordVisible,
|
||||
obscuringCharacter: '*',
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.singleLineFormatter
|
||||
],
|
||||
@ -87,7 +83,7 @@ class _EnterPasswordDialogState extends State<EnterPasswordDialog> {
|
||||
? null
|
||||
: Icon(Icons.check_circle,
|
||||
color: passwordController.text == widget.matchPass
|
||||
? scale.primaryScale.background
|
||||
? scale.primaryScale.primary
|
||||
: scale.grayScale.subtleBackground),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
|
@ -67,20 +67,16 @@ class _EnterPinDialogState extends State<EnterPinDialog> {
|
||||
);
|
||||
|
||||
/// Optionally you can use form to validate the Pinput
|
||||
return Dialog(
|
||||
backgroundColor: scale.grayScale.subtleBackground,
|
||||
return StyledDialog(
|
||||
title: !widget.reenter
|
||||
? translate('enter_pin_dialog.enter_pin')
|
||||
: translate('enter_pin_dialog.reenter_pin'),
|
||||
child: Form(
|
||||
key: formKey,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
!widget.reenter
|
||||
? translate('enter_pin_dialog.enter_pin')
|
||||
: translate('enter_pin_dialog.reenter_pin'),
|
||||
style: theme.textTheme.titleLarge,
|
||||
).paddingAll(16),
|
||||
Directionality(
|
||||
// Specify direction if desired
|
||||
textDirection: TextDirection.ltr,
|
||||
|
@ -5,10 +5,7 @@ export 'loggy.dart';
|
||||
export 'phono_byte.dart';
|
||||
export 'pop_control.dart';
|
||||
export 'responsive.dart';
|
||||
export 'scanner_error_widget.dart';
|
||||
export 'shared_preferences.dart';
|
||||
export 'state_logger.dart';
|
||||
export 'stream_listenable.dart';
|
||||
export 'styled_dialog.dart';
|
||||
export 'widget_helpers.dart';
|
||||
export 'window_control.dart';
|
||||
|
@ -140,17 +140,18 @@ class _DeveloperPageState extends State<DeveloperPage> {
|
||||
// });
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: scale.primaryScale.primary,
|
||||
appBar: DefaultAppBar(
|
||||
title: Text(translate('developer.title')),
|
||||
leading: IconButton(
|
||||
icon: Icon(Icons.arrow_back, color: scale.primaryScale.appText),
|
||||
icon: Icon(Icons.arrow_back, color: scale.primaryScale.primaryText),
|
||||
onPressed: () => GoRouterHelper(context).pop(),
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.copy),
|
||||
color: scale.primaryScale.appText,
|
||||
disabledColor: scale.grayScale.subtleText,
|
||||
color: scale.primaryScale.primaryText,
|
||||
disabledColor: scale.primaryScale.primaryText.withAlpha(0x3F),
|
||||
onPressed: _terminalController.selection == null
|
||||
? null
|
||||
: () async {
|
||||
@ -158,17 +159,22 @@ class _DeveloperPageState extends State<DeveloperPage> {
|
||||
}),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.clear_all),
|
||||
color: scale.primaryScale.appText,
|
||||
disabledColor: scale.grayScale.subtleText,
|
||||
color: scale.primaryScale.primaryText,
|
||||
disabledColor: scale.primaryScale.primaryText.withAlpha(0x3F),
|
||||
onPressed: () async {
|
||||
await QuickAlert.show(
|
||||
context: context,
|
||||
type: QuickAlertType.confirm,
|
||||
title: translate('developer.are_you_sure_clear'),
|
||||
textColor: scale.primaryScale.appText,
|
||||
confirmBtnColor: scale.primaryScale.elementBackground,
|
||||
backgroundColor: scale.primaryScale.subtleBackground,
|
||||
headerBackgroundColor: scale.primaryScale.background,
|
||||
titleColor: scale.primaryScale.appText,
|
||||
textColor: scale.primaryScale.subtleText,
|
||||
confirmBtnColor: scale.primaryScale.primary,
|
||||
cancelBtnTextStyle: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 18,
|
||||
color: scale.primaryScale.appText),
|
||||
backgroundColor: scale.primaryScale.appBackground,
|
||||
headerBackgroundColor: scale.primaryScale.primary,
|
||||
confirmBtnText: translate('button.ok'),
|
||||
cancelBtnText: translate('button.cancel'),
|
||||
onConfirmBtnTap: () async {
|
||||
@ -194,13 +200,23 @@ class _DeveloperPageState extends State<DeveloperPage> {
|
||||
width: 64,
|
||||
height: 40,
|
||||
render: ResultRender.icon,
|
||||
icon: SizedBox(
|
||||
width: 10,
|
||||
height: 10,
|
||||
child: CustomPaint(
|
||||
painter: DropdownArrowPainter(
|
||||
color: scale.primaryScale.primaryText))),
|
||||
textStyle: textTheme.labelMedium!
|
||||
.copyWith(color: scale.primaryScale.appText),
|
||||
.copyWith(color: scale.primaryScale.primaryText),
|
||||
padding: const EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||
openBoxDecoration: BoxDecoration(
|
||||
color: scale.primaryScale.activeElementBackground),
|
||||
boxDecoration:
|
||||
BoxDecoration(color: scale.primaryScale.elementBackground),
|
||||
color: scale.primaryScale.border,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
boxDecoration: BoxDecoration(
|
||||
color: scale.primaryScale.hoverBorder,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
dropdownOptions: DropdownOptions(
|
||||
width: 160,
|
||||
@ -224,7 +240,7 @@ class _DeveloperPageState extends State<DeveloperPage> {
|
||||
padding: const EdgeInsets.fromLTRB(8, 4, 8, 4),
|
||||
selectedPadding: const EdgeInsets.fromLTRB(8, 4, 8, 4)),
|
||||
dropdownList: _logLevelDropdownItems,
|
||||
)
|
||||
).paddingLTRB(0, 0, 8, 0)
|
||||
],
|
||||
),
|
||||
body: SafeArea(
|
||||
@ -245,13 +261,19 @@ class _DeveloperPageState extends State<DeveloperPage> {
|
||||
decoration: InputDecoration(
|
||||
filled: true,
|
||||
contentPadding: const EdgeInsets.fromLTRB(8, 2, 8, 2),
|
||||
border: OutlineInputBorder(
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: scale.primaryScale.border)),
|
||||
borderSide: BorderSide.none),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
fillColor: scale.primaryScale.subtleBackground,
|
||||
hintText: translate('developer.command'),
|
||||
suffixIcon: IconButton(
|
||||
icon: const Icon(Icons.send),
|
||||
icon: Icon(Icons.send,
|
||||
color: _debugCommandController.text.isEmpty
|
||||
? scale.primaryScale.primary.withAlpha(0x3F)
|
||||
: scale.primaryScale.primary),
|
||||
onPressed: _debugCommandController.text.isEmpty
|
||||
? null
|
||||
: () async {
|
||||
|
@ -33,32 +33,32 @@ class SignalStrengthMeterWidget extends StatelessWidget {
|
||||
switch (connectionState.attachment.state) {
|
||||
case AttachmentState.detached:
|
||||
iconWidget = Icon(Icons.signal_cellular_nodata,
|
||||
size: iconSize, color: scale.grayScale.appText);
|
||||
size: iconSize, color: scale.primaryScale.primaryText);
|
||||
return;
|
||||
case AttachmentState.detaching:
|
||||
iconWidget = Icon(Icons.signal_cellular_off,
|
||||
size: iconSize, color: scale.grayScale.appText);
|
||||
size: iconSize, color: scale.primaryScale.primaryText);
|
||||
return;
|
||||
case AttachmentState.attaching:
|
||||
value = 0;
|
||||
color = scale.primaryScale.appText;
|
||||
color = scale.primaryScale.primaryText;
|
||||
case AttachmentState.attachedWeak:
|
||||
value = 1;
|
||||
color = scale.primaryScale.appText;
|
||||
color = scale.primaryScale.primaryText;
|
||||
case AttachmentState.attachedStrong:
|
||||
value = 2;
|
||||
color = scale.primaryScale.appText;
|
||||
color = scale.primaryScale.primaryText;
|
||||
case AttachmentState.attachedGood:
|
||||
value = 3;
|
||||
color = scale.primaryScale.appText;
|
||||
color = scale.primaryScale.primaryText;
|
||||
case AttachmentState.fullyAttached:
|
||||
value = 4;
|
||||
color = scale.primaryScale.appText;
|
||||
color = scale.primaryScale.primaryText;
|
||||
case AttachmentState.overAttached:
|
||||
value = 4;
|
||||
color = scale.secondaryScale.subtleText;
|
||||
color = scale.primaryScale.primaryText;
|
||||
}
|
||||
inactiveColor = scale.grayScale.subtleText;
|
||||
inactiveColor = scale.primaryScale.primaryText;
|
||||
|
||||
iconWidget = SignalStrengthIndicator.bars(
|
||||
value: value,
|
||||
@ -66,7 +66,7 @@ class SignalStrengthMeterWidget extends StatelessWidget {
|
||||
inactiveColor: inactiveColor,
|
||||
size: iconSize,
|
||||
barCount: 4,
|
||||
spacing: 1);
|
||||
spacing: 2);
|
||||
},
|
||||
loading: () => {iconWidget = const Icon(Icons.warning)},
|
||||
error: (e, st) => {
|
||||
|
@ -69,7 +69,7 @@ class DHTShortArrayCubit<T> extends Cubit<DHTShortArrayBusyState<T>>
|
||||
// Because this is async, we could get an update while we're
|
||||
// still processing the last one. Only called after init future has run
|
||||
// so we dont have to wait for that here.
|
||||
_sspUpdate.busyUpdate<T, AsyncValue<IList<T>>>(
|
||||
_sspUpdate.busyUpdate<T, DHTShortArrayState<T>>(
|
||||
busy, (emit) async => _refreshInner(emit));
|
||||
}
|
||||
|
||||
|
@ -470,6 +470,8 @@ class _DHTShortArrayHead {
|
||||
_subscription = null;
|
||||
}
|
||||
|
||||
// Called when the shortarray changes online and we find out from a watch
|
||||
// but not when we make a change locally
|
||||
Future<void> _onHeadValueChanged(
|
||||
DHTRecord record, Uint8List? data, List<ValueSubkeyRange> subkeys) async {
|
||||
// If head record subkey zero changes, then the layout
|
||||
|
Loading…
Reference in New Issue
Block a user