0

I would link to create a TextField that shrinks to the text inside. To do so I used this answer that says to use an IntrinsicWidth widget. It works well, but if I specify a hintText, the width of the TextField won't be smaller than the width of the hintText even if the user enters a very short text.

Here is an example:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Container(
            alignment: Alignment.center,
            height: 50,
            width: 300,
            child: Container(
              alignment: Alignment.center,
              padding: EdgeInsets.all(10),
              color: Colors.grey[100],
              child: IntrinsicWidth(
                child: TextField(
                  decoration: InputDecoration(
                    hintText: 'My very very long hint', // Remove it and the text field can be as small as the text inside
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Here is a text field with no hint and a small text: enter image description here

The width of the text field is the width of the text inside, which is what I want. ✔️


Here is the empty field with a hint: enter image description here

When there is no text, the width of the text field is the width of the hint, which is what I want. ✔️


Here is the text field with a hint a long text that the hint: enter image description here

The field's width is the width of the text inside, which is what I want. ✔️


And here is a non-empty field with a hint and a small text: enter image description here

As you can see the field width is the width of the hint and not the text inside (which is not what I want ❌).

How can I force it to be the width of the actual text inside?

Valentin Vignal
  • 6,151
  • 2
  • 33
  • 73

2 Answers2

1

You could conditionally change the hintText based on the content of your TextEditingController:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

void main() => runApp(MyApp());

class MyApp extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final _controller = useTextEditingController();
    final _editing = useState(false);
    _controller.addListener(() => _editing.value = _controller.text.isNotEmpty);
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Container(
            alignment: Alignment.center,
            width: 300,
            child: Container(
              alignment: Alignment.center,
              padding: EdgeInsets.all(10),
              color: Colors.grey[100],
              child: Column(
                children: [
                  IntrinsicWidth(
                    child: TextFormField(
                      controller: _controller,
                      decoration: InputDecoration(
                        hintText: _editing.value
                            ? '' // empty hintText if field is not empty
                            : 'My very very long hint',
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}
Thierry
  • 7,775
  • 2
  • 15
  • 33
1

1. Set field width to the width of the text and not the hint

This was possible by using the onChanged() function in the TextField widget as shown below.

Also to be able to use setState, MyApp class needs to be a StatefulWidget.

onChanged: (value) => {
    if (value.isNotEmpty) {
        setState(() {
            hText = '';
        })
    } else {
        setState(() {
            hText = 'My very very long hint';
        })
    }
}

2. The Two Containers wrapping the TextField

I noticed despite using the IntrinsicWidth, the two containers are the ones constraining the size of the TextField.

If you were using the containers to set the TextField visible, another way to do that is to set the borders of the TextField.

The body of the Scaffold can look like this:

Center(
    child: IntrinsicWidth(
        child: TextField(
            onChanged: (value) => {
                if (value.isNotEmpty) {
                    setState(() {
                        hText = '';
                    })
                } else {
                    setState(() {
                        hText = 'My very very long hint';
                    })
                }
            },
            decoration: InputDecoration(
                enabledBorder: UnderlineInputBorder(
                    borderSide: BorderSide(color: Colors.black),
                ),
                focusedBorder: UnderlineInputBorder(
                    borderSide: BorderSide(color: Colors.teal),
                ),
                hintText:
                hText, // Remove it and the text field can be as small as the text inside
            ),
        ),
    ),
)

Full Code

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String hText = 'My very very long hint';
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: IntrinsicWidth(
            child: TextField(
              onChanged: (value) => {
                if (value.isNotEmpty)
                  {
                    setState(() {
                      hText = '';
                    })
                  }
                else
                  {
                    setState(() {
                      hText = 'My very very long hint';
                    })
                  }
              },
              decoration: InputDecoration(
                enabledBorder: UnderlineInputBorder(
                  borderSide: BorderSide(color: Colors.black),
                ),
                //When the TextFormField is ON focus
                focusedBorder: UnderlineInputBorder(
                  borderSide: BorderSide(color: Colors.teal),
                ),
                hintText:
                    hText, // Remove it and the text field can be as small as the text inside
              ),
            ),
          ),
        ),
      ),
    );
  }
}