Flutter文本框的宽度应该与所包含文本的宽度相匹配。

23
在Flutter中,TextField没有固有的宽度;它只知道如何将自己大小调整为其父容器的整个宽度。我该如何将其宽度设置为所包含文本的宽度?
我尝试将TextField放在一个Container中。
如此描述: How to update flutter TextField's height and width?
new Container(              
  width: 100.0,
  child: new TextField()
)

我希望TextField的宽度能够匹配其所包含文本的宽度。随着输入文本的增加,TextField应该变得更宽;当删除文本时,它应该变得更窄。


这里有一个很好的软件包,它将解决此问题的方案封装起来。https://pub.dev/packages/fitted_text_field_container - SteveM
3个回答

46

Flutter有IntrinsicWidth可以为您进行计算。只需像下面这样将TextFieldTextFormField包装在其中:

IntrinsicWidth(child: TextField())

2
如果TextFormField为空,则显示一个丑陋的、狭窄的小部件。 - TechAurelian
4
要实施最小宽度,可以使用 Container 添加带有最小宽度的 BoxConstraintsContainer(constraints: BoxConstraints(minWidth: 100),child: TextField()) - jayjw
3
@AlexVang 你也可以通过传递提示文本来避免这种情况。 - Bensal
1
我尝试了一下,但对我没有用。https://dartpad.dev/7125844edd33f665432fb8b414d32efd TextField 仍然占据了整个宽度。我该怎么办? - John
@John,它正在工作。你看不到它工作的原因是你使用了一个ListTile作为父元素,其中文本字段是左对齐的,并将其限制在一个SizedBox中。 - Ashutosh Singh

7

通过使用 TextPainter 计算所需文本的宽度,我能够实现预期结果。然后,我将该宽度用作包含 TextFieldContainer 的宽度。

记得在你的 TextField 的 onChanged 方法中调用 setState()。这会告诉小部件重新绘制自己,从而使 TextField 调整其内容的新宽度。

import 'package:flutter/material.dart';

class FitTextField extends StatefulWidget {

  final String initialValue;
  final double minWidth;

  const FitTextField({Key key, this.initialValue, this.minWidth: 30}): super(key: key);

  @override
  State<StatefulWidget> createState() => new FitTextFieldState();
}

class FitTextFieldState extends State<FitTextField>{

  TextEditingController txt = TextEditingController();

  // We will use this text style for the TextPainter used to calculate the width
  // and for the TextField so that we calculate the correct size for the text
  // we are actually displaying
  TextStyle textStyle = TextStyle(color: Colors.grey[600]);

  initState() {
    super.initState();
    // Set the text in the TextField to our initialValue
    txt.text = widget.initialValue;
  }

  @override
  Widget build(BuildContext context) {
    // Use TextPainter to calculate the width of our text
    TextSpan ts = new TextSpan(style: textStyle, text: txt.text);
    TextPainter tp = new TextPainter(text: ts, textDirection: TextDirection.ltr);
    tp.layout();
    var textWidth = tp.width; // We will use this width for the container wrapping our TextField

    // Enforce a minimum width
    if ( textWidth < widget.minWidth ) {
      textWidth = widget.minWidth;
    }

    return Container(
      width: textWidth,
      child: TextField(
        style: textStyle,
        controller: txt,
        onChanged: (text) {
          // Tells the framework to redraw the widget
          // The widget will redraw with a new width
          setState(() {});
        },
      ),
    );
  }
}

太棒了!谢谢! - librajt
当有更多的字母时,它就不再正确地扩展了。 - Mi Be
我能够为您解决这个问题,只需在TextField中添加以下内容:decoration: InputDecoration( contentPadding: EdgeInsets.only(left:0,right:-5),) - Mi Be
1
@MichaelBerger,EdgeInset的值取决于字体大小、字体系列和其他参数。请查看我下面的答案,其中包含适用于所有字体大小和字体系列的解决方案。 - Alexander Tee

5

我稍微修改了SteveM的答案以修复当小部件未正确扩展时的错误。在计算小部件宽度时,我们应该考虑这两个小事情:

  1. TextField会合并给定的textStyle参数和当前主题的TextStyle。在自定义小部件中也必须这样做:
final ThemeData themeData = Theme.of(context);
final TextStyle style = themeData.textTheme.subtitle1.merge(textStyle);
  1. TextFieldcursorWidth必须计入小部件的宽度计算中。由于无法从TextField类中获取默认光标宽度,因此我检查了其代码并在FitsTextField类中添加了一个新常量。不要忘记将其传递给TextField的构造函数:
final textWidth = max(widget.minWidth, tp.width + _CURSOR_WIDTH);

// When building a TextField:
child: TextField(
        cursorWidth: _CURSOR_WIDTH,

完整代码:

import 'dart:math';

import 'package:flutter/material.dart';

class FitTextField extends StatefulWidget {
  final String initialValue;
  final double minWidth;

  const FitTextField({
    Key key,
    this.initialValue,
    this.minWidth: 30,
  }) : super(key: key);

  @override
  State<StatefulWidget> createState() => new FitTextFieldState();
}

class FitTextFieldState extends State<FitTextField> {
  // 2.0 is the default from TextField class
  static const _CURSOR_WIDTH = 2.0;

  TextEditingController txt = TextEditingController();

  // We will use this text style for the TextPainter used to calculate the width
  // and for the TextField so that we calculate the correct size for the text
  // we are actually displaying
  TextStyle textStyle = TextStyle(
    color: Colors.grey[600],
    fontSize: 16,
  );

  initState() {
    super.initState();
    // Set the text in the TextField to our initialValue
    txt.text = widget.initialValue;
  }

  @override
  Widget build(BuildContext context) {
    // TextField merges given textStyle with text style from current theme
    // Do the same to get final TextStyle
    final ThemeData themeData = Theme.of(context);
    final TextStyle style = themeData.textTheme.subtitle1.merge(textStyle);

    // Use TextPainter to calculate the width of our text
    TextSpan ts = new TextSpan(style: style, text: txt.text);
    TextPainter tp = new TextPainter(
      text: ts,
      textDirection: TextDirection.ltr,
    );
    tp.layout();

    // Enforce a minimum width
    final textWidth = max(widget.minWidth, tp.width + _CURSOR_WIDTH);

    return Container(
      width: textWidth,
      child: TextField(
        cursorWidth: _CURSOR_WIDTH,
        style: style,
        controller: txt,
        onChanged: (text) {
          // Redraw the widget
          setState(() {});
        },
      ),
    );
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接