In my code, the third Container should not be included since it is wrapped in the Visibility widget. This is what I have tried,
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Container(...),
Container(...),
Visibility(visible:false, child: Container(...))
]
)
What it should look like:
What actually happened:
According to the Visibility documentation:
By default, the visible property controls whether the child is included in the subtree or not; when it is not visible, the replacement child (typically a zero-sized box) is included instead.
This shows that just because a widget is wrapped in the Visibility widget, doesn't mean it doesn't exist in the widget tree.
The best way to go about this would be to make the widget null so it isn't included in the widget tree.
bool notNull(Object o) => o != null;
bool isVisible = false;
...
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Container(...),
Container(...),
isVisible? Container(...) : null,
].where(notNull).toList(),
)
Or you could optionally create the list in a build method so that you can simply add your widget to the list if a certain condition is met.
Builder(
build: (context) {
bool isVisible = false;
List<Widget> _children = [
Container(...),
Container(...),
];
if (isVisible) {
_children.add(Container(...)); // Optional widget
}
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: _children,
);
}
),
Indeed your widget is not visible, but it does counts when calculating the spacing.
You can try using a builder
Builder(
build: (context) {
var children = [
Container(...),
Container(...),
];
if (your condition) {
childern.add(Container(...));
}
return Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: children
);
}
);
Related
I have a bunch of nested Columns and ListViews, and I get this exception, telling me that the app can't paint a Scrollbar. The app works fine, but of course I want to fix this issue.
This is part of the output in the console:
======== Exception caught by animation library =====================================================
The following assertion was thrown while notifying status listeners for AnimationController:
The provided ScrollController is currently attached to more than one ScrollPosition.
The Scrollbar requires a single ScrollPosition in order to be painted.
When the scrollbar is interactive, the associated Scrollable widgets must have unique ScrollControllers. The provided ScrollController must be unique to a Scrollable widget.
Below are two alternatives of my code (once with a ListView at the top level, once with a Column). I left out the parts I believe to be irrelevant to this problem:
#override
Widget build(BuildContext context) {
return Column(
children: [
//I originally used ListView.builder, this didn't work
ListView.builder(
shrinkWrap: true,
itemCount: _altControllers.length,
itemBuilder: (context, index) {
return Column(
key: ValueKey('alt $index'), //no widget.targetLangCode and ws here (see a few lines below)
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _writingSystems.map((String ws) {
return Row(
key: ValueKey('alt $index: $widget.targetLangCode $ws'),
// children: ...,
);
}).toList(),
),
// ...
],
);
},
),
//then I tried this alternative with a Column, it still didn't work
Column(
children: _altControllers
.asMap()
.map((int index, Map<String, TextEditingController> controllers) {
return MapEntry(
index,
Column(
key: ValueKey('alt $index'),
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: _writingSystems.map((String ws) {
return Row(
key: ValueKey('alt $index: $widget.targetLangCode $ws'),
// children: ...,
);
}).toList(),
),
// ...
],
),
);
})
.values
.toList(),
),
],
);
}
I've also tried using ScrollController in some places, but didn't manage to solve my problem with that.
My card's text part has multiple text widgets that I have to put under column. I want to add this whole part under a row widget, so that I can add the image part to the right of this column widget.
This is what I've done:
class ContactMe extends StatelessWidget {
static const String route = '/contact_me';
const ContactMe({Key? key}) : super(key: key);
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
double height = MediaQuery.of(context).size.height;
return Scaffold(
body: Column(
children: [
CommonNavBar(height: height),
Expanded(
child: FutureBuilder(
future: contactMe(),
builder: (context, snapshot) {
if (snapshot.data == null)
return Center(child: CircularProgressIndicator());
else {
var data = snapshot.data as List<String>;
return LayoutBuilder(builder: (context, constraints) {
if (constraints.maxWidth < 1000) {
return Center();
} else {
return Row( //this is parent row
children: [
Container(
child: Column( //the column that contains multiple other widgets
children: [
text('Reach Out to me!', 25,
Theme.of(context).primaryColorLight),
Text('anything'),
],
),
),
Image.asset('assets/contact_me/' + data[2], scale: 2), //the image widget
],
);
}
});
}
}),
),
],
),
);
}
}
But instead of a card, the layout looks like this:
As visible in the picture, the image for some reason is not in line with the column widget. What's the mistake I'm making here?
In continuation to #OlegBezr's answer, I fixed it by using mainAxisAlignment and crossAxisAlignment for both the widgets.
return Row(
crossAxisAlignment: CrossAxisAlignment.start, //takes the row to the top
mainAxisAlignment: MainAxisAlignment.spaceAround, //Used this for spacing between the children
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start, //used for aligning the children vertically
children: [
text('Reach Out to me!', 25,
Theme.of(context).primaryColorLight),
text(
'DISCUSS A PROJECT OR JUST WANT TO SAY HI? MY INBOX IS OPEN FOR ALL.',
18,
Theme.of(context)
.primaryColorLight
.withOpacity(0.3)),
],
),
Image.asset('assets/contact_me/' + data[2], scale: 2),
],
);
I think your problem is related to different alignments. The default crossAxisAlignment for a Row is CrossAxisAlignment.center, thus your image and column are located in the vertical middle of the row. The default mainAxisAlignment for a Column is MainAxisAlignment.start, so the content inside your column is located at its top.
I can see two possible ways to put your content on the same horizontal line:
Set mainAxisAlignment for the Column to MainAxisAlignment.center
Set crossAxisAlignment for the Row to CrossAxisAlignment.start
For the future, you might find this resource helpful when dealing with different layouts: https://docs.flutter.dev/development/ui/layout
I'm already losing sleep over this.
I'm trying to display a chart inside a ListView (for scrolling). For some reason the contents of the Card flickers when scrolling and randomly completely disappears (the Card itself stays visible though).
Any idea why would that happen?
(...) ListView (...)
children: [Row ( children: [buildChartBox()] )] (...)
Expanded buildChartBox() {
return Expanded(
child: Card(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
chartTitles(
title: 'Items',
subtitle: 'by value'),
SizedBox(
height: 300,
child: ValuesChart(data: calculateValues(items)))
],
),
],
),
),
),
);
}
Row chartTitles({String title = '', String subtitle = ''}) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: text_charttitle),
Text(subtitle, style: text_chartsubtitle),
],
)
],
);
}
Things tried:
Both of these were originally Stateless Widgets; I changed to simple
methods to simplify but it didn't change the weird behaviour.
Replacing the chartTitles return with an empty Container (i.e. removing the titles) does mitigate the issue. The chart then stays displayed but also flickers slightly.
Replacing the ListView with a SingleChildScrollView doesn't change anything.
EDIT: Code for the ValuesChart:
import 'package:fl_chart/fl_chart.dart';
class ValuesChart extends StatelessWidget {
final Map<String, int> data;
const ValuesChart({required this.data});
#override
Widget build(BuildContext context) {
return Container(
child: PieChart(
_theData(data),
));
}
}
Note I'm using a package called 'fl_chart'. _theData just returns various parameters for the chart, I don't think it's relevant.
Try to replace ListView with SingleChildScrollView
ListViews in flutter by default using what it is called in Android RecyclerView to efficiently use render resources.
If you are interested here an article
https://medium.com/1mgofficial/how-recyclerview-works-internally-71290de5d2c4
I am pretty new in Flutter and I have created the following widget:
class Registration extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Text("Hello1")]),
),
Expanded(child: Text("Hello2")),
],
);
}
}
it looks:
I would like to know, why the text Hello2 is also placed in the middle of screen. I did not tell it explicitly.
What I am trying to achieve is
Row and Column all have default alignment even you didn't assign any value. Here is the source of Column:
...
Column({
Key? key,
MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
MainAxisSize mainAxisSize = MainAxisSize.max,
CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
TextDirection? textDirection,
VerticalDirection verticalDirection = VerticalDirection.down,
TextBaseline? textBaseline,
List<Widget> children = const <Widget>[],
}) : super(
...
To your question:
why the text Hello2 is also placed in the middle of screen. I did not tell it explicitly.
It is because crossAxisAlignment is default CrossAxisAlignment.center.
You can get more reference about flutter layout/constraint here: Flutter Layout Cheat Sheet and Understanding constraints
You can align Hello2 with Align Widget. By default Hello2 is placed at the beginning of the second Expanded which takes half the screen.
class Registration extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Text("Hello1")]),
),
Expanded(child: Align(alignment: Alignment.bottomRight,child:Text("Hello2"))),
],
);
}
}
You can achieve the same with a Container in place of Align.
To understand wrap you zones with containers and add colors:
class Registration extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
Expanded(
child: Container(color: Colors.green,child:Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[Text("Hello1")]),
),
),
Expanded(
child:
Container(color: Colors.red, child: Text("Hello2"))),
],
);
}
}
The Expanded widget takes all the space available. if you put 2 or more Expanded widgets in a column, they will share equally the available space unless you specify a different flex property for one of them. By default flex = 1 for an Expanded widget.
Flutter widget of the week (Expanded):
https://www.youtube.com/watch?v=_rnZaagadyo
How to place a Widget below fixed Centered Widget inside a Container? I am using a GridView to show widgets horizontally. GridView item will have a Text Widget which has to be fixed at the Centered everytime in the screen. I have to place a Text widget below that Centered Widget.
Reference Screenshot:
Adding the build method code of the GridView item I have tried till now. But the Text Widget is not getting centered. The output I am getting. How to fix this part ?
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CustomText(
(dayModel?.date?.day ?? "").toString(),
AppTextStyle.Body,
customColor: _getColorBasedOnStyle(
dayModel.style,
),
),
Visibility(
visible: dayModel?.style == CalendarDayStyles.NOT_AVAILABLE,
child: CustomText(
Strings.no_slots_label,
AppTextStyle.SublineForCalendar,
customColor: AppColors.BLACK_20,
),
),
],
);
}
I believe the secret to doing this right is not only in how you build "6", but also in how you build "5" and "7".
E.g. you could build every one of them as column with 3 boxes on top of each other, pseudocode:
Column(
children: [
SizedBox(height: fixedHeight, child: empty)
SizedBox(height: fixedHeight, child: Text("5")) // or "6" or "7"
SizedBox(height: fixedHeihgt, child: empty) // or booking status
]
)
or other way of doing it if we have to avoid using fixedHeight is by using the Expanded Widget inside the Column Widget
Column(
children: [
Expanded(child: Container()),
Expanded(child: Center(child : Text("5"))), // or "6" or "7"
Expanded(child: Center(child : Text("No Slots"))) // or booking status
]
)
If you set crossAxisAlignment of the row to start and then show a column with "no slots" underneath, shouldn't this fix your issue?
You could use the CrossAxisAlignment.center:
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
),
Full snippet code:
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
CustomText(
(dayModel?.date?.day ?? "").toString(),
AppTextStyle.Body,
customColor: _getColorBasedOnStyle(
dayModel.style,
),
),
Visibility(
visible: dayModel?.style == CalendarDayStyles.NOT_AVAILABLE,
child: CustomText(
Strings.no_slots_label,
AppTextStyle.SublineForCalendar,
customColor: AppColors.BLACK_20,
),
),
],
);