Skip to content

Commit

Permalink
Make text box decorations affected by the current widget state (#987)
Browse files Browse the repository at this point in the history
  • Loading branch information
bdlukaa authored Nov 22, 2024
2 parents f688240 + 0d97ca2 commit 5f8abe3
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 82 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,6 @@ build/
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3

.fvm/
.fvm/
example/pubspec.lock
pubspec.lock
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,31 @@
- fix: Do try to scroll Date and Time at build time ([#1117](https://github.com/bdlukaa/fluent_ui/issues/1117))
- feat: Use a `Decoration` instead of `Color` in `NavigationAppBar` ([#1118](https://github.com/bdlukaa/fluent_ui/issues/1118))
- feat: Add `EditableComboBox.inputFormatters` ([#1041](https://github.com/bdlukaa/fluent_ui/issues/1041))
- **BREAKING** feat: `TextBox.decoration` and `TextBox.foregroundDecoration` are now of type `WidgetStateProperty` ([#987](https://github.com/bdlukaa/fluent_ui/pull/987))

Before:
```dart
TextBox(
decoration: BoxDecoration(
color: Colors.red,
),
foregroundDecoration: BoxDecoration(
color: Colors.blue,
),
),
```

After:
```dart
TextBox(
decoration: WidgetStateProperty.all(BoxDecoration(
color: Colors.red,
)),
foregroundDecoration: WidgetStateProperty.all(BoxDecoration(
color: Colors.blue,
)),
),
```

## 4.9.2

Expand Down
7 changes: 5 additions & 2 deletions lib/src/controls/form/auto_suggest_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -244,12 +244,15 @@ class AutoSuggestBox<T> extends StatefulWidget {
final TextStyle? style;

/// Controls the [BoxDecoration] of the box behind the text input.
final BoxDecoration? decoration;
///
/// Defaults to having a rounded rectangle grey border and can be null to have
/// no box decoration.
final WidgetStateProperty<BoxDecoration>? decoration;

/// Controls the [BoxDecoration] of the box in front of the text input.
///
/// If [highlightColor] is provided, this must not be provided
final BoxDecoration? foregroundDecoration;
final WidgetStateProperty<BoxDecoration>? foregroundDecoration;

/// The highlight color of the text box.
///
Expand Down
17 changes: 16 additions & 1 deletion lib/src/controls/form/number_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,17 @@ class NumberBox<T extends num> extends StatefulWidget {
/// {@macro flutter.widgets.editableText.showCursor}
final bool? showCursor;

/// Controls the [BoxDecoration] of the box behind the text input.
///
/// Defaults to having a rounded rectangle grey border and can be null to have
/// no box decoration.
final WidgetStateProperty<BoxDecoration>? decoration;

/// Controls the [BoxDecoration] of the box in front of the text input.
///
/// If [highlightColor] is provided, this must not be provided
final WidgetStateProperty<BoxDecoration>? foregroundDecoration;

/// The highlight color of the text box.
///
/// If [foregroundDecoration] is provided, this must not be provided.
Expand Down Expand Up @@ -290,6 +301,8 @@ class NumberBox<T extends num> extends StatefulWidget {
this.showCursor,
this.highlightColor,
this.unfocusedColor,
this.decoration,
this.foregroundDecoration,
this.style,
this.textAlign,
this.keyboardType = TextInputType.number,
Expand Down Expand Up @@ -542,14 +555,16 @@ class NumberBoxState<T extends num> extends State<NumberBox<T>> {
cursorRadius: widget.cursorRadius,
cursorWidth: widget.cursorWidth,
highlightColor: widget.highlightColor,
unfocusedColor: widget.unfocusedColor,
decoration: widget.decoration,
foregroundDecoration: widget.foregroundDecoration,
prefix: widget.leadingIcon,
focusNode: focusNode,
controller: controller,
keyboardType: widget.keyboardType,
enabled: widget.onChanged != null,
suffix:
textFieldSuffix.isNotEmpty ? Row(children: textFieldSuffix) : null,
unfocusedColor: widget.unfocusedColor,
style: widget.style,
textAlign: widget.textAlign ?? TextAlign.start,
keyboardAppearance: widget.keyboardAppearance,
Expand Down
4 changes: 2 additions & 2 deletions lib/src/controls/form/password_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -123,12 +123,12 @@ class PasswordBox extends StatefulWidget {
///
/// Defaults to having a rounded rectangle grey border and can be null to have
/// no box decoration.
final BoxDecoration? decoration;
final WidgetStateProperty<BoxDecoration>? decoration;

/// Controls the [BoxDecoration] of the box in front of the text input.
///
/// If [highlightColor] is provided, this must not be provided
final BoxDecoration? foregroundDecoration;
final WidgetStateProperty<BoxDecoration>? foregroundDecoration;

/// The highlight color of the text box.
///
Expand Down
158 changes: 83 additions & 75 deletions lib/src/controls/form/text_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -236,12 +236,12 @@ class TextBox extends StatefulWidget {
///
/// Defaults to having a rounded rectangle grey border and can be null to have
/// no box decoration.
final BoxDecoration? decoration;
final WidgetStateProperty<BoxDecoration>? decoration;

/// Controls the [BoxDecoration] of the box in front of the text input.
///
/// If [highlightColor] is provided, this must not be provided
final BoxDecoration? foregroundDecoration;
final WidgetStateProperty<BoxDecoration>? foregroundDecoration;

/// The highlight color of the text box.
///
Expand Down Expand Up @@ -567,7 +567,10 @@ class TextBox extends StatefulWidget {
defaultValue: null))
..add(DiagnosticsProperty<FocusNode>('focusNode', focusNode,
defaultValue: null))
..add(DiagnosticsProperty<BoxDecoration>('decoration', decoration))
..add(DiagnosticsProperty<WidgetStateProperty<BoxDecoration>>(
'decoration', decoration))
..add(DiagnosticsProperty<WidgetStateProperty<BoxDecoration>>(
'foregroundDecoration', foregroundDecoration))
..add(DiagnosticsProperty<EdgeInsetsGeometry>(
'padding',
padding,
Expand Down Expand Up @@ -1120,38 +1123,6 @@ class _TextBoxState extends State<TextBox>
.merge(widget.placeholderStyle);
}

final foregroundDecoration = BoxDecoration(
border: Border(
bottom: BorderSide(
color: _effectiveFocusNode.hasFocus
? widget.highlightColor ??
themeData.accentColor.defaultBrushFor(
themeData.brightness,
)
: !enabled
? Colors.transparent
: widget.unfocusedColor ??
(themeData.brightness.isLight
? const Color.fromRGBO(0, 0, 0, 0.45)
: const Color.fromRGBO(255, 255, 255, 0.54)),
width: _effectiveFocusNode.hasFocus ? 2 : 0,
),
),
).copyWith(
backgroundBlendMode: widget.foregroundDecoration?.backgroundBlendMode,
border: widget.foregroundDecoration?.border,
borderRadius: widget.foregroundDecoration?.borderRadius,
boxShadow: widget.foregroundDecoration?.boxShadow,
color: widget.foregroundDecoration?.color,
gradient: widget.foregroundDecoration?.gradient,
image: widget.foregroundDecoration?.image,
shape: widget.foregroundDecoration?.shape,
);

final radius =
widget.decoration?.borderRadius?.resolve(Directionality.of(context)) ??
BorderRadius.circular(4.0);

return Semantics(
enabled: enabled,
onTap: !enabled || widget.readOnly
Expand All @@ -1167,45 +1138,82 @@ class _TextBoxState extends State<TextBox>
child: TextFieldTapRegion(
child: IgnorePointer(
ignoring: !enabled,
child: ClipRRect(
borderRadius: radius,
clipBehavior: widget.clipBehavior,
child: HoverButton(
focusEnabled: false,
forceEnabled: enabled,
hitTestBehavior: HitTestBehavior.translucent,
builder: (context, states) {
// Since we manage focus outside of the HoverButton (see focusEnabled: false)
// we need to add the focused state when the field is focused
//
// widgets below this can call `HoverButton.of(context).states.isFocused`
// and have the correct value
if (_effectiveFocusNode.hasFocus) {
states = {...states, WidgetState.focused};
}
child: HoverButton(
focusEnabled: false,
forceEnabled: enabled,
hitTestBehavior: HitTestBehavior.translucent,
builder: (context, states) {
// Since we manage focus outside of the HoverButton (see focusEnabled: false)
// we need to add the focused state when the field is focused
//
// widgets below this can call `HoverButton.of(context).states.isFocused`
// and have the correct value
if (_effectiveFocusNode.hasFocus) {
states = {...states, WidgetState.focused};
}

return DecoratedBox(
decoration: BoxDecoration(
borderRadius: radius,
border: Border.all(
color: themeData.resources.controlStrokeColorDefault,
),
color: backgroundColor(states),
).copyWith(
backgroundBlendMode: widget.decoration?.backgroundBlendMode,
border: widget.decoration?.border,

/// This border radius can't be applied, otherwise the error "A borderRadius
/// can only be given for a uniform Border." will be thrown. Instead,
/// [radius] is already set to get the value from [widget.decoration?.borderRadius],
/// if any.
// borderRadius: widget.decoration?.borderRadius,
boxShadow: widget.decoration?.boxShadow,
color: widget.decoration?.color,
gradient: widget.decoration?.gradient,
image: widget.decoration?.image,
shape: widget.decoration?.shape,
final resolvedWidgetDecoration =
widget.decoration?.resolve(states);
final radius = resolvedWidgetDecoration?.borderRadius
?.resolve(Directionality.of(context)) ??
BorderRadius.circular(4.0);
final decoration = WidgetStateProperty.resolveWith((states) {
return BoxDecoration(
borderRadius: radius,
border: Border.all(
color: themeData.resources.controlStrokeColorDefault,
),
color: backgroundColor(states),
);
}).resolve(states).copyWith(
backgroundBlendMode:
resolvedWidgetDecoration?.backgroundBlendMode,
border: resolvedWidgetDecoration?.border,
borderRadius: resolvedWidgetDecoration?.borderRadius,
boxShadow: resolvedWidgetDecoration?.boxShadow,
color: resolvedWidgetDecoration?.color,
gradient: resolvedWidgetDecoration?.gradient,
image: resolvedWidgetDecoration?.image,
shape: resolvedWidgetDecoration?.shape,
);

final resolvedWidgetForegroundDecoration =
widget.foregroundDecoration?.resolve(states);
final foregroundDecoration =
WidgetStateProperty.resolveWith((states) {
if (states.isFocused) {
return BoxDecoration(
border: Border(
bottom: BorderSide(
color: widget.highlightColor ??
themeData.accentColor.defaultBrushFor(
themeData.brightness,
),
width: 2,
),
),
);
}
return const BoxDecoration();
}).resolve(states).copyWith(
backgroundBlendMode: resolvedWidgetForegroundDecoration
?.backgroundBlendMode,
border: resolvedWidgetForegroundDecoration?.border,
borderRadius:
resolvedWidgetForegroundDecoration?.borderRadius,
boxShadow:
resolvedWidgetForegroundDecoration?.boxShadow,
color: resolvedWidgetForegroundDecoration?.color,
gradient: resolvedWidgetForegroundDecoration?.gradient,
image: resolvedWidgetForegroundDecoration?.image,
shape: resolvedWidgetForegroundDecoration?.shape,
);

return ClipRRect(
borderRadius: radius,
clipBehavior: widget.clipBehavior,
child: DecoratedBox(
decoration: decoration,
child: Container(
foregroundDecoration: foregroundDecoration,
constraints: const BoxConstraints(
Expand All @@ -1226,9 +1234,9 @@ class _TextBoxState extends State<TextBox>
),
),
),
);
},
),
),
);
},
),
),
),
Expand Down
2 changes: 1 addition & 1 deletion lib/src/controls/form/text_form_box.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ class TextFormBox extends ControllableFormBox {
MaxLengthEnforcement? maxLengthEnforcement,
ui.BoxHeightStyle selectionHeightStyle = ui.BoxHeightStyle.tight,
ui.BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.tight,
BoxDecoration? decoration,
WidgetStateProperty<BoxDecoration>? decoration,
bool enableIMEPersonalizedLearning = true,
MouseCursor? mouseCursor,
bool scribbleEnabled = true,
Expand Down

0 comments on commit 5f8abe3

Please sign in to comment.