atreeon
atreeon

Reputation: 24087

Flutter TextField - how to shrink the font if the text entered overflows

I have a TextField (not a Text) widget that must remain on one line. I want to reduce it's font size if the text entered is too large for the TextField box, ie shrink it if it overflows. How can I do this?

I have written some code like this in a stateful component

if (textLength < 32) {
  newAutoTextVM.fontSize = 35.0;
} else if (textLength < 42) {
  newAutoTextVM.fontSize = 25.0;

In the view

fontSize: 25.0,

but it isn't very intelligent, it doesn't cope with resizing, also, because the font size isn't monospaced (courier etc), different characters take up different amounts of space.

Upvotes: 2

Views: 8228

Answers (2)

boformer
boformer

Reputation: 30103

Use a TextPainter to calculate the width of your text. Use a GlobalKey to get the size of your widget (A LayoutBuilder might be better to handle screen rotation).

import 'package:flutter/material.dart';

main() => runApp(MaterialApp(home: Home()));

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

const textFieldPadding = EdgeInsets.all(8.0);
const textFieldTextStyle = TextStyle(fontSize: 30.0);

class _HomeState extends State<Home> {
  final TextEditingController _controller = TextEditingController();
  final GlobalKey _textFieldKey = GlobalKey();

  double _textWidth = 0.0;
  double _fontSize = textFieldTextStyle.fontSize;

  @override
  void initState() {
    super.initState();
    _controller.addListener(_onTextChanged);
  }

  void _onTextChanged() {
    // substract text field padding to get available space
    final inputWidth = _textFieldKey.currentContext.size.width - textFieldPadding.horizontal;

    // calculate width of text using text painter
    final textPainter = TextPainter(
      textDirection: TextDirection.ltr,
      text: TextSpan(
        text: _controller.text,
        style: textFieldTextStyle,
      ),
    );
    textPainter.layout();

    var textWidth = textPainter.width;
    var fontSize = textFieldTextStyle.fontSize;

    // not really efficient and doesn't find the perfect size, but you got all you need!
    while (textWidth > inputWidth && fontSize > 1.0) {
      fontSize -= 0.5;
      textPainter.text = TextSpan(
        text: _controller.text,
        style: textFieldTextStyle.copyWith(fontSize: fontSize),
      );
      textPainter.layout();
      textWidth = textPainter.width;
    }

    setState(() {
      _textWidth = textPainter.width;
      _fontSize = fontSize;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Autosize TextField'),
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            TextField(
              key: _textFieldKey,
              controller: _controller,
              decoration: InputDecoration(
                border: InputBorder.none,
                fillColor: Colors.orange,
                filled: true,
                contentPadding: textFieldPadding,
              ),
              style: textFieldTextStyle.copyWith(fontSize: _fontSize),
            ),
            Text('Text width:'),
            Container(
              padding: textFieldPadding,
              color: Colors.orange,
              child: Row(
                children: <Widget>[
                  Container(width: _textWidth, height: 20.0, color: Colors.blue),
                ],
              ),
            )
          ],
        ),
      ),
    );
  }
}

Upvotes: 5

Andrea Grippi
Andrea Grippi

Reputation: 1589

I have searched through the docs and found a couple of solutions that could come at your help:

  • L̶o̶o̶k̶ ̶a̶t̶ ̶t̶h̶e̶ ̶o̶f̶f̶i̶c̶i̶a̶l̶ ̶d̶o̶c̶s̶[̶1̶]̶,̶ ̶i̶n̶ ̶p̶a̶r̶t̶i̶c̶u̶l̶a̶r̶e̶ ̶a̶t̶ ̶t̶h̶e̶s̶e̶ ̶p̶r̶o̶p̶e̶r̶t̶i̶e̶s̶:̶ ̶ ̶m̶a̶x̶L̶i̶n̶e̶s̶,̶ ̶o̶v̶e̶r̶f̶l̶o̶w̶ ̶a̶n̶d̶ ̶s̶o̶f̶t̶W̶r̶a̶p̶ (These are TextBox properties, not TextFields)
  • Have a look at this thread where they suggest to wrap the TextBox/TextFeld with a Flexible Widget

Depending on the rest of your code one of these solutions could be better, try tweaking around.
Hope it helps.

Upvotes: -1

Related Questions