flutter 如何使浮动标注保持在边界内

kq0g1dla  于 2023-01-09  发布在  Flutter
关注(0)|答案(3)|浏览(128)

我试图在Flutter中达到这样的效果

FloatingLabel不直接在边界上,有没有官方的方法在flutter中实现这一点。
我试过在标签小部件中添加填充,但也不起作用。

htrmnn0y

htrmnn0y1#

我也很惊讶地看到它不是开箱即用的,在查看了InputBorder类之后,我明白了它可以变得相当复杂。
复制整个UnderlineInputBorder类并将onPaint方法替换为OutlineInputBorder类中的方法对我很有效。

    • 自定义输入边框类:**
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参数,它将使用主题Colors作为onFocus(默认为蓝色)和onError(默认为红色)。如果你想让BorderSide(color: [yourCustomColor])正常工作,你必须为每个特定的状态传递border参数:

errorBorder
focusedBorder
focusedErrorBorder
disabledBorder
enabledBorder
s4n0splo

s4n0splo2#

您可以使用contentPadding添加填充,然后使用FloatingLabelBehavior.always使浮动标签始终显示。您可以根据浮动标签是否具有焦点,为浮动标签指定不同的样式。例如:

TextField(
  decoration: InputDecoration(
    labelText: 'Email',
    floatingLabelBehavior: FloatingLabelBehavior.always,
    floatingLabelStyle: TextStyle(color: Colors.red),
    labelStyle: TextStyle(color: Colors.blue),
    contentPadding: const EdgeInsets.all(16),
  ),
)
gorkyyrv

gorkyyrv3#

上面的答案并不完全适合我,我不得不稍微调整一下绘制方法:

void paint(
    Canvas canvas,
    Rect rect, {
    double? gapStart,
    double gapExtent = 0.0,
    double gapPercentage = 0.0,
    TextDirection? textDirection,
  }) {
    final Paint paint = borderSide.toPaint();
    final Path outlinePath = getOuterPath(rect);
    canvas.drawPath(outlinePath, paint);
  }

相关问题