bimbo1989
bimbo1989

Reputation: 822

How to make text as big as the width allows in flutter

I have a Container where I need to show a barcode and I'd love to have the barcode to be as wide as possible on the screen. For now I set the font size at a reasonable size that suits all devices, but it's only temporary of course. How can I solve this? This is the code I am using for building the Widget.

@override
Widget build(BuildContext context) {
  return Scaffold(
      appBar: AppBar(
      title: Text(_title),
    ),
    body: Container(
    padding: const EdgeInsets.all(12.0),
    child: Column(
      children: <Widget>[ 
        SizedBox(
          width: double.infinity,
          child: Text(_barcode, style: TextStyle(fontFamily: 'Code128', fontSize: 90.0))
        ),
        Text(_barcode, style: TextStyle(fontSize: 40.0))
        ]
      ),
    )
  );
}

Upvotes: 53

Views: 113483

Answers (8)

Henri Sauer
Henri Sauer

Reputation: 1

I have changed boformers answer so that the maximum font size is now calculated using a poroprtinoality factor. This approach saves considerable computation time because there is no loop anymore; each font size is now computed in the same time. In addition, the maximum height is also taken into account. Furthermore there is now an option to set a maximum font size, this can be useful for example if you want to have multiple text widgets with the same font size.

double calculateAutoscaleFontSize(String text, TextStyle style,
    double maxFontSize, double maxWidth, double maxHeight) {
  double calcWidth(
      double fontSize, TextPainter textPainter, String text, TextStyle style) {
    final nextTextStyle = style.copyWith(fontSize: fontSize);
    textPainter.text = TextSpan(text: text, style: nextTextStyle);
    textPainter.layout();
    return textPainter.width;
  }

  final textPainter = TextPainter(textDirection: TextDirection.ltr);
  final double width10 = calcWidth(10, textPainter, text, style);
  final double height10 = textPainter.height;
  final double width110 = calcWidth(110, textPainter, text, style);
  final double height110 = textPainter.height;

  final double withPerFontSize = (width110 - width10) / 100;
  final double heightPerFontSize = (height110 - height10) / 100;

  final double maxWidthFontSize = maxWidth / withPerFontSize;
  final double maxHeightFontSize = maxHeight / heightPerFontSize;
  final double maxCalculatedFontSize = maxWidthFontSize < maxHeightFontSize
      ? maxWidthFontSize
      : maxHeightFontSize;

  return maxCalculatedFontSize < maxFontSize
      ? maxCalculatedFontSize
      : maxFontSize;
}

I have tesed this, and if you round the result, it outputs the same font size as boformers answer. I could not test it for much fonts, but according to this answer, the font size is an approximation of the maximum height a letter should have. In this case, the width of each letter is proportionally dependent on this maximum height, which means it is also proportionally dependent on the font size. While this factor is not the same for every letter in every case, in my approach the letters do not change, so the factor remains the same for the entire text passage, which means my approach should work for every font.

Upvotes: 0

rmtmckenzie
rmtmckenzie

Reputation: 40503

I believe what you're looking for is FittedBox.

BoxFit applies whichever 'fit' you want to stretch/scale the child to fit in the box. It doesn't perform a pure 'stretch' on the text but rather the space it should take up. You shouldn't specify the text's size at the same time.

That looks like this:

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  MyAppState createState() {
    return new MyAppState();
  }
}

class MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: SafeArea(
          child: Center(
            child: Container(
              color: Colors.blue,
              width: 300.0,
              height: 200.0,
              child: FittedBox(
                fit: BoxFit.contain,
                child: Text("Whee"),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

If you're wanting to actually 'stretch' the text (i.e. make the actual characters wider or taller) you'll have to do something a bit more custom.

If that's the case, look at CustomPaint, CustomPainter, TextPainter, and the Canvas translate & scale options. Basically, you would need to create a class extending CustomPainter in which you created a TextPainter, laid it out at a particular size, painted it onto the canvas, and then scaled it to fit the actual size of the CustomPainter (or do you scale the canvas first - I forget...). Then you'd pass an instance of that class to CustomPaint.

Upvotes: 71

g2server
g2server

Reputation: 5367

The code sample in the question has a Text widget as one of the children: of a Column widget. The width of the Text parent is unknown.

So to maximise the width and size of the Text widget in this case, wrap the Text widget in a FittedBox, then an Expanded.

child: Column(children: <Widget>[
  Expanded(
    child: FittedBox(
        fit: BoxFit.contain,
        child: Text(
          '123',
        )),
  ),
]),

The Text size should also automatically resize correctly even when the device is rotatated, or the screen resized, without overflow issues.

Expanded:

/// A widget that expands a child of a [Row], [Column], or [Flex]
/// so that the child fills the available space.
///
/// Using an [Expanded] widget makes a child of a [Row], [Column], or [Flex]
/// expand to fill the available space along the main axis (e.g., horizontally for
/// a [Row] or vertically for a [Column]). If multiple children are expanded,
/// the available space is divided among them according to the [flex] factor.

from /flutter/packages/flutter/lib/src/widgets/basic.dart

FittedBox:

/// Creates a widget that scales and positions its child within itself according to [fit].

enter image description here

Upvotes: 12

Richard Bonneau
Richard Bonneau

Reputation: 1214

FittedBox is what worked for me but there is a twist. I also had to style my fontSize to a big number for it to work. Hope this helps.

child: FittedBox(
                  fit: BoxFit.fitHeight,
                  child: Text(
                    "Your Expanded Text :)",
                    style: TextStyle(fontSize: 400.0),
                  ),
                ),

Upvotes: 13

CopsOnRoad
CopsOnRoad

Reputation: 268444

FittedBox would only work if it is provided some constraints, so make sure to provide one, like provide height as shown below:

SizedBox(
  height: 400, // 1st set height
  child: FittedBox(child: Text("*")), // 2nd wrap in FittedBox
)

Upvotes: 4

wowlol
wowlol

Reputation: 301

Wrap the text within a FittedBox widget, to force the text to be enclosed by a box. The FittedBox's size will depend on it's parent's widget. Within the FittedBox, the Text widget, can simply 'cover' the box, so the text doesn't stretch to fill the available space within the FittedBox. The enum BoxFit.fill, is a way to stretch the text to fit the entire space available within the FittedBox. You can change the dimensions of the box by altering the height and width of the FittedBox's parent, the Container.

Container(
  height: _height,
  width: _width,
  FittedBox(               
    fit: BoxFit.fill,
    child: Text("Whee"),
  )
)

Upvotes: 2

hamza ahmad
hamza ahmad

Reputation: 61

you can use fitted box widget.

FittedBox(child:Text('text sample'));

https://api.flutter.dev/flutter/widgets/FittedBox-class.html

Upvotes: 3

boformer
boformer

Reputation: 30103

Use TextPainter.width and a for loop to find the largest fitting font size (adding +1 is not very efficient, you may want to fine-tune that):

import 'package:flutter/material.dart';

main() => runApp(MaterialApp(
      home: MyHomePage(),
      theme: ThemeData(platform: TargetPlatform.iOS),
    ));

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Text autoscale'),
      ),
      body: Padding(
        padding: EdgeInsets.all(32.0),
        child: Center(
          child: LayoutBuilder(
            builder: (BuildContext context, BoxConstraints constraints) {
              final text = 'Hello World';
              final style = TextStyle(fontWeight: FontWeight.bold); // apply your barcode font here
              final fontSize = calculateAutoscaleFontSize(text, style, 30.0, constraints.maxWidth);
              return Text(
                text,
                style: style.copyWith(fontSize: fontSize),
                maxLines: 1,
              );
            },
          ),
        ),
      ),
    );
  }
}

double calculateAutoscaleFontSize(String text, TextStyle style, double startFontSize, double maxWidth) {
  final textPainter = TextPainter(textDirection: TextDirection.ltr);

  var currentFontSize = startFontSize;

  for (var i = 0; i < 100; i++) {
    // limit max iterations to 100
    final nextFontSize = currentFontSize + 1;
    final nextTextStyle = style.copyWith(fontSize: nextFontSize);
    textPainter.text = TextSpan(text: text, style: nextTextStyle);
    textPainter.layout();
    if (textPainter.width >= maxWidth) {
      break;
    } else {
      currentFontSize = nextFontSize;
      // continue iteration
    }
  }

  return currentFontSize;
}

Upvotes: 2

Related Questions