如何在Flutter中将投影添加到TextFormField

Nic*_*leZ 7 flutter flutter-layout

我在颤抖中有文本形式的领域,我想向其中添加阴影。我该怎么做?

 final password = TextFormField(
    obscureText: true,
    autofocus: false,
    decoration: InputDecoration(
        icon: new Icon(Icons.lock, color: Color(0xff224597)),
        hintText: 'Password',
        fillColor: Colors.white,
        filled: true,
        contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
        enabledBorder: OutlineInputBorder(borderRadius:BorderRadius.circular(5.0),
        borderSide: BorderSide(color: Colors.white, width: 3.0))
     ),
   );
Run Code Online (Sandbox Code Playgroud)

Vic*_*r K 17

您可以使用此类作为元素边框的包装器。它采用控件的边框并在控件上方的边框上绘制阴影。为了产生阴影位于控件后面的错觉,控件上方的阴影区域会被切除。

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

class DecoratedInputBorder extends InputBorder {
  DecoratedInputBorder({
    required this.child,
    required this.shadow,
  }) : super(borderSide: child.borderSide);

  final InputBorder child;

  final BoxShadow shadow;

  @override
  bool get isOutline => child.isOutline;

  @override
  Path getInnerPath(Rect rect, {TextDirection? textDirection}) => child.getInnerPath(rect, textDirection: textDirection);

  @override
  Path getOuterPath(Rect rect, {TextDirection? textDirection}) => child.getOuterPath(rect, textDirection: textDirection);

  @override
  EdgeInsetsGeometry get dimensions => child.dimensions;

  @override
  InputBorder copyWith({BorderSide? borderSide, InputBorder? child, BoxShadow? shadow, bool? isOutline}) {
    return DecoratedInputBorder(
      child: (child ?? this.child).copyWith(borderSide: borderSide),
      shadow: shadow ?? this.shadow,
    );
  }

  @override
  ShapeBorder scale(double t) {
    final scalledChild = child.scale(t);

    return DecoratedInputBorder(
      child: scalledChild is InputBorder ? scalledChild : child,
      shadow: BoxShadow.lerp(null, shadow, t)!,
    );
  }

  @override
  void paint(Canvas canvas, Rect rect, {double? gapStart, double gapExtent = 0.0, double gapPercentage = 0.0, TextDirection? textDirection}) {
    final clipPath = Path()
      ..addRect(const Rect.fromLTWH(-5000, -5000, 10000, 10000))
      ..addPath(getInnerPath(rect), Offset.zero)
      ..fillType = PathFillType.evenOdd;
    canvas.clipPath(clipPath);

    final Paint paint = shadow.toPaint();
    final Rect bounds = rect.shift(shadow.offset).inflate(shadow.spreadRadius);

    canvas.drawPath(getOuterPath(bounds), paint);

    child.paint(canvas, rect, gapStart: gapStart, gapExtent: gapExtent, gapPercentage: gapPercentage, textDirection: textDirection);
  }

  @override
  bool operator ==(Object other) {
    if (other.runtimeType != runtimeType) return false;
    return other is DecoratedInputBorder && other.borderSide == borderSide && other.child == child && other.shadow == shadow;
  }

  @override
  int get hashCode => hashValues(borderSide, child, shadow);

  @override
  String toString() {
    return '${objectRuntimeType(this, 'DecoratedInputBorder')}($borderSide, $shadow, $child)';
  }
}
Run Code Online (Sandbox Code Playgroud)
MaterialApp(
  theme: ThemeData(
    primarySwatch: Colors.blue,
    inputDecorationTheme: InputDecorationTheme(
      border: DecoratedInputBorder(
        child: const OutlineInputBorder(
          borderRadius: BorderRadius.all(Radius.circular(16.0)),
        ),
        shadow: const BoxShadow(
          color: Colors.blue,
          blurRadius: 15,
        ),
      ),
    ),
  ),
Run Code Online (Sandbox Code Playgroud)

它应该看起来像这样:

在此输入图像描述

交互示例:https://dartpad.dev/?id =35f1249b52d177d47bc91c87d0a8c08c

或者,您可以使用我的包control_style。它实现了这种方法的更深入的实现。


say*_*bir 10

您可以使用PhysicalModel在每个小部件上添加阴影,如下所示:

PhysicalModel(
   borderRadius: BorderRadius.circular(25),
   color: Colors.white,
   elevation: 5.0,
   shadowColor: Color(0xff44BD32),
   child: CustomTextField(...
Run Code Online (Sandbox Code Playgroud)


hnn*_*lch 6

这是一个可能的解决方案,其中BoxShadow只显示在 后面TextField,但如果显示错误文本则不会垂直扩展。

在此输入图像描述

我的解决方案是使用该小部件在实际后面Stack创建一个额外的部件,负责显示阴影。ContainerTextField

ATextPainter用于根据错误文本的样式确定其高度:

import 'package:flutter/material.dart';

class TextFieldWithBoxShadow extends StatelessWidget {
  final String? errorText;
  final String? labelText;
  final TextEditingController? controller;
  final double height;

  const TextFieldWithBoxShadow({
    Key? key,
    this.errorText,
    this.labelText,
    this.controller,
    this.height = 40,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final errorStyle = const TextStyle(
      fontSize: 14,
    );
    
    // Wrap everything in LayoutBuilder so that the available maxWidth is taken into account for the height calculation (important if you error text exceeds one line)
    return LayoutBuilder(builder: (context, constraints) {
      // Use tp to calculate the height of the errorText
      final textPainter = TextPainter()
        ..text = TextSpan(text: errorText, style: errorStyle)
        ..textDirection = TextDirection.ltr
        ..layout(maxWidth: constraints.maxWidth);

      final heightErrorMessage = textPainter.size.height + 8;
      return Stack(
        children: [
          // Separate container with identical height of text field which is placed behind the actual textfield
          Container(
            height: height,
            decoration: BoxDecoration(
              boxShadow: const [
                BoxShadow(
                  color: Colors.black,
                  blurRadius: 3,
                  offset: Offset(3, 3),
                ),
              ],
              borderRadius: BorderRadius.circular(
                10.0,
              ),
            ),
          ),
          Container(
            // Add height of error message if it is displayed
            height: errorText != null ? height + heightErrorMessage : height,
            child: TextField(
              decoration: InputDecoration(
                fillColor: Colors.black,
                filled: true,
                errorStyle: errorStyle,
                errorText: errorText,
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(
                    10.0,
                  ),
                ),
                labelText: labelText,
              ),
              controller: controller,
            ),
          ),
        ],
      );
    });
  }
}
Run Code Online (Sandbox Code Playgroud)


non*_*hto 5

您可以TextFormFieldMaterial小部件包装并编辑其属性,例如elevationshadowColor

例:

Material(
              elevation: 20.0,
              shadowColor: Colors.blue,
                          child: TextFormField(
                obscureText: true,
                autofocus: false,
                decoration: InputDecoration(
                    icon: new Icon(Icons.lock, color: Color(0xff224597)),
                    hintText: 'Password',
                    fillColor: Colors.white,
                    filled: true,
                    contentPadding: EdgeInsets.fromLTRB(20.0, 10.0, 20.0, 10.0),
                    enabledBorder: OutlineInputBorder(borderRadius:BorderRadius.circular(5.0),
                    borderSide: BorderSide(color: Colors.white, width: 3.0))
                ),
              ),
            )  
Run Code Online (Sandbox Code Playgroud)

您将获得类似于下图的内容。
在此处输入图片说明

  • 在您不使用“helperText”或“errorText”之前,它看起来不错。否则阴影会占据额外的位置,看起来不太好。:( (16认同)

小智 5

当我们使用容器、材质或任何其他小部件来包装输入文本字段以应用阴影时,问题是如果我们使用提示文本、错误文本或任何其他更改文本框大小的属性,设计将被打破。

您可以使用扩展 InputBorder 类的自定义画家,而不是将输入包装在另一个小部件中。例如:

class MyInputBorder extends OutlineInputBorder {}
Run Code Online (Sandbox Code Playgroud)

将以下方法从 OutlineInputBorder 实现(用于本示例)复制到您的新类: _gapBorderPath _cornersAreCircular Paint

然后在paint方法中你可以添加以下几行

Path path = Path();
path.addRRect(center);
canvas.drawShadow(path, Colors.black, 4, true);
Run Code Online (Sandbox Code Playgroud)

上述行必须包含在 canvas.drawRRect 行之前: 示例:

if (gapStart == null || gapExtent <= 0.0 || gapPercentage == 0.0) {
  // paint the shadow for the outlined shape
  Path path = Path();
  path.addRRect(center);
  canvas.drawShadow(path, shadowColor!, elevation, true);

  canvas.drawRRect(center, paint);
} else {... other code omitted to keep simple}
Run Code Online (Sandbox Code Playgroud)

然后,在您的小部件中,使用新的输入边框:

TextField(
         decoration: InputDecoration(
           border: MyInputBorder()
         ),
       )
Run Code Online (Sandbox Code Playgroud)

生成的结果如下所示,不存在包装解决方案的任何问题:

带阴影的文本框

这是一个完整的示例实现,该帖子是西班牙语的,但它解释了想法:完整文章供参考