I want to have a widget of arbitrary width that contains a row of three widgets sized relatively to its parent.
FractionallySizedBox sounds like the right tool for the job, so I tried it like this:
Container(height: 24.0, color: Colors.black, child:
Row(children: [
FractionallySizedBox(heightFactor: 1, widthFactor: 0.25,
child: Container(color: Colors.orange)),
FractionallySizedBox(heightFactor: 1, widthFactor: 0.15,
child: Container(color: Colors.green)),
FractionallySizedBox(heightFactor: 1, widthFactor: 0.05,
child: Container(color: Colors.blue)),
]
)
But I'm getting an error
BoxConstraints forces an infinite width.
This happens even when I set some width on the enclosing container.
Why doesn't this work?
As explained in the "FractionallySizedBox (Flutter Widget of the Week)" video by the Flutter team, the FractionallySizedBox needs to be wrapped in Flexible widget, "so it plays well with a row or a column".
Reference: https://www.youtube.com/watch?v=PEsY654EGZ0
This should be the solution to the original question, formatted below:
Container(
height: 24.0,
color: Colors.black,
child: Row(
children: [
Flexible(
child: FractionallySizedBox(
heightFactor: 1, widthFactor: 0.25,
child: Container(color: Colors.orange),
),
),
Flexible(
child: FractionallySizedBox(
heightFactor: 1, widthFactor: 0.15,
child: Container(color: Colors.green)),
),
Flexible(
child: FractionallySizedBox(
heightFactor: 1, widthFactor: 0.05,
child: Container(color: Colors.blue)
),
),
]
)
)
I have also worked on something similar and was looking for a solution to the problem, this is my code for representing data levels:
Row(
children: <Widget>[
Flexible(
child: FractionallySizedBox(
widthFactor: 1,
child: Container(
margin: EdgeInsets.only(right: 5),
height: 6,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(125)),
),
)
),
),
Flexible(
child: FractionallySizedBox(
widthFactor: 1,
child: Container(
margin: EdgeInsets.only(right: 5),
height: 6,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(125)),
),
)
),
),
Flexible(
child: FractionallySizedBox(
widthFactor: 1,
child: Container(
margin: EdgeInsets.only(right: 5),
height: 6,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(125)),
),
)
),
),
Flexible(
child: FractionallySizedBox(
widthFactor: 1,
child: Container(
margin: EdgeInsets.only(right: 5),
height: 6,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(125)),
),
)
),
),
Flexible(
child: FractionallySizedBox(
widthFactor: 1,
child: Container(
margin: EdgeInsets.only(right: 5),
height: 6,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(125)),
),
)
),
),
],
),
Resulting in:
evenly spaced bars
You can't use FractionallySizedBox in Row or Column directly. You can split as percent Row and Column use flex parameter of Expand widget like below.
Row(
children: <Widget>[
Expanded(
flex: 6, // 60% of space => (6/(6 + 4))
child: Container(
color: Colors.red,
),
),
Expanded(
flex: 4, // 40% of space
child: Container(
color: Colors.purple,
),
),
],
),
FractionallySizedBox seems to only work outside of Row, Column, and Wrap widgets. I would recommend this approach instead:
class FractionalPositioningQuestion extends StatelessWidget {
#override
Widget build(BuildContext context) {
// Replace with the size your parent widget is.
final pWidth = MediaQuery.of(context).size.width;
return Row(children: <Widget>[
Container(
width: pWidth * 0.25,
color: Colors.orange,
),
Container(
width: pWidth * 0.15,
color: Colors.green,
),
Container(
width: pWidth * 0.05,
color: Colors.blue,
),
],
mainAxisAlignment: MainAxisAlignment.center, // Depending on what you want
);
}
}
You still need to find a way to obtain the arbitrary width of the parent widget though.
Hope this helps a little at least :-)
FractionallySizedBox refused to work within Rows.
Much easier to use the flex field of Expanded.
import 'package:flutter/material.dart';
myRow() => Row(children: [
Expanded(
flex: 8,
child: Container(
height: 100,
color: Colors.green,
),
),
Expanded(
flex: 2,
child: Container(
height: 100,
color: Colors.red,
),
),
]);
content() => Column(children: [
Text(" ----Start ----"),
myRow(),
Text(" ----End ----"),
]);
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) => MaterialApp(
title: 'Flutter Playground',
home: Material(
child: SafeArea(child: Container(width: 80, child: content()))));
}
FractionallySizedBox doesn't work as expected in rows and columns, even wrapped in a Flexible widget.
tldr: It seems like the heightFactor (for columns) is applied not to the box itself, but to the column overall. This is a neat effect, but not what is expected
Details:
I made a DartPad that lets you see it.
OrangeBoxes have heighFactor=0.5 (you can change it in the code)
BlueBoxes are fixed at 20px by 20px.
Problem1
Expected Behavior: Two OrangeBoxes take up the whole screen. Half each.
Actual Behavior: Two OrangeBoxes each take up 1/4.
Problem2
Expected Behavior: One OrangeBox takes up half the screen. Adding BlueBoxes doesn't make the Orange Box smaller.
Actual Behavior: Adding BlueBoxes makes the OrangeBox smaller. So that the whole column takes up half the screen. Then, if many Blue Boxes added, the whole column eventually grows.
Photos
I also tested this on Android, and I get the same rendering behavior from a quick-and-dirty check.
Extra Notes
The gist is here if someday the dartpad link breaks
https://gist.github.com/chonghorizons/0d97a4202a0cd8838aa25713f1c19c13
Related
This is my first question and I'm new in Flutter. I searched a lot but I did not find any suitable answers.
Flutter: I need to set my box height flexible to fit any size of screen. I also need to fit my last box to the right side of screen to fit any screen. I also mentioned my problem in the image of screen. Kindly help. I'm adding my code here.
Output and requirement of my code
void main() {
runApp(const Challange2());
}
class Challange2 extends StatelessWidget {
const Challange2({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
backgroundColor: Colors.blueGrey,
body: SafeArea(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Container(
height: 850.0,
width: 100.0,
color: Colors.amberAccent,
child: Text(
'This box height should be flexible according to screen size',
style: TextStyle(color: Colors.black, fontSize: 25),
),
),
const SizedBox(
width: 65,
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 100.0,
width: 100.0,
color: Colors.deepPurpleAccent,
child: Text(
'Text2',
style: TextStyle(color: Colors.white, fontSize: 25),
),
),
Container(
height: 100.0,
width: 100.0,
color: Colors.deepOrangeAccent,
//Flexible(
child: Text(
'Text3',
style: TextStyle(color: Colors.white, fontSize: 25),
),
),
//),
],
),
const SizedBox(
width: 65,
),
Container(
height: 850.0,
width: 100.0,
color: Colors.blue,
child: Text(
'This box need to be right alignment and height should be flexible according to screen size',
style: TextStyle(color: Colors.black, fontSize: 25),
),
// child: const Align(
// alignment: Alignment.topCenter,
),
],
),
),
),
//),
//return Container();
);
}
}
To adapt to screen height, you can set height of containers to double.infinity,
To keep rigth container aligned to the right, use expanded in the center row child, in order to make this widget grow or shrink as needed.
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Row(children: [
Container(
width: 100,
height: double.infinity,
color: Colors.red,
child: Text(
'This box height should be flexible according to screen size'),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 50,
height: 50,
color: Colors.green,
),
Container(
width: 50,
height: 50,
color: Colors.yellow,
),
]),
),
Container(width: 100,
height: double.infinity,
color: Colors.blue, child:Text("Right Panel"),),
]),
);
}
}
LayoutBuilder() Provides a way to get the actual screen size dynamically and allows you to calculate relative sizes: Youtube - Flutter: LayoutBuidler
Generally when using Rows and Columns you can use Expanded widgets to have relative instead of absolute sizes:
Row(
children: [
Expanded(flex: 1, child: Container()), // This will take up 1 third of the screen
Expanded(flex: 2, child: Container()), // This will take up 2 thirds of the screen
]
),
Expanded(flex: 1, child: Container()),
Expanded(flex: 1, child: Container()),
I'm building an AR app with Flutter and using the ar_flutter_plugin.
The position of the AR widget seems to be fixed to the top left and it doesn't move anywhere else. Whatever the other widgets in the tree, doesn't change anything. It's always at top left corner (see image).
It works with a dummy widget just fine (compare second img).
Child Ar widget
Widget build(BuildContext context) {
return SizedBox(
width: widget.width,
height: widget.height,
child: Column(
children: [
Expanded(
child: Column(children: [
Expanded(
child: ARView(
onARViewCreated: onARViewCreated,
planeDetectionConfig:
PlaneDetectionConfig.horizontalAndVertical,
),
),
Align(
alignment: Alignment(1, 1),
child: FloatingActionButton(onPressed: takePicture))
]),
),
],
));
}
Parent Widget
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Title'),
),
key: scaffoldKey,
backgroundColor: FlutterFlowTheme.of(context).primaryBackground,
body: SafeArea(
child: GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Stack(
children: [
Container(
width: 350,
height: 350,
child: ObjectsOnPlanesWidget(
// child: custom_widgets.ArPlaceholder(
width: double.infinity,
height: double.infinity,
modelUrl: widget.modelUrl,
),
),
Align(
alignment: AlignmentDirectional(-0.23, -0.92),
child: Padding(
padding: EdgeInsetsDirectional.fromSTEB(8, 24, 8, 24),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color:
FlutterFlowTheme.of(context).secondaryBackground,
borderRadius: BorderRadius.circular(12),
),
child: InkWell(
onTap: () async {
Navigator.pop(context);
},
child: Icon(
Icons.chevron_left,
color: Colors.black,
size: 24,
),
),
),
Text(
'Try On',
style: FlutterFlowTheme.of(context).bodyText1,
),
Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color:
FlutterFlowTheme.of(context).secondaryBackground,
borderRadius: BorderRadius.circular(12),
),
child: Icon(
Icons.share_outlined,
color: Colors.black,
size: 24,
),
),
],
),
),
),
Align(
alignment: AlignmentDirectional(0, 0.95),
child: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: FlutterFlowTheme.of(context).secondaryBackground,
borderRadius: BorderRadius.circular(12),
),
child: Icon(
Icons.photo_camera,
color: Colors.black,
size: 24,
),
),
),
],
),
),
),
);
}
I want to put the camera in the background and put other widgets (such as photo-taking-button) on top of it.
How do I reposition it into a container? At least under the App Bar?
Logically this should have worked, what i would suggest you try would be to first have a SafeArea widget.
SafeArea(
child: build(buildContext),
),
I had a similar issue in the past and using this solved it.
Also, there seems to be an open issue which seems related to this with the following possible solution :
With Flutter 2.10.5. it works without problems. This seems to be a Flutter 3 problem.
The way platforms views are rendered was changed in the 3.0 release.
probably same issue:
flutter/flutter#106246
explained here:
flutter/flutter#103630
I hope this will be fixed in new Flutter 3 releases soon.
And also this
I try the master channel Flutter v3.1.0-0.0.pre.1372 and the issue is fixed there.
Fixed will come to a stable channel soon with Flutter v3.1.0.
Also, I am not sure why are you using SizedBox as your parent widget.
Why not try a basic Container instead?
Good luck, Let me know if I can help with anything else
I have a simple card design, my card is wrapped with a container with a specific height and width, so the children are bounded
The card has a picture from Image.network and a text at the bottom, both occupying half the height of the card
However, my code receives this error:
Error: Cannot hit test a render box that has never been laid out.
From what I know, constraints goes down the tree and children return the size to the parent. I know the column would not pass constraints to the children, however, the two expanded widget SHOULD know to take up half of the card's height?
My goal is to understand WHY this doesn't work, moreover than the actual working code. thank you!
Code:
var height = MediaQuery.of(context).size.height;
return Container(
height: height * 0.45,
width: double.infinity,
child: Card(
elevation: 4,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
child: Column(
children: [
Expanded(
flex: 1,
child: FittedBox(
child: Image.network('https://source.unsplash.com/random'),
),
),
Expanded(
flex: 1,
child: Container(
child: Text("hello"),
),
)
],
)),
);
The problem is that FittedBox tries to scale its child to fit accordingly; thus the child needs to be laid out before. Because the child is an image being loaded in this example, it's not laid before the fitting. To overcome this issue, you can use a SizedBox.expand instead of the FittedBox:
Container(
height: height * 0.45,
width: double.infinity,
child: Card(
elevation: 4,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
child: Column(
children: [
Expanded(
flex: 1,
child: SizedBox.expand(
child: Image.network('https://source.unsplash.com/random'),
),
),
Expanded(
flex: 1,
child: Container(
child: Text("hello"),
),
)
],
)),
)
-Don't use MediaQuery inside the build Methode, before the return.
-Avoid redundant argument: https://dart-lang.github.io/linter/lints/avoid_redundant_argument_values.html for the flex: 1
Use fit: BoxFit.contain
return Container(
height: MediaQuery.of(context).size.height* 0.45,
width: double.infinity,
child: Card(
elevation: 4,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
child: Column(
children: [
Expanded(
flex: 1,
child: Image.network('https://source.unsplash.com/random',fit: BoxFit.contain),
),
Expanded(
flex: 1,
child: Container(
child: Text("hello"),
),
)
],
)),
);
I want to have more(in this example 4) items in Row. I tried use Expanded or Flexible or mainAxisAlignment: MainAxisAlignment.spaceEvenly to fill up all width of screen. But I want to heve items lets say specific width and height 50 pixels, and GestureDetector which is around item to be 1/4 of screen(4 items = full width). So everywhere I tap in the row something will be selected. My problem is that every my solution deforms items(circles) or circle must be very huge to width = height to not be deformed.
I also tried wrapping whole _ratingEmoji in Expanded and giving ratingItem.ratingIndicator constraints to max width but with same result.
Row of items:
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (var ratingItem in editGroceryRatingProvider.foodRatings)
_ratingEmoji(editGroceryRatingProvider, ratingItem, context),
],
),
Item:
Widget _ratingEmoji(EditGroceryRatingProvider editGroceryRatingProvider,
SelectedFoodRating ratingItem, BuildContext context) {
return GestureDetector(
child: ratingItem.selected
? Container(
width: MediaQuery.of(context).size.width / 8,
height: MediaQuery.of(context).size.width / 8,
padding: const EdgeInsets.all(2.0),
decoration: BoxDecoration(
border: Border.all(
color: Color(0xFF312ADB),
width: 3.0,
),
borderRadius: BorderRadius.circular(32.0)),
child: Center(
child: ratingItem.ratingIndicator,
),
)
: ratingItem.ratingIndicator,
onTap: () => editGroceryRatingProvider.updateSelectedRating(ratingItem),
);
}
RatingIndicator which is "circle" item to be on the screen:
Container(
decoration: BoxDecoration(
border: Border.all(
color: color,
width: 1.5,
),
borderRadius: BorderRadius.circular(64.0),
),
child: Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.white,
width: 3.0,
),
borderRadius: BorderRadius.circular(64.0),
),
child: ClipOval(
child: Container(
width: width ?? MediaQuery.of(context).size.width / 7,
height: height ?? MediaQuery.of(context).size.width / 7,
color: color,
),
),
),
);
Thanks a lot.
Update:
Items should stay as they are on screenshot, but "whole" row should be clickable(means even if you click between 2 items, one closer to click should be selected)
Check, please solution.
The whole area is clickable. You just need to add your item instead of mine.
#override
Widget build(BuildContext context) {
final colors = [Colors.red, Colors.blue, Colors.brown, Colors.cyan];
return Scaffold(
body: Container(
alignment: Alignment.center,
color: const Color(0xffFAFAFA),
child: Row(
children: List.generate(
colors.length,
(index) => Expanded(
child: GestureDetector(
onTap: () => print('Tapped:$index'),
child: Container(
height: 50,
color: Colors.transparent,
child: Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: colors[index],
),
),
)),
)),
),
),
);
In Flutter, is there an option to draw a vertical lines between components as in the image.
Not as far as I know. However, it is quite simple to create one — if you look at the source for Flutter's Divider you'll see that it is simply a SizedBox with a single (bottom) border. You could do the same but with dimensions switched.
Update (Oct 4, 2018): a VerticalDivider implementation has been merged in by the Flutter team. Check out the docs but it's very simple to use — simply put it between two other items in a row.
Note: If you are using VerticalDivider as separator in Row widget then wrap Row with IntrinsicHeight , Container or SizedBox else VerticalDivider will not show up. For Container and SizedBox widget you need define height.
As of 10 days ago, flutter has merged a VerticalDivider implementation. It will be available in the default channel very soon, but for now you have to switch to the dev channel to use it: flutter channel dev.
Here is a example of how to use it:
IntrinsicHeight(
child: new Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('Foo'),
VerticalDivider(),
Text('Bar'),
VerticalDivider(),
Text('Baz'),
],
))
Vertical divider:
As a direct child:
VerticalDivider(
color: Colors.black,
thickness: 2,
)
In a Row:
IntrinsicHeight(
child: Row(
children: [
Text('Hello'),
VerticalDivider(
color: Colors.black,
thickness: 2,
),
Text('World'),
],
),
)
Horizontal divider:
As a direct child:
Divider(
color: Colors.black,
thickness: 2,
)
In a Column:
IntrinsicWidth(
child: Column(
children: [
Text('Hello'),
Divider(
color: Colors.black,
thickness: 2,
),
Text('World'),
],
),
)
import 'package:flutter/material.dart';
class VerticalDivider extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new Container(
height: 30.0,
width: 1.0,
color: Colors.white30,
margin: const EdgeInsets.only(left: 10.0, right: 10.0),
);
}
}
Try to wrap it inside the Container with some height as
Container(height: 80, child: VerticalDivider(color: Colors.red)),
Tried with VerticalDivider() but cannot get any divider. I Solved it with
Container(color: Colors.black45, height: 50, width: 2,),
You can use a vertical divider with a thickness of 1.
VerticalDivider(
thickness: 1,
color: Color(0xFFF6F4F4),
),
And if you can't see the vertical divider wrap the row with a IntrinsicHeight widget.
Just wrap your Row in IntrinsicHeight widget and you should get the desired result:
IntrinsicHeight(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text('Name'),
VerticalDivider(),
Text('Contact'),
],
))
As #rwynnchristian suggested, this seems to be the simplest solution IMO. Just leaving the code here:
import 'package:flutter/material.dart';
class VerticalDivider extends StatelessWidget {
#override
Widget build(BuildContext context) => RotatedBox(
quarterTurns: 1,
child: Divider(),
);
}
add this method anywhere.
_verticalDivider() => BoxDecoration(
border: Border(
right: BorderSide(
color: Theme.of(context).dividerColor,
width: 0.5,
),
),
);
now wrap your content in container
Container(
decoration: _verticalDivider(),
child: //your widget code
);
Use Container for divider is easy, wrap your row in IntrinsicHeight()
IntrinsicHeight(
child: Row(
children: [
Text(
'Admissions',
style: TextStyle(fontSize: 34),
),
Container(width: 1, color: Colors.black), // This is divider
Text('another text'),
],
),
You need to wrap VerticalDivider() widget with the IntrinsicHeight widget. Otherwise, the vertical divider will not show up. And to gain some padding over the top and bottom you can add indent.
IntrinsicHeight(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: VerticalDivider(
thickness: 0.8,
color: Colors.grey,
),
),
Flexible(
child: Text(
"Random Text",
style: TextStyle(
fontSize: 12,
color: AppColor.darkHintTextColor,),
),
),
],
),
)
I guess i found a more robust solution when dealing with this problem;
IntrinsicHeight(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Container(
decoration: BoxDecoration(borderRadius: BoxRadius.circular(),color: Colors.gray),
height: 5,
margin: CustomPaddings.horizontal(),
),
),
Text(
"TEST",
style: Theme.of(context)
.textTheme
.subtitle1!
.copyWith(
color: Colors.black,
fontWeight: FontWeight.bold),
),
Expanded(
child: Container(
decoration: BoxDecoration(borderRadius: BoxRadius.circular(),color: Colors.gray),
height: 5,
margin: CustomPaddings.horizontal(),
),
),
],
),
),
Try RotatedBox in combination with a divider to get it vertical, RotatedBox is a widget of flutter that automatically rotates it's child based on the quarterTurn property you have to specify. Head over to here for a detailed explanation https://docs.flutter.io/flutter/widgets/RotatedBox-class.html