Flutter: Constraints not constraining widget (explanation needed) - dart

class Test extends StatelessWidget {
Widget build(BuildContext context) {
return UnconstrainedBox(
child: Container(
height: 250.0,
width: 250.0,
decoration:
BoxDecoration(shape: BoxShape.circle, color: Colors.red),
child: Opacity(
opacity: 0.5,
child: Container( // WIDGET IN QUESTION
constraints:
BoxConstraints.expand(width: 50.0, height: 50.0),
color: Colors.yellow))));
}
}
According to the Container class documentation...
If the widget has no child and no alignment, but a height, width, or constraints are provided, then the Container tries to be as small as possible given the combination of those constraints and the parent's constraints.
Instead, the widget is trying to be as large as possible (size of parent) rather than 50x50. I understand that I can use something like UnconstrainedBox, but I'm looking for an explanation of this behavior.
Looking for:
Currently getting:

The problem is your root Container.
By setting a width+height without an alignment, Container forces its child to fill the available space.
If you want that child take the least amount of space, you need to specify your root container how it should align its child within its bounds.
Container(
width: 250,
height: 250,
alignment: Alignment.center,
child: Whatever(),
);

Related

Is there a more optimal way to space widgets within a column?

So i'm emulating an old app I have where the UI looks like this
Currently i'm focused on creating the UI layout for the purple side bar, I did that by creating a Container with a purple background. Within the container I created a column with multiple children and just used an empty SizedBox to create distance between one widget from another.
import 'package:flutter/material.dart';
class SignInPage extends StatelessWidget {
const SignInPage({super.key});
#override
Widget build(BuildContext context) {
return Scaffold(
// appBar: AppBar(
// title: Text('Sample Text'),
// elevation: 5.0,
// ),
body: _buildContent(),
);
}
Widget _buildContent() {
// private method
return Container(
color: Colors.deepPurpleAccent,
padding: EdgeInsets.all(16.0),
child: Column(
// crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Container(
color: Colors.orange,
child: SizedBox(
height: 100.0,
width: 80.0,
),
),
SizedBox(
height: 140.0,
),
Container(
color: Colors.red,
child: SizedBox(
height: 50.0,
width: 80.0,
),
),
SizedBox(
height: 8.0,
),
Container(
color: Colors.purple,
child: SizedBox(
height: 50.0,
width: 80.0,
),
),
],
),
);
}
}
I'm quite new to flutter so i'm wondering if there's a better way to structure my layout of the side bar? Also to think in the future, since the name "BagTrack" Is on the same level as "Analytic Overview" Should that just be one giant row"?
Actual answer
Method 1
#Mahfuzur Rahman answer is good, but to actually answer your question about other ways. Flex widgets (Column and Row extend the Flex widget), have both mainAxisAlignment and crossAxisAlignment, they can be used to align them more easily between different devices/screens sizes.
So maybe grouping your red and purple boxes in a Column with mainAxisSize set to MainAxisSize.min, and aligning the surrounding column with a specific size.
https://api.flutter.dev/flutter/widgets/Column-class.html
Method 2
Another way, if you would like some widget to occupy some percentage amount of space from it's parent, I suggest you look into Expanded widget, Flexible widget.
Method 3
Or even FractionallySizedBox could be a good widget for you, but then I would also look at this LayoutBuilder widget to solve the potential Unbounded Height/Width exception.
Second smaller question
It's entirely up to you to decide about your second question concerning the giant row. I wouldn't do it though. Probably would use a const SizedBox or const EdgeInsets (for Padding) and keep them at the same height this way.
Just complementing Flutter knowledge
PS: Since you are new to Flutter. As a suggestion for future performance: avoid the Container widget as much as you know, there are a lot of simpler widgets like SizedBox, ColoredBox, DecoratedBox and Padding that you can use in its place that could be marked as const sometimes and be less expensive.
For understanding final and const:
final is a variable that cannot be reassigned by accident inside your code. When you instantiate it you give it a value and that's it. (Using late changes that a bit but not much);
const is a variable assigned by the compiler, if you are familiar with C it's like #define but there is a little difference, every time you say const EdgeInsets.all(8) for example, the compiler will detect that and use the same variable, so you don't have to remember a specific constant variable name.
Yes there is. But using SizedBox also wont hurt.
I usually Prefer ListTile for each element in the drawer.
ListTile(
leading: GestureDetector(
behavior: HitTestBehavior.translucent,
onTap: () {},
child: Container(
width: 48,
height: 48,
padding: const EdgeInsets.symmetric(vertical: 4.0),
alignment: Alignment.center,
child: const CircleAvatar(),
),
),
title: const Text('title'),
dense: false,
)

Flutter PageView.builder with animated Children sizes

I'm trying to recreate this:
I've tried with a PageView.builder, and got this far (sorry for the ugliness of colors but it's for visualization purposes):
This is the Code:
Container(
height: 40 * 2 + 100.0,
child: PageView.builder(
scrollDirection: Axis.horizontal,
controller: controller,
itemCount: 9,
itemBuilder: (context, index) {
double value = 1.0;
try {
value = controller.hasClients
? controller.page - index
: 1.0;
} catch (e) {
value = 1.0;
}
value = value.abs().clamp(0.0, 1.0);
value = 1 - value;
return Padding(
padding: EdgeInsets.symmetric(horizontal: 6.0),
child: Container(
width: value == 1.0 ? 80.0 : 60.0,
height: 30.0,
color: Color.lerp(Colors.green,
Colors.red, value),
child: Center(
child: Text(
index.toString(),
style: TextStyle(
color: Color.lerp(
Colors.red, Colors.black, value),
fontSize: lerpDouble(20.0, 40.0, value)),
)),
),
);
},
))
And the controller variable is this:
controller = new PageController(initialPage: 5, viewportFraction: 1/4);
controller.addListener(() {
setState(() {});
As you can see I managed to change the color and textSize to the "selected" item (the red ones), but even If I try to set its size to be bigger, it has no effects. I tried with a ListView.builder which made me set different sizes but with it I didn't have page Snapping and a default initial Page, so I opted out.
hope everything is clear.
Is it possible to do it, and if it is, how Could I do it?
I know its an old question but you can change width and height by changing margin of the container. You have to basically change a container (this one inside the Padding widget) margins values by value of your "value" variable and also I would highly suggest using AnimatedContainer for out of the box animations. Add this code to your container:
EdgeInsets.only(bottom: 50, right: value == 1.0? 20 : 40, left: value == 1.0? 20 : 40, top: value == 1.0? 150 : 250),
You should also remove this code from the container:
width: value == 1.0 ? 80.0 : 60.0,
height: 30.0,

CustomPainter Path extra line on canvas

I am using CustomPainter where I need to draw line
class ShapesPainter extends CustomPainter {
#override
void paint(Canvas canvas, Size size) {
final Paint firstPaint = Paint();
firstPaint.color = const Color.fromARGB(255, 236, 0, 140);
final Path firstPath = Path();
firstPath.lineTo(size.width, 0);
firstPath.lineTo(0, size.height * 0.10);
firstPath.close();
canvas.drawShadow(firstPath, Colors.black87, 2.0, false);
canvas.drawPath(firstPath, firstPaint);
}
}
I need to leave margin around the screen so I am using margin in container:
........
Container(
color: Colors.white,
margin:
EdgeInsets.only(top: 60.0, bottom: 20.0, left: 15.0, right: 15.0),
child: Container(
child: CustomPaint(
painter: ShapesPainter(),
child: Container(
.......
I need to draw a shadow under my custom path which I used anvas.drawShadow method in my build Widget, But there is a little shadow also coming over the left side, Please see the image below I pointed an error, here is pointing arrow to small shadow:
As I couldn't find any solution for the canvas.drawShadow effect on Path, I just created another Path() on top of the shadowed path, which solved the issue, but its kind of a hack.
final Path firstPathb = Path();
firstPathHide.lineTo(size.width, 0);
firstPathHide.lineTo(-10.0, size.height * 0.10);
firstPathHide.close();
canvas.drawPath(firstPathHide, firstPaint);

Flutter AutoResizeTextView, setting high fontSize overflows

In Android, we can use AutoResizeTextView and give it any text size of our choice, it will not flow out of its constraints, I was looking for similar solution in Flutter.
I tried following,
Container(
color: Colors.blue,
constraints: BoxConstraints(maxHeight: 200.0, minWidth: 600.0),
child: Text("8", style: TextStyle(fontSize: 400.0)),
);
Here is the ugly output. So, how can I force the Text to always stay inside the Container no matter how much fontSize is given to it
Thanks to pskink,
I need to wrap the Text inside FittedBox which is further wrapped inside a Container. Here is the solution.
Container(
color: Colors.blue,
constraints: BoxConstraints(maxHeight: 200.0, minWidth: 600.0),
child: FittedBox(child: Text("8", style: TextStyle(fontSize: 400.0))),
);

Position widget in stack with percent

Let's say I want to position a widget inside a Stack but with a percent of the Stack position instead of a fixed size. How to do that in flutter ?
I'd expect that the Positionned.fromRelativeRect constructor would be the thing, using floats between 0 and 1. But seems like no.
Align allows to position the widget in percent. But heightFactor and widthFactor changes the Align size instead of the child size. Which is not what I want.
You can combine a Positioned.fill and LayoutBuilder to achieve such result.
new Stack(
children: <Widget>[
new Positioned.fill(
child: new LayoutBuilder(
builder: (context, constraints) {
return new Padding(
padding: new EdgeInsets.only(top: constraints.biggest.height * .59, bottom: constraints.biggest.height * .31),
child: new Text("toto", textAlign: TextAlign.center,),
);
},
),
)
],
),
one thing that i figured out not long ago is that you can position a widget on the screen using a container its alignment parameter with the help of the Alignment.lerp(x,y,z) function
//the widget will be placed in the center of the container
alignment: Alignment.lerp(Alignment.topCenter, Alignment.bottomCenter, 0),
//the widget will be placed in the bottom of the container
alignment: Alignment.lerp(Alignment.topCenter, Alignment.bottomCenter, 1),
//the widget will be placed in the bottom quarter of the container
alignment: Alignment.lerp(Alignment.topCenter, Alignment.bottomCenter, 0.5),
//the widget will be placed in the top quarter of the container
alignment: Alignment.lerp(Alignment.topCenter, Alignment.bottomCenter, -0.5),
use FractionalOffset & FractionallySizedBox it's very simple in contrast
around no unnecessary code like Positioned.fill
no no extra calculations like Alignment
...
Container(
color: Colors.blue[200],
alignment: FractionalOffset(0.7, 0.6),
child: FractionallySizedBox(
widthFactor: 0.1,
heightFactor: 1/3,
child: Container(color: Colors.red[900])
),
),
...
If you want to use LayoutBuilder then do without Positioned.fill, like this:
you need one LayoutBuilder, no need to turn around every elements and use Transform.translate instead of Padding.
new LayoutBuilder(
builder: (context, constraints) {
return Stack(
children: <Widget>[
Transform.translate(
offset: Offset(
constraints.biggest.width * left,
constraints.biggest.height * top),
child: new Text("toto", textAlign: TextAlign.center,),
),
...
],
);
}
)

Resources