Reputation: 772
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.
Upvotes: 25
Views: 17381
Reputation: 681
Flutter has IntrinsicWidth to do the calculation for you. Just wrap your TextField
or TextFormField
in it as follow:
IntrinsicWidth(child: TextField())
Upvotes: 54
Reputation: 135
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:
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);
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(() {});
},
),
);
}
}
Upvotes: 5
Reputation: 772
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(() {});
},
),
);
}
}
Upvotes: 7