From 7dc25b92c7651e5ae0a621efaf6425c4cea9c077 Mon Sep 17 00:00:00 2001 From: Dan Ballard Date: Wed, 7 Feb 2024 08:16:33 -0800 Subject: [PATCH] theme fixes and new fields: messageSelectionColor, portraitOnlineAwayColor, portraitOnlineBusyColor, textfieldSelectionColor, menuBackgroundColor --- assets/themes/cwtch/theme.yml | 12 +++++ lib/themes/cwtch.dart | 12 +++++ lib/themes/opaque.dart | 30 ++++++++++- lib/themes/yamltheme.dart | 7 ++- lib/views/messageview.dart | 4 +- lib/widgets/buttontextfield.dart | 81 ++++++++++++++++-------------- lib/widgets/messagebubble.dart | 22 ++++++-- lib/widgets/passwordfield.dart | 83 +++++++++++++++++-------------- lib/widgets/textfield.dart | 73 +++++++++++++++------------ test/textfield_basic.png | Bin 5648 -> 5648 bytes test/textfield_form_42.png | Bin 5595 -> 5595 bytes 11 files changed, 206 insertions(+), 118 deletions(-) diff --git a/assets/themes/cwtch/theme.yml b/assets/themes/cwtch/theme.yml index d1c4f48d..423129e2 100644 --- a/assets/themes/cwtch/theme.yml +++ b/assets/themes/cwtch/theme.yml @@ -43,6 +43,7 @@ themes: messageFromMeTextColor: font # whiteishPurple messageFromOtherBackgroundColor: peerBubble # deepPurple messageFromOtherTextColor: font # whiteishPurple + messageSelectionColor: accent portraitBackgroundColor: deepPurple portraitBlockedBorderColor: lightGrey portraitBlockedTextColor: lightGrey @@ -50,6 +51,8 @@ themes: portraitContactBadgeTextColor: whiteishPurple portraitOfflineBorderColor: purple portraitOnlineBorderColor: whiteishPurple + portraitOnlineAwayColor: 0xFFFFF59D + portraitOnlineBusyColor: 0xFFEF9A9A portraitProfileBadgeColor: hotPink portraitProfileBadgeTextColor: whiteishPurple scrollbarDefaultColor: purple @@ -59,8 +62,10 @@ themes: textfieldBorderColor: deepPurple textfieldErrorColor: hotPink textfieldHintColor: mainTextColor + textfieldSelectionColor: accent toolbarIconColor: settings # whiteishPurple topbarColor: header # darkGreyPurple + menuBackgroundColor: accent; snackbarBackgroundColor: accent snackbarTextColor: whiteishPurple chatImageColor: purple @@ -87,6 +92,7 @@ themes: messageFromMeTextColor: font # mainTextColor messageFromOtherBackgroundColor: peerBubble # purple messageFromOtherTextColor: font # darkPurple + messageSelectionColor: 0x80FF80 portraitBackgroundColor: softPurple portraitBlockedBorderColor: softGrey portraitBlockedTextColor: softGrey @@ -94,6 +100,8 @@ themes: portraitContactBadgeTextColor: whitePurple portraitOfflineBorderColor: greyPurple portraitOnlineBorderColor: greyPurple + portraitOnlineAwayColor: 0xFFFFF59D + portraitOnlineBusyColor: 0xFFEF9A9A portraitProfileBadgeColor: accent portraitProfileBadgeTextColor: whitePurple scrollbarDefaultColor: accent @@ -103,6 +111,10 @@ themes: textfieldBorderColor: purple textfieldErrorColor: hotPink textfieldHintColor: font + textfieldSelectionColor: accent toolbarIconColor: settings # darkPurple topbarColor: header # softPurple + menuBackgroundColor: accent; + snackbarBackgroundColor: accent + snackbarTextColor: whitePurple chatImageColor: purple diff --git a/lib/themes/cwtch.dart b/lib/themes/cwtch.dart index e81d5509..ffdd8ba7 100644 --- a/lib/themes/cwtch.dart +++ b/lib/themes/cwtch.dart @@ -63,6 +63,7 @@ class CwtchDark extends OpaqueThemeType { get messageFromMeTextColor => font; //whiteishPurple; get messageFromOtherBackgroundColor => peerBubble; //deepPurple; get messageFromOtherTextColor => font; //whiteishPurple; + get messageSelectionColor => accent; get portraitBackgroundColor => deepPurple; get portraitBlockedBorderColor => lightGrey; get portraitBlockedTextColor => lightGrey; @@ -70,6 +71,8 @@ class CwtchDark extends OpaqueThemeType { get portraitContactBadgeTextColor => whiteishPurple; get portraitOfflineBorderColor => purple; get portraitOnlineBorderColor => whiteishPurple; + get portraitOnlineAwayColor => Color(0xFFFFF59D); + get portraitOnlineBusyColor => Color(0xFFEF9A9A); get portraitProfileBadgeColor => hotPink; get portraitProfileBadgeTextColor => whiteishPurple; get scrollbarDefaultColor => purple; @@ -79,8 +82,11 @@ class CwtchDark extends OpaqueThemeType { get textfieldBorderColor => deepPurple; get textfieldErrorColor => hotPink; get textfieldHintColor => mainTextColor; + get textfieldSelectionColor => accent; get toolbarIconColor => settings; //whiteishPurple; get topbarColor => header; //darkGreyPurple; + get menuBackgroundColor => accent; + get menuTextColor => darkGreyPurple; get snackbarBackgroundColor => accent; get snackbarTextColor => whitePurple; get chatImageColor => purple; @@ -111,6 +117,7 @@ class CwtchLight extends OpaqueThemeType { get messageFromMeTextColor => font; //mainTextColor; get messageFromOtherBackgroundColor => peerBubble; //purple; get messageFromOtherTextColor => font; //darkPurple; + get messageSelectionColor => accent; get portraitBackgroundColor => softPurple; get portraitBlockedBorderColor => softGrey; get portraitBlockedTextColor => softGrey; @@ -118,6 +125,8 @@ class CwtchLight extends OpaqueThemeType { get portraitContactBadgeTextColor => whitePurple; get portraitOfflineBorderColor => greyPurple; get portraitOnlineBorderColor => greyPurple; + get portraitOnlineAwayColor => Color(0xFFFFF59D); + get portraitOnlineBusyColor => Color(0xFFEF9A9A); get portraitProfileBadgeColor => accent; get portraitProfileBadgeTextColor => whitePurple; get scrollbarDefaultColor => accent; @@ -127,8 +136,11 @@ class CwtchLight extends OpaqueThemeType { get textfieldBorderColor => purple; get textfieldErrorColor => hotPink; get textfieldHintColor => font; + get textfieldSelectionColor => accent; get toolbarIconColor => settings; //darkPurple; get topbarColor => header; //softPurple; + get menuBackgroundColor => accent; + get menuTextColor => whitePurple; get snackbarBackgroundColor => accent; get snackbarTextColor => whitePurple; get chatImageColor => purple; diff --git a/lib/themes/opaque.dart b/lib/themes/opaque.dart index e1c5b514..bab22160 100644 --- a/lib/themes/opaque.dart +++ b/lib/themes/opaque.dart @@ -99,6 +99,7 @@ abstract class OpaqueThemeType { get textfieldBorderColor => red; get textfieldHintColor => red; get textfieldErrorColor => red; + get textfieldSelectionColor => red; get scrollbarDefaultColor => red; get portraitBackgroundColor => red; get portraitOnlineBorderColor => red; @@ -123,6 +124,9 @@ abstract class OpaqueThemeType { get messageFromMeTextColor => red; get messageFromOtherBackgroundColor => red; get messageFromOtherTextColor => red; + get messageSelectionColor => red; + + get menuBackgroundColor => red; get snackbarBackgroundColor => red; get snackbarTextColor => red; @@ -142,11 +146,33 @@ abstract class OpaqueThemeType { } } +// Borrowed from Stackoverflow +MaterialColor getMaterialColor(Color color) { + final int red = color.red; + final int green = color.green; + final int blue = color.blue; + + final Map shades = { + 50: Color.fromRGBO(red, green, blue, .1), + 100: Color.fromRGBO(red, green, blue, .2), + 200: Color.fromRGBO(red, green, blue, .3), + 300: Color.fromRGBO(red, green, blue, .4), + 400: Color.fromRGBO(red, green, blue, .5), + 500: Color.fromRGBO(red, green, blue, .6), + 600: Color.fromRGBO(red, green, blue, .7), + 700: Color.fromRGBO(red, green, blue, .8), + 800: Color.fromRGBO(red, green, blue, .9), + 900: Color.fromRGBO(red, green, blue, 1), + }; + + return MaterialColor(color.value, shades); +} + ThemeData mkThemeData(Settings opaque) { return ThemeData( hoverColor: opaque.current().backgroundHilightElementColor.withOpacity(0.5), visualDensity: VisualDensity.adaptivePlatformDensity, - primarySwatch: Colors.red, + primarySwatch: getMaterialColor(opaque.current().topbarColor), primaryIconTheme: IconThemeData( color: opaque.current().mainTextColor, ), @@ -249,7 +275,7 @@ ThemeData mkThemeData(Settings opaque) { enableFeedback: true, splashColor: opaque.current().defaultButtonActiveColor), textSelectionTheme: TextSelectionThemeData( - cursorColor: opaque.current().defaultButtonActiveColor, selectionColor: opaque.current().defaultButtonActiveColor, selectionHandleColor: opaque.current().defaultButtonActiveColor), + cursorColor: opaque.current().textfieldSelectionColor, selectionColor: opaque.current().textfieldSelectionColor, selectionHandleColor: opaque.current().textfieldSelectionColor), popupMenuTheme: PopupMenuThemeData( color: opaque.current().backgroundPaneColor.withOpacity(0.9), ), diff --git a/lib/themes/yamltheme.dart b/lib/themes/yamltheme.dart index eb90c7a0..7e32b529 100644 --- a/lib/themes/yamltheme.dart +++ b/lib/themes/yamltheme.dart @@ -175,6 +175,7 @@ class YmlTheme extends OpaqueThemeType { get textfieldBorderColor => getColor("textfieldBorderColor") ?? fallbackTheme.textfieldBorderColor; get textfieldHintColor => getColor("textfieldHintColor") ?? fallbackTheme.textfieldHintColor; get textfieldErrorColor => getColor("textfieldErrorColor") ?? fallbackTheme.textfieldErrorColor; + get textfieldSelectionColor => getColor("textfieldSelectionColor") ?? fallbackTheme.textfieldSelectionColor; get scrollbarDefaultColor => getColor("scrollbarDefaultColor") ?? fallbackTheme.scrollbarDefaultColor; get portraitBackgroundColor => getColor("portraitBackgroundColor") ?? fallbackTheme.portraitBackgroundColor; get portraitOnlineBorderColor => getColor("portraitOnlineBorderColor") ?? fallbackTheme.portraitOnlineBorderColor; @@ -185,8 +186,8 @@ class YmlTheme extends OpaqueThemeType { get portraitContactBadgeTextColor => getColor("portraitContactBadgeTextColor") ?? fallbackTheme.portraitContactBadgeTextColor; get portraitProfileBadgeColor => getColor("portraitProfileBadgeColor") ?? fallbackTheme.portraitProfileBadgeColor; get portraitProfileBadgeTextColor => getColor("portraitProfileBadgeTextColor") ?? fallbackTheme.portraitProfileBadgeTextColor; - get portraitOnlineAwayColor => Color(0xFFFFF59D) ?? fallbackTheme.portraitOnlineAwayColor; - get portraitOnlineBusyColor => Color(0xFFEF9A9A) ?? fallbackTheme.portraitOnlineBusyColor; + get portraitOnlineAwayColor => getColor("portraitOnlineAwayColor") ?? fallbackTheme.portraitOnlineAwayColor; + get portraitOnlineBusyColor => getColor("portraitOnlineBusyColor") ?? fallbackTheme.portraitOnlineBusyColor; get dropShadowColor => getColor("dropShadowColor") ?? fallbackTheme.dropShadowColor; get toolbarIconColor => getColor("toolbarIconColor") ?? fallbackTheme.toolbarIconColor; get chatReactionIconColor => getColor("chatReactionIconColor") ?? fallbackTheme.chatReactionIconColor; @@ -194,6 +195,8 @@ class YmlTheme extends OpaqueThemeType { get messageFromMeTextColor => getColor("messageFromMeTextColor") ?? fallbackTheme.messageFromMeTextColor; get messageFromOtherBackgroundColor => getColor("messageFromOtherBackgroundColor") ?? fallbackTheme.messageFromOtherBackgroundColor; get messageFromOtherTextColor => getColor("messageFromOtherTextColor") ?? fallbackTheme.messageFromOtherTextColor; + get messageSelectionColor => getColor("messageSelectionColor") ?? fallbackTheme.messageSelectionColor; + get menuBackgroundColor => getColor("menuBackgroundColor") ?? fallbackTheme.menuBackgroundColor; get snackbarBackgroundColor => getColor("snackbarBackgroundColor") ?? fallbackTheme.snackbarBackgroundColor; get snackbarTextColor => getColor("snackbarTextColor") ?? fallbackTheme.snackbarTextColor; diff --git a/lib/views/messageview.dart b/lib/views/messageview.dart index 0f5a18a7..5136ce8f 100644 --- a/lib/views/messageview.dart +++ b/lib/views/messageview.dart @@ -516,7 +516,9 @@ class _MessageViewState extends State { height: 164 + ((Provider.of(context).messageDraft.messageText.split("\n").length - 1) * 16), child: Column( children: [ - Row(mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [preview]), + Container( + decoration: BoxDecoration(color: Provider.of(context).theme.defaultButtonActiveColor), + child: Row(mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: [preview])), Container( decoration: BoxDecoration(border: Border(top: BorderSide(color: Provider.of(context).theme.defaultButtonActiveColor))), child: Row(mainAxisAlignment: MainAxisAlignment.start, children: [wdgMessage])), diff --git a/lib/widgets/buttontextfield.dart b/lib/widgets/buttontextfield.dart index 0307b367..3d4c6afd 100644 --- a/lib/widgets/buttontextfield.dart +++ b/lib/widgets/buttontextfield.dart @@ -49,44 +49,51 @@ class _CwtchButtonTextFieldState extends State { return Container( clipBehavior: Clip.antiAlias, decoration: BoxDecoration(), - child: TextFormField( - key: widget.testKey, - controller: widget.controller, - readOnly: widget.readonly, - showCursor: !widget.readonly, - focusNode: _focusNode, - enableIMEPersonalizedLearning: false, - onChanged: widget.onChanged, - maxLines: 1, - style: widget.textStyle == null ? TextStyle(overflow: TextOverflow.clip) : widget.textStyle, - decoration: InputDecoration( - labelText: widget.labelText, - labelStyle: TextStyle(color: theme.current().mainTextColor, backgroundColor: theme.current().textfieldBackgroundColor), - suffixIcon: IconButton( - onPressed: widget.onPressed, - icon: widget.icon, - splashRadius: Material.defaultSplashRadius / 2, - padding: EdgeInsets.fromLTRB(0.0, 4.0, 2.0, 2.0), - tooltip: widget.tooltip, - enableFeedback: true, - color: theme.current().mainTextColor, - highlightColor: theme.current().defaultButtonColor, - focusColor: theme.current().defaultButtonActiveColor, - splashColor: theme.current().defaultButtonActiveColor, + // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯ + child: Theme( + data: Theme.of(context).copyWith( + textButtonTheme: TextButtonThemeData( + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.menuBackgroundColor)), ), - floatingLabelBehavior: FloatingLabelBehavior.never, - filled: true, - fillColor: theme.current().textfieldBackgroundColor, - focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)), - focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)), - errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)), - errorStyle: TextStyle( - color: theme.current().textfieldErrorColor, - fontWeight: FontWeight.bold, - ), - contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), - enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0))), - )); + ), + child: TextFormField( + key: widget.testKey, + controller: widget.controller, + readOnly: widget.readonly, + showCursor: !widget.readonly, + focusNode: _focusNode, + enableIMEPersonalizedLearning: false, + onChanged: widget.onChanged, + maxLines: 1, + style: widget.textStyle == null ? TextStyle(overflow: TextOverflow.clip) : widget.textStyle, + decoration: InputDecoration( + labelText: widget.labelText, + labelStyle: TextStyle(color: theme.current().mainTextColor, backgroundColor: theme.current().textfieldBackgroundColor), + suffixIcon: IconButton( + onPressed: widget.onPressed, + icon: widget.icon, + splashRadius: Material.defaultSplashRadius / 2, + padding: EdgeInsets.fromLTRB(0.0, 4.0, 2.0, 2.0), + tooltip: widget.tooltip, + enableFeedback: true, + color: theme.current().mainTextColor, + highlightColor: theme.current().defaultButtonColor, + focusColor: theme.current().defaultButtonActiveColor, + splashColor: theme.current().defaultButtonActiveColor, + ), + floatingLabelBehavior: FloatingLabelBehavior.never, + filled: true, + fillColor: theme.current().textfieldBackgroundColor, + focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)), + focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)), + errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)), + errorStyle: TextStyle( + color: theme.current().textfieldErrorColor, + fontWeight: FontWeight.bold, + ), + contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0), + enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0))), + ))); }); } } diff --git a/lib/widgets/messagebubble.dart b/lib/widgets/messagebubble.dart index f956025f..5f36f6c6 100644 --- a/lib/widgets/messagebubble.dart +++ b/lib/widgets/messagebubble.dart @@ -71,11 +71,23 @@ class MessageBubbleState extends State { ), child: Padding( padding: EdgeInsets.all(9.0), - child: Column( - crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, - mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations]))))); + child: Theme( + data: Theme.of(context).copyWith( + textSelectionTheme: TextSelectionThemeData( + cursorColor: Provider.of(context).theme.messageSelectionColor, + selectionColor: Provider.of(context).theme.messageSelectionColor, + selectionHandleColor: Provider.of(context).theme.messageSelectionColor), + + // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯ + textButtonTheme: TextButtonThemeData( + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.menuBackgroundColor)), + ), + ), + child: Column( + crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start, + mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations])))))); }); } } diff --git a/lib/widgets/passwordfield.dart b/lib/widgets/passwordfield.dart index 83dd5d76..e22ca6fe 100644 --- a/lib/widgets/passwordfield.dart +++ b/lib/widgets/passwordfield.dart @@ -38,45 +38,52 @@ class _CwtchPasswordTextFieldState extends State { } return Consumer(builder: (context, theme, child) { - return TextFormField( - autofocus: widget.autofocus, - controller: widget.controller, - validator: widget.validator, - obscureText: obscureText, - obscuringCharacter: '*', - enableIMEPersonalizedLearning: false, - autofillHints: widget.autoFillHints, - autovalidateMode: AutovalidateMode.always, - onFieldSubmitted: widget.action, - enableSuggestions: false, - autocorrect: false, - decoration: InputDecoration( - suffixIcon: IconButton( - onPressed: () { - setState(() { - obscureText = !obscureText; - }); - }, - icon: Icon((obscureText ? CwtchIcons.eye_closed : CwtchIcons.eye_open), semanticLabel: label), - tooltip: label, - color: theme.current().mainTextColor, - highlightColor: theme.current().defaultButtonColor, - focusColor: theme.current().defaultButtonActiveColor, - splashColor: theme.current().defaultButtonActiveColor, - splashRadius: Material.defaultSplashRadius / 2, + // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯ + return Theme( + data: Theme.of(context).copyWith( + textButtonTheme: TextButtonThemeData( + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.menuBackgroundColor)), + ), ), - errorStyle: TextStyle( - color: theme.current().textfieldErrorColor, - fontWeight: FontWeight.bold, - ), - focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)), - focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)), - errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)), - filled: true, - fillColor: theme.current().textfieldBackgroundColor, - enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)), - ), - ); + child: TextFormField( + autofocus: widget.autofocus, + controller: widget.controller, + validator: widget.validator, + obscureText: obscureText, + obscuringCharacter: '*', + enableIMEPersonalizedLearning: false, + autofillHints: widget.autoFillHints, + autovalidateMode: AutovalidateMode.always, + onFieldSubmitted: widget.action, + enableSuggestions: false, + autocorrect: false, + decoration: InputDecoration( + suffixIcon: IconButton( + onPressed: () { + setState(() { + obscureText = !obscureText; + }); + }, + icon: Icon((obscureText ? CwtchIcons.eye_closed : CwtchIcons.eye_open), semanticLabel: label), + tooltip: label, + color: theme.current().mainTextColor, + highlightColor: theme.current().defaultButtonColor, + focusColor: theme.current().defaultButtonActiveColor, + splashColor: theme.current().defaultButtonActiveColor, + splashRadius: Material.defaultSplashRadius / 2, + ), + errorStyle: TextStyle( + color: theme.current().textfieldErrorColor, + fontWeight: FontWeight.bold, + ), + focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)), + focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)), + errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)), + filled: true, + fillColor: theme.current().textfieldBackgroundColor, + enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)), + ), + )); }); } } diff --git a/lib/widgets/textfield.dart b/lib/widgets/textfield.dart index 95ec20c7..38e194f2 100644 --- a/lib/widgets/textfield.dart +++ b/lib/widgets/textfield.dart @@ -47,39 +47,46 @@ class _CwtchTextFieldState extends State { return Container( clipBehavior: Clip.antiAlias, decoration: BoxDecoration(), - child: TextFormField( - key: widget.testKey, - controller: widget.controller, - validator: widget.validator, - onChanged: widget.onChanged, - autofocus: widget.autofocus, - autovalidateMode: AutovalidateMode.onUserInteraction, - textAlign: widget.number ? TextAlign.end : TextAlign.start, - keyboardType: widget.multiLine - ? TextInputType.multiline - : widget.number - ? TextInputType.number - : TextInputType.text, - inputFormatters: widget.number ? [FilteringTextInputFormatter.digitsOnly] : null, - maxLines: widget.multiLine ? null : 1, - scrollController: _scrollController, - enableIMEPersonalizedLearning: false, - focusNode: _focusNode, - style: Provider.of(context).scaleFonts(defaultTextStyle).copyWith(overflow: TextOverflow.clip), - decoration: InputDecoration( - errorMaxLines: 2, - hintText: widget.hintText, - hintStyle: TextStyle(color: (theme.current().mainTextColor as Color).withOpacity(0.5)), - floatingLabelBehavior: FloatingLabelBehavior.never, - filled: true, - focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)), - focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)), - errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)), - errorStyle: TextStyle(color: theme.current().textfieldErrorColor, fontWeight: FontWeight.bold, overflow: TextOverflow.visible), - fillColor: theme.current().textfieldBackgroundColor, - contentPadding: EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 5.0), - enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0))), - )); + // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯ + child: Theme( + data: Theme.of(context).copyWith( + textButtonTheme: TextButtonThemeData( + style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of(context).theme.menuBackgroundColor)), + ), + ), + child: TextFormField( + key: widget.testKey, + controller: widget.controller, + validator: widget.validator, + onChanged: widget.onChanged, + autofocus: widget.autofocus, + autovalidateMode: AutovalidateMode.onUserInteraction, + textAlign: widget.number ? TextAlign.end : TextAlign.start, + keyboardType: widget.multiLine + ? TextInputType.multiline + : widget.number + ? TextInputType.number + : TextInputType.text, + inputFormatters: widget.number ? [FilteringTextInputFormatter.digitsOnly] : null, + maxLines: widget.multiLine ? null : 1, + scrollController: _scrollController, + enableIMEPersonalizedLearning: false, + focusNode: _focusNode, + style: Provider.of(context).scaleFonts(defaultTextStyle).copyWith(overflow: TextOverflow.clip), + decoration: InputDecoration( + errorMaxLines: 2, + hintText: widget.hintText, + hintStyle: TextStyle(color: (theme.current().mainTextColor as Color).withOpacity(0.5)), + floatingLabelBehavior: FloatingLabelBehavior.never, + filled: true, + focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0)), + focusedErrorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)), + errorBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldErrorColor, width: 1.0)), + errorStyle: TextStyle(color: theme.current().textfieldErrorColor, fontWeight: FontWeight.bold, overflow: TextOverflow.visible), + fillColor: theme.current().textfieldBackgroundColor, + contentPadding: EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 5.0), + enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(6.0), borderSide: BorderSide(color: theme.current().textfieldBorderColor, width: 1.0))), + ))); }); } } diff --git a/test/textfield_basic.png b/test/textfield_basic.png index b26e46c58e9bc6dc04d9a9ddd00a246083bfdaf8..311e38d041fe9a67011422c400b2d8f91cc81b22 100644 GIT binary patch literal 5648 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 literal 5648 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 diff --git a/test/textfield_form_42.png b/test/textfield_form_42.png index cd8bc5aa85f15a47f7b2a049f2f824379b1a1823..3682c4dc7f5613cbb7003a040b37cd36537e9def 100644 GIT binary patch literal 5595 zcmeHLYdn-|*S}@&%-*2}k)$z9R0=t6jZ+%TNYoHHr6O|95ZhqrHJu#C?J14ZltYqG zO_GG#a>}Vll2DODPB|N9%yZqN_xF3gyr15$&wRP>xvsU=wbpMj{f(5G1SsK|=db8^H*EfA>M~5DF$+m_j9OJI27D--1m^b|~7HrVV%G#NEz&p>m2>%z>=I76$#wgTKPj}^ep~sTf?o0VYxjJ9 zT)9UU-nB=%?&dQ~p*tH2cj*x{@2AgR&3*EuL{h~4aN?dn{1qQIUXOdtIoI&o_#1v8 zxv#f3tB?6LdBNqw@KmU0=r9wrO6BRaPs|PC7r6(fBeJLGq=cZ;`yx*dRAcZ)Ity}= zP490vJht^3U*MlG3?7bBNliSIe7XO=I2D93UoT_tz^XY%nN{7?^#iY}w1y9SO0GC_ z&U5%`fB0C3SGM)`*qxiJv^w0ZAgG^6*O)M0Ir0bel5a!TXfa>e1YU1k-*Tmk7%eLV zJv)g0N8`Szz;Ju)NE@$P#-}Vo5B_w!p`$os`nhi)JW*;NScEZH4zy90jNK{OCpRXs z5jq`_8>u|xidp?QQy%>@)IWV14EOcebq5JE?uR$tF9unOEH`ODx-0n$_I# zgQK0kP@JI+b8~T+vEje_vV}N$q_v<^$;sE>5K5f?N_SuFPruCMG1G4~-QmblF05Vf zELil&Impb}f`On~kE(;I-Fvn4Uj$)q2(gSuaYk^f7PcWE!r4=LY1r3>Pa4h`XF9sQ zVUF>}CbdUqx>vG{j%T$@ywBd(GS%Gj+HZTSxwPlv_m|n@nqd6XiX|9$%)u=jHcLOddPOne#JcF zI4R}>>vJ^ffj#MtrQMqN3YFDi@pJy}I%O+PzFm0Og*fY#HBr+EamGos^0Eu(*N6cp z4YG6Yo;bx$Nj8(2*=*^rZ+5(?zlZ0oTw&)N zP#o$7x2te`Dpx0QCY~_292ZP_9JXjuG7#Yow;p*2p8LAuu97!E2|0%djn;#?X7qW7 zGIDG7Se0WGPvU{0?Iy?4QZN5OvhR(Wn?vQW-;=T$@g&|IT(lNx_^vd?kAF0yz)SgV z@ZVpFasSXUP|R_APx_11R|75n#;cw9q-1Gyhnv}PkM?;}dp-Nk^Y68c*?L7~@gQ=z zRZ>a@jx5QFx+tN*H)@K6U22xAnRaw?W1}&NVhis$_1Dd&A9mtxn9y=n zDCWS2X4D>4BPRke`sj%Q_KQ#>v$*24GsU|&VZHvnq?jF6x+BpO1HrC$?{~V2NkcL3 z-o>N#;1LBrppIpJdSX4b%_TB_VbKXr4k-7pWH$u9h<)X!@0YyicmPOe7M;!{saU*Nm% zO*?HRI}K~xg~0YG&OphA2uQR$PSIA<)_LGlcU!2u*95nv-&zlpbkPtAY^{WVx5Hl2 z`2M?t5MZx=jRXX_7iOVkpDv$AW2?H4%7yGuxRI0UB?FubDA|gw6>zZF^+52KyX92t zM)9b@`Lw5MYb&ASo3=nuXx>(=m(fB*Pgw1OKIPkolngBh%bc3DJ&xLr=myL76k|r6+>&T{f2^| z!)EjZJz(qQTW_;QP6J;f-dzG)dUVppd`N3P717;Y?BGQyx%H-^0V5laVisk!GYf?) zjZ6jFMY;#@x@gTbF|-6?gdvF?G>ZY@%@Z1zk#!P&Lu>9tB$W=+6O0fMA{v`}6cM2i z8b>7%5gb}G{h%1bl7D3b1Z6p<8!>FP8s1Pq>F4vI&MxQ7ST?%`l z)y^AZ<@apM!u$m3Ns5?~#yXr4M<6KFQ5mYc8r_h|zA69AX0qQ^4? z(fTBm>?s7%M>x|f0#y92GrkHy_XQ#-L?jy}yAu&{Jwz83L3}E~nrMMJD6@7ons3}#oR z;*jG;T*o1}xr5E=_@~udI(J6)ppGUm4>>5AnqNiY+joueP66>&PSImo;B6dAHbCI5 zlIfK+fe7#xhln@>k%Xy6A?UW)kxmhc&+y+ntCp$_=6A7>V{Z=s`VT=7cu(^5U!lC-!8sq7Ri48oz@LW z{fki*b3;6mIbOh!N^@1yoy#eh_Cn+3+?3}#itgD_z)nw&q~D^81B@u)XIDgwZY>;1 zv$9fWBEEJ9g*|wMR)C-hOGOV2ggcctQF~a(l!GMpJ3^TWs$!l2SQ%0@eWPdscIQRK zJU$}nhc#U}5(7amGr-A$*tqaJIJ1x$m&>RzLj<;N1yia(M3+FEMiO^E7Bwb@%=k-S z@krKh3oCkfAvz&M`ox#zC}S{}a0khgPOw38e}r3h_;RYZ?*H<#f34cBag`OhkECWn zeCIdM|G2JESd6Bu6uHUGUpPpXGE2&QrW2O^!k83}Rs?ID7I8w)eo5vLPDL%Mdh4!4 zOW#SGdNwjeRwBdz_r+b47&Rdy!{1LTNnwlivFFULkwzitt+4Xlatk0}l6;LZnP?Tm zhP0TN5G#eR1-UWa~Rveg8a|>@jm?Jb37J@t(M| zZ)<&yfR^i{e_(UVxBkeirYs zXd5Hw8l$JKE|ODN@VL~ewc0SEdNsnqerKgU9m(Og+sBg`gXIG>ebbU6d%gQ?wlTGM z-Nn;`5?FOZ-h6YXSHBsVI`NQ(*;REtgzLaWt%`L^E5`6(&@W{Ipu9!R z5nk-cpiYCj>D*zz$oc^Oom5pR$K#q0w^};H&7VYT(}jx z`Nd$C{(?yKzdUmfjAH$Zdd_R}mufe&Xxuc~*yZBsz{D+Sr-eZE^l?qPINVudJRfYj zeSx~hm1O~94P7gR zMU?rI$zf6$q-w^bXo{jc;*wRYY0n1zk)$TvI%M8x{Y?ptBp%*~#^Mu`1#D>Ct7S?> z|I)HIr_b*-LJk8+sg#B8XkI2F3`n?4BM?gm*OhgtvRK}=b>)*|plefIS8(@IL3gzO zQpgWb%o~_V5%}+%MIg%ldiR|q5FLNLM|8(AG}nzjRCU>7n%?SJxIXn!=czgIcwyet z$7G*Koa^sIEz-NWOArL{!t_mP3*BTAdwwvCd-Am>n|lcOTE*6mQYi3DDmV}$xk+Ax zcsK^^!bUkeI_M1K5*KI*!Vt8($66^bqbT>FIOBWQoID25cAGW+I)lpYpV5^UY5Y`w zKODiv{COgS+!ljt%3p}@^81EA2jZRCYMf6;9e(f$YXk*a;5e}B1t_Qn8Zk2sZWaLOz$>pXz2-q!soTjjQ(3fc?`Ueu)h01+K(C@Xjq$Yxs0NRCm%8 zWDs)IcV)KbHaE+gAq5P2zW8~CYJpZXfuI<9Z!=<5&*zT;t_W8rJ_e7EbB62dn6jF128VA?D)XP=gSm)?-MzNfq<%$z0cmS zPn;+!4RD?Cjxp$@J|g>$w}6j(8pAic)%;u|hkeHVy8>b&SN4hk>V_7^2l~a@l(E5* zvv%+eT%rGiY7U_DOEu)iIKJTIJ41#Gdcma)D%{06@B8&eeqe>1G}vk6R!=Q;x4+Mf zJ8#Anx}96l+kNn_wtigut{xUHo-GpWF|%LZPaGT>v1x6_thuyPzWb}whl;~_afOp1 zvkL!gBCXl`e&yzC=~sRJKnMeS-YWAHE!;N}*A&s|2|gy3)ec7OZz99^)K>i2;u5zv z;5sOE`RaXP=;bpX?QzbBiV@0G$5?S7KNiOt^)SxbNQAlG@Hosp2EYH|Tk(*p3+|ZU zo6Oi*VEv_r^C>)wAF(*@=o(#nJt*(s%bh(SIU&SSZoAIv1;qUsxt+*xSikb}G^b>K~ z-lkYm(&PnWpz$*T+oWF5>P2&DvQ`N&fQRaFOE}Iq@g-1mMGL&(&sjtJdk#h7@W#Tk zo>x=39x#ST@r9>JlzR;gpm42>guuo&l)X(dut7?z^CT#4|L3o=l`p*4g>*A~e2tC> Nk<6?KC8nqU=ie5|I}ZQ= literal 5595 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 I#5w-{U#sg!&j0`b