text_wrap_auto_size

Wraps text and auto sizes it with respect to the given dimensions, including style, text properties and correct hyphenation.

  • Scales font size of widget of automatically.
  • Accessing the font size calculation programmatically.
  • Hyphenation for various languages.
  • Setting min font size and max font size.
  • Binary search instead of linear search for best font size.

Live demo here.

Hyphenation

hyphenatorx does the hyphenation for various languages.

Requirements

The widget requires a given width and height. It will throw an Exception, if it reveives an unbound (infinite) width or height. More about Flutter contraints here.

Philosophy

Text and TextStyle contain all relevant properties. The widgets and method calls of this package accept a Text object and respect its TextStyle.

Quickstart

Generally, several Text attributes are respected, style probably being the most important one. The attributes given below are not complete.

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

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

TextWrapAutoSize(text, 
    minFontSize: 50, // optional 
    maxFontSize: 100, // optional
);

// Or with automatic hyphenation:

TextWrapAutoSizeHyphend(text,'en_us', 
    minFontSize: 50, // optional 
    maxFontSize: 100, // optional
);

minFontSize and maxFontSize

minFontSize and maxFontSize will never cut off (clip) the text. In case minFontSize or maxFontSize result in a font size that does not render all the text, these parameters are ignored.

Language codes

Language codes available for hyphenation, based on tex codes:

[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]

Usage

Function call

The static method solution allows for accessing the computed data progrmmatically. The mmost important one is probably TextStyle, its fontSize set to the calculated font size.

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

// Or with automatic hyphenation:

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

// String text for easy reference.

print(sol.textString); // String 

// Text with TextStyle set.

print(sol.textString); // Text 

// TextStyle with adjusted font size.  
// All other style properties of the Text-parameter 
// are merged into it.

print(sol.style); // TextStyle 

// Resulting Size of the wrapped and auto sized text.
// Smaller or equal to the Size-parameter of the outer box.

print(sol.sizeInner); // Size

// The Size-parameter of the outer box for easy reference.

print(sol.sizeOuter); // Size

// Whether the calculated font size fits the outer box.
// This should not happen, except the font size is `1` 
// and the text still does not fit the outer box.

print(sol.isValid);

// How to output the font adjusted text yourself.

SizedBox(
    width: sol.sizeOuter.width,
    height: sol.sizeOuter.height,
    child: sol.text,
);

// How to output the font adjusted text yourself.
// Variant #2

SizedBox(
    width: c.widthExample,
    height: c.widthExample,
    child: Text(
        sol.text.data!,
        textScaleFactor: 1.0,
        style: sol.style
    ),
);

In case the Widgets are placed inside a Container with a hard padding and the Text misbehaves, calculate a soft padding to avoid sudden jumps of the text. These jumps should not happen and are most likely caused by internal rounding. In the future, a padding-parameter will be added which takes care of that.

// PROBLEM
// The hard padding may let the Text misbehave,
// especially on dynamic resizing.

Container(
    padding: EdgeInsets.all(16),
    width: 100,
    height: 100,
    child: TextWrapAutoSize(Text('text')),
)

// SOLUTION
// Use a soft padding, which is blank space around
// the centered Text.

Size outerBox = Size(100, 100);
double textPadding = 16;

final outerBoxAdjusted = Size(
    outerBox.width - textPadding * 2,
    outerBox.height - textPadding * 2);

Solution sol = TextWrapAutoSize.solution(
    outerBoxAdjusted, text);

final text = Container(
    width: outerBox.width,
    height: outerBox.height,
    alignment: Alignment.center,
    child: sol.text,
);

Widget

// Define width and height.

SizedBox(
    width:250,
    height:250,
    child: TextWrapAutoSize(Text('text'))
);

// With hyphens:

SizedBox(
    width:250,
    height:250,
    child: TextWrapAutoSizeHyphend(Text('text'), 'en_us')
);

// In some cases, width and height can be determined 
// by wrapping the widget in `Expanded`.

Expanded(
    child: TextWrapAutoSize(Text('text'))
);

// Or use it as the `Scaffold`'s body, also allows for 
// correct determination of width and height.

@override
Widget build(BuildContext context) {
    return SafeArea(
        child: Scaffold(
            body: TextWrapAutoSize(Text('text')),
        ),
    );
}

Render debug info.

TextWrapAutoSize(
    Text('text'), 
    doShowDebug: true
)

TextWrapAutoSizeHyphend(Text('text'), 
    'en_us', 
    doShowDebug: true
)

Alternatives

auto_size_text does something similar.

magic_text does something similar.

Background

Internally, the widget performs a binary-search for the optimal font size and renders the text multiple times in its own render-tree. In typical use cases, the widgets needs nine steps to find the optimal font size.

Todo

  • Clipping the text?
  • Add soft-padding support to avoid jumpy text using hard paddings?
  • Remodel interface.

Issues

Open an issue on Github.