How to fix "`RenderFlex children have non-zero flex but incoming height constraints are unbounded." in Flutter? - flutter

I'm very new to Flutter and frontend design in general. I've tried looking for the answer online and have tried some of the suggestions on other posts, but they don't match my situation exactly and I keep getting confused. If anyone could offer some guidance I would really appreciate it!
I'm trying to make a custom table widget composed of a title, ListView, and a row of IconButtons. I'm having trouble wrapping my head around how to limit to and fit to containers. I keep getting a error stating RenderFlex children have non-zero flex but incoming height constraints are unbounded. I know it has something to do with the boundaries and I need to use either Flexible or Expanded to fix it, but I've been at it for a while and am not getting anywhere.
#override
Widget build(BuildContext context) {
return Focus(
child: Container(
decoration: BoxDecoration(
border: Border.all(color: Theme.of(context).colorScheme.tertiary)),
padding: const EdgeInsets.fromLTRB(0, 0, 0, 2),
margin: const EdgeInsets.fromLTRB(13, 2, 13, 2),
clipBehavior: Clip.antiAlias,
child: Column(children: [
Text(
widget.title,
style: Theme.of(context).textTheme.bodyLarge,
),
ListView(
shrinkWrap: true,
children: widget.children,
),
Flexible(
child: Row(
children: [
//PLUS BUTTON
Expanded(
child: IconButton(
onPressed: () {
setState(() {
// updating the state
widget.children.add(ReportInputTableRow(
rowIndex: widget.children.isNotEmpty
? widget.children.length - 1
: 0,
onFocus: (row, column) {
updateCurrent(row, column);
},
));
});
},
icon: Icon(
Icons.plus_one_sharp,
color: Theme.of(context).colorScheme.secondary,
),
splashRadius: 15.0,
),
)
//PLUS BUTTON
],
))
]),
));
}
EDIT:
As requested, here is the code for ReportTableInputRow
class _ReportInputTableRowState extends State<ReportInputTableRow> {
#override
Widget build(BuildContext context) {
return Focus(
child: Row(
children: [
Focus(
child: const Expanded(
child: TextInputField(
text: "Original",
size: 13,
padded: false,
),
),
onFocusChange: (hasFocus) {
if (hasFocus) widget.columnIndex = 0;
}),
Focus(
child: const Expanded(
child: TextInputField(
text: "Note",
size: 13,
padded: false,
)),
onFocusChange: (hasFocus) {
if (hasFocus) widget.columnIndex = 1;
}),
],
),
onFocusChange: (hasFocus) {
widget.onFocus != null
? widget.onFocus!(widget.rowIndex, widget.columnIndex)
: null;
},
);
}
}

EDIT:
The solution was to swap Expanded with Focus in ReportInputTableRowState.
Maybe this one can help you:
Put the ListView inside Expanded and remove the Flexible which wraps the Row beneath the ListView.
child: Column(children: [
Text(
widget.title,
style: Theme.of(context).textTheme.bodyLarge,
),
ListView(
shrinkWrap: true,
children: children,
),
Flexible(
child: Row(
children: [
//PLUS BUTTON
Expanded(
child: IconButton(
onPressed: () {
...
You should also avoid changing widget variables, because they are considered to be immutable. If you want to mutate variables, put them inside the state.
Instead of widget.children.add(...) you should call children.add(...) with children being a state variable.

Actually you may not need Expanded or Flex, since you icons should have a defined size, you can wrap it using a SizedBox with defined height.
SizedBox(
height: 32, // It can be the height you desire, no need to define width
child: Row(
children: [
... // The icons that are children of the row go here
],
),
),

Related

Listview.builder causes RenderFlex overflow

I have the code below that I'm using to generate a list view
class PostListsWidget extends StatefulWidget {
const PostListsWidget({Key? key}) : super(key: key);
#override
_PostListsWidget createState() => _PostListsWidget();
}
class _PostListsWidget extends State<PostListsWidget> {
static const List<PostListModel> _latestPosts = [
PostListModel(post: 'this is a test as.', votes: 453, like: 1, iconID: 0xe908),
PostListModel(post: 'this is a test as', votes: 2, like: 2, iconID: 0xe904),
PostListModel(post: 'this is a test a', votes: 324, like: 1, iconID: 0xe908),
PostListModel(post: 'this is a test s', votes: 435, like: 3, iconID: 0xe90d),
];
Widget _buildListItem(BuildContext context, PostListModel postList){
return Container(
margin: const EdgeInsets.all(20),
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(8)
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: Container(
padding: EdgeInsets.only(bottom: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget> [
PentEmotionWidget(iconid: postList.iconID),
Expanded(child: Text(
postList.post,
overflow: TextOverflow.clip,
) )
],
) ,
)
),
Flexible(
child: Container(
child: Row(
children: <Widget> [
Expanded(child:
PentLikesCountWidget(votes: postList.votes),
),
PentLikesActionWidget(like: postList.like)
],
),
)
)
],
),
);
}
#override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: _latestPosts.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemBuilder: (context, index) =>
_buildListItem(context, _latestPosts[index]),
);
}
}
The code works but the last tile is cut
I've tried playing around with Expanded and Flexible but that doesn't make a difference. Seems like the problem is the List cannot scroll therefore it cuts the content overflow. Can anybody help?
Have to add random text here because SO won't let me post without adding more 'details'
If your layout is correct, then you only need to wrap you ListView.builder inside a Container/SizedBox and give it to some height
SizedBox(
height: 200,
child: ListView.builder(
...
)
)
If that's not the case, maybe try not using expanded or flexible.

ListView flutter, how to keep at full height always

Im trying to remove the scrolling of my ListView widget. Currently, the user only has a small window of tiles to scroll through. The listview data is paged so only shows 20 items at once- i want to show all 20 items and the whole page to scroll, rather than the user scroll through the list view a few at a time. How can i achieve this?
return Column(
children: [
Expanded(
child: ListView.separated(
separatorBuilder: (context, index) => Container(
height: 1,
color: Constants.listSeparatorGrey,
),
itemCount: pageState.data?.data?.length ?? 0,
itemBuilder: (context, idx) =>
itemBuilder(pageState.data?.data?[idx]),
),
),
Text('${pageState.data?.total ?? 0} results',
style: TextStyles.rowHeader(context)),
Text(
'Page ${pageState.data?.currentPage()} of ${pageState.data?.pageNum()}',
style: TextStyles.rowHeader(context),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(width: 20),
if (pageState.data?.hasPrev ?? false)
MyButton(
// isLoading: pageState is Loading,
type: ButtonType.secondary,
label: "Previous",
onPressed: pageApi.prev,
)
else
Container(width: 8),
const SizedBox(
width: 8,
),
(pageState.data?.hasNext ?? false)
? MyButton(
// isLoading: pageState is Loading,
type: ButtonType.secondary,
label: "Next",
onPressed: pageApi.next,
)
: const SizedBox(width: 8)
],
),
],
);
If I correctly understand you. Just move all your content to ListView. So all your page starts scrolling with content.
Widget header = Container(color: Colors.green, height: 100);
List<Widget> content = const [Text("firstCell"), Text("secondCell")];
Widget button =
const TextButton(onPressed: null, child: Text("Next page"));
List<Widget> widgets = [header] + content + [button];
return Scaffold(
appBar: AppBar(
title: const Text('All content in list'),
),
body: ListView(children: widgets),
);
Here result:
All content in ListView
I solved my problem.
I just put the root column in a SingleChildScrollView()
very simple in the end

fl_chart custom Legend gets pushed down when section is empty

I'm using the PieChart of fl_chart to display the distribution of locally saved documents. The percentages displayed in the chart are the result of the length of the two document type lists (See image below).
But when one List is empty I have a weird bug were my custom Legend gets pushed downwards. The PieChart and the Legend are positioned inside of a Row with flex factors on each children (2 for the PieChart and 4 for the Legend).
I really don't understand what pushes the Legend downwards because my Expanded widgets are always positioned inside of Rows so that the PieChart and Legend only take up the available, horizontal space and not the vertical space which happens in the bug (image 2).
PieChart widget:
class PersonalFilesCircularGraph extends StatefulWidget {
const PersonalFilesCircularGraph();
#override
_PersonalFilesCircularGraphState createState() =>
_PersonalFilesCircularGraphState();
}
class _PersonalFilesCircularGraphState
extends State<PersonalFilesCircularGraph> {
late List<FileTypeData> data;
List<PieChartSectionData> getSections() => data
.asMap()
.map<int, PieChartSectionData>((index, data) {
final value = PieChartSectionData(
color: data.color,
value: data.percent,
showTitle: false,
radius: 3,
);
return MapEntry(index, value);
})
.values
.toList();
#override
void initState() {
/* Example getFileTypeData result
[
FileTypeData(
"Patient Questionnaire",
patientQuestionnaires.length /
(patientQuestionnaires.length +
receivedPatientQuestionnaires.length) *
100,
const Color(0xFF3861FB),
),
FileTypeData(
"Received Patient Questionnaire",
receivedPatientQuestionnaires.length /
(receivedPatientQuestionnaires.length +
patientQuestionnaires.length) *
100,
Colors.teal.shade400,
),
];
*/
data = context.read<SessionBloc>().state.getFileTypeData;
super.initState();
}
#override
Widget build(BuildContext context) {
return BlocConsumer<SessionBloc, SessionState>(
listenWhen: (previous, current) {
final bool listenWhen = previous.patientQuestionnaires.length !=
current.patientQuestionnaires.length ||
previous.receivedPatientQuestionnaires.length !=
current.receivedPatientQuestionnaires.length;
return !listenWhen;
},
listener: (context, state) {
data = context.read<SessionBloc>().state.getFileTypeData;
},
builder: (context, state) {
return Row(
children: [
Expanded(
flex: 2,
child: Container(
constraints: const BoxConstraints(
maxWidth: 60,
maxHeight: 60,
),
child: PieChart(
PieChartData(
sections: getSections(),
),
),
),
),
const SizedBox(
width: kMediumPadding,
),
Expanded(
flex: 4,
child: Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: data
.map(
(data) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: buildLegend(
percent: data.percent,
text: data.fileName == "Patient Questionnaire"
? L.of(context).patientQuestionnaires
: L.of(context).receivedPatientQuestionnaire,
color: data.color,
),
),
)
.toList(),
),
),
],
);
},
);
}
Widget buildLegend({
required double percent,
required String text,
required Color color,
}) =>
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
child: Row(
children: [
Container(
width: 10,
height: 10,
color: color,
),
const SizedBox(
width: kSmallPadding,
),
Expanded(
child: Text(
text,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
Text(
"${percent.toStringAsFixed(0)}%",
overflow: TextOverflow.ellipsis,
)
],
);
}
I display the chart widget inside a CustomScrollView, wrapped with a SliverToBoxAdapter inside of my home screen:
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
#override
Widget build(BuildContext context) {
return CustomScrollView(
physics: const BouncingScrollPhysics(),
slivers: <Widget>[
SliverAppBar(
elevation: 0.0,
floating: true,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
title: Text(
"Home",
style: Theme.of(context).textTheme.headline5,
),
centerTitle: true,
),
const SliverPadding(
padding: EdgeInsets.symmetric(
vertical: kSmallPadding,
horizontal: kMediumPadding,
),
sliver: SliverToBoxAdapter(
child: PersonalFilesCircularGraph(),
),
)
],
);
}
}
EDIT:
I just did some more investigation on this bug and placed a colored Container in my CustomScrollView, below the SliverPadding of the CircularGraph to check if the Column of labels expands downwards. But as you can see the colored Container is not effected. It just looks like the Legend is inside a Stack and positioned without effecting other widgets above and below.
const SliverPadding(
padding: EdgeInsets.symmetric(
vertical: kSmallPadding,
horizontal: kMediumPadding,
),
sliver: SliverToBoxAdapter(
child: PersonalFilesCircularGraph(),
),
),
SliverToBoxAdapter(
child: Container(
width: double.infinity,
height: 60,
color: Colors.green,
),
)

Flutter: How can I make an button to show a random by me defined stateless Widget?

I have an button, and when it gets pressed, an stateless Widget consisting of an Column with a few Text and an Image.Asset is shown. So now I have a few of these stateless Widgetswith different content. When I press the button, i want him now to show a ramdom one of these stateless Widgets, maybe an option for a specific order too.
How do I do that?
Button and comamnd for showing the Widget at the moment:
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
if (isShown == true) Zauberwuerfel(),
SizedBox(
width: 150,
height: 100,
),
Container(
padding: EdgeInsets.only(bottom: 60),
width: 230,
height: 130,
child: RaisedButton(
color: Colors.red,
child: Text('Skill', style: TextStyle(fontSize: 30)),
onPressed: () {
setState(() {
isShown = true;
One of those stateless Widgets:
class SkillJonglieren extends StatelessWidget {
Widget build(BuildContext context) {
return Column(children: <Widget>[
Text(
'Jonglieren',
style: TextStyle(fontSize: 35),
),
SizedBox(
width: 20,
height: 25,
),
Image.asset('images/jonglieren.jpg', scale: 5),
SizedBox(
width: 20,
height: 30,
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Text>[
Text(
'Schwierigkeit:',
style: TextStyle(fontSize: 25),
),
Text(
' Einfach',
style: TextStyle(fontSize: 25, color: Colors.green),
Use Random class to generate a random number.
The range of the random will always be equal to the number of StatelessWidget you have
You store a List which will keep your StatelessWidget
When the button is pressed, you get the random number, and then pick up the index based upon that random number generated
Assumptions: Let us assume you have 5 StatelessWidgets namely Widget1(), Widget2(), Widget3(), Widget4() and Widget5()
Let us quickly jump into the code
import 'dart:math';
// Each Widget is located at an index which is unique
int randomNumber;
List<Widget> _widgets = [Widget1(), Widget2(), Widget3(), Widget4(), Widget5()];
// generating random number
void _generateRandomNumber(){
var random = new Random();
// now the range works like 0 to n-1
setState(() => randomNumber = random.nextInt(_widgets.length));
}
//Triggering the function when showing the item
RaisedButton(
color: Colors.red,
child: Text('Skill', style: TextStyle(fontSize: 30)),
onPressed: () {
//calling our method
_generateRandomNumber();
setState(() => isShown = true);
}
)
Now showing the data finally
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
if (isShown == true) _widgets[randomNumber]
....
]
)
Assign random numbers with the rand() function in dart:math to some variables. then use condition.
First of all, you need to make your main widget a StateFulWidget since you want to toggle the isShown variable and make changes to the views. Then you need to make a list of Widgets(e.g., Widget1 and Widget2) in your main widget(which has the button in):
final List<Widget> widgets = [Widget1(), Widget2()];
To select one of these widgets randomly you can use:
(widgets..shuffle()).first
You need to select a new random widget in onPressed. To show the selected widget only when isShown is true, you can use a Visibility widget(the if method that you're using is ok, too). Finally set the child of this Visibility widget to the selected widget.
Full code:
final List<Widget> widgets = [Zauberwuerfel(), SkillJonglieren()];
var _isShown = false;
Widget _selectedWidget = Zauberwuerfel();
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey,
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Visibility(
visible: _isShown,
child: _selectedWidget,
),
SizedBox(
width: 150,
height: 100,
),
Container(
padding: EdgeInsets.only(bottom: 60),
width: 230,
height: 130,
),
RaisedButton(
color: Colors.red,
child: Text('Skill', style: TextStyle(fontSize: 30)),
onPressed: () {
setState(() {
_isShown = true;
_selectedWidget = (widgets..shuffle()).first;
});
},
)
]),
));
}

How to add the widgets dynamically to column in Flutter?

I have added a few widgets inside the ListView. So that I can scroll all widgets. Now I required to add one more widget to the ListView to load the list of comments. I can not add the ListView inside the ListView. And moreover, I do not require the separate scroll for my comments. It should be scroll along with the widgets inside the ListView. So I planned to add the Column instead of ListView. Could any help to add my comments dynamically in the Columns?
new Expanded(
child:
new ListView(
shrinkWrap: true,
children: <Widget>[
// Title
new Padding(padding: const EdgeInsets.only(
top: 10.00, left: 10.00),
child: new Text(
_feed.title, textAlign: TextAlign.start,),
),
// content
new Container(
child: new Text(
_feed.content, textAlign: TextAlign.start,),
),
// Comments List will go here
],
),
),
If you have the comments data already, simply create a List, then pass it to the children property of the Column. Something like:
var commentWidgets = List<Widget>();
for (var comment in comments) {
commentWidgets.Add(Text(comment.text)); // TODO: Whatever layout you need for each widget.
}
…
new Expanded(
child:
new ListView(
shrinkWrap: true,
children: <Widget>[
// Title
new Padding(padding: const EdgeInsets.only(
top: 10.00, left: 10.00),
child: new Text(
_feed.title, textAlign: TextAlign.start,),
),
// content
new Container(
child: new Text(
_feed.content, textAlign: TextAlign.start,),
),
// Comments List will go here
Column(children: commentWidgets,),
],
),
),
If you don't have the comments data already and need to fetch it, use a FutureBuilder to build the UI once the future completes.
Another way:
return Column(
children: [
for(String item in list) Text(item);
]);
You can also mix static and dynamic fields eaisly in this case:
return Column(
children: [
for(String item in list) Text(item),
Text("Static text control"),
]);
By maintaining a reference to the Column object, the .children field/property can be referenced after the Column object has been declared - like so:
Column someColumn = Column(
children: [],
);
someColumn.children.add(Text('Hello 123'));
Currently(2021-06), Flutter 2.x implemented null-safe.
The answer from #Gene Bo should work but needs little modification.
This should work.
var pwdWidgets = <Widget>[];
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: this.pwdWidgets,
),
ElevatedButton(
onPressed: (){
setState((){
this.pwdWidgets.add(Text("Hello World"),);
});
},
child: Text("click me"),
),
In Addition to #Derek Lakin's Answer which worked for me too, it is also required to call setState(), if you need to update the comments and reload.
var commentWidgets = List<Widget>();
for (var comment in comments) {
commentWidgets.Add(Text(comment.text)); // TODO: Whatever layout you need foreach widget.
}
setState(() {
});
import 'package:flutter/material.dart';
class LoginRegisterPage extends StatefulWidget {
#override
_LoginRegisterPageState createState() => _LoginRegisterPageState();
}
class _LoginRegisterPageState extends State<LoginRegisterPage> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: AppBar(
title: Text("App"),
),
body: new Container(
margin: EdgeInsets.all(15.0),
child: new Form(
child: new Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: createInputs() + createButtons(),
),
),
),
);
}
List<Widget> createInputs(){
return{
SizedBox(height: 10.0),
logo(),
SizedBox(height: 20.0),
new TextFormField(
decoration: new InputDecoration(labelText: 'Email'),
),
SizedBox(height: 10.0),
new TextFormField(
decoration: new InputDecoration(labelText: 'Passwors')
),
SizedBox(height: 20.0),
};
}
Widget logo(){
return new Hero(
tag:'hero',
child: new CircleAvatar(
backgroundColor:Colors.transparent,
radius: 110.0,
child: Image.asset("images\logo.PNG"),
),
);
}
List<Widget> createButtons(){
return{
new RaisedButton(
child: new Text("Login", style: new TextStyle(fontSize: 20.0),),
textColor: Colors.pink,
onPressed: () {
},
),
new FlatButton(
child: new Text("Already not have an account?", style: new TextStyle(fontSize: 14.0),),
textColor: Colors.white,
onPressed: () {
},
)
};
}
}
Yes you can add dynamic child on column widgets.
class CategoriesItemWidget extends StatelessWidget {
final List<String> list;
const CategoriesItemWidget({Key? key, required this.list})
: super(key: key);
#override
Widget build(BuildContext context) {
return Column(
children: list
.map((string) => Text(string))
.toList(),
);
}
}