21

In flutter, TextField doesn't have an intrinsic width; it only knows how to size itself to the full width of its parent container. How do I set the width to the width of the contained text.

I tried putting the TextField inside a container

As outlined here How to update flutter TextField's height and width?

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

I expect the width of the TextField to match the width of the text it contains. The TextField should grow wider as text is typed, and shrink narrower as text is deleted.

SteveM
  • 722
  • 1
  • 6
  • 12
  • Here's a nice package that wraps up a solution to this problem. https://pub.dev/packages/fitted_text_field_container – SteveM Jun 29 '20 at 20:44

3 Answers3

40

Flutter has IntrinsicWidth to do the calculation for you. Just wrap your TextField or TextFormField in it as follow:

IntrinsicWidth(child: TextField())
Philip Ch'ng
  • 541
  • 5
  • 4
  • 1
    If the TextFormField is empty it shows an ugly, narrow widget. – TechAurelian Nov 08 '20 at 12:05
  • 3
    To impose a minimum width, use a Container to add [BoxConstraints](https://api.flutter.dev/flutter/rendering/BoxConstraints/minWidth.html) with a minimum width. `Container(constraints: BoxConstraints(minWidth: 100),child: TextField())` – jayjw Jul 08 '21 at 16:42
  • 3
    @AlexVang you can also avoid that by passing a hint text – Bensal Jul 13 '21 at 15:41
  • 1
    I tried it and it does not work for me. https://dartpad.dev/7125844edd33f665432fb8b414d32efd The `TextField` still takes the full width. What can I do? – John Aug 07 '21 at 16:04
  • @John it's working. The reason you don't see it working is that you've used a ListTile as a parent where the text field is left aligned and constrained it in a sized box. – Ashutosh Singh Apr 27 '23 at 10:33
7

I was able to achieve the expected result by using a TextPainter to compute the desired width of the text. I then used that width as the width of the Container containing the TextField.

Remember to call setState() in your TextFields onChanged method. This tells the widget to redraw itself causing the TextField to adjust to the new width of its content.

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
  • 722
  • 1
  • 6
  • 12
  • It's great ! Thanks ! – librajt Apr 20 '19 at 07:41
  • When there are more letters, it's not expanding correctly any more. – Mi Be Nov 02 '19 at 19:19
  • Was able to fix this problem for me to add: ````decoration: InputDecoration( contentPadding: EdgeInsets.only(left:0,right:-5),)````to the TextField. – Mi Be Nov 13 '19 at 16:45
  • 1
    @MichaelBerger that EdgeInset value depends on font size, font family and other parameters. Please, check my answer below for a solution that works with all font sizes and font faces – Alexander Tee Feb 05 '20 at 08:26
4

I modified SteveM's answer a little bit to fix the bug when widget is not expanding correctly. We should respect these two small things when calculating widget's width:

  1. TextField merges given textStyle parameter and current theme's TextStyle. This has to be done in custom widget too:
final ThemeData themeData = Theme.of(context);
final TextStyle style = themeData.textTheme.subtitle1.merge(textStyle);
  1. TextField's cursorWidth has to be taken into widget's width calculations. Since there's no way to get default cursor width from TextField class, I checked its code and added a new constant to FitsTextField class. Don't forget to pass it to TextField's constructor:
final textWidth = max(widget.minWidth, tp.width + _CURSOR_WIDTH);

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

Full code:

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(() {});
        },
      ),
    );
  }
}
Alexander Tee
  • 125
  • 2
  • 9