flutter - 如何让 float 标签停留在边框内

标签 flutter

我正试图在 flutter 中实现这样的目标;

enter image description here

FloatingLabel不是直接在border上,flutter有官方的实现方式吗

我试过向标签小部件添加填充,但这也不起作用。

最佳答案

我也很惊讶地发现它不支持开箱即用。在查看了 InputBorder 类之后,我了解到它可能会变得相当复杂。

复制整个 UnderlineInputBorder 类并将 onPaint 方法替换为 OutlineInputBorder 类中的方法对我有用。

自定义 InputBorder 类:

import 'dart:math' as math;

import 'package:flutter/material.dart';

class CustomOutlineInputBorder extends InputBorder {
  /// Creates an underline border for an [InputDecorator].
  ///
  /// The [borderSide] parameter defaults to [BorderSide.none] (it must not be
  /// null). Applications typically do not specify a [borderSide] parameter
  /// because the input decorator substitutes its own, using [copyWith], based
  /// on the current theme and [InputDecorator.isFocused].
  ///
  /// The [borderRadius] parameter defaults to a value where the top left
  /// and right corners have a circular radius of 4.0. The [borderRadius]
  /// parameter must not be null.
  const CustomOutlineInputBorder({
    BorderSide borderSide = const BorderSide(),
    this.borderRadius = const BorderRadius.only(
      topLeft: Radius.circular(4.0),
      topRight: Radius.circular(4.0),
      bottomLeft: Radius.circular(4.0),
      bottomRight: Radius.circular(4.0),
    ),
  })  : assert(borderRadius != null),
        super(borderSide: borderSide);

  /// The radii of the border's rounded rectangle corners.
  ///
  /// When this border is used with a filled input decorator, see
  /// [InputDecoration.filled], the border radius defines the shape
  /// of the background fill as well as the bottom left and right
  /// edges of the underline itself.
  ///
  /// By default the top right and top left corners have a circular radius
  /// of 4.0.
  final BorderRadius borderRadius;

  @override
  bool get isOutline => false;

  @override
  CustomOutlineInputBorder copyWith(
      {BorderSide? borderSide, BorderRadius? borderRadius}) {
    return CustomOutlineInputBorder(
      borderSide: borderSide ?? this.borderSide,
      borderRadius: borderRadius ?? this.borderRadius,
    );
  }

  @override
  EdgeInsetsGeometry get dimensions {
    return EdgeInsets.only(bottom: borderSide.width);
  }

  @override
  CustomOutlineInputBorder scale(double t) {
    return CustomOutlineInputBorder(borderSide: borderSide.scale(t));
  }

  @override
  Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
    return Path()
      ..addRect(Rect.fromLTWH(rect.left, rect.top, rect.width,
          math.max(0.0, rect.height - borderSide.width)));
  }

  @override
  Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
    return Path()..addRRect(borderRadius.resolve(textDirection).toRRect(rect));
  }

  @override
  ShapeBorder? lerpFrom(ShapeBorder? a, double t) {
    if (a is CustomOutlineInputBorder) {
      return CustomOutlineInputBorder(
        borderSide: BorderSide.lerp(a.borderSide, borderSide, t),
        borderRadius: BorderRadius.lerp(a.borderRadius, borderRadius, t)!,
      );
    }
    return super.lerpFrom(a, t);
  }

  @override
  ShapeBorder? lerpTo(ShapeBorder? b, double t) {
    if (b is CustomOutlineInputBorder) {
      return CustomOutlineInputBorder(
        borderSide: BorderSide.lerp(borderSide, b.borderSide, t),
        borderRadius: BorderRadius.lerp(borderRadius, b.borderRadius, t)!,
      );
    }
    return super.lerpTo(b, t);
  }

  /// Draw a horizontal line at the bottom of [rect].
  ///
  /// The [borderSide] defines the line's color and weight. The `textDirection`
  /// `gap` and `textDirection` parameters are ignored.
  @override
  void paint(
    Canvas canvas,
    Rect rect, {
    double? gapStart,
    double gapExtent = 0.0,
    double gapPercentage = 0.0,
    TextDirection? textDirection,
  }) {
    final Paint paint = borderSide.toPaint();
    final RRect outer = borderRadius.toRRect(rect);
    final RRect center = outer.deflate(borderSide.width / 2.0);
    canvas.drawRRect(center, paint);
  }

  @override
  bool operator ==(Object other) {
    if (identical(this, other)) return true;
    if (other.runtimeType != runtimeType) return false;
    return other is InputBorder && other.borderSide == borderSide;
  }

  @override
  int get hashCode => borderSide.hashCode;
}

用法:

TextFormField(
decoration: InputDecoration(
            border: CustomOutlineInputBorder(
                borderRadius: BorderRadius.all(Radius.circular(8)),
                borderSide: BorderSide(
                  color: Colors.white,
                  /// NOTE: Color argument won't work on border argument 
                  width: 1,
                  style: BorderStyle.solid,
                )),
            enabledBorder: CustomOutlineInputBorder(
                borderRadius: BorderRadius.all(Radius.circular(8)),
                borderSide: BorderSide(
                  color: Colors.white,
                  /// NOTE: Color argument works here! :hooray: 
                  width: 1,
                  style: BorderStyle.solid,
                )),
          )
),

重要提示:如果您只设置 TextField 的 border 参数,它将使用 onFocus(默认为蓝色)和 onError(默认为红色)的主题颜色。如果您想让 BorderSide(color: [yourCustomColor]) 正常工作,您必须为每个特定状态传递边框参数:

errorBorder
focusedBorder
focusedErrorBorder
disabledBorder
enabledBorder

关于flutter - 如何让 float 标签停留在边框内,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71621132/

相关文章:

flutter - 从 ElevatedButton.styleFrom() 更改 ElevatedButton 禁用状态的文本颜色

webview - 在flutter webview中禁用水平滚动

flutter - 使用 flutter_secure_storage 保存数据列表文本......这可能吗?

dart - Flutter UI - 如何向尾随的 CupertinoSliverNavigationBar 添加更多 IconButtons?

flutter - Flutter 中的不可变与可观察集合

flutter - Build Bundle(s)/Apk(s) 禁用

css - 为什么我的窗口小部件不在屏幕中央?

flutter - 使用命令 flutter build apk 时如何解决音频播放器的配置问题?

flutter - 在 flutter 中更新 android studio 后出错

firebase - 如何处理 Firebase 密码重置电子邮件错误 Flutter