TextFieldやTextFormFieldの枠線の色を変えたいときに、安直にInputDecorationのborderに引数を渡しても変わりませんでした。
原因を調べてみるとInputDecorationのborderは使用される場合とされない場合がありましたので、備忘録として残しておきます。
環境
Flutter ... 3.0.3
Dart ... 2.17.5
使用するborderが決定されるまで
TextFieldの装飾に使用するInputDecorationには、border関連の引数が6種類あります。
- border
- disabledBorder
- enabledBorder
- errorBorder
- focusedBorder
- focusedErrorBorder
どの引数が使用されるはinput_decorator.dart内の下記コードによって決定されます。
// input_decorator.dart buid関数内 2132行目 ~
final bool isError = decoration!.errorText != null;
InputBorder? border; // これが最終的に設定されるborder
if (!decoration!.enabled)
border = isError ? decoration!.errorBorder : decoration!.disabledBorder;
else if (isFocused) // isFocusedについての詳しい解説は割愛します。名前の通りフォーカスが当たっているかを表すbool型の変数です。
border = isError ? decoration!.focusedErrorBorder : decoration!.focusedBorder;
else
border = isError ? decoration!.errorBorder : decoration!.enabledBorder;
border ??= _getDefaultBorder(themeData);
// input_decorator.dart 2078行目 ~
InputBorder _getDefaultBorder(ThemeData themeData) {
final InputBorder border = MaterialStateProperty.resolveAs(decoration!.border, materialState)
?? const UnderlineInputBorder();
if (decoration!.border is MaterialStateProperty<InputBorder>) {
return border;
}
if (border.borderSide == BorderSide.none) {
return border;
}
final Color borderColor;
if (decoration!.enabled || isFocused) {
borderColor = decoration!.errorText == null
? _getDefaultBorderColor(themeData)
: themeData.errorColor;
} else {
borderColor = ((decoration!.filled ?? false) && !(decoration!.border?.isOutline ?? false))
? Colors.transparent
: themeData.disabledColor;
}
final double borderWeight;
if (decoration!.isCollapsed || decoration?.border == InputBorder.none || !decoration!.enabled)
borderWeight = 0.0;
else
borderWeight = isFocused ? 2.0 : 1.0;
// ここで作成されたborderは以降defaultBorderと呼びます。
return border.copyWith(borderSide: BorderSide(color: borderColor, width: borderWeight));
}
これを元に、borderが適用されないときに考えられる原因を解説していきます。
原因
他の引数が優先されている
ソースコードを元に、TextFieldの状態と使用されるborderの関係は以下の表にまとめられます。
状態 | 使用されるborder |
enabled == false && isError == true | errorBorder ?? _getDefaultBorder() |
enabled == false && isError == false | disabledBorder ?? _getDefaultBorder() |
enabled == true && isFocused = true && isError == true | focusedErrorBorder ?? _getDefaultBorder() |
enabled == true && isFocused = true && isError == false | focusedBorder ?? _getDefaultBorder() |
enabled == true && isFocused = false && isError == true | errorBorder ?? _getDefaultBorder() |
enabled == true && isFocused = false && isError == false | enabledBorder ?? _getDefaultBorder() |
表から分かる通り、どの状態においてもまずはborder以外の引数が優先されるため、border以外の引数を渡しているとborderが使用されない場合があります。
borderSide != BorderSide.noneになっている
_getDefaultBorder関数内でborderを使うか判定する箇所は下記になります。
if (decoration!.border is MaterialStateProperty<InputBorder>) {
return border;
}
if (border.borderSide == BorderSide.none) {
return border;
}
コードからもわかる通り、borderは MaterialStateProperty<InputBorder>のインスタンス または borderSide == BorderSide.none の場合に使用されます。
そのためborderに色などの装飾を適用した場合、borderSide != BorderSide.noneになるためborderは使用されません。
色をつけたい場合は、enabledBorderなどの他の引数を使用しましょう。
解説で間違っている箇所や他に考えられる原因がありましたら、コメントを頂けると嬉しいです。