I'm learning flutter to make an app. Here I'm trying to make a page to show the full post. The problem is that the page isn't scrolling as a single unit.
class SinglePostPage extends StatefulWidget {
final Post? post;
const SinglePostPage({Key? key, this.post}) : super(key: key);
#override
State<SinglePostPage> createState() => _SinglePostPageState();
}
class _SinglePostPageState extends State<SinglePostPage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
margin: const EdgeInsets.only(top: 22.5),
padding: const EdgeInsets.fromLTRB(0, 5, 0, 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const BackButton(),
Row(
children: [
InkWell(
onTap: () {
showDialog(...);
},
child: CircleAvatar(...),
Container(
padding: const EdgeInsets.only(left: 5.4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: () {
showDialog(...);
},
child: Text(
widget.post!.author[0].profileName,
),
),
const SizedBox(height: 4),
Text(
showTimeAgo(widget.post!.postTime).toString(),
),
],
),
),
],
),
PopupMenuButton(
icon: const Icon(Icons.more_vert),
itemBuilder: (context) => [...],
),
Container(
margin: const EdgeInsets.fromLTRB(12, 9, 12, 3),
// when the text is longer than the screen height it showed RenderFlex overflowed error so I put constraints. how to show it full and make this scroll
constraints: BoxConstraints(
maxHeight: MediaQuery.of(context).size.height * 0.54,
minHeight: 50),
child: SingleChildScrollView(
child: Text(
widget.post!.postText,
textAlign: TextAlign.start,
),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (widget.post!.postMedia.isNotEmpty)
CarouselSlider.builder(...),
const SizedBox(height: 4.5),
if (widget.post!.postMedia.length > 1)
buildDotIndicator(widget.post!.postMedia),
],
),
],
),
),
Container(
// post reactions row
),
CommentBodyWidget(
postId: widget.post!.postId,
),
],
),
);
}
}
I looked up for other answers and tried wrapping it with SingleChildScrollView, but it didn't work , and ListView with shrinkWrap: true also gave 'Incorrect use of ParentDataWidget' error.
CommentBodyWidget has a listview builder. It's scrolling on its own but the widget above isn't scrolling along with it.
How can I show this whole page and scroll together without having to limit the long post in a constrained container? Please help.
You can wrap body Column with SingleChildScrollView and use shrinkWrap: true, and use physics: NeverScrollableScrollPhysics(),. This will solve the issue.
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: [
//...
ListView.builder(
itemCount: itemlength,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return Text("item $index");
},
),
/// instead of listView
...List.generate(itemlength, (index) => Text("itemWidget"))
],
),
),
);
But I will encourage you to check CustomScrollView.
In order for the SingleChildScrollView to work, its parent's height should be defined.
You can achieve this by wrapping the SingleChildScrollView in a Container/SizedBox with defined height, or by wrapping it with the Expanded widget.
Pass SingleChildScrollView as body of your scaffold. Also shrinkwrap: true for the ListView is recommended so that it only takes up as much space as required, which will avoid unbounded height errors.
So instead of
...
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
...
Do this
...
Widget build(BuildContext context)
return Scaffold(
body: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(...
I recommend you to use CustomScrollView.
Try this:
CustomScrollView(
slivers: [
SliverFillRemaining(
hasScrollBody: false,
child: Column("""HERE PUT YOUR CODE""")
)]
),
Related
I want to draw a vertical list.
Each item in list should be filled only wrapped size.
But my code draw the list items with full width like:
I tried to use MainAxisSize.min but it doesn't work.
class HomePage extends StatelessWidget {
HomePage({super.key});
final List<String> list = ["apple", "banana", "cat", "dragon"];
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder: (context, index) {
return Container(
decoration:
BoxDecoration(border: Border.all(color: Colors.black)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('[$index]'),
const SizedBox(width: 4),
Text(list[index])
],
),
);
}),
);
}
}
because you use ListView, the child will will fill the width.
if you want to show not much data, consider use SingleChildScrollView
i wrote an article here to compare them and explain when to use it
https://medium.com/easyread/my-october-flutter-notes-2-6e0c78cf2e56
By default ListView take all avaialble width, even if you wrap your container with Align, this just change the item's width not the width that ListView takes(if you wrap your listview with colored container you will see that).
If you are not using long list, You can use SingleChildScrollView and Column like this:
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: list.mapIndexed((index, e) {
return Container(
decoration:
BoxDecoration(border: Border.all(color: Colors.black)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('[$index]'),
const SizedBox(width: 4),
Text(e)
],
),
);
}).toList(),
),
)
Add Align and align left:
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
shrinkWrap: true,
itemCount: list.length,
itemBuilder: (context, index) {
return Align(
alignment: Alignment.centerLeft,
child: Container(
decoration:
BoxDecoration(border: Border.all(color: Colors.black)),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text('[$index]'),
const SizedBox(width: 4),
Text(list[index])
],
),
),
);
}),
);
}
More of a workaround: depending on the layout seeked, either place the ListView into a Row or put another widget at the end of the row.
I can't work this out. It should be really simple. (This is Flutter Desktop)
I want a Row with 2 columns. The first column should be full height available but only 200 wide. Column 2 should be full height and use the remainder of the width.
I have tried all sorts but always get an overflow rather than a scrollable list.
What am I doing wrong?
This is the last iteration of my code.
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bnode Management'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('This is the title'),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Container(
width: 200,
child: ListView.builder(
shrinkWrap: true,
itemCount: piItems.length,
itemBuilder: (BuildContext context, int index) {
return Container(width: 40, color: Colors.red, child: Text(piItems[index].id));
}),
),
),
Container(color: Colors.blue, child: const Text('Column 1')),
],
),
],
),
);
}
}
The second Column like to have max space, therefor we can use Expanded on it instead of using on 1st Column. Also, the Row is needed to have max height. For this, we will wrap this Row with Expanded.
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('This is the title'),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 200,
child: ListView.builder(
shrinkWrap: true,
itemCount: piItems.length,
itemBuilder: (BuildContext context, int index) {
return Container(
width: 40,
color: Colors.red,
child: Text(piItems[index].id));
}),
),
Expanded(
child: Container(
color: Colors.blue,
child: Column(
children: [
const Text('Column 1'),
],
),
),
)
],
),
),
],
),
I also prefer using LayoutBuilder on body in this case.
You almost got it right, just mixed some things. Let's go step by step.
I want a Row with 2 columns
Ok, that's just a Row with two children.
Row(
children: [child1, child2],
)
The first column should be full height available but only 200 wide.
It would take the full height automatically but I see that in your code example you have a Column with Text title, so we should wrap our Row with Expanded. Also, the first Column should take 200 px.
Column(
children: [
const Text('This is the title'),
Expanded(
child: Row(
children: [
Container(
width: 200,
child: child1,
),
child2,
],
),
),
],
)
Column 2 should be full height and use the remainder of the width.
So we wrap our 2nd child with Expanded inside the Row.
Column(
children: [
const Text('This is the title'),
Expanded(
child: Row(
children: [
Container(
width: 200,
child: child1,
),
Expanded(
child: child2,
),
],
),
),
],
)
The end result based on your code:
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bnode Management'),
),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('This is the title'),
Expanded(
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: 200,
child: ListView.builder(
shrinkWrap: true,
itemCount: piItems.length,
itemBuilder: (BuildContext context, int index) {
return Container(
width: 40,
color: Colors.red,
child: Text(piItems[index].id));
},
),
),
Expanded(
child: Container(
color: Colors.blue,
child: const Text('Column 1'),
),
),
],
),
),
],
),
);
}
}
I'm trying to display the data using GridView.count but it is not displaying all items. here is my code
class CategoriesBody extends StatefulWidget {
const CategoriesBody({Key? key}) : super(key: key);
#override
_CategoriesBodyState createState() => _CategoriesBodyState();
}
class _CategoriesBodyState extends State<CategoriesBody> {
Widget header() {
return Center(
child: SizedBox(
height: MediaQuery.of(context).size.height * 0.2,
width: MediaQuery.of(context).size.width,
child: Container(
color: appThemeColor,
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Align(
alignment: Alignment.topLeft,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
customText(context, "Available", 25.0,
FontWeight.w600, Colors.white),
customText(context, "Categories", 35.0,
FontWeight.w600, Colors.white),
]))))),
);
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: SingleChildScrollView(
child: objResult == ""
? Column(
children: [
header(),
showCircularLoader(context),
],
)
: objResult == "not connected"
? Column(
children: [
header(),
SizedBox30(),
noInternetConnection(context)
],
)
: apiError == "Server down"
? Column(
children: [
header(),
SizedBox30(),
serverError(context),
],
)
: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
header(),
//list of all categries
SizedBox(
height: MediaQuery.of(context).size.height *
0.97,
child: GridView.count(
crossAxisCount: 2,
scrollDirection: Axis.vertical,
children: List.generate(category.length,
(index) {
return InkWell(
child: customCategoryContainer(
context,
"assets/img/cat2.jpg",
category.length != 0
? category[index].title
: "",
18.0,
MediaQuery.of(context)
.size
.height *
0.18,
MediaQuery.of(context)
.size
.height *
0.18));
}))),
]))));
}
}
api response count is 12, but its displaying 10 items. please help where i'm doing wrong.
The items are probably there but larger than the screen height. And because you have a SingleChildScrollView and it's a scrollable widget, then the Grid is not scrollable, and the Grid and header are taking in total the full height of the screen and not more than that, so the SingleChildScrollView is also not scrollable.
One solution I can think of is to disable the scroll for the SingleChildScrollView widget and force scroll always for the Grid like so:
SingleChildScrollView(
physics: const NeverScrollableScrollPhysisc(),
//...
child: Column(
children: [
//...
SizedBox(
height: MediaQuery.of(context).size.height * 0.97,
child: GridView.count(
physics: const AlwaysScrollableScrollPhysics(),
//...
)
),
]
),
)
This will make the header fixed, and the grid scrollable within the screen height it occupies.
SingleChildScrollView(
physics: const NeverScrollableScrollPhysisc(),
//...
child: Column(
children: [
//...
Expanded(
child: GridView.count(
physics: const AlwaysScrollableScrollPhysics(),
//...
)
),
]
),
)
If this error happens to someone then set the height of SizedBox of GridView.count instead giving it to full screen height, this solve the issue.
Im trying to implement a non scrollable ListView builder here but can't seem to find how to do it. Reason is because i want everything to be scrollable and i dont want to have a Scrollable widget inside a scrollable parent.
class _DashboardState extends State<Dashboard> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('App Bar Here')),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Hello World'),
Container(
child: ListView.builder(
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
Container(
color: Color(0xffaaaaaa),
height: 20,
child: Text('Jss One')),
Text(
'English',
style: TextStyle(fontSize: 20),
),
],
),
),
);
},
itemCount: 50,
),
),],),));
}}
class _DashboardState extends State<Dashboard> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('App Bar Here')),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Hello World'),
Container(
child: ListView.builder(
physics: NeverScrollableScrollPhysics() //add this line,
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
Container(
color: Color(0xffaaaaaa),
height: 20,
child: Text('Jss One')),
Text(
'English',
style: TextStyle(fontSize: 20),
),
],
),
),
);
},
itemCount: 50,
),
),],),));
}}
set physics property to NeverScrollablePhysics() in order to not scroll the lisview
try using physics property of listview
physics: NeverScrollableScrollPhysics()
hope it helps..
The Solution to what i was going for.
What happens here is the ListBuilder widget will build a List of items not in a Scrollable widget. The parent widget itself is scrollable which makes the whole Screen scrollable. To do this, add shrinkWrap: true and physics: NeverScrollableScrollPhysics() to the ListView.builder() property.
class _DashboardState extends State<Dashboard> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('App Bar Here')),
body: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('Hello World'),
Container(
child: ListView.builder(
shrinkWrap: true, //add this line
physics: NeverScrollableScrollPhysics() //add this line,
itemBuilder: (context, index) {
return Card(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
Container(
color: Color(0xffaaaaaa),
height: 20,
child: Text('Jss One')),
Text(
'English',
style: TextStyle(fontSize: 20),
),
],
),
),
);
},
itemCount: 50,
),
),],),));
}}
I need to have some structure like this
I use LayoutBuilder to get the height of content (between App Bar and TabsBottomNavigation). Here i build Profile Info Container and want to build ListView with some List Tiles, but when I try to build it in Layout Builder I have errors in console.
If I create ListView out of LayoutBuilder it works!
Please help me to solve it.
My code below:
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
Container(
height: viewportConstraints.maxHeight * .44,
color: Theme.of(context).primaryColor,
padding: EdgeInsets.only(bottom: 2),
child: Align(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildProfileImage(context),
SizedBox(height: 17),
Text(userName)
],
),
),
),
Expanded(
child: ListView(
children: <Widget>[
ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
)
],
),
)
],
),
),
);
},
);
}
Use below build method will work in your case. (I have checked and it's working, so I hope it will work in your case also and will fit in your requirement.)
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return Container(
child: Column(
children: <Widget>[
Container(
height: viewportConstraints.maxHeight * .44,
color: Theme.of(context).primaryColor,
padding: EdgeInsets.only(bottom: 2),
child: Align(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildProfileImage(context),
SizedBox(height: 17),
Text(userName),
],
),
),
),
SizedBox(height: 16),
Flexible(
child: ListView(
children: <Widget>[
Card(
child: ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
),
),
],
),
),
],
),
);
},
);
}
I think SingleChildScrollView is of no use in this case so I removed it but you can use it if you fill so.
You still need to do some UI improvement as per your wish as this is the basic structure as per your requirement.
Hope this will help you.
You are using the LayoutBuilder in a wrong way.
It's supposed to be used to change the layout with the size of the device and/or orientation.
What you are trying to do is best accomplished with MediaQuery:
MediaQuery.of(context).padding.top //APP bar height
MediaQuery.of(context).padding.bottom //Bottom bar height
MediaQuery.of(context).size.height //Screen height
Widget build(BuildContext context) {
return Column(
children:<Widget>[
Expanded(
child:LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
Container(
height: viewportConstraints.maxHeight * .44,
color: Theme.of(context).primaryColor,
padding: EdgeInsets.only(bottom: 2),
child: Align(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildProfileImage(context),
SizedBox(height: 17),
Text(userName)
],
),
),
),
Expanded(
child: ListView(
children: <Widget>[
ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
)
],
),
)
],
),
),
);
},
),
),
]
);
}
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return SingleChildScrollView(
child: ListView(
shrinkWrap: true,
children: <Widget>[
new Container(
height: MediaQuery.of(context).size.height/3,
color: Theme.of(context).primaryColor,
padding: EdgeInsets.only(bottom: 2),
child: Align(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildProfileImage(context),
SizedBox(height: 17),
Text(userName)
],
),
),
),
new ListView(
shrinkWrap: true,
children: <Widget>[
ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
)
],
)
],
),
);
},
);
}
If you read the error log, it says non-zero flex incoming but constraints are unbounded. To understand that clearly, imagine Flutter is trying to draw pixels of something that not finite. That's the our problem.
Widgets like ListView or SingleChildScrollView are flex widgets and has no limits unlike Column or Row.
If you have children that sizes are not definite, then you have to define flex for your ListView. For that, you can use shrinkWrap or Flexible, Expanded widgets for both ListView itself or children.
And here is the my solution, simplified as much as possible:
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return ListView(
shrinkWrap: true, // That's it
children: <Widget>[
Container(
color: Theme.of(context).primaryColor,
height: viewportConstraints.maxHeight * .44,
padding: EdgeInsets.only(bottom: 2),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildProfileImage(context),
SizedBox(height: 17),
Text(userName)
],
),
),
ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
),
ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
),
ListTile(
leading: Icon(Icons.print),
title: Text('asdad'),
)
],
);
},
);
}
Meanwhile, you have too many children that does nothing. If you want different scrolling behavior inside the Profile Info and ListTiles parts, tell me because we will must create yet another ListView for achieve that.
Also if you share your buildProfileImage widget, we can optimize your code even further, because you may even not need the LayoutBuilder widget in this case.