Mary
Mary

Reputation: 20435

How to dynamically resize text in Flutter?

I retrieve a piece of text from an API. I want to allot a set amount of space to it (say a max Container with width: 300.0 and height: 100.0). Sometimes, the piece of text fits in this Container with font size 30.0. In other times, it won't fit unless I set the text size to 24.0.

Is there a way to dynamically resize text based on its parent container space?

I've built a Container with a ConstrainedBox, which lets me define the max size of the text space. I've also wrapped my Text with a LayoutBuilder. I was hoping that I could check the height of the space of the text, and based on that, determine how to size the text. Like this:

    Container(
      child: ConstrainedBox(
        constraints: BoxConstraints(
          minWidth: 300.0,
          maxWidth: 300.0,
          minHeight: 30.0,
          maxHeight: 100.0,
        ),
        child: LayoutBuilder(
            builder: (BuildContext context, BoxConstraints constraints) {
          if (/* height is larger than 100.0? height is over the constraints? */) { return textWithSize24(); }
          return textWithSize30();
        }),
      ),
    ),

How can I determine the "height that the text would take up if it were size 30.0"? Maybe I'm approaching this the wrong way and I'm supposed to use maxLines to determine this instead? But how do we know that we've reached more than maxLines?

The other way to do it is to use the number of characters in my String to determine when to change font sizes. This seems kind of manual.

Upvotes: 200

Views: 285240

Answers (14)

Dharaneshvar
Dharaneshvar

Reputation: 1678

Using a FittedBox with BoxFit.scaleDown and fixing the FontSize you can adjust the maximum size of the font.

If the content is small, it occupies the minimum width with the specified font size. At the same time, if the content is large, it resizes to the smallest font size.

FittedBox(
    fit: BoxFit.scaleDown,
    child: Text(
        "Text here",
        style: TextStyle(fontSize: 18),
    ),
)

If you need the text to fill the entire width, using any font size, use BoxFit.cover:

FittedBox(
    fit: BoxFit.cover,
    child: Text(
        "Text here",
        //style: TextStyle(fontSize: 18),
    ),
)

Upvotes: 148

Abdelah Chami
Abdelah Chami

Reputation: 51

I found A solution to shrink down the text with a max font size without using the plugin:

Expanded(
  child: FittedBox(
    fit: BoxFit.scaleDown,
    child: Text('${widget.article.price * chosenQuantity} DH',
      style: const TextStyle(fontSize: 25 , fontWeight: FontWeight.w700),
    ),
  ),
),

Normal state : normal text with 25 in fontSize

And if the Text is larger : the text shrinks as there is no more space

Upvotes: 5

Dabbel
Dabbel

Reputation: 2825

Had the same problem and wrote my own package, as my requirements where slightly different from the already known packages.

  • Scales font size of automatically.
  • But also accessing the font size calculation programmatically.
  • Hyphenation for various languages.
  • Binary search instead of linear search for best font size.

Status is "works for me", really wrote that primarily for my use case.

Package:

https://pub.dev/packages/text_wrap_auto_size

Demo:

https://xerik.github.io/text_wrap_auto_size/#/

final style = TextStyle(
    fontWeight: FontWeight.bold, 
    color: Colors.red,
    fontFamiliy: 'Courier',  
    // ...
);

final text = Text(
    'text',
    style: style,
    textAlign: TextAlign.center,
    // ...
);

TextWrapAutoSize(text);

// Or with automatic hyphenation:

TextWrapAutoSizeHyphend(text,'en_us');
Solution sol = TextWrapAutoSize.solution(
    Size size, Text text);

// Or with automatic hyphenation:

Solution sol = TextWrapAutoSizeHyphend.solution(
    Size size, Text text, 'en_us');

print(sol.textString); // String for easy reference

print(sol.style); // TextStyle with Font Size and all other attrs.

print(sol.sizeInner); // Size of Text Box

print(sol.sizeOuter); // Size of Outer Box

SizedBox(
    width: sol.sizeOuter.width,
    height: sol.sizeOuter.height,
    child: Text(sol.textString, style: sol.style),
);

Language code for hyphenation:

[af, as, bg, bn, ca, cop, cs, cy, da, de_1901, de_1996, de_ch_1901, el_monoton, el_polyton, en_gb, en_us, eo, es, et, eu, fi, fr, fur, ga, gl, grc, gu, hi, hr, hsb, hu, hy, ia, id, is, it, ka, kmr, kn, la_x_classic, la, lt, lv, ml, mn_cyrl_x_lmc, mn_cyrl, mr, mul_ethi, nb, nl, nn, or, pa, pl, pms, pt, rm, ro, ru, sa, sh_cyrl, sk, sl, sv, ta, te, th, tk, tr, uk, zh_latn_pinyin]

Upvotes: 1

Zia
Zia

Reputation: 683

For me, I had to surround FittedBox with Flexible. as bellow example.

Row(
    mainAxisAlignment: MainAxisAlignment.center,
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      Flexible(
        child: FittedBox(
          fit: BoxFit.fitWidth,
          child: Text(
              "${(matrix.duration! / 60).toStringAsFixed(0)} min. ",
              style: textStyle(context)),
        ),
      ),
      dot(context),
      Flexible(
        child: FittedBox(
          fit: BoxFit.fitWidth,
          child: Text(
              "${(matrix.distance! / 1000).toStringAsFixed(1)} km",
              style: textStyle(context)),
        ),
      )
    ]),

Both fit:BoxFit.fitWith and fit:BoxFit.scaleDown worked and have same experience.

Upvotes: 1

Felipe Sales
Felipe Sales

Reputation: 1139

I used it this way and it worked perfectly:

Flexible(
  child: FittedBox(
    fit: BoxFit.contain,
    child: AutoSizeText(
      'your specific text',
      maxLines: 1,
      style: TextStyle(),
    ),
  ),
),

This gives us flexibility and ensures that our child widget is inside and behaving as expected (containing), in addition to using auto_size_text.

Upvotes: 13

Baxter
Baxter

Reputation: 332

Responsive_Flutter, I had the same issues since reading your problem. I found it Works every time using this package to resize your fonts.

This Flutter package is for scaling the size your apps UI and fontSize across different sized devices. (The example shows the top text using the Responsive_Flutter package and the bottom text without plugin.

https://github.com/layounisl/responsive_flutter

You can install in 30 seconds - 3 Steps

  1. Add dependencies to yaml file responsive_flutter: ^0.0.4
  2. import 'package:responsive_flutter/responsive_flutter.dart';
  3. child: Text("Responsive flutter", style: TextStyle(fontSize: ResponsiveFlutter.of(context).fontSize(3)),

App using Flutter

Upvotes: 11

buckleyJohnson
buckleyJohnson

Reputation: 489

Easiest thing I found was auto_size_text: ^2.1.0. As simple as importing the dependency and using AutoSizeText() instead of Text()

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

void main() {
  runApp(App());
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: SizedBox(
            width: 200.0,
            height: 140.0,
            child: AutoSizeText(
              'This string will be automatically resized to fit in two lines.',
              style: TextStyle(fontSize: 30.0),
              maxLines: 2,
            ),
          ),
        ),
      ),
    );
  }
}

Upvotes: 3

Xheghun
Xheghun

Reputation: 21

You can also use the textScaleFactor attribute in the MediaQueryData class to limit how big a text get

Upvotes: 0

Diksha Pruthi
Diksha Pruthi

Reputation: 354

There are two approaches to do this.

  1. Using FittedBox
  2. Using TextPainter (calculate width with text Painter and update font )

https://prafullkumar77.medium.com/how-to-dynamically-resize-text-in-flutter-bca80415a4d2

Upvotes: 1

RTXGamer
RTXGamer

Reputation: 3714

FittedBox worked in my case with multiple lines.

SizedBox(
            width: MediaQuery.of(context).size.width,
            child: FittedBox(
              fit: BoxFit.contain,
              child: Text(
                widget.model.poem,
                textAlign: TextAlign.justify,
                style: TextStyle(
                    color: Colors.black,
                    fontWeight: FontWeight.w600,
                    fontStyle: FontStyle.normal),
              ),
            ),
          ),

Upvotes: 20

iPatel
iPatel

Reputation: 47049

You can use FittedBox to manage text based on height or width.

For Ex.

Simply just wrap your Text widget to FittedBox widget like, Here I want to resize my AppBar text based on width.

AppBar(
    centerTitle: true,
    title: FittedBox(
        fit: BoxFit.fitWidth, 
        child: Text('Hey this is my long text appbar title')
    ),
),

Text will be resized based on width of AppBar.

Upvotes: 383

Taur
Taur

Reputation: 564

Just generate the font size according to the text length and the maximum rendering area. 300.0 * 100.0 in your case, without forgetting the areas lost during the rendering of the text.

like this :

class MyWidget extends StatefulWidget {
  @override
  _StateMyWidget createState() => _StateMyWidget();
}

class _StateMyWidget extends State<MyWidget> {
  static const _QUOTES = [
    {"quote": "mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm", "author": "test"},
    {"quote": "Talk is cheap. Show me the code.", "author": "Linus Torvalds"},
    {"quote": "First, solve the problem. Then, write the code.", "author": "John Johnson"},
    {"quote": "To iterate is human, to recurse divine.", "author": "L. Peter Deutsch"},
    {"quote": "The best thing about a boolean is even if you are wrong, you are only off by a bit.", "author": "Anonymous"},
    {"quote": "Software is like sex: It’s better when it’s free.", "author": "Linus Torvalds"},
    {"quote": "The first 90% of the code accounts for the first 90% of the development time.  The remaining 10% of the code accounts for the other 90% of the development time.", "author": "Tom Cargill"},
    {"quote": "I think that it’s extraordinarily important that we in computer science keep fun in computing. When it started out it was an awful lot of fun. Of course the paying customers got shafted every now and then and after a while we began to take their complaints seriously. We began to feel as if we really were responsible for the successful error-free perfect use of these machines. I don’t think we are. I think we’re responsible for stretching them setting them off in new directions and keeping fun in the house. I hope the field of computer science never loses its sense of fun. Above all I hope we don’t become missionaries. Don’t feel as if you’re Bible sales-men. The world has too many of those already. What you know about computing other people will learn. Don’t feel as if the key to successful computing is only in your hands. What’s in your hands I think and hope is intelligence: the ability to see the machine as more than when you were first led up to it that you can make it more.", "author": "Alan J. Perlis"},
    {"quote":"Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live","author": "John Woods"},
    {"quote":"You've baked a really lovely cake, but then you've used dog shit for frosting.","author": "Steve Jobs"},
    {"quote": "Most software today is very much like an Egyptian pyramid with millions of bricks piled on top of each other, with no structural integrity, but just done by brute force and thousands of slaves.","author": "Alan Kay" },
    {"quote": "Software suppliers are trying to make their software packages more ‘user-friendly’…  Their best approach so far has been to take all the old brochures and stamp the words ‘user-friendly’ on the cover.","author": "Bill Gates"},
  ];

  static const AREA_LOST_PERCENT = 5;

  final rand = math.Random();

  @override
  initState() {
    super.initState();
    Timer.periodic(Duration(seconds: 3), (timeVal) {
      setState(() {});
    });
  }

  @override
  Widget build(BuildContext context) {
    final Map<String, String> mapQuote = _QUOTES[rand.nextInt(_QUOTES.length)];

    final authorW =
        Text(mapQuote["author"], style: TextStyle(fontStyle: FontStyle.italic));

    final quoteW = Text(
      mapQuote["quote"],
      textAlign: TextAlign.center,
      style: TextStyle(
        fontSize: autoSize(
          quoteLength: mapQuote["quote"].length,
          parentArea: (350 - 10 * 2 - 16 * 2) * (450 - 10 * 2),
        ),
      ),
    );

    final containerW0 = Container(
        height: 450.0,
        padding: EdgeInsets.all(10.0),
        color: Colors.grey,
        child: Center(child: quoteW));

    final containerW1 = Container(
        height: 500.0,
        width: 350,
        padding: EdgeInsets.all(16.0),
        color: Colors.purple,
        child: Column(children: [authorW, containerW0]));

    return containerW1;
  }

  double autoSize({@required int quoteLength, @required int parentArea}) {
    assert(quoteLength != null, "`quoteLength` may not be null");
    assert(parentArea != null, "`parentArea` may not be null");
    final areaOfLetter = parentArea / quoteLength;
    final pixelOfLetter = math.sqrt(areaOfLetter);
    final pixelOfLetterP = pixelOfLetter - (pixelOfLetter * AREA_LOST_PERCENT) / 100;
    return pixelOfLetterP;
  }
}

the escape characters must be taken into account during the allocation of the percentage of lost areas. it is best to remove them if the variation is excessive.

Online view -> dartpad.dev

Upvotes: 1

Simon Leier
Simon Leier

Reputation: 749

You can do it using the auto_size_text package:

Container(
  child: ConstrainedBox(
    constraints: BoxConstraints(
      minWidth: 300.0,
      maxWidth: 300.0,
      minHeight: 30.0,
      maxHeight: 100.0,
    ),
    child: AutoSizeText(
      "yourText",
      style: TextStyle(fontSize: 30.0),
    ),
  ),
);

You can also set maxLines to constrain the text even further or use presetFontSizes if you only want to allow specific font sizes.

Upvotes: 70

Mary
Mary

Reputation: 20435

Here is a list of things I tried which may work for you depending on your use case: https://github.com/flutter/flutter/issues/18431

Ultimately I went with a function that sets the font size based on the length of the String (i.e. with if-else statements).

Upvotes: 8

Related Questions