Reputation: 4316
I'm wondering if there are ways to detect the overflow happening in widgets and return a boolean to make some changes Depending on the true
or false
value.
For example, I have a Container with text inside. I want to detect when the text overflows the container, after which I will make the container bigger with some conditions. I know there are some ways like auto_size_text
but the point here is to detect the overflow.
Upvotes: 17
Views: 11901
Reputation: 36333
Trying to detect when an overflow happens, looks like an XY Problem to me. Let's stop and think: why do we want to detect an overflow? What can we do when it overflows?
In Flutter, child widgets' sizes are constrained by their parents, there are very few cases where an overflow can happen. For example, consider this code below:
Container(
height: 100,
width: 100,
color: Colors.red,
child: Container(
width: 200,
height: 800,
color: Colors.green,
),
)
To an inexperienced Flutter developer, it may look like the inner Container (200x800) will overflow its parent (100x100), but in fact, the inner's sizes will be auto adjusted to match its parent, so you won't see the black/yellow overflow stripes, you will just see a 100x100 container on your screen, in green.
So, when can we expect children to overflow in Flutter? Usually there are 4 types of cases where things can overflow:
Column
and Row
widgets are inherited from Flex
widget, which you can also use directly. In these widgets, when performing layout, they will pass an "unbounded" constraint in their main axis. For example, in the case of a Column
, its main axis is the vertical axis, so it would allow its children to be however tall they want (but not however wide). If its children's combined height exceeds the layout constraint for Column
itself (passed down by Column's parent), it will overflow. Basically, if you put a Column inside a 100x100 Container, then the Column cannot exceed 100 in height, and if its children do, it overflows.For these cases, detection is doable but often unnecessary. To detect, for example, you can first use LayoutBuilder
to get the parental constraint for the Column
itself, so you know what's the maximum size you can possibly have (say 100x100, because the Column is placed in such Container), then you need to obtain the size of each children, which is not too easy for a Column because you won't know their sizes before the layout process is completed. (If you must know their size before layout is completed, you can consider looking into CustomMultiChildLayout
or write a custom RenderBox
). You can maybe use GlobalKey
to get the size of any widget, but that would have to be done in the next frame, once the children are first laid out.
Perhaps a better question to ask here is: why do you want to know whether it will overflow? And if it overflows, what can you do? If you can allocate more space to the Column, then why not do so in the first place? You can set MainAxisSize.min
on Column to tell it to shrink, and not waste all those extra space unless it really needs to. Or, perhaps you want to allow scrolling? If so, simply wrap the whole Column
with a SingleChildScrollView
widget, and it will start scrolling automatically whenever the Column grows too tall. (BTW, this is NOT the same as using ListView
, you should use ListView
if you have lots of items to scroll.)
Stack: widgets in a stack, especially those Positioned
ones, can overflow the stack if you purposely do so. For example, by setting left: -10, top: -20
you are knowingly rendering a widget outside of the bound (a little bit over the top left corner). But stack will clip it for you automatically, so you won't even see those overflown content. To see them, you actually need to manually change the clipBehavior
of the stack. If you are doing all that work, I don't think a "detection algorithm" is exactly necessary.
Text: text is tricky, depending on the font size (might even be modified by the user at system level), font weight, font family, and many other factors, it's very difficult to calculate how much room a paragraph will take. This is where TextPainter
can be used to help you do all the calculations, layout
the text first, and then you can read its width and height.
OverflowBox: other possible ways to overflow in Flutter are to intentionally use widgets such as OverflowBox
or SizedOverflowBox
and etc. These widgets purposely break the parent constraints, but if you are using these, I'm sure you know what you are doing. Depending on the use case, you can consider using CustomMultiChildLayout
to get the size of each child, before deciding where to place them.
Final remarks: since you probably want a "general solution", so here it is: in Flutter, most things cannot overflow. If needed, LayoutBuilder
widget can be used to determine the current layout constraints of a widget, so you know the max and min size your parent wants you to be. To get the size of your child widget(s), the best way is to use either CustomSingleChildLayout
or CustomMultiChildLayout
during the layout process. To get the size and position of any widget that's already on the screen, you can use GlobalKey
. However, it's often more important to consider WHY you want to know these sizes, rather than HOW to get them.
Upvotes: 15
Reputation: 4750
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
var dimension = 40.0;
increaseWidgetSize() {
setState(() {
dimension += 20;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(children: <Widget>[
Text('Dimension: $dimension'),
Container(
color: Colors.teal,
alignment: Alignment.center,
height: dimension,
width: dimension,
// LayoutBuilder inherits its parent widget's dimension. In this case, the Container in teal
child: LayoutBuilder(builder: (context, constraints) {
debugPrint('Max height: ${constraints.maxHeight}, max width: ${constraints.maxWidth}');
return Container(); // create function here to adapt to the parent widget's constraints
}),
),
]),
),
floatingActionButton: FloatingActionButton(
onPressed: increaseWidgetSize,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Via this Code you will get the current container height and width now you can compare this to the device height and width and check overflow .
height = MediaQuery.of(context).size.height;
weight = MediaQuery.of(context).size.width;
Upvotes: 0
Reputation: 7601
I don't think altering size of container is a good idea, what if the container overflow again it's parent? the proper way is to resize the text inside the container by using this, auto_size_text:
AutoSizeText(
'The text to display',
style: TextStyle(fontSize: 20),
maxLines: 2,
)
Upvotes: -2
Reputation: 20118
Using TextPainter we can measure width of text, and with LayoutBuilder we can determine the bounding box:
import 'package:flutter/material.dart';
class Demo extends StatefulWidget {
@override
_DemoState createState() => _DemoState();
}
class _DemoState extends State<Demo> {
String text = "text";
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
setState(() {
text += " text";
});
},
),
body: Padding(
padding: const EdgeInsets.all(32.0),
child: LayoutBuilder(
builder: (ctx, constraints) {
final style = Theme.of(context).textTheme.headline1;
final span = TextSpan(
text: text,
style: style,
);
final painter = TextPainter(
text: span,
maxLines: 1,
textScaleFactor: MediaQuery.of(context).textScaleFactor,
textDirection: TextDirection.ltr,
);
painter.layout();
final overflow = painter.size.width > constraints.maxWidth;
return Container(
color: overflow ? Colors.red : Colors.green,
child: Text.rich(span, style: style),
);
},
),
),
);
}
}
Demo on CodePen
Upvotes: 6