Flutter TextField 宽度应与包含文本的宽度相匹配

Ste*_*veM 11 flutter flutter-layout

在 flutter 中,TextField 没有固有宽度;它只知道如何将自己的大小调整为其父容器的全宽。如何将宽度设置为包含文本的宽度。

我试着把 TextField 放在一个容器里

如此处概述 如何更新颤振 TextField 的高度和宽度?

new Container(              
  width: 100.0,
  child: new TextField()
)
Run Code Online (Sandbox Code Playgroud)

我希望 TextField 的宽度与其包含的文本的宽度相匹配。TextField 应该在输入文本时变宽,并在删除文本时收缩变窄。

小智 20

Flutter 有IntrinsicWidth为你做计算。只需将您的TextField或包裹TextFormField在其中,如下所示:

IntrinsicWidth(child: TextField())
Run Code Online (Sandbox Code Playgroud)

  • 要施加最小宽度,请使用容器添加具有最小宽度的 [BoxConstraints](https://api.flutter.dev/flutter/rendering/BoxConstraints/minWidth.html)。`容器(约束:BoxConstraints(minWidth:100),子项:TextField())` (4认同)
  • @AlexVang,您还可以通过传递提示文本来避免这种情况 (3认同)
  • 如果 TextFormField 为空,它会显示一个丑陋、狭窄的小部件。 (2认同)

Ste*_*veM 7

通过使用 aTextPainter来计算所需的文本宽度,我能够达到预期的结果。然后我使用该宽度作为Container包含TextField.

请记住调用setState()您的 TextFieldsonChanged方法。这告诉小部件重新绘制自身,导致 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(() {});
        },
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)


Ale*_*Tee 5

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

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

// When building a TextField:
child: TextField(
        cursorWidth: _CURSOR_WIDTH,
Run Code Online (Sandbox Code Playgroud)

完整代码:

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(() {});
        },
      ),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)