48

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))
        ]
      ),
    )
  );
}
bimbo1989
  • 727
  • 1
  • 8
  • 20

7 Answers7

62

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.

Farshid Zaker
  • 1,960
  • 2
  • 22
  • 39
rmtmckenzie
  • 37,718
  • 9
  • 112
  • 99
  • Actually you can measure the width of text before you paint it with the `TextPainter` with `TextPainter.width` – boformer Aug 03 '18 at 21:08
  • yeah I could have been clearer, that's exactly what I was telling you to use; but if you want the text then scaled at a non-natural width/height ratio you have to do it yourself. – rmtmckenzie Aug 03 '18 at 21:46
  • 1
    FittedBox did it! I only made a few changes: removed the height setting and set the width to MediaQuery.of(context).size.width. – bimbo1989 Aug 06 '18 at 08:42
  • 1
    @bimbo1989 An easier way to make the text as wide as the screen is to set the width to `double.infinity`. – Carl Smith Mar 31 '23 at 20:59
  • @CarlSmith that will only be as wide as the screen if the surrounding box _can_ expand to the size of the screen, which greatly depends on the bounding box and the parent of the Container in the example I gave. Setting `width=double.infinity` will only make the text as wide as the screen in specific cases. – rmtmckenzie Apr 02 '23 at 01:16
13

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),
                  ),
                ),
Richard Bonneau
  • 1,076
  • 11
  • 9
10

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

g2server
  • 5,037
  • 3
  • 29
  • 40
3

you can use fitted box widget.

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

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

3

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
)
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
2

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;
}
boformer
  • 28,207
  • 10
  • 81
  • 66
  • While this might work, it's not really the greatest way of doing things.... if you have multiple of these on your page, it could cause performance issues. – rmtmckenzie Aug 03 '18 at 21:54
  • 1
    I won't downvote since it seems like it would work, but I'd advise you to find another solution. – rmtmckenzie Aug 03 '18 at 21:56
  • yeah, of course the for loop is not really efficient, but it should work when there are just a few barcodes. – boformer Aug 04 '18 at 11:52
2

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"),
  )
)
wowlol
  • 241
  • 2
  • 5