ui_kit-ui_components/feat/add-textinputs-components #145

Merged
hugo merged 25 commits from ui_kit-ui_components/feat/add-textinputs-components into master 2023-02-21 15:11:35 +00:00
61 changed files with 2550 additions and 451 deletions

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'enums/control_state.dart';
export 'enums/enums.dart';
export 'extensions/build_context_extensions.dart';
export 'extensions/string_extension.dart';
export 'mixins/copy_with_mixin.dart';

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export './control_state.dart';
export './status_state.dart';

View File

@ -0,0 +1,22 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
enum StatusState {
initial,
success,
loading,
error;
}

View File

@ -23,32 +23,57 @@ class MultiColor {
final List<Color>? _colors;
final Color? _color;
Color get color {
if (_color != null) {
return _color!;
}
if (_colors?.isNotEmpty ?? false) {
return _colors!.first;
}
throw IndexError.withLength(
0,
_colors?.length ?? 0,
message: '_color is not defined or _colors is empty.',
);
}
Color get color => _color != null
? _color!
: _colors?.isNotEmpty ?? false
? _colors!.first
: throw IndexError.withLength(
0,
_colors?.length ?? 0,
message: '_color is not defined or _colors is empty.',
);
List<Color> get colors => _colors ?? [];
bool get isGradient =>
(_colors?.isNotEmpty ?? false) && (_colors?.length ?? 0) > 1;
bool get isGradient => (_colors?.length ?? 0) > 1;
bool get isColor => _color != null || isGradient;
static MultiColor? lerp(MultiColor? a, MultiColor? b, double t) {
if (a == null && b == null) {
return null;
}
if (b == null) {
} else if (a == null) {
return b;
} else if (b == null) {
return a;
}
if (a.isColor && b.isColor) {
return MultiColor.single(Color.lerp(a.color, b.color, t));
} else if (a.isColor && b.isGradient) {
return MultiColor(
b.colors
.map((e) => Color.lerp(a.color, e, t))
.whereType<Color>()
.toList(),
);
} else if (a.isGradient && b.isColor) {
return MultiColor(
a.colors
.map((e) => Color.lerp(b.color, e, t))
.whereType<Color>()
.toList(),
);
} else if (a.isGradient && b.isGradient) {
final colors = List<Color>.empty(growable: true);
final shortestList =
(a.colors.length > b.colors.length) ? b.colors : a.colors;
for (int i = 0; i < shortestList.length; i++) {
final lerpColor = Color.lerp(a.colors[i], b.colors[i], t);
if (lerpColor != null) {
colors.add(lerpColor);
}
}
return MultiColor(colors);
}
return b;
}

View File

@ -35,60 +35,55 @@ abstract class ThemeResolver<S extends ThemeStyle<S>, T, E> {
/// {@macro theme_resolver}
const ThemeResolver();
S? Function(
BuildContext context,
S defaultValue,
T themeExtension, {
E? extra,
}) get computeExtensionValueFn;
S? Function(BuildContext context, {E? extra}) get customStyleFn;
/// Compute default value from Flutter Theme or with hardcoded values.
S computeDefaultValue(BuildContext context, {E? extra});
/// Compute values from the extension if found
S? computeExtensionValue(
S? computeExtensionValueFn(
BuildContext context,
S defaultValue, {
T themeExtension, {
E? extra,
});
/// Compute values from the extension if found
S? _computeExtensionValue(
BuildContext context, {
E? extra,
}) {
final themeExtension = findExtension(context);
final themeExtension = Theme.of(context).extension<T>();
if (themeExtension != null) {
return computeExtensionValueFn(
context,
defaultValue,
themeExtension,
extra: extra,
);
}
return defaultValue;
return null;
}
/// Compute custom value
S? computeCustomValue(
BuildContext context,
S previousPhaseValue, {
S? _computeCustomValue(
BuildContext context, {
E? extra,
}) {
final customStyle = customStyleFn(context, extra: extra);
final customStyle = customStyleFn(
context,
extra: extra,
);
if (customStyle != null) {
return customStyle;
}
return previousPhaseValue;
return null;
}
T? findExtension(BuildContext context) => Theme.of(context).extension<T>();
/// Choose most suitable style for a given context.
S negotiate(BuildContext context, {E? extra}) {
S style = computeDefaultValue(context, extra: extra);
final S? extensionStyle =
computeExtensionValue(context, style, extra: extra);
style = style.mergeWith(extensionStyle);
final S? customStyle = computeCustomValue(context, style, extra: extra);
return style.mergeWith(customStyle);
style =
style.mergeWith(_computeExtensionValue(context, extra: extra)) ?? style;
style =
style.mergeWith(_computeCustomValue(context, extra: extra)) ?? style;
return style;
}
}

View File

@ -104,18 +104,11 @@ class FileSelectionButtonStyle extends ButtonStyle<FileSelectionButtonStyle> {
final TextStyle? subTitle;
@override
FileSelectionButtonStyle mergeWith(FileSelectionButtonStyle? other) =>
FileSelectionButtonStyle.merge(this, other)!;
FileSelectionButtonStyle? mergeWith(FileSelectionButtonStyle? other) =>
FileSelectionButtonStyle.merge(this, other);
@override
FileSelectionButtonStyle? lerpWith(
FileSelectionButtonStyle? other,
double t,
) =>
FileSelectionButtonStyle.lerp(this, other, t);
@override
FileSelectionButtonStyle copyWith({
FileSelectionButtonStyle? copyWith({
TextStyle? title,
TextStyle? subTitle,
BorderRadiusGeometry? radius,

View File

@ -96,15 +96,11 @@ class FlatButtonStyle extends ButtonStyle<FlatButtonStyle> {
final TextStyle? label;
@override
FlatButtonStyle mergeWith(FlatButtonStyle? other) =>
FlatButtonStyle.merge(this, other)!;
FlatButtonStyle? mergeWith(FlatButtonStyle? other) =>
FlatButtonStyle.merge(this, other);
@override
FlatButtonStyle? lerpWith(FlatButtonStyle? other, double t) =>
FlatButtonStyle.lerp(this, other, t);
@override
FlatButtonStyle copyWith({
FlatButtonStyle? copyWith({
TextStyle? label,
BorderRadiusGeometry? radius,
EdgeInsetsGeometry? padding,

View File

@ -96,12 +96,8 @@ class SimpleIconButtonStyle extends ButtonStyle<SimpleIconButtonStyle> {
final double? dimension;
@override
SimpleIconButtonStyle mergeWith(SimpleIconButtonStyle? other) =>
SimpleIconButtonStyle.merge(this, other)!;
@override
SimpleIconButtonStyle? lerpWith(SimpleIconButtonStyle? other, double t) =>
SimpleIconButtonStyle.lerp(this, other, t);
SimpleIconButtonStyle? mergeWith(SimpleIconButtonStyle? other) =>
SimpleIconButtonStyle.merge(this, other);
@override
SimpleIconButtonStyle copyWith({

View File

@ -104,15 +104,11 @@ class SymbolButtonStyle extends ButtonStyle<SymbolButtonStyle> {
final double? dimension;
@override
SymbolButtonStyle mergeWith(SymbolButtonStyle? other) =>
SymbolButtonStyle.merge(this, other)!;
SymbolButtonStyle? mergeWith(SymbolButtonStyle? other) =>
SymbolButtonStyle.merge(this, other);
@override
SymbolButtonStyle? lerpWith(SymbolButtonStyle? other, double t) =>
SymbolButtonStyle.lerp(this, other, t);
@override
SymbolButtonStyle copyWith({
SymbolButtonStyle? copyWith({
TextStyle? label,
double? dimension,
BorderRadiusGeometry? radius,

View File

@ -15,6 +15,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/widgets.dart';
import 'package:wyatt_ui_components/src/core/utils/multi_color.dart';
import 'package:wyatt_ui_components/src/domain/entities/component.dart';
abstract class CardComponent extends Component {
@ -41,10 +42,10 @@ abstract class CardComponent extends Component {
final double? padding;
/// Border gradient color (from left to right)
final List<Color>? borderColors;
final MultiColor? borderColors;
/// Card background color
final List<Color>? backgroundColors;
final MultiColor? backgroundColors;
/// Minimum size for this card
final Size? minSize;

View File

@ -14,8 +14,8 @@ abstract class $InformationCardComponentCWProxy {
InformationCardComponent axis(Axis? axis);
InformationCardComponent radius(double? radius);
InformationCardComponent padding(double? padding);
InformationCardComponent borderColors(List<Color>? borderColors);
InformationCardComponent backgroundColors(List<Color>? backgroundColors);
InformationCardComponent borderColors(MultiColor? borderColors);
InformationCardComponent backgroundColors(MultiColor? backgroundColors);
InformationCardComponent minSize(Size? minSize);
InformationCardComponent maxSize(Size? maxSize);
InformationCardComponent shadow(BoxShadow? shadow);
@ -29,8 +29,8 @@ abstract class $InformationCardComponentCWProxy {
Axis? axis,
double? radius,
double? padding,
List<Color>? borderColors,
List<Color>? backgroundColors,
MultiColor? borderColors,
MultiColor? backgroundColors,
Size? minSize,
Size? maxSize,
BoxShadow? shadow,

View File

@ -19,8 +19,8 @@ abstract class $PortfolioCardComponentCWProxy {
PortfolioCardComponent assets(List<Widget>? assets);
PortfolioCardComponent radius(double? radius);
PortfolioCardComponent padding(double? padding);
PortfolioCardComponent borderColors(List<Color>? borderColors);
PortfolioCardComponent backgroundColors(List<Color>? backgroundColors);
PortfolioCardComponent borderColors(MultiColor? borderColors);
PortfolioCardComponent backgroundColors(MultiColor? backgroundColors);
PortfolioCardComponent minSize(Size? minSize);
PortfolioCardComponent maxSize(Size? maxSize);
PortfolioCardComponent shadow(BoxShadow? shadow);
@ -38,8 +38,8 @@ abstract class $PortfolioCardComponentCWProxy {
List<Widget>? assets,
double? radius,
double? padding,
List<Color>? borderColors,
List<Color>? backgroundColors,
MultiColor? borderColors,
MultiColor? backgroundColors,
Size? minSize,
Size? maxSize,
BoxShadow? shadow,

View File

@ -16,8 +16,8 @@ abstract class $QuoteCardComponentCWProxy {
QuoteCardComponent rightQuote(Widget? rightQuote);
QuoteCardComponent radius(double? radius);
QuoteCardComponent padding(double? padding);
QuoteCardComponent borderColors(List<Color>? borderColors);
QuoteCardComponent backgroundColors(List<Color>? backgroundColors);
QuoteCardComponent borderColors(MultiColor? borderColors);
QuoteCardComponent backgroundColors(MultiColor? backgroundColors);
QuoteCardComponent minSize(Size? minSize);
QuoteCardComponent maxSize(Size? maxSize);
QuoteCardComponent shadow(BoxShadow? shadow);
@ -33,8 +33,8 @@ abstract class $QuoteCardComponentCWProxy {
Widget? rightQuote,
double? radius,
double? padding,
List<Color>? borderColors,
List<Color>? backgroundColors,
MultiColor? borderColors,
MultiColor? backgroundColors,
Size? minSize,
Size? maxSize,
BoxShadow? shadow,

View File

@ -17,8 +17,8 @@ abstract class $SkillCardComponentCWProxy {
Color? secondaryBackgroundColors);
SkillCardComponent radius(double? radius);
SkillCardComponent padding(double? padding);
SkillCardComponent borderColors(List<Color>? borderColors);
SkillCardComponent backgroundColors(List<Color>? backgroundColors);
SkillCardComponent borderColors(MultiColor? borderColors);
SkillCardComponent backgroundColors(MultiColor? backgroundColors);
SkillCardComponent minSize(Size? minSize);
SkillCardComponent maxSize(Size? maxSize);
SkillCardComponent shadow(BoxShadow? shadow);
@ -34,8 +34,8 @@ abstract class $SkillCardComponentCWProxy {
Color? secondaryBackgroundColors,
double? radius,
double? padding,
List<Color>? borderColors,
List<Color>? backgroundColors,
MultiColor? borderColors,
MultiColor? backgroundColors,
Size? minSize,
Size? maxSize,
BoxShadow? shadow,

View File

@ -24,4 +24,5 @@ export './loader_component.dart';
export './loader_style.dart';
export './loading_widget_component.dart';
export './rich_text_builder/rich_text_builder.dart';
export './text_inputs/text_inputs.dart';
export './theme_style.dart';

View File

@ -65,11 +65,7 @@ class LoaderStyle extends ThemeStyle<LoaderStyle> {
final double? stroke;
@override
LoaderStyle mergeWith(LoaderStyle? other) => LoaderStyle.merge(this, other)!;
@override
LoaderStyle? lerpWith(LoaderStyle? other, double t) =>
LoaderStyle.lerp(this, other, t);
LoaderStyle? mergeWith(LoaderStyle? other) => LoaderStyle.merge(this, other);
@override
LoaderStyle copyWith({

View File

@ -23,7 +23,7 @@ class RichTextStyleParameter {
this.styleName,
);
final TextStyle defaultStyle;
final TextStyle? defaultStyle;
final Map<String, TextStyle> definedStyle;
final String? styleName;
@ -31,7 +31,7 @@ class RichTextStyleParameter {
if (definedStyle.containsKey(styleName)) {
return definedStyle[styleName]!;
}
return defaultStyle;
return defaultStyle ?? const TextStyle();
}
RichTextStyleParameter copyWith({

View File

@ -64,15 +64,11 @@ class RichTextBuilderStyle extends ThemeStyle<RichTextBuilderStyle> {
final Map<String, TextStyle>? styles;
@override
RichTextBuilderStyle mergeWith(RichTextBuilderStyle? other) =>
RichTextBuilderStyle.merge(this, other)!;
RichTextBuilderStyle? mergeWith(RichTextBuilderStyle? other) =>
RichTextBuilderStyle.merge(this, other);
@override
RichTextBuilderStyle? lerpWith(RichTextBuilderStyle? other, double t) =>
RichTextBuilderStyle.lerp(this, other, t);
@override
RichTextBuilderStyle copyWith({
RichTextBuilderStyle? copyWith({
TextStyle? defaultStyle,
Map<String, TextStyle>? styles,
}) =>

View File

@ -0,0 +1,173 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:ui';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
part 'text_input_component.g.dart';
@ComponentProxyExtension()
abstract class TextInputComponent extends Component
with CopyWithMixin<$TextInputComponentCWProxy> {
const TextInputComponent({
this.expand,
this.onError,
this.validator,
this.suffixText,
this.prefixText,
this.prefixIcon,
this.suffixIcon,
this.label,
this.hint,
this.normalStyle,
this.focusedStyle,
this.errorStyle,
this.disableStyle,
this.controller,
this.focusNode,
this.keyboardType,
this.smartDashesType,
this.smartQuotesType,
this.enableInteractiveSelection,
this.textInputAction,
this.textCapitalization,
this.style,
this.strutStyle,
this.textAlign,
this.textAlignVertical,
this.textDirection,
this.readOnly,
this.showCursor,
this.autofocus,
this.obscuringCharacter,
this.obscureText,
this.autocorrect,
this.enableSuggestions,
this.maxLines,
this.minLines,
this.expands,
this.maxLength,
this.maxLengthEnforcement,
this.onChanged,
this.onEditingComplete,
this.onSubmitted,
this.onAppPrivateCommand,
this.inputFormatters,
this.enabled,
this.cursorWidth,
this.cursorHeight,
this.cursorRadius,
this.cursorColor,
this.selectionHeightStyle,
this.selectionWidthStyle,
this.keyboardAppearance,
this.scrollPadding,
this.dragStartBehavior,
this.selectionControls,
this.onTap,
this.onTapOutside,
this.mouseCursor,
this.scrollController,
this.scrollPhysics,
this.autofillHints,
this.clipBehavior,
this.restorationId,
this.scribbleEnabled,
this.enableIMEPersonalizedLearning,
this.contextMenuBuilder,
this.spellCheckConfiguration,
this.magnifierConfiguration,
super.key,
});
final TextMagnifierConfiguration? magnifierConfiguration;
final TextEditingController? controller;
final FocusNode? focusNode;
final TextInputType? keyboardType;
final TextInputAction? textInputAction;
final TextCapitalization? textCapitalization;
final TextStyle? style;
final StrutStyle? strutStyle;
final TextAlign? textAlign;
final TextAlignVertical? textAlignVertical;
final TextDirection? textDirection;
final bool? autofocus;
final String? obscuringCharacter;
final bool? obscureText;
final bool? autocorrect;
final SmartDashesType? smartDashesType;
final SmartQuotesType? smartQuotesType;
final bool? enableSuggestions;
final int? maxLines;
final int? minLines;
final bool? expands;
final bool? readOnly;
final bool? showCursor;
final int? maxLength;
final MaxLengthEnforcement? maxLengthEnforcement;
final ValueChanged<String>? onChanged;
final VoidCallback? onEditingComplete;
final ValueChanged<String>? onSubmitted;
final AppPrivateCommandCallback? onAppPrivateCommand;
final List<TextInputFormatter>? inputFormatters;
final ValueNotifier<bool>? enabled;
final double? cursorWidth;
final double? cursorHeight;
final Radius? cursorRadius;
final Color? cursorColor;
final BoxHeightStyle? selectionHeightStyle;
final BoxWidthStyle? selectionWidthStyle;
final Brightness? keyboardAppearance;
final EdgeInsets? scrollPadding;
final bool? enableInteractiveSelection;
final TextSelectionControls? selectionControls;
final DragStartBehavior? dragStartBehavior;
final GestureTapCallback? onTap;
final TapRegionCallback? onTapOutside;
final MouseCursor? mouseCursor;
final ScrollPhysics? scrollPhysics;
final ScrollController? scrollController;
final Iterable<String>? autofillHints;
final Clip? clipBehavior;
final String? restorationId;
final bool? scribbleEnabled;
final bool? enableIMEPersonalizedLearning;
final EditableTextContextMenuBuilder? contextMenuBuilder;
final SpellCheckConfiguration? spellCheckConfiguration;
final bool Function(String)? validator;
final String? Function(String)? onError;
final TextInputStyle? normalStyle;
final TextInputStyle? focusedStyle;
final TextInputStyle? errorStyle;
final TextInputStyle? disableStyle;
final bool? expand;
final TextWrapper? label;
final TextWrapper? hint;
final TextWrapper? prefixText;
final TextWrapper? suffixText;
final Icon? prefixIcon;
final Icon? suffixIcon;
}

View File

@ -0,0 +1,157 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'text_input_component.dart';
// **************************************************************************
// ComponentProxyGenerator
// **************************************************************************
abstract class $TextInputComponentCWProxy {
TextInputComponent expand(bool? expand);
TextInputComponent onError(String Function(String)? onError);
TextInputComponent validator(bool Function(String)? validator);
TextInputComponent suffixText(TextWrapper? suffixText);
TextInputComponent prefixText(TextWrapper? prefixText);
TextInputComponent prefixIcon(Icon? prefixIcon);
TextInputComponent suffixIcon(Icon? suffixIcon);
TextInputComponent label(TextWrapper? label);
TextInputComponent hint(TextWrapper? hint);
TextInputComponent normalStyle(TextInputStyle? normalStyle);
TextInputComponent focusedStyle(TextInputStyle? focusedStyle);
TextInputComponent errorStyle(TextInputStyle? errorStyle);
TextInputComponent disableStyle(TextInputStyle? disableStyle);
TextInputComponent controller(TextEditingController? controller);
TextInputComponent focusNode(FocusNode? focusNode);
TextInputComponent keyboardType(TextInputType? keyboardType);
TextInputComponent smartDashesType(SmartDashesType? smartDashesType);
TextInputComponent smartQuotesType(SmartQuotesType? smartQuotesType);
TextInputComponent enableInteractiveSelection(
bool? enableInteractiveSelection);
TextInputComponent textInputAction(TextInputAction? textInputAction);
TextInputComponent textCapitalization(TextCapitalization? textCapitalization);
TextInputComponent style(TextStyle? style);
TextInputComponent strutStyle(StrutStyle? strutStyle);
TextInputComponent textAlign(TextAlign? textAlign);
TextInputComponent textAlignVertical(TextAlignVertical? textAlignVertical);
TextInputComponent textDirection(TextDirection? textDirection);
TextInputComponent readOnly(bool? readOnly);
TextInputComponent showCursor(bool? showCursor);
TextInputComponent autofocus(bool? autofocus);
TextInputComponent obscuringCharacter(String? obscuringCharacter);
TextInputComponent obscureText(bool? obscureText);
TextInputComponent autocorrect(bool? autocorrect);
TextInputComponent enableSuggestions(bool? enableSuggestions);
TextInputComponent maxLines(int? maxLines);
TextInputComponent minLines(int? minLines);
TextInputComponent expands(bool? expands);
TextInputComponent maxLength(int? maxLength);
TextInputComponent maxLengthEnforcement(
MaxLengthEnforcement? maxLengthEnforcement);
TextInputComponent onChanged(void Function(String)? onChanged);
TextInputComponent onEditingComplete(void Function()? onEditingComplete);
TextInputComponent onSubmitted(void Function(String)? onSubmitted);
TextInputComponent onAppPrivateCommand(
void Function(String, Map<String, dynamic>)? onAppPrivateCommand);
TextInputComponent inputFormatters(List<TextInputFormatter>? inputFormatters);
TextInputComponent enabled(ValueNotifier<bool>? enabled);
TextInputComponent cursorWidth(double? cursorWidth);
TextInputComponent cursorHeight(double? cursorHeight);
TextInputComponent cursorRadius(Radius? cursorRadius);
TextInputComponent cursorColor(Color? cursorColor);
TextInputComponent selectionHeightStyle(BoxHeightStyle? selectionHeightStyle);
TextInputComponent selectionWidthStyle(BoxWidthStyle? selectionWidthStyle);
TextInputComponent keyboardAppearance(Brightness? keyboardAppearance);
TextInputComponent scrollPadding(EdgeInsets? scrollPadding);
TextInputComponent dragStartBehavior(DragStartBehavior? dragStartBehavior);
TextInputComponent selectionControls(
TextSelectionControls? selectionControls);
TextInputComponent onTap(void Function()? onTap);
TextInputComponent onTapOutside(
void Function(PointerDownEvent)? onTapOutside);
TextInputComponent mouseCursor(MouseCursor? mouseCursor);
TextInputComponent scrollController(ScrollController? scrollController);
TextInputComponent scrollPhysics(ScrollPhysics? scrollPhysics);
TextInputComponent autofillHints(Iterable<String>? autofillHints);
TextInputComponent clipBehavior(Clip? clipBehavior);
TextInputComponent restorationId(String? restorationId);
TextInputComponent scribbleEnabled(bool? scribbleEnabled);
TextInputComponent enableIMEPersonalizedLearning(
bool? enableIMEPersonalizedLearning);
TextInputComponent contextMenuBuilder(
Widget Function(BuildContext, EditableTextState)? contextMenuBuilder);
TextInputComponent spellCheckConfiguration(
SpellCheckConfiguration? spellCheckConfiguration);
TextInputComponent magnifierConfiguration(
TextMagnifierConfiguration? magnifierConfiguration);
TextInputComponent key(Key? key);
TextInputComponent call({
bool? expand,
String Function(String)? onError,
bool Function(String)? validator,
TextWrapper? suffixText,
TextWrapper? prefixText,
Icon? prefixIcon,
Icon? suffixIcon,
TextWrapper? label,
TextWrapper? hint,
TextInputStyle? normalStyle,
TextInputStyle? focusedStyle,
TextInputStyle? errorStyle,
TextInputStyle? disableStyle,
TextEditingController? controller,
FocusNode? focusNode,
TextInputType? keyboardType,
SmartDashesType? smartDashesType,
SmartQuotesType? smartQuotesType,
bool? enableInteractiveSelection,
TextInputAction? textInputAction,
TextCapitalization? textCapitalization,
TextStyle? style,
StrutStyle? strutStyle,
TextAlign? textAlign,
TextAlignVertical? textAlignVertical,
TextDirection? textDirection,
bool? readOnly,
bool? showCursor,
bool? autofocus,
String? obscuringCharacter,
bool? obscureText,
bool? autocorrect,
bool? enableSuggestions,
int? maxLines,
int? minLines,
bool? expands,
int? maxLength,
MaxLengthEnforcement? maxLengthEnforcement,
void Function(String)? onChanged,
void Function()? onEditingComplete,
void Function(String)? onSubmitted,
void Function(String, Map<String, dynamic>)? onAppPrivateCommand,
List<TextInputFormatter>? inputFormatters,
ValueNotifier<bool>? enabled,
double? cursorWidth,
double? cursorHeight,
Radius? cursorRadius,
Color? cursorColor,
BoxHeightStyle? selectionHeightStyle,
BoxWidthStyle? selectionWidthStyle,
Brightness? keyboardAppearance,
EdgeInsets? scrollPadding,
DragStartBehavior? dragStartBehavior,
TextSelectionControls? selectionControls,
void Function()? onTap,
void Function(PointerDownEvent)? onTapOutside,
MouseCursor? mouseCursor,
ScrollController? scrollController,
ScrollPhysics? scrollPhysics,
Iterable<String>? autofillHints,
Clip? clipBehavior,
String? restorationId,
bool? scribbleEnabled,
bool? enableIMEPersonalizedLearning,
Widget Function(BuildContext, EditableTextState)? contextMenuBuilder,
SpellCheckConfiguration? spellCheckConfiguration,
TextMagnifierConfiguration? magnifierConfiguration,
Key? key,
});
}

View File

@ -0,0 +1,133 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
class TextInputStyle extends ThemeStyle<TextInputStyle> {
TextInputStyle({
this.labelStyle,
this.hintStyle,
this.backgroundColors,
this.borderColors,
this.boxShadow,
this.radius,
this.inputStyle,
this.iconColor,
this.prefixStyle,
this.prefixIconColor,
this.suffixStyle,
this.suffixIconColor,
});
final TextStyle? labelStyle;
final TextStyle? hintStyle;
final Color? iconColor;
final TextStyle? prefixStyle;
final Color? prefixIconColor;
final TextStyle? suffixStyle;
final Color? suffixIconColor;
final MultiColor? backgroundColors;
final Color? borderColors;
final BoxShadow? boxShadow;
final BorderRadiusGeometry? radius;
final TextStyle? inputStyle;
static TextInputStyle? merge(TextInputStyle? a, TextInputStyle? b) {
if (b == null) {
return a?.copyWith();
}
if (a == null) {
return b.copyWith();
}
return a.copyWith(
labelStyle: b.labelStyle,
hintStyle: b.hintStyle,
backgroundColors: b.backgroundColors,
borderColors: b.borderColors,
boxShadow: b.boxShadow,
radius: b.radius,
inputStyle: b.inputStyle,
iconColor: b.iconColor,
prefixStyle: b.prefixStyle,
prefixIconColor: b.prefixIconColor,
suffixIconColor: b.suffixIconColor,
suffixStyle: b.suffixStyle,
);
}
static TextInputStyle? lerp(
TextInputStyle? a,
TextInputStyle? b,
double t,
) {
if (a == null || b == null) {
return null;
}
return b.copyWith(
labelStyle: TextStyle.lerp(a.labelStyle, b.labelStyle, t),
hintStyle: TextStyle.lerp(a.hintStyle, b.hintStyle, t),
backgroundColors:
MultiColor.lerp(a.backgroundColors, b.backgroundColors, t),
radius: BorderRadiusGeometry.lerp(a.radius, b.radius, t),
borderColors: Color.lerp(a.borderColors, b.borderColors, t),
boxShadow: BoxShadow.lerp(a.boxShadow, b.boxShadow, t),
inputStyle: TextStyle.lerp(a.inputStyle, b.inputStyle, t),
prefixStyle: TextStyle.lerp(a.prefixStyle, b.prefixStyle, t),
suffixStyle: TextStyle.lerp(a.suffixStyle, b.suffixStyle, t),
prefixIconColor: Color.lerp(a.prefixIconColor, b.prefixIconColor, t),
suffixIconColor: Color.lerp(a.suffixIconColor, b.suffixIconColor, t),
iconColor: Color.lerp(a.iconColor, b.iconColor, t),
);
}
@override
TextInputStyle? mergeWith(TextInputStyle? other) =>
TextInputStyle.merge(this, other);
@override
TextInputStyle copyWith({
TextStyle? labelStyle,
TextStyle? hintStyle,
MultiColor? backgroundColors,
Color? borderColors,
BoxShadow? boxShadow,
BorderRadiusGeometry? radius,
TextStyle? inputStyle,
Color? iconColor,
TextStyle? prefixStyle,
Color? prefixIconColor,
TextStyle? suffixStyle,
Color? suffixIconColor,
}) =>
TextInputStyle(
labelStyle: labelStyle ?? this.labelStyle,
hintStyle: hintStyle ?? this.hintStyle,
backgroundColors: backgroundColors ?? this.backgroundColors,
radius: radius ?? this.radius,
borderColors: borderColors ?? this.borderColors,
boxShadow: boxShadow ?? this.boxShadow,
inputStyle: inputStyle ?? this.inputStyle,
prefixStyle: prefixStyle ?? this.prefixStyle,
suffixStyle: suffixStyle ?? this.suffixStyle,
prefixIconColor: prefixIconColor ?? this.prefixIconColor,
suffixIconColor: suffixIconColor ?? this.suffixIconColor,
iconColor: iconColor ?? this.iconColor,
);
}

View File

@ -0,0 +1,18 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export './text_input_component.dart';
export './text_input_style.dart';

View File

@ -18,11 +18,8 @@ abstract class ThemeStyle<T> {
const ThemeStyle();
/// Merges non-null `other` attributes in `this` and returns a copy.
T mergeWith(T? other);
/// Used for interpolation.
T? lerpWith(T? other, double t);
T? mergeWith(T? other);
/// Copy with (mandatory for mergeWith, needs to be simple and ignore `null`)
T copyWith();
T? copyWith();
}

View File

@ -6,6 +6,7 @@ import 'package:wyatt_ui_kit_example/cards/cards.dart';
import 'package:wyatt_ui_kit_example/demo_page.dart';
import 'package:wyatt_ui_kit_example/loaders/loaders.dart';
import 'package:wyatt_ui_kit_example/rich_text_builders/rich_text_builders.dart';
import 'package:wyatt_ui_kit_example/text_input/text_inputs.dart';
import 'package:wyatt_ui_kit_example/theme/themes.dart';
const String title = 'Wyatt UIKit Example';
@ -26,6 +27,7 @@ class _HomeState extends State<Home> {
Buttons(),
Loaders(),
RichTextBuilders(),
TextInputs(),
];
int currentIndex = 0;

View File

@ -64,5 +64,4 @@ class Loaders extends DemoPage {
const Gap(20),
],
);
}

View File

@ -0,0 +1,140 @@
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
import 'package:wyatt_ui_kit/wyatt_ui_kit.dart';
import 'package:wyatt_ui_kit_example/demo_page.dart';
class TextInputs extends DemoPage {
const TextInputs({super.key});
@override
Widget build(BuildContext context) => const TextInputsCore();
@override
String get title => 'Text Inputs';
}
class TextInputsCore extends StatefulWidget {
const TextInputsCore({super.key});
@override
State<TextInputsCore> createState() => _TextInputsCoreState();
}
class _TextInputsCoreState extends State<TextInputsCore> {
final _formKey = GlobalKey<FormState>();
final _controller = TextEditingController();
final _focusNode = FocusNode();
final _formKey2 = GlobalKey<FormState>();
final _controller2 = TextEditingController();
final _focusNode2 = FocusNode();
final _formKey3 = GlobalKey<FormState>();
final _controller3 = TextEditingController();
final _focusNode3 = FocusNode();
final _formKey4 = GlobalKey<FormState>();
final _controller4 = TextEditingController();
final _focusNode4 = FocusNode();
final _formKey5 = GlobalKey<FormState>();
final _controller5 = TextEditingController();
final _focusNode5 = FocusNode();
final _formKey6 = GlobalKey<FormState>();
final _controller6 = TextEditingController();
final _focusNode6 = FocusNode();
final ValueNotifier<bool> _enable = ValueNotifier(true);
@override
void initState() {
super.initState();
Future.delayed(const Duration(milliseconds: 500), () {
setState(() {
_enable.value = false;
});
});
}
@override
Widget build(BuildContext context) => Form(
child: ListView(
cacheExtent: 1000,
children: [
const Gap(20),
Align(
child: Text(
'Text inputs',
style: Theme.of(context).textTheme.titleLarge,
),
),
const Gap(20),
TextInput(
key: _formKey6,
controller: _controller6,
focusNode: _focusNode6,
label: 'Nom / Prénom'.wrap(),
onError: (value) => 'Erreur : ${value.length} > 5.',
validator: (value) => value.length > 5,
),
const Gap(20),
TextInput(
key: _formKey,
enabled: _enable,
controller: _controller,
focusNode: _focusNode,
label: 'Nom / Prénom'.wrap(),
onError: (value) => 'Erreur : ${value.length} > 5.',
validator: (value) => value.length > 5,
),
const Gap(20),
TextInput(
key: _formKey2,
controller: _controller2,
focusNode: _focusNode2,
maxLines: 3,
onError: (value) => 'Erreur : ${value.length} > 5.',
validator: (value) => value.length > 5,
onChanged: (value) {},
),
const Gap(20),
TextInput(
key: _formKey3,
controller: _controller3,
focusNode: _focusNode3,
expand: false,
label: 'Nom / Prénom'.wrap(),
onError: (value) => 'Erreur : ${value.length} > 5.',
validator: (value) => value.length > 5,
onChanged: (value) {},
),
const Gap(20),
TextInput(
key: _formKey4,
expand: false,
controller: _controller4,
focusNode: _focusNode4,
label: 'Nom / Prénom'.wrap(),
maxLines: 3,
onError: (value) => 'Erreur : ${value.length} > 5.',
validator: (value) => value.length > 5,
onChanged: (value) {},
),
const Gap(20),
TextInput(
key: _formKey5,
prefixIcon: const Icon(Icons.architecture),
suffixIcon: const Icon(Icons.architecture),
controller: _controller5,
focusNode: _focusNode5,
label: 'Nom / Prénom'.wrap(),
onError: (value) => 'Erreur : ${value.length} > 5.',
validator: (value) => value.length > 5,
),
const Gap(20),
],
),
);
}

View File

@ -0,0 +1,161 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
import 'package:wyatt_ui_kit/wyatt_ui_kit.dart';
class TextInputTheme extends TextInputThemeExtension {
const TextInputTheme({
super.normalStyle,
super.focusedStyle,
super.disableStyle,
super.errorStyle,
});
factory TextInputTheme.light() => TextInputTheme(
normalStyle: TextInputStyle(
radius: BorderRadius.circular(12),
borderColors: const Color.fromRGBO(221, 224, 227, 1),
labelStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(55, 65, 81, 1),
),
inputStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(55, 65, 81, 1),
),
),
focusedStyle: TextInputStyle(
radius: BorderRadius.circular(12),
borderColors: const Color.fromRGBO(60, 125, 251, 1),
labelStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(55, 65, 81, 1),
),
inputStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(55, 65, 81, 1),
),
),
errorStyle: TextInputStyle(
radius: BorderRadius.circular(12),
borderColors: const Color.fromRGBO(244, 68, 100, 1),
labelStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(244, 68, 100, 1),
),
inputStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(55, 65, 81, 1),
),
),
disableStyle: TextInputStyle(
radius: BorderRadius.circular(12),
borderColors: const Color.fromRGBO(229, 231, 235, 1),
labelStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(156, 163, 175, 1),
),
inputStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(156, 163, 175, 1),
),
),
);
factory TextInputTheme.dark() => TextInputTheme(
normalStyle: TextInputStyle(
radius: BorderRadius.circular(12),
borderColors: const Color.fromRGBO(96, 101, 106, 1),
labelStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(204, 204, 204, 1),
),
inputStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(255, 255, 255, 1),
),
),
focusedStyle: TextInputStyle(
radius: BorderRadius.circular(12),
borderColors: const Color.fromRGBO(60, 125, 251, 1),
labelStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(204, 204, 204, 1),
),
inputStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(255, 255, 255, 1),
),
),
errorStyle: TextInputStyle(
radius: BorderRadius.circular(12),
borderColors: const Color.fromRGBO(244, 68, 100, 1),
labelStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(244, 68, 100, 1),
),
inputStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(255, 255, 255, 1),
),
),
disableStyle: TextInputStyle(
radius: BorderRadius.circular(12),
borderColors: const Color.fromRGBO(96, 101, 106, 1),
labelStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(96, 101, 106, 1),
),
inputStyle: GoogleFonts.montserrat(
fontWeight: FontWeight.w300,
color: const Color.fromRGBO(255, 255, 255, 1),
),
),
);
@override
ThemeExtension<TextInputThemeExtension> lerp(
covariant ThemeExtension<TextInputThemeExtension>? other,
double t,
) {
if (other is! TextInputTheme) {
return this;
}
return TextInputTheme(
normalStyle: TextInputStyle.lerp(normalStyle, other.normalStyle, t),
focusedStyle: TextInputStyle.lerp(focusedStyle, other.focusedStyle, t),
disableStyle: TextInputStyle.lerp(disableStyle, other.disableStyle, t),
errorStyle: TextInputStyle.lerp(errorStyle, other.errorStyle, t),
);
}
@override
ThemeExtension<TextInputThemeExtension> copyWith({
TextInputStyle? normalStyle,
TextInputStyle? focusedStyle,
TextInputStyle? disableStyle,
TextInputStyle? errorStyle,
}) =>
TextInputTheme(
normalStyle: normalStyle ?? this.normalStyle,
focusedStyle: focusedStyle ?? this.focusedStyle,
disableStyle: disableStyle ?? this.disableStyle,
errorStyle: errorStyle ?? this.errorStyle,
);
}

View File

@ -24,6 +24,7 @@ import 'package:wyatt_ui_kit_example/theme/loader_theme.dart';
import 'package:wyatt_ui_kit_example/theme/rich_text_builder_theme.dart';
import 'package:wyatt_ui_kit_example/theme/simple_icon_button_theme.dart';
import 'package:wyatt_ui_kit_example/theme/symbol_button_theme.dart';
import 'package:wyatt_ui_kit_example/theme/text_input_theme.dart';
/// Easely switch between Material and Studio themes.
abstract class Themes {
@ -90,6 +91,7 @@ abstract class Themes {
LoaderTheme.light(),
// Rich Text
RichTextBuilderTheme.light(),
TextInputTheme.light(),
],
);
@ -121,6 +123,7 @@ abstract class Themes {
LoaderTheme.dark(),
// Rich Text
RichTextBuilderTheme.dark(),
TextInputTheme.dark(),
],
);
}

View File

@ -67,40 +67,6 @@ class FileSelectionButtonScreen
FileSelectionButtonStyle _resolve(BuildContext context, ButtonState state) {
final FileSelectionButtonThemeResolver resolver = themeResolver ??
FileSelectionButtonThemeResolver(
computeExtensionValueFn: (
context,
defaultValue,
themeExtension, {
extra,
}) {
FileSelectionButtonStyle? style = defaultValue;
switch (extra?.state) {
case ControlState.disabled:
style = themeExtension.disabledStyle;
break;
case ControlState.focused:
style = themeExtension.focusedStyle;
break;
case ControlState.hovered:
style = themeExtension.hoveredStyle;
break;
case ControlState.tapped:
style = themeExtension.tappedStyle;
break;
case ControlState.normal:
case null:
style = themeExtension.normalStyle;
break;
}
if (extra?.isSelected ?? false) {
style = themeExtension.selectedStyle;
}
if (extra?.isInvalid ?? false) {
style = themeExtension.invalidStyle;
}
return style;
},
customStyleFn: (context, {extra}) {
FileSelectionButtonStyle? style;
switch (extra?.state) {

View File

@ -22,7 +22,6 @@ import 'package:wyatt_ui_kit/wyatt_ui_kit.dart';
class FileSelectionButtonThemeResolver extends ThemeResolver<
FileSelectionButtonStyle, FileSelectionButtonThemeExtension, ButtonState> {
const FileSelectionButtonThemeResolver({
required this.computeExtensionValueFn,
required this.customStyleFn,
});
@ -57,8 +56,7 @@ class FileSelectionButtonThemeResolver extends ThemeResolver<
}
if (extra?.isInvalid ?? false) {
backgroundColor =
MultiColor.single(context.colorScheme.error);
backgroundColor = MultiColor.single(context.colorScheme.error);
}
return FileSelectionButtonStyle(
@ -73,17 +71,43 @@ class FileSelectionButtonThemeResolver extends ThemeResolver<
);
}
@override
final FileSelectionButtonStyle? Function(
BuildContext context,
FileSelectionButtonStyle defaultValue,
FileSelectionButtonThemeExtension themeExtension, {
ButtonState? extra,
}) computeExtensionValueFn;
@override
final FileSelectionButtonStyle? Function(
BuildContext context, {
ButtonState? extra,
}) customStyleFn;
@override
FileSelectionButtonStyle? computeExtensionValueFn(
BuildContext context,
FileSelectionButtonThemeExtension themeExtension, {
ButtonState? extra,
}) {
FileSelectionButtonStyle? style;
switch (extra?.state) {
case ControlState.disabled:
style = themeExtension.disabledStyle;
break;
case ControlState.focused:
style = themeExtension.focusedStyle;
break;
case ControlState.hovered:
style = themeExtension.hoveredStyle;
break;
case ControlState.tapped:
style = themeExtension.tappedStyle;
break;
case ControlState.normal:
case null:
style = themeExtension.normalStyle;
break;
}
if (extra?.isSelected ?? false) {
style = themeExtension.selectedStyle;
}
if (extra?.isInvalid ?? false) {
style = themeExtension.invalidStyle;
}
return style;
}
}

View File

@ -61,26 +61,6 @@ class FlatButtonScreen extends CubitScreen<ButtonCubit, ButtonState> {
FlatButtonStyle _resolve(BuildContext context, ControlState state) {
final FlatButtonThemeResolver resolver = themeResolver ??
FlatButtonThemeResolver(
computeExtensionValueFn: (
context,
defaultValue,
themeExtension, {
extra,
}) {
switch (extra) {
case ControlState.disabled:
return themeExtension.disabledStyle;
case ControlState.focused:
return themeExtension.focusedStyle;
case ControlState.hovered:
return themeExtension.hoveredStyle;
case ControlState.tapped:
return themeExtension.tappedStyle;
case ControlState.normal:
case null:
return themeExtension.normalStyle;
}
},
customStyleFn: (context, {extra}) {
switch (extra) {
case ControlState.disabled:

View File

@ -21,7 +21,6 @@ import 'package:wyatt_ui_kit/wyatt_ui_kit.dart';
class FlatButtonThemeResolver extends ThemeResolver<FlatButtonStyle,
FlatButtonThemeExtension, ControlState> {
const FlatButtonThemeResolver({
required this.computeExtensionValueFn,
required this.customStyleFn,
});
@ -70,13 +69,28 @@ class FlatButtonThemeResolver extends ThemeResolver<FlatButtonStyle,
@override
final FlatButtonStyle? Function(
BuildContext context,
FlatButtonStyle defaultValue,
FlatButtonThemeExtension themeExtension, {
BuildContext context, {
ControlState? extra,
}) computeExtensionValueFn;
}) customStyleFn;
@override
final FlatButtonStyle? Function(BuildContext context, {ControlState? extra})
customStyleFn;
FlatButtonStyle? computeExtensionValueFn(
BuildContext context,
FlatButtonThemeExtension themeExtension, {
ControlState? extra,
}) {
switch (extra) {
case ControlState.disabled:
return themeExtension.disabledStyle;
case ControlState.focused:
return themeExtension.focusedStyle;
case ControlState.hovered:
return themeExtension.hoveredStyle;
case ControlState.tapped:
return themeExtension.tappedStyle;
case ControlState.normal:
case null:
return themeExtension.normalStyle;
}
}
}

View File

@ -21,7 +21,6 @@ import 'package:wyatt_ui_kit/wyatt_ui_kit.dart';
class SimpleIconButtonThemeResolver extends ThemeResolver<SimpleIconButtonStyle,
SimpleIconButtonThemeExtension, ControlState> {
const SimpleIconButtonThemeResolver({
required this.computeExtensionValueFn,
required this.customStyleFn,
});
@ -66,17 +65,30 @@ class SimpleIconButtonThemeResolver extends ThemeResolver<SimpleIconButtonStyle,
);
}
@override
final SimpleIconButtonStyle? Function(
BuildContext context,
SimpleIconButtonStyle defaultValue,
SimpleIconButtonThemeExtension themeExtension, {
ControlState? extra,
}) computeExtensionValueFn;
@override
final SimpleIconButtonStyle? Function(
BuildContext context, {
ControlState? extra,
}) customStyleFn;
@override
SimpleIconButtonStyle? computeExtensionValueFn(
BuildContext context,
SimpleIconButtonThemeExtension themeExtension, {
ControlState? extra,
}) {
switch (extra) {
case ControlState.disabled:
return themeExtension.disabledStyle;
case ControlState.focused:
return themeExtension.focusedStyle;
case ControlState.hovered:
return themeExtension.hoveredStyle;
case ControlState.tapped:
return themeExtension.tappedStyle;
case ControlState.normal:
case null:
return themeExtension.normalStyle;
}
}
}

View File

@ -55,26 +55,6 @@ class SimpleIconButtonScreen extends CubitScreen<ButtonCubit, ButtonState> {
SimpleIconButtonStyle _resolve(BuildContext context, ControlState state) {
final SimpleIconButtonThemeResolver resolver = themeResolver ??
SimpleIconButtonThemeResolver(
computeExtensionValueFn: (
context,
defaultValue,
themeExtension, {
extra,
}) {
switch (extra) {
case ControlState.disabled:
return themeExtension.disabledStyle;
case ControlState.focused:
return themeExtension.focusedStyle;
case ControlState.hovered:
return themeExtension.hoveredStyle;
case ControlState.tapped:
return themeExtension.tappedStyle;
case ControlState.normal:
case null:
return themeExtension.normalStyle;
}
},
customStyleFn: (context, {extra}) {
switch (extra) {
case ControlState.disabled:

View File

@ -63,37 +63,6 @@ class SymbolButtonScreen
SymbolButtonStyle _resolve(BuildContext context, ButtonState state) {
final SymbolButtonThemeResolver resolver = themeResolver ??
SymbolButtonThemeResolver(
computeExtensionValueFn: (
context,
defaultValue,
themeExtension, {
extra,
}) {
SymbolButtonStyle? style = defaultValue;
switch (extra?.state) {
case ControlState.disabled:
style = themeExtension.disabledStyle;
break;
case ControlState.focused:
style = themeExtension.focusedStyle;
break;
case ControlState.hovered:
style = themeExtension.hoveredStyle;
break;
case ControlState.tapped:
style = themeExtension.tappedStyle;
break;
case ControlState.normal:
case null:
style = themeExtension.normalStyle;
break;
}
if (extra?.isSelected ?? false) {
style = themeExtension.selectedStyle;
}
return style;
},
customStyleFn: (context, {extra}) {
SymbolButtonStyle? style;
switch (extra?.state) {

View File

@ -22,7 +22,6 @@ import 'package:wyatt_ui_kit/wyatt_ui_kit.dart';
class SymbolButtonThemeResolver extends ThemeResolver<SymbolButtonStyle,
SymbolButtonThemeExtension, ButtonState> {
const SymbolButtonThemeResolver({
required this.computeExtensionValueFn,
required this.customStyleFn,
});
@ -58,7 +57,7 @@ class SymbolButtonThemeResolver extends ThemeResolver<SymbolButtonStyle,
return SymbolButtonStyle(
label: context.textTheme.labelLarge,
dimension: context.buttonTheme.height*1.5,
dimension: context.buttonTheme.height * 1.5,
radius: (context.buttonTheme.shape is RoundedRectangleBorder)
? (context.buttonTheme.shape as RoundedRectangleBorder).borderRadius
: null,
@ -68,17 +67,41 @@ class SymbolButtonThemeResolver extends ThemeResolver<SymbolButtonStyle,
);
}
@override
final SymbolButtonStyle? Function(
BuildContext context,
SymbolButtonStyle defaultValue,
SymbolButtonThemeExtension themeExtension, {
ButtonState? extra,
}) computeExtensionValueFn;
@override
final SymbolButtonStyle? Function(
BuildContext context, {
ButtonState? extra,
}) customStyleFn;
@override
SymbolButtonStyle? computeExtensionValueFn(
BuildContext context,
SymbolButtonThemeExtension themeExtension, {
ButtonState? extra,
}) {
SymbolButtonStyle? style;
switch (extra?.state) {
case ControlState.disabled:
style = themeExtension.disabledStyle;
break;
case ControlState.focused:
style = themeExtension.focusedStyle;
break;
case ControlState.hovered:
style = themeExtension.hoveredStyle;
break;
case ControlState.tapped:
style = themeExtension.tappedStyle;
break;
case ControlState.normal:
case null:
style = themeExtension.normalStyle;
break;
}
if (extra?.isSelected ?? false) {
style = themeExtension.selectedStyle;
}
return style;
}
}

View File

@ -24,10 +24,10 @@ class $InformationCardCWProxyImpl implements $InformationCardComponentCWProxy {
@override
InformationCard padding(double? padding) => this(padding: padding);
@override
InformationCard borderColors(List<Color>? borderColors) =>
InformationCard borderColors(MultiColor? borderColors) =>
this(borderColors: borderColors);
@override
InformationCard backgroundColors(List<Color>? backgroundColors) =>
InformationCard backgroundColors(MultiColor? backgroundColors) =>
this(backgroundColors: backgroundColors);
@override
InformationCard minSize(Size? minSize) => this(minSize: minSize);
@ -49,8 +49,8 @@ class $InformationCardCWProxyImpl implements $InformationCardComponentCWProxy {
Axis? axis,
double? radius,
double? padding,
List<Color>? borderColors,
List<Color>? backgroundColors,
MultiColor? borderColors,
MultiColor? backgroundColors,
Size? minSize,
Size? maxSize,
BoxShadow? shadow,

View File

@ -36,10 +36,10 @@ class $PortfolioCardCWProxyImpl implements $PortfolioCardComponentCWProxy {
@override
PortfolioCard padding(double? padding) => this(padding: padding);
@override
PortfolioCard borderColors(List<Color>? borderColors) =>
PortfolioCard borderColors(MultiColor? borderColors) =>
this(borderColors: borderColors);
@override
PortfolioCard backgroundColors(List<Color>? backgroundColors) =>
PortfolioCard backgroundColors(MultiColor? backgroundColors) =>
this(backgroundColors: backgroundColors);
@override
PortfolioCard minSize(Size? minSize) => this(minSize: minSize);
@ -64,8 +64,8 @@ class $PortfolioCardCWProxyImpl implements $PortfolioCardComponentCWProxy {
List<Widget>? assets,
double? radius,
double? padding,
List<Color>? borderColors,
List<Color>? backgroundColors,
MultiColor? borderColors,
MultiColor? backgroundColors,
Size? minSize,
Size? maxSize,
BoxShadow? shadow,

View File

@ -28,10 +28,10 @@ class $QuoteCardCWProxyImpl implements $QuoteCardComponentCWProxy {
@override
QuoteCard padding(double? padding) => this(padding: padding);
@override
QuoteCard borderColors(List<Color>? borderColors) =>
QuoteCard borderColors(MultiColor? borderColors) =>
this(borderColors: borderColors);
@override
QuoteCard backgroundColors(List<Color>? backgroundColors) =>
QuoteCard backgroundColors(MultiColor? backgroundColors) =>
this(backgroundColors: backgroundColors);
@override
QuoteCard minSize(Size? minSize) => this(minSize: minSize);
@ -54,8 +54,8 @@ class $QuoteCardCWProxyImpl implements $QuoteCardComponentCWProxy {
Widget? rightQuote,
double? radius,
double? padding,
List<Color>? borderColors,
List<Color>? backgroundColors,
MultiColor? borderColors,
MultiColor? backgroundColors,
Size? minSize,
Size? maxSize,
BoxShadow? shadow,

View File

@ -31,10 +31,10 @@ class $SkillCardCWProxyImpl implements $SkillCardComponentCWProxy {
@override
SkillCard padding(double? padding) => this(padding: padding);
@override
SkillCard borderColors(List<Color>? borderColors) =>
SkillCard borderColors(MultiColor? borderColors) =>
this(borderColors: borderColors);
@override
SkillCard backgroundColors(List<Color>? backgroundColors) =>
SkillCard backgroundColors(MultiColor? backgroundColors) =>
this(backgroundColors: backgroundColors);
@override
SkillCard minSize(Size? minSize) => this(minSize: minSize);
@ -57,8 +57,8 @@ class $SkillCardCWProxyImpl implements $SkillCardComponentCWProxy {
Color? secondaryBackgroundColors,
double? radius,
double? padding,
List<Color>? borderColors,
List<Color>? backgroundColors,
MultiColor? borderColors,
MultiColor? backgroundColors,
Size? minSize,
Size? maxSize,
BoxShadow? shadow,

View File

@ -0,0 +1,51 @@
import 'package:flutter/material.dart';
class CardBackground extends StatefulWidget {
const CardBackground({super.key, this.background, this.cardKey});
final Widget? background;
final GlobalKey? cardKey;
@override
State<CardBackground> createState() => _CardBackgroundState();
}
class _CardBackgroundState extends State<CardBackground> {
Size _cardSize = Size.zero;
@override
void initState() {
super.initState();
if (widget.background != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_resizeCard();
});
}
}
@override
void didUpdateWidget(covariant CardBackground oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.background != null) {
_resizeCard();
}
}
void _resizeCard() {
final RenderObject? renderBox =
widget.cardKey?.currentContext?.findRenderObject();
if (renderBox != null) {
setState(() {
_cardSize =
Size(renderBox.paintBounds.width, renderBox.paintBounds.height);
});
}
}
@override
Widget build(BuildContext context) => SizedBox(
width: _cardSize.width,
height: _cardSize.height,
child: Center(child: widget.background),
);
}

View File

@ -15,11 +15,12 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:wyatt_ui_kit/src/components/gradients/gradient_box_border.dart';
import 'package:wyatt_ui_kit/src/domain/card_theme_extension.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
import 'package:wyatt_ui_kit/src/components/cards/widgets/card_background.dart';
import 'package:wyatt_ui_kit/wyatt_ui_kit.dart';
class CardWrapper extends StatefulWidget {
const CardWrapper({
class CardWrapper extends StatelessWidget {
CardWrapper({
required this.child,
required this.backgroundColors,
required this.borderColors,
@ -33,61 +34,26 @@ class CardWrapper extends StatefulWidget {
final Widget? background;
final Widget child;
final List<Color>? backgroundColors;
final List<Color>? borderColors;
final MultiColor? backgroundColors;
final MultiColor? borderColors;
final BoxShadow? shadow;
final Size? minSize;
final Size? maxSize;
final double? padding;
@override
State<CardWrapper> createState() => _CardWrapperState();
}
class _CardWrapperState extends State<CardWrapper> {
Size _cardSize = Size.zero;
final GlobalKey _key = GlobalKey();
@override
void initState() {
super.initState();
if (widget.background != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
_resizeCard();
});
}
}
@override
void didUpdateWidget(covariant CardWrapper oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.background != null) {
_resizeCard();
}
}
void _resizeCard() {
final RenderObject? renderBox = _key.currentContext?.findRenderObject();
if (renderBox != null) {
setState(() {
_cardSize =
Size(renderBox.paintBounds.width, renderBox.paintBounds.height);
});
}
}
Widget _buildChild(Widget child) => (widget.background != null)
Widget _buildChild(Widget child) => (background != null)
? Stack(
alignment: Alignment.center,
children: [
SizedBox(
width: _cardSize.width,
height: _cardSize.height,
child: Center(child: widget.background),
CardBackground(
cardKey: _key,
background: background,
),
Padding(
padding: EdgeInsets.all(
widget.padding ?? 25,
padding ?? 25,
),
child: child,
),
@ -95,92 +61,22 @@ class _CardWrapperState extends State<CardWrapper> {
)
: Padding(
padding: EdgeInsets.all(
widget.padding ?? 25,
padding ?? 25,
),
child: child,
);
Gradient? _cardGradient(BuildContext context) {
if (widget.backgroundColors != null &&
widget.backgroundColors!.length >= 2) {
return LinearGradient(colors: widget.backgroundColors!);
} else {
final extensionCardColor =
Theme.of(context).extension<CardThemeExtension>();
if (extensionCardColor != null &&
extensionCardColor.backgroundColors != null &&
extensionCardColor.backgroundColors!.isGradient) {
return LinearGradient(
colors: extensionCardColor.backgroundColors!.colors,);
}
}
return null;
}
Color? _cardColor(BuildContext context) {
if (widget.backgroundColors != null &&
widget.backgroundColors!.length == 1) {
return widget.backgroundColors!.first;
} else {
final extensionCardColor =
Theme.of(context).extension<CardThemeExtension>();
if (extensionCardColor != null &&
extensionCardColor.backgroundColors != null) {
return extensionCardColor.backgroundColors!.color;
}
}
return Theme.of(context).cardColor;
}
BoxBorder? _boxBorder(BuildContext context) {
if (widget.borderColors != null) {
if (widget.borderColors!.length >= 2) {
return GradientBoxBorder(
gradient: LinearGradient(
colors: widget.borderColors!,
),
);
} else if (widget.borderColors!.isNotEmpty) {
return Border.all(
color: widget.borderColors!.first,
);
}
} else {
final extensionCardColor =
Theme.of(context).extension<CardThemeExtension>();
if (extensionCardColor != null &&
extensionCardColor.borderColors != null) {
if (extensionCardColor.borderColors!.isGradient) {
return GradientBoxBorder(
gradient: LinearGradient(
colors: extensionCardColor.borderColors!.colors,
),
);
} else if (extensionCardColor.backgroundColors!.colors.isNotEmpty) {
return Border.all(
color: extensionCardColor.backgroundColors!.color,
);
}
}
}
return null;
}
List<BoxShadow> _shadow(BuildContext context) {
final shadows = List<BoxShadow>.empty(growable: true);
if (widget.shadow != null) {
shadows.add(widget.shadow!);
} else {
final extensionCardColor =
Theme.of(context).extension<CardThemeExtension>();
if (extensionCardColor != null &&
extensionCardColor.shadowColor != null) {
shadows.add(extensionCardColor.shadowColor!);
}
}
return shadows;
final themeShadow = ThemeHelper.getThemeElement<BoxShadow, BoxShadow>(
[
shadow,
Theme.of(context).extension<CardThemeExtension>()?.shadowColor,
],
valueValidator: (shadow) => shadow != null,
transform: (shadow) => shadow,
defaultValue: null,
);
return (themeShadow != null) ? [themeShadow] : [];
}
@override
@ -189,22 +85,66 @@ class _CardWrapperState extends State<CardWrapper> {
key: _key,
decoration: BoxDecoration(
borderRadius: const BorderRadius.all(Radius.circular(12)),
gradient: _cardGradient(context),
color: _cardColor(context),
border: _boxBorder(context),
gradient: ThemeHelper.getThemeElement<MultiColor, Gradient>(
[
backgroundColors,
Theme.of(context)
.extension<CardThemeExtension>()
?.backgroundColors,
],
valueValidator: (multiColor) =>
multiColor != null && multiColor.isGradient,
transform: (multiColor) =>
LinearGradientHelper.fromMultiColor(multiColor!),
defaultValue: null,
),
color: ThemeHelper.getThemeElement<MultiColor, Color>(
[
backgroundColors,
Theme.of(context)
.extension<CardThemeExtension>()
?.backgroundColors,
],
valueValidator: (multiColor) =>
multiColor != null && multiColor.isColor,
transform: (multiColor) => multiColor?.color,
defaultValue: Theme.of(context).cardColor,
),
border: ThemeHelper.getThemeElement<MultiColor, BoxBorder>(
[
borderColors,
Theme.of(context).extension<CardThemeExtension>()?.borderColors,
Theme.of(context)
.extension<CardThemeExtension>()
?.backgroundColors,
],
valueValidator: (multiColor) =>
multiColor != null && multiColor.isColor,
transform: (multiColor) {
if (multiColor!.isGradient) {
return GradientBoxBorder(
gradient: LinearGradientHelper.fromMultiColor(multiColor),
);
}
return Border.all(
color: multiColor.color,
);
},
defaultValue: null,
),
boxShadow: _shadow(context),
),
child: (widget.minSize != null && widget.maxSize != null)
child: (minSize != null && maxSize != null)
? ConstrainedBox(
constraints: BoxConstraints(
minWidth: widget.minSize!.width,
minHeight: widget.minSize!.height,
maxWidth: widget.maxSize!.width,
maxHeight: widget.maxSize!.height,
minWidth: minSize!.width,
minHeight: minSize!.height,
maxWidth: maxSize!.width,
maxHeight: maxSize!.height,
),
child: _buildChild(widget.child),
child: _buildChild(child),
)
: _buildChild(widget.child),
: _buildChild(child),
),
);
}

View File

@ -19,3 +19,4 @@ export './cards/cards.dart';
export './gradients/gradients.dart';
export './loader/loader.dart';
export './rich_text_builder/rich_text_builder.dart';
export './text_inputs/text_input.dart';

View File

@ -14,9 +14,10 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
class GradientBoxBorder extends BoxBorder {
class GradientBoxBorder extends Border {
const GradientBoxBorder({this.gradient, this.width = 1});
final Gradient? gradient;
@ -34,9 +35,6 @@ class GradientBoxBorder extends BoxBorder {
@override
bool get isUniform => true;
@override
ShapeBorder scale(double t) => this;
@override
void paint(
Canvas canvas,

View File

@ -45,16 +45,6 @@ class Loader extends LoaderComponent with $LoaderCWMixin {
LoaderStyle _resolve(BuildContext context) {
final LoaderThemeResolver resolver = themeResolver ??
LoaderThemeResolver(
computeExtensionValueFn: (
context,
defaultValue,
themeExtension, {
extra,
}) =>
LoaderStyle(
colors: themeExtension.colors,
stroke: themeExtension.stroke,
),
customStyleFn: (context, {extra}) => LoaderStyle(
colors: colors,
stroke: stroke,
@ -69,15 +59,14 @@ class Loader extends LoaderComponent with $LoaderCWMixin {
final style = _resolve(context);
final dimension =
(radius != null) ? radius! * 2 : context.buttonTheme.height;
return SizedBox.square(
dimension: dimension,
child: RepaintBoundary(
child: CustomPaint(
painter: _LoaderPainter(
style.colors!,
style.colors ?? const MultiColor([]),
dimension / 2,
style.stroke!,
style.stroke ?? 4,
flip: flip ?? false,
),
)

View File

@ -21,10 +21,15 @@ import 'package:wyatt_ui_kit/wyatt_ui_kit.dart';
class LoaderThemeResolver
extends ThemeResolver<LoaderStyle, LoaderThemeExtension, void> {
const LoaderThemeResolver({
required this.computeExtensionValueFn,
required this.customStyleFn,
});
@override
final LoaderStyle? Function(
BuildContext context, {
void extra,
}) customStyleFn;
/// Values taken from <https://api.flutter.dev/flutter/material/ElevatedButton/defaultStyleOf.html>
@override
LoaderStyle computeDefaultValue(
@ -41,13 +46,13 @@ class LoaderThemeResolver
);
@override
final LoaderStyle? Function(
LoaderStyle? computeExtensionValueFn(
BuildContext context,
LoaderStyle defaultValue,
LoaderThemeExtension themeExtension, {
void extra,
}) computeExtensionValueFn;
@override
final LoaderStyle? Function(BuildContext context, {void extra}) customStyleFn;
}) =>
LoaderStyle(
colors: themeExtension.colors,
stroke: themeExtension.stroke,
);
}

View File

@ -42,16 +42,6 @@ class RichTextBuilder extends RichTextBuilderComponent
RichTextBuilderStyle _resolve(BuildContext context) {
final RichTextBuilderThemeResolver resolver = themeResolver ??
RichTextBuilderThemeResolver(
computeExtensionValueFn: (
context,
defaultValue,
themeExtension, {
extra,
}) =>
RichTextBuilderStyle(
defaultStyle: themeExtension.defaultStyle,
styles: themeExtension.styles,
),
customStyleFn: (context, {extra}) => RichTextBuilderStyle(
defaultStyle: defaultStyle,
styles: styles,
@ -69,7 +59,7 @@ class RichTextBuilder extends RichTextBuilderComponent
text ?? '',
regex,
RichTextStyleParameter(
style.defaultStyle!,
style.defaultStyle,
style.styles ?? {},
null,
),

View File

@ -21,7 +21,6 @@ import 'package:wyatt_ui_kit/wyatt_ui_kit.dart';
class RichTextBuilderThemeResolver extends ThemeResolver<RichTextBuilderStyle,
RichTextBuilderThemeExtension, void> {
const RichTextBuilderThemeResolver({
required this.computeExtensionValueFn,
required this.customStyleFn,
});
@ -37,13 +36,18 @@ class RichTextBuilderThemeResolver extends ThemeResolver<RichTextBuilderStyle,
@override
final RichTextBuilderStyle? Function(
BuildContext context,
RichTextBuilderStyle defaultValue,
RichTextBuilderThemeExtension themeExtension, {
BuildContext context, {
void extra,
}) computeExtensionValueFn;
}) customStyleFn;
@override
final RichTextBuilderStyle? Function(BuildContext context, {void extra})
customStyleFn;
RichTextBuilderStyle? computeExtensionValueFn(
BuildContext context,
RichTextBuilderThemeExtension themeExtension, {
void extra,
}) =>
RichTextBuilderStyle(
defaultStyle: themeExtension.defaultStyle,
styles: themeExtension.styles,
);
}

View File

@ -0,0 +1,57 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
part 'text_input_state.dart';
class TextInputCubit extends Cubit<TextInputState> {
TextInputCubit() : super(const TextInputState.initial());
// ControlState logic
FutureOr<void> onFocus() async {
if (state.controlState != ControlState.disabled) {
emit(state.copyWith(controlState: ControlState.focused));
}
}
FutureOr<void> onUnfocus() async {
if (state.controlState != ControlState.disabled) {
emit(state.copyWith(controlState: ControlState.normal));
}
}
Future<void> disable() async {
emit(state.copyWith(controlState: ControlState.disabled));
}
Future<void> enable() async {
emit(state.copyWith(controlState: ControlState.normal));
}
// StatusState logic
FutureOr<void> onInvalid(String? error) async {
emit(state.copyWith(statusState: StatusState.error, statusMessage: error));
}
FutureOr<void> onSuccess() async {
emit(state.copyWith(statusState: StatusState.initial, statusMessage: ''));
}
}

View File

@ -0,0 +1,49 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'text_input_cubit.dart';
class TextInputState extends Equatable {
const TextInputState({
required this.controlState,
required this.statusState,
this.statusMessage,
});
const TextInputState.initial()
: controlState = ControlState.normal,
statusState = StatusState.initial,
statusMessage = null;
final ControlState controlState;
final StatusState statusState;
final String? statusMessage;
@override
List<Object?> get props => [controlState, statusState, statusMessage];
TextInputState copyWith({
ControlState? controlState,
StatusState? statusState,
String? statusMessage,
}) =>
TextInputState(
controlState: controlState ?? this.controlState,
statusState: statusState ?? this.statusState,
statusMessage: statusMessage ?? this.statusMessage,
);
}

View File

@ -0,0 +1,168 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// super program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// super program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with super program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:ui';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:wyatt_component_copy_with_extension/component_copy_with_extension.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
import 'package:wyatt_ui_kit/src/components/text_inputs/text_input_screen.dart';
part 'text_input.g.dart';
@ComponentCopyWithExtension()
class TextInput extends TextInputComponent with $TextInputCWMixin {
TextInput({
super.expand,
super.validator,
super.key,
super.prefixIcon,
super.prefixText,
super.suffixIcon,
super.suffixText,
super.label,
super.onError,
super.hint,
super.normalStyle,
super.focusedStyle,
super.errorStyle,
super.disableStyle,
super.controller,
super.focusNode,
super.keyboardType,
super.smartDashesType,
super.smartQuotesType,
super.enableInteractiveSelection,
super.textInputAction,
super.textCapitalization,
super.style,
super.strutStyle,
super.textAlign,
super.textAlignVertical,
super.textDirection,
super.readOnly,
super.showCursor,
super.autofocus,
super.obscuringCharacter,
super.obscureText,
super.autocorrect,
super.enableSuggestions,
super.maxLines,
super.minLines,
super.expands,
super.maxLength,
super.maxLengthEnforcement,
super.onChanged,
super.onEditingComplete,
super.onSubmitted,
super.onAppPrivateCommand,
super.inputFormatters,
super.enabled,
super.cursorWidth,
super.cursorHeight,
super.cursorRadius,
super.cursorColor,
super.selectionHeightStyle,
super.selectionWidthStyle,
super.keyboardAppearance,
super.scrollPadding,
super.dragStartBehavior,
super.selectionControls,
super.onTap,
super.onTapOutside,
super.mouseCursor,
super.scrollController,
super.scrollPhysics,
super.autofillHints,
super.clipBehavior,
super.restorationId,
super.scribbleEnabled,
super.enableIMEPersonalizedLearning,
super.contextMenuBuilder,
super.spellCheckConfiguration,
super.magnifierConfiguration,
});
@override
Widget build(BuildContext context) => TextInputScreen(
expand: expand,
validator: validator,
label: label,
onError: onError,
hint: hint,
focusedStyle: focusedStyle,
normalStyle: normalStyle,
errorStyle: errorStyle,
disableStyle: disableStyle,
prefixIcon: prefixIcon,
prefixText: prefixText,
suffixIcon: suffixIcon,
magnifierConfiguration: magnifierConfiguration,
controller: controller,
focusNode: focusNode,
keyboardType: keyboardType,
textInputAction: textInputAction,
textCapitalization: textCapitalization,
style: style,
strutStyle: strutStyle,
textAlign: textAlign,
textAlignVertical: textAlignVertical,
textDirection: textDirection,
autofocus: autofocus,
obscuringCharacter: obscuringCharacter,
obscureText: obscureText,
autocorrect: autocorrect,
smartDashesType: smartDashesType,
smartQuotesType: smartQuotesType,
enableSuggestions: enableSuggestions,
maxLines: maxLines,
minLines: minLines,
expands: expands,
readOnly: readOnly,
showCursor: showCursor,
maxLength: maxLength,
maxLengthEnforcement: maxLengthEnforcement,
onChanged: onChanged,
onEditingComplete: onEditingComplete,
onSubmitted: onSubmitted,
onAppPrivateCommand: onAppPrivateCommand,
inputFormatters: inputFormatters,
enabled: enabled,
cursorWidth: cursorWidth,
cursorHeight: cursorHeight,
cursorRadius: cursorRadius,
cursorColor: cursorColor,
selectionHeightStyle: selectionHeightStyle,
selectionWidthStyle: selectionWidthStyle,
keyboardAppearance: keyboardAppearance,
scrollPadding: scrollPadding,
enableInteractiveSelection: enableInteractiveSelection,
selectionControls: selectionControls,
dragStartBehavior: dragStartBehavior,
onTap: onTap,
onTapOutside: onTapOutside,
mouseCursor: mouseCursor,
scrollPhysics: scrollPhysics,
scrollController: scrollController,
autofillHints: autofillHints,
clipBehavior: clipBehavior,
restorationId: restorationId,
scribbleEnabled: scribbleEnabled,
enableIMEPersonalizedLearning: enableIMEPersonalizedLearning,
contextMenuBuilder: contextMenuBuilder,
spellCheckConfiguration: spellCheckConfiguration,
);
}

View File

@ -0,0 +1,348 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'text_input.dart';
// **************************************************************************
// ComponentCopyWithGenerator
// **************************************************************************
class $TextInputCWProxyImpl implements $TextInputComponentCWProxy {
const $TextInputCWProxyImpl(this._value);
final TextInput _value;
@override
TextInput expand(bool? expand) => this(expand: expand);
@override
TextInput onError(String Function(String)? onError) => this(onError: onError);
@override
TextInput validator(bool Function(String)? validator) =>
this(validator: validator);
@override
TextInput suffixText(TextWrapper? suffixText) => this(suffixText: suffixText);
@override
TextInput prefixText(TextWrapper? prefixText) => this(prefixText: prefixText);
@override
TextInput prefixIcon(Icon? prefixIcon) => this(prefixIcon: prefixIcon);
@override
TextInput suffixIcon(Icon? suffixIcon) => this(suffixIcon: suffixIcon);
@override
TextInput label(TextWrapper? label) => this(label: label);
@override
TextInput hint(TextWrapper? hint) => this(hint: hint);
@override
TextInput normalStyle(TextInputStyle? normalStyle) =>
this(normalStyle: normalStyle);
@override
TextInput focusedStyle(TextInputStyle? focusedStyle) =>
this(focusedStyle: focusedStyle);
@override
TextInput errorStyle(TextInputStyle? errorStyle) =>
this(errorStyle: errorStyle);
@override
TextInput disableStyle(TextInputStyle? disableStyle) =>
this(disableStyle: disableStyle);
@override
TextInput controller(TextEditingController? controller) =>
this(controller: controller);
@override
TextInput focusNode(FocusNode? focusNode) => this(focusNode: focusNode);
@override
TextInput keyboardType(TextInputType? keyboardType) =>
this(keyboardType: keyboardType);
@override
TextInput smartDashesType(SmartDashesType? smartDashesType) =>
this(smartDashesType: smartDashesType);
@override
TextInput smartQuotesType(SmartQuotesType? smartQuotesType) =>
this(smartQuotesType: smartQuotesType);
@override
TextInput enableInteractiveSelection(bool? enableInteractiveSelection) =>
this(enableInteractiveSelection: enableInteractiveSelection);
@override
TextInput textInputAction(TextInputAction? textInputAction) =>
this(textInputAction: textInputAction);
@override
TextInput textCapitalization(TextCapitalization? textCapitalization) =>
this(textCapitalization: textCapitalization);
@override
TextInput style(TextStyle? style) => this(style: style);
@override
TextInput strutStyle(StrutStyle? strutStyle) => this(strutStyle: strutStyle);
@override
TextInput textAlign(TextAlign? textAlign) => this(textAlign: textAlign);
@override
TextInput textAlignVertical(TextAlignVertical? textAlignVertical) =>
this(textAlignVertical: textAlignVertical);
@override
TextInput textDirection(TextDirection? textDirection) =>
this(textDirection: textDirection);
@override
TextInput readOnly(bool? readOnly) => this(readOnly: readOnly);
@override
TextInput showCursor(bool? showCursor) => this(showCursor: showCursor);
@override
TextInput autofocus(bool? autofocus) => this(autofocus: autofocus);
@override
TextInput obscuringCharacter(String? obscuringCharacter) =>
this(obscuringCharacter: obscuringCharacter);
@override
TextInput obscureText(bool? obscureText) => this(obscureText: obscureText);
@override
TextInput autocorrect(bool? autocorrect) => this(autocorrect: autocorrect);
@override
TextInput enableSuggestions(bool? enableSuggestions) =>
this(enableSuggestions: enableSuggestions);
@override
TextInput maxLines(int? maxLines) => this(maxLines: maxLines);
@override
TextInput minLines(int? minLines) => this(minLines: minLines);
@override
TextInput expands(bool? expands) => this(expands: expands);
@override
TextInput maxLength(int? maxLength) => this(maxLength: maxLength);
@override
TextInput maxLengthEnforcement(MaxLengthEnforcement? maxLengthEnforcement) =>
this(maxLengthEnforcement: maxLengthEnforcement);
@override
TextInput onChanged(void Function(String)? onChanged) =>
this(onChanged: onChanged);
@override
TextInput onEditingComplete(void Function()? onEditingComplete) =>
this(onEditingComplete: onEditingComplete);
@override
TextInput onSubmitted(void Function(String)? onSubmitted) =>
this(onSubmitted: onSubmitted);
@override
TextInput onAppPrivateCommand(
void Function(String, Map<String, dynamic>)? onAppPrivateCommand) =>
this(onAppPrivateCommand: onAppPrivateCommand);
@override
TextInput inputFormatters(List<TextInputFormatter>? inputFormatters) =>
this(inputFormatters: inputFormatters);
@override
TextInput enabled(ValueNotifier<bool>? enabled) => this(enabled: enabled);
@override
TextInput cursorWidth(double? cursorWidth) => this(cursorWidth: cursorWidth);
@override
TextInput cursorHeight(double? cursorHeight) =>
this(cursorHeight: cursorHeight);
@override
TextInput cursorRadius(Radius? cursorRadius) =>
this(cursorRadius: cursorRadius);
@override
TextInput cursorColor(Color? cursorColor) => this(cursorColor: cursorColor);
@override
TextInput selectionHeightStyle(BoxHeightStyle? selectionHeightStyle) =>
this(selectionHeightStyle: selectionHeightStyle);
@override
TextInput selectionWidthStyle(BoxWidthStyle? selectionWidthStyle) =>
this(selectionWidthStyle: selectionWidthStyle);
@override
TextInput keyboardAppearance(Brightness? keyboardAppearance) =>
this(keyboardAppearance: keyboardAppearance);
@override
TextInput scrollPadding(EdgeInsets? scrollPadding) =>
this(scrollPadding: scrollPadding);
@override
TextInput dragStartBehavior(DragStartBehavior? dragStartBehavior) =>
this(dragStartBehavior: dragStartBehavior);
@override
TextInput selectionControls(TextSelectionControls? selectionControls) =>
this(selectionControls: selectionControls);
@override
TextInput onTap(void Function()? onTap) => this(onTap: onTap);
@override
TextInput onTapOutside(void Function(PointerDownEvent)? onTapOutside) =>
this(onTapOutside: onTapOutside);
@override
TextInput mouseCursor(MouseCursor? mouseCursor) =>
this(mouseCursor: mouseCursor);
@override
TextInput scrollController(ScrollController? scrollController) =>
this(scrollController: scrollController);
@override
TextInput scrollPhysics(ScrollPhysics? scrollPhysics) =>
this(scrollPhysics: scrollPhysics);
@override
TextInput autofillHints(Iterable<String>? autofillHints) =>
this(autofillHints: autofillHints);
@override
TextInput clipBehavior(Clip? clipBehavior) =>
this(clipBehavior: clipBehavior);
@override
TextInput restorationId(String? restorationId) =>
this(restorationId: restorationId);
@override
TextInput scribbleEnabled(bool? scribbleEnabled) =>
this(scribbleEnabled: scribbleEnabled);
@override
TextInput enableIMEPersonalizedLearning(
bool? enableIMEPersonalizedLearning) =>
this(enableIMEPersonalizedLearning: enableIMEPersonalizedLearning);
@override
TextInput contextMenuBuilder(
Widget Function(BuildContext, EditableTextState)?
contextMenuBuilder) =>
this(contextMenuBuilder: contextMenuBuilder);
@override
TextInput spellCheckConfiguration(
SpellCheckConfiguration? spellCheckConfiguration) =>
this(spellCheckConfiguration: spellCheckConfiguration);
@override
TextInput magnifierConfiguration(
TextMagnifierConfiguration? magnifierConfiguration) =>
this(magnifierConfiguration: magnifierConfiguration);
@override
TextInput key(Key? key) => this(key: key);
@override
TextInput call({
bool? expand,
String Function(String)? onError,
bool Function(String)? validator,
TextWrapper? suffixText,
TextWrapper? prefixText,
Icon? prefixIcon,
Icon? suffixIcon,
TextWrapper? label,
TextWrapper? hint,
TextInputStyle? normalStyle,
TextInputStyle? focusedStyle,
TextInputStyle? errorStyle,
TextInputStyle? disableStyle,
TextEditingController? controller,
FocusNode? focusNode,
TextInputType? keyboardType,
SmartDashesType? smartDashesType,
SmartQuotesType? smartQuotesType,
bool? enableInteractiveSelection,
TextInputAction? textInputAction,
TextCapitalization? textCapitalization,
TextStyle? style,
StrutStyle? strutStyle,
TextAlign? textAlign,
TextAlignVertical? textAlignVertical,
TextDirection? textDirection,
bool? readOnly,
bool? showCursor,
bool? autofocus,
String? obscuringCharacter,
bool? obscureText,
bool? autocorrect,
bool? enableSuggestions,
int? maxLines,
int? minLines,
bool? expands,
int? maxLength,
MaxLengthEnforcement? maxLengthEnforcement,
void Function(String)? onChanged,
void Function()? onEditingComplete,
void Function(String)? onSubmitted,
void Function(String, Map<String, dynamic>)? onAppPrivateCommand,
List<TextInputFormatter>? inputFormatters,
ValueNotifier<bool>? enabled,
double? cursorWidth,
double? cursorHeight,
Radius? cursorRadius,
Color? cursorColor,
BoxHeightStyle? selectionHeightStyle,
BoxWidthStyle? selectionWidthStyle,
Brightness? keyboardAppearance,
EdgeInsets? scrollPadding,
DragStartBehavior? dragStartBehavior,
TextSelectionControls? selectionControls,
void Function()? onTap,
void Function(PointerDownEvent)? onTapOutside,
MouseCursor? mouseCursor,
ScrollController? scrollController,
ScrollPhysics? scrollPhysics,
Iterable<String>? autofillHints,
Clip? clipBehavior,
String? restorationId,
bool? scribbleEnabled,
bool? enableIMEPersonalizedLearning,
Widget Function(BuildContext, EditableTextState)? contextMenuBuilder,
SpellCheckConfiguration? spellCheckConfiguration,
TextMagnifierConfiguration? magnifierConfiguration,
Key? key,
}) =>
TextInput(
expand: expand ?? _value.expand,
validator: validator ?? _value.validator,
key: key ?? _value.key,
prefixIcon: prefixIcon ?? _value.prefixIcon,
prefixText: prefixText ?? _value.prefixText,
suffixIcon: suffixIcon ?? _value.suffixIcon,
suffixText: suffixText ?? _value.suffixText,
label: label ?? _value.label,
onError: onError ?? _value.onError,
hint: hint ?? _value.hint,
normalStyle: normalStyle ?? _value.normalStyle,
focusedStyle: focusedStyle ?? _value.focusedStyle,
errorStyle: errorStyle ?? _value.errorStyle,
disableStyle: disableStyle ?? _value.disableStyle,
controller: controller ?? _value.controller,
focusNode: focusNode ?? _value.focusNode,
keyboardType: keyboardType ?? _value.keyboardType,
smartDashesType: smartDashesType ?? _value.smartDashesType,
smartQuotesType: smartQuotesType ?? _value.smartQuotesType,
enableInteractiveSelection:
enableInteractiveSelection ?? _value.enableInteractiveSelection,
textInputAction: textInputAction ?? _value.textInputAction,
textCapitalization: textCapitalization ?? _value.textCapitalization,
style: style ?? _value.style,
strutStyle: strutStyle ?? _value.strutStyle,
textAlign: textAlign ?? _value.textAlign,
textAlignVertical: textAlignVertical ?? _value.textAlignVertical,
textDirection: textDirection ?? _value.textDirection,
readOnly: readOnly ?? _value.readOnly,
showCursor: showCursor ?? _value.showCursor,
autofocus: autofocus ?? _value.autofocus,
obscuringCharacter: obscuringCharacter ?? _value.obscuringCharacter,
obscureText: obscureText ?? _value.obscureText,
autocorrect: autocorrect ?? _value.autocorrect,
enableSuggestions: enableSuggestions ?? _value.enableSuggestions,
maxLines: maxLines ?? _value.maxLines,
minLines: minLines ?? _value.minLines,
expands: expands ?? _value.expands,
maxLength: maxLength ?? _value.maxLength,
maxLengthEnforcement:
maxLengthEnforcement ?? _value.maxLengthEnforcement,
onChanged: onChanged ?? _value.onChanged,
onEditingComplete: onEditingComplete ?? _value.onEditingComplete,
onSubmitted: onSubmitted ?? _value.onSubmitted,
onAppPrivateCommand: onAppPrivateCommand ?? _value.onAppPrivateCommand,
inputFormatters: inputFormatters ?? _value.inputFormatters,
enabled: enabled ?? _value.enabled,
cursorWidth: cursorWidth ?? _value.cursorWidth,
cursorHeight: cursorHeight ?? _value.cursorHeight,
cursorRadius: cursorRadius ?? _value.cursorRadius,
cursorColor: cursorColor ?? _value.cursorColor,
selectionHeightStyle:
selectionHeightStyle ?? _value.selectionHeightStyle,
selectionWidthStyle: selectionWidthStyle ?? _value.selectionWidthStyle,
keyboardAppearance: keyboardAppearance ?? _value.keyboardAppearance,
scrollPadding: scrollPadding ?? _value.scrollPadding,
dragStartBehavior: dragStartBehavior ?? _value.dragStartBehavior,
selectionControls: selectionControls ?? _value.selectionControls,
onTap: onTap ?? _value.onTap,
onTapOutside: onTapOutside ?? _value.onTapOutside,
mouseCursor: mouseCursor ?? _value.mouseCursor,
scrollController: scrollController ?? _value.scrollController,
scrollPhysics: scrollPhysics ?? _value.scrollPhysics,
autofillHints: autofillHints ?? _value.autofillHints,
clipBehavior: clipBehavior ?? _value.clipBehavior,
restorationId: restorationId ?? _value.restorationId,
scribbleEnabled: scribbleEnabled ?? _value.scribbleEnabled,
enableIMEPersonalizedLearning: enableIMEPersonalizedLearning ??
_value.enableIMEPersonalizedLearning,
contextMenuBuilder: contextMenuBuilder ?? _value.contextMenuBuilder,
spellCheckConfiguration:
spellCheckConfiguration ?? _value.spellCheckConfiguration,
magnifierConfiguration:
magnifierConfiguration ?? _value.magnifierConfiguration,
);
}
mixin $TextInputCWMixin on Component {
$TextInputComponentCWProxy get copyWith =>
$TextInputCWProxyImpl(this as TextInput);
}

View File

@ -0,0 +1,383 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:ui';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
import 'package:wyatt_ui_kit/src/components/text_inputs/cubit/text_input_cubit.dart';
import 'package:wyatt_ui_kit/src/components/text_inputs/text_input_theme_resolver.dart';
import 'package:wyatt_ui_kit/src/components/text_inputs/widgets/label_widget.dart';
import 'package:wyatt_ui_kit/src/components/text_inputs/widgets/text_input_wrapper.dart';
class TextInputScreen extends CubitScreen<TextInputCubit, TextInputState> {
TextInputScreen({
this.expand,
this.onError,
this.validator,
super.key,
this.suffixText,
this.prefixText,
this.prefixIcon,
this.suffixIcon,
this.label,
this.hint,
this.normalStyle,
this.focusedStyle,
this.errorStyle,
this.disableStyle,
this.magnifierConfiguration,
this.controller,
this.focusNode,
this.keyboardType,
this.textInputAction,
this.textCapitalization,
this.style,
this.strutStyle,
this.textAlign,
this.textAlignVertical,
this.textDirection,
this.autofocus,
this.obscuringCharacter,
this.obscureText,
this.autocorrect,
this.smartDashesType,
this.smartQuotesType,
this.enableSuggestions,
this.maxLines,
this.minLines,
this.expands,
this.readOnly,
this.showCursor,
this.maxLength,
this.maxLengthEnforcement,
this.onChanged,
this.onEditingComplete,
this.onSubmitted,
this.onAppPrivateCommand,
this.inputFormatters,
this.enabled,
this.cursorWidth,
this.cursorHeight,
this.cursorRadius,
this.cursorColor,
this.selectionHeightStyle,
this.selectionWidthStyle,
this.keyboardAppearance,
this.scrollPadding,
this.enableInteractiveSelection,
this.selectionControls,
this.dragStartBehavior,
this.onTap,
this.onTapOutside,
this.mouseCursor,
this.scrollPhysics,
this.scrollController,
this.autofillHints,
this.clipBehavior,
this.restorationId,
this.scribbleEnabled,
this.enableIMEPersonalizedLearning,
this.contextMenuBuilder,
this.spellCheckConfiguration,
});
final TextMagnifierConfiguration? magnifierConfiguration;
final TextEditingController? controller;
final FocusNode? focusNode;
final TextInputType? keyboardType;
final TextInputAction? textInputAction;
final TextCapitalization? textCapitalization;
final TextStyle? style;
final StrutStyle? strutStyle;
final TextAlign? textAlign;
final TextAlignVertical? textAlignVertical;
final TextDirection? textDirection;
final bool? autofocus;
final String? obscuringCharacter;
final bool? obscureText;
final bool? autocorrect;
final SmartDashesType? smartDashesType;
final SmartQuotesType? smartQuotesType;
final bool? enableSuggestions;
final int? maxLines;
final int? minLines;
final bool? expands;
final bool? readOnly;
final bool? showCursor;
final int? maxLength;
final MaxLengthEnforcement? maxLengthEnforcement;
final ValueChanged<String>? onChanged;
final VoidCallback? onEditingComplete;
final ValueChanged<String>? onSubmitted;
final AppPrivateCommandCallback? onAppPrivateCommand;
final List<TextInputFormatter>? inputFormatters;
final double? cursorWidth;
final double? cursorHeight;
final Radius? cursorRadius;
final Color? cursorColor;
final BoxHeightStyle? selectionHeightStyle;
final BoxWidthStyle? selectionWidthStyle;
final Brightness? keyboardAppearance;
final EdgeInsets? scrollPadding;
final bool? enableInteractiveSelection;
final TextSelectionControls? selectionControls;
final DragStartBehavior? dragStartBehavior;
final GestureTapCallback? onTap;
final TapRegionCallback? onTapOutside;
final MouseCursor? mouseCursor;
final ScrollPhysics? scrollPhysics;
final ScrollController? scrollController;
final Iterable<String>? autofillHints;
final Clip? clipBehavior;
final String? restorationId;
final bool? scribbleEnabled;
final bool? enableIMEPersonalizedLearning;
final EditableTextContextMenuBuilder? contextMenuBuilder;
final SpellCheckConfiguration? spellCheckConfiguration;
final bool Function(String)? validator;
final String? Function(String)? onError;
final TextInputStyle? normalStyle;
final TextInputStyle? focusedStyle;
final TextInputStyle? errorStyle;
final TextInputStyle? disableStyle;
final TextWrapper? label;
final TextWrapper? hint;
final ValueNotifier<bool>? enabled;
final bool? expand;
final TextWrapper? prefixText;
final Icon? prefixIcon;
final Icon? suffixIcon;
final TextWrapper? suffixText;
@override
TextInputCubit create(BuildContext context) => TextInputCubit();
@override
TextInputCubit init(BuildContext context, TextInputCubit bloc) {
enabled?.addListener(() {
if (enabled?.value ?? false) {
bloc.enable();
} else {
bloc.disable();
}
});
return bloc;
}
final _focusNode = FocusNode();
final _controller = TextEditingController();
final _notOutilinedBorder = const OutlineInputBorder(
borderSide: BorderSide(color: Colors.transparent),
);
TextInputStyle _resolve(BuildContext context, TextInputState state) {
final resolver = TextInputThemeResolver(
customStyleFn: (context, {extra}) {
TextInputStyle? textInputStyle;
switch (extra?.controlState) {
case ControlState.focused:
textInputStyle = focusedStyle;
break;
case ControlState.disabled:
textInputStyle = disableStyle;
break;
case ControlState.normal:
textInputStyle = normalStyle;
break;
case ControlState.hovered:
case ControlState.tapped:
case null:
break;
}
TextInputStyle? style;
switch (extra?.statusState) {
case StatusState.error:
style = errorStyle;
break;
case StatusState.initial:
case StatusState.success:
case StatusState.loading:
case null:
break;
}
return TextInputStyle.merge(textInputStyle, style);
},
);
return resolver.negotiate(context, extra: state);
}
bool _wrapperExpanded(TextInputState state) {
final fn = focusNode ?? _focusNode;
final tec = controller ?? _controller;
if (fn.hasFocus && label != null) {
return true;
} else if (tec.value.text.isNotEmpty && label != null) {
return true;
} else if (state.statusState == StatusState.error &&
(state.statusMessage?.isNotEmpty ?? false)) {
return true;
}
return false;
}
@override
Widget onBuild(BuildContext context, TextInputState state) {
final style = _resolve(context, state);
return Focus(
onFocusChange: (hasFocus) {
if (hasFocus) {
bloc(context).onFocus();
} else {
bloc(context).onUnfocus();
}
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 600),
decoration: BoxDecoration(
boxShadow: style.boxShadow != null ? [style.boxShadow!] : null,
gradient: style.backgroundColors?.isGradient ?? false
? LinearGradient(colors: style.backgroundColors!.colors)
: null,
color: style.backgroundColors?.isColor ?? false
? style.backgroundColors?.color
: null,
borderRadius: style.radius ?? BorderRadius.circular(4),
border: (style.borderColors != null)
? Border.all(
width: 1.5,
color: style.borderColors!,
)
: null,
),
child: TextInputWrapper(
expand: expand ?? true,
expanded: _wrapperExpanded(state),
child: TextField(
onTap: onTap,
onChanged: (value) {
onChanged?.call(value);
if (validator?.call(value) ?? false) {
bloc(context).onInvalid(onError?.call(value));
} else {
bloc(context).onSuccess();
}
},
onTapOutside: onTapOutside,
controller: controller ?? _controller,
focusNode: focusNode ?? _focusNode,
textAlignVertical: textAlignVertical ?? TextAlignVertical.top,
style: style.inputStyle ?? this.style,
decoration: InputDecoration(
focusedErrorBorder: _notOutilinedBorder,
focusedBorder: _notOutilinedBorder,
errorBorder: _notOutilinedBorder,
disabledBorder: _notOutilinedBorder,
enabledBorder: _notOutilinedBorder,
border: _notOutilinedBorder,
isDense: true,
iconColor: style.iconColor,
alignLabelWithHint: true,
label: (state.statusState == StatusState.error &&
(state.statusMessage?.isNotEmpty ?? false)) ||
label != null
? LabelWidget(
focusNode: focusNode ?? _focusNode,
label: (state.statusState == StatusState.error &&
(state.statusMessage?.isNotEmpty ?? false))
? state.statusMessage?.wrap()
: label,
labelStyle: style.labelStyle,
)
: null,
hintText: hint?.text,
hintStyle: hint?.style,
prefixIcon: prefixIcon,
prefixText: prefixText?.text,
prefixStyle: prefixText?.style,
prefixIconColor: style.prefixIconColor,
suffixIcon: suffixIcon,
suffixText: suffixText?.text,
suffixStyle: suffixText?.style,
suffixIconColor: style.suffixIconColor,
enabled: state.controlState != ControlState.disabled,
),
keyboardType: keyboardType,
smartDashesType: smartDashesType,
smartQuotesType: smartQuotesType,
enableInteractiveSelection: enableInteractiveSelection,
textInputAction: textInputAction,
textCapitalization: textCapitalization ?? TextCapitalization.none,
strutStyle: strutStyle,
textAlign: textAlign ?? TextAlign.start,
textDirection: textDirection,
readOnly: readOnly ?? false,
showCursor: showCursor,
autofocus: autofocus ?? false,
obscuringCharacter: obscuringCharacter ?? '*',
obscureText: obscureText ?? false,
autocorrect: autocorrect ?? true,
enableSuggestions: enableSuggestions ?? true,
maxLines: maxLines ?? 1,
minLines: minLines,
expands: expands ?? false,
maxLength: maxLength,
maxLengthEnforcement: maxLengthEnforcement,
onEditingComplete: onEditingComplete,
onSubmitted: onSubmitted,
onAppPrivateCommand: onAppPrivateCommand,
inputFormatters: inputFormatters,
enabled: state.controlState != ControlState.disabled,
cursorWidth: cursorWidth ?? 2.0,
cursorHeight: cursorHeight,
cursorRadius: cursorRadius,
cursorColor: cursorColor,
selectionHeightStyle: selectionHeightStyle ?? BoxHeightStyle.tight,
selectionWidthStyle: selectionWidthStyle ?? BoxWidthStyle.tight,
keyboardAppearance: keyboardAppearance,
scrollPadding: scrollPadding ?? const EdgeInsets.all(20),
dragStartBehavior: dragStartBehavior ?? DragStartBehavior.start,
selectionControls: selectionControls,
mouseCursor: mouseCursor,
scrollController: scrollController,
scrollPhysics: scrollPhysics,
autofillHints: autofillHints,
clipBehavior: clipBehavior ?? Clip.hardEdge,
restorationId: restorationId,
scribbleEnabled: scribbleEnabled ?? true,
enableIMEPersonalizedLearning:
enableIMEPersonalizedLearning ?? true,
contextMenuBuilder: contextMenuBuilder,
spellCheckConfiguration: spellCheckConfiguration,
magnifierConfiguration: magnifierConfiguration,
),
),
),
);
}
}

View File

@ -0,0 +1,155 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
import 'package:wyatt_ui_kit/src/components/text_inputs/cubit/text_input_cubit.dart';
import 'package:wyatt_ui_kit/src/core/core.dart';
import 'package:wyatt_ui_kit/src/domain/text_input_theme_extension.dart';
class TextInputThemeResolver extends ThemeResolver<TextInputStyle,
TextInputThemeExtension, TextInputState> {
const TextInputThemeResolver({
required this.customStyleFn,
});
@override
final TextInputStyle? Function(
BuildContext context, {
TextInputState? extra,
}) customStyleFn;
@override
TextInputStyle computeDefaultValue(
BuildContext context, {
TextInputState? extra,
}) {
TextStyle? labelStyle = context.textTheme.labelLarge
?.copyWith(color: Theme.of(context).unselectedWidgetColor);
TextStyle? hintStyle = context.textTheme.labelLarge;
TextStyle? prefixStyle = context.textTheme.bodyMedium;
TextStyle? suffixStyle = context.textTheme.bodyMedium;
TextStyle? inputStyle = context.textTheme.bodyMedium;
Color? iconColor = context.colorScheme.inversePrimary;
Color? prefixIconColor = Theme.of(context).unselectedWidgetColor;
Color? suffixIconColor = Theme.of(context).unselectedWidgetColor;
Color? borderColors = Theme.of(context).unselectedWidgetColor;
MultiColor? backgroundColors;
BoxShadow? boxShadow;
final BorderRadiusGeometry radius = BorderRadius.circular(4);
switch (extra?.controlState) {
case ControlState.disabled:
labelStyle =
labelStyle?.copyWith(color: Theme.of(context).disabledColor);
hintStyle = hintStyle?.copyWith(color: Theme.of(context).disabledColor);
prefixStyle =
prefixStyle?.copyWith(color: Theme.of(context).disabledColor);
suffixStyle =
suffixStyle?.copyWith(color: Theme.of(context).disabledColor);
inputStyle =
inputStyle?.copyWith(color: Theme.of(context).disabledColor);
borderColors = Theme.of(context).disabledColor;
prefixIconColor = Theme.of(context).disabledColor;
suffixIconColor = Theme.of(context).disabledColor;
break;
case ControlState.focused:
prefixIconColor = context.colorScheme.primary;
suffixIconColor = context.colorScheme.primary;
iconColor = context.colorScheme.primary;
borderColors = context.colorScheme.primary;
labelStyle = labelStyle?.copyWith(color: context.colorScheme.primary);
break;
case ControlState.hovered:
case ControlState.tapped:
case ControlState.normal:
case null:
break;
}
switch (extra?.statusState) {
case StatusState.error:
labelStyle = context.textTheme.labelLarge
?.copyWith(color: context.colorScheme.error);
borderColors = context.colorScheme.error;
break;
case StatusState.initial:
case StatusState.success:
case StatusState.loading:
case null:
break;
}
return TextInputStyle(
labelStyle: labelStyle,
hintStyle: hintStyle,
iconColor: iconColor,
prefixIconColor: prefixIconColor,
prefixStyle: prefixStyle,
suffixStyle: suffixStyle,
suffixIconColor: suffixIconColor,
backgroundColors: backgroundColors,
borderColors: borderColors,
boxShadow: boxShadow,
radius: radius,
inputStyle: inputStyle,
);
}
@override
TextInputStyle? computeExtensionValueFn(
BuildContext context,
TextInputThemeExtension themeExtension, {
TextInputState? extra,
}) {
TextInputStyle? textInputStyle;
switch (extra?.controlState) {
case ControlState.focused:
textInputStyle = themeExtension.focusedStyle;
break;
case ControlState.disabled:
textInputStyle = themeExtension.disableStyle;
break;
case ControlState.normal:
textInputStyle = themeExtension.normalStyle;
break;
case ControlState.hovered:
case ControlState.tapped:
case null:
break;
}
TextInputStyle? style;
switch (extra?.statusState) {
case StatusState.error:
style = themeExtension.errorStyle;
break;
case StatusState.initial:
case StatusState.success:
case StatusState.loading:
case null:
break;
}
return TextInputStyle.merge(textInputStyle, style);
}
}

View File

@ -0,0 +1,37 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// super program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// super program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with super program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
class LabelWidget extends StatelessWidget {
const LabelWidget({
required this.label,
required this.focusNode,
required this.labelStyle,
super.key,
});
final TextWrapper? label;
final FocusNode focusNode;
final TextStyle? labelStyle;
@override
Widget build(BuildContext context) => Text(
label?.text ?? '',
style: labelStyle,
);
}

View File

@ -0,0 +1,60 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// super program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// super program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with super program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
const _defaultPadding = 7.5;
const _paddingAnimationDuration = Duration(milliseconds: 200);
class TextInputWrapper extends StatelessWidget {
const TextInputWrapper({
required this.expand,
required this.expanded,
required this.child,
super.key,
});
final bool expand;
final bool expanded;
final Widget child;
double _top() {
if (expanded) {
return 2 * _defaultPadding;
} else if (!expand) {
return _defaultPadding;
}
return 0;
}
double _bottom() {
if (expanded) {
return 0;
} else if (!expand) {
return _defaultPadding;
}
return 0;
}
@override
Widget build(BuildContext context) => AnimatedPadding(
duration: _paddingAnimationDuration,
padding: EdgeInsets.only(
top: _top(),
bottom: _bottom(),
),
child: child,
);
}

View File

@ -21,4 +21,6 @@ extension BuildContextThemeExtension on BuildContext {
TextTheme get textTheme => Theme.of(this).textTheme;
ColorScheme get colorScheme => Theme.of(this).colorScheme;
ButtonThemeData get buttonTheme => Theme.of(this).buttonTheme;
InputDecorationTheme get inputDecorationTheme =>
Theme.of(this).inputDecorationTheme;
}

View File

@ -15,3 +15,4 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
export './linear_gradient_helper.dart';
export './theme_helper.dart';

View File

@ -0,0 +1,41 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// super program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// super program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with super program. If not, see <https://www.gnu.org/licenses/>.
/// A helper class for getting theme elements.
abstract class ThemeHelper {
/// Gets a theme element from a list of styles.
/// Styles are checked in order, and the first one that passes the
/// [valueValidator] is returned.
/// If no style passes the [valueValidator], the [defaultValue] is returned.
/// If [styles] is null or empty, the [defaultValue] is returned.
/// Style elements are transformed using the [transform] function.
static T? getThemeElement<P, T>(
List<P?>? styles, {
required T? Function(P?)? transform,
required T? defaultValue,
bool? Function(P?)? valueValidator,
}) {
if (styles?.isNotEmpty ?? false) {
for (final element in styles!) {
if (valueValidator?.call(element) ?? false) {
return transform?.call(element);
}
}
}
return defaultValue;
}
}

View File

@ -18,3 +18,4 @@ export './button_theme_extension/button_theme_extension.dart';
export './card_theme_extension.dart';
export './loader_theme_extension.dart';
export './rich_text_builder_theme_extension.dart';
export './text_input_theme_extension.dart';

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/material.dart';
import 'package:wyatt_ui_components/wyatt_wyatt_ui_components.dart';
abstract class TextInputThemeExtension
extends ThemeExtension<TextInputThemeExtension> {
const TextInputThemeExtension({
this.normalStyle,
this.focusedStyle,
this.errorStyle,
this.disableStyle,
});
final TextInputStyle? normalStyle;
final TextInputStyle? focusedStyle;
final TextInputStyle? errorStyle;
final TextInputStyle? disableStyle;
}