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

Flutter TextField width should match width of contained text

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

我尝试将 TextField 放入容器中

如此处所述

new Container(              
  width: 100.0,
  child: new TextField()
)

我希望 TextField 的宽度与其包含的文本的宽度相匹配。 TextField 应随着文本的键入而变宽,并随着文本的删除而变窄。

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

Container 的宽度

记得在您的 TextFields 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(() {});
        },
      ),
    );
  }
}

我稍微修改了 SteveM 的回答以修复小部件未正确扩展时的错误。我们在计算小部件的宽度时应该尊重这两个小东西:

  1. TextField 合并给定 textStyle 参数和当前主题的 TextStyle。这也必须在自定义小部件中完成:
final ThemeData themeData = Theme.of(context);
final TextStyle style = themeData.textTheme.subtitle1.merge(textStyle);
  1. TextFieldcursorWidth 必须考虑到小部件的宽度计算中。由于无法从 TextField class 获取默认光标宽度,我检查了它的代码并向 FitsTextField class 添加了一个新常量。不要忘记将它传递给 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(() {});
        },
      ),
    );
  }
}

Flutter IntrinsicWidth 可以为您进行计算。只需将 TextFieldTextFormField 包裹在其中,如下所示:

IntrinsicWidth(child: TextField())