How to get the height of ListView.builder() widgets to be the height of the entire stack? - flutter

I have a ListView.builder() inside a column, and I would like to tell the LisView.builder to set the height according to the height of the widget that it will be returning from the builder method. How do I do that?

Wrap your ListView in a SizedBox with some fixed height. Setting to children height is impractical since Flutter would have to check the size of every element in the list and calculate the maximum.

I am not sure but looking at your question it looks like you want to render listview within the sizes which child widgets are taking
For that we can assign mainAxisSize: MainAxisSize.min, to column and shrinkWrap: true to Listview.builder
class _MyHomePageState extends State<MyHomePage> {
List<String> itemsList = [
"item0",
"item1",
"item2",
"item3",
"item4",
"item5"
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("ListView Sample"),
),
body: Container(
color: Colors.black54,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
ListView.builder(
shrinkWrap: true,
itemCount: itemsList.length,
itemBuilder: (context, index) {
return Container(
child:
Align(
alignment: Alignment.centerRight,
child:Text(
itemsList[index],
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),),
width: 100,
height: 20,
margin: EdgeInsets.only(bottom: 10),
color: Colors.red);
}),
],
),
),
);
}
}

You can use global key to get height of list view or column,
But the height can be fetched only after a build is completed
//user gloabl key to get height of list view or column
GlobalKey key = GlobalKey();
SingleChildScrollView(
child: Column(
key: key,
children: [...]
)
//here you can get widget height
double totalWidgetHeight = key.currentContext!.size!.height;

Related

Flutter - Can't have nested list view in column without using fixed height Container

I am trying to create a nested list view each wrapped by a column. The parent widget (widget 1) has a column with a vertical list view and each list view item (widget 2) is a column with a horizontal list view. So far I am able to get it to render with the following code where in widget 2 I wrap the horizontal list view with a Container and a specified height. I am trying to use not use a fixed height, however, so I have tried using Flexible and Expanded instead of Container but both of these result in the unbounded height constraints error.
class Widget1State extends State<Widget1> {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
child: Scrollbar(
child: ListView.builder(
padding: const EdgeInsets.all(8.0),
itemCount: getWidgets().length,
itemBuilder: (BuildContext context, int index) {
return Widget2();
},
),
),
),
],
),
);
}
}
class Widget2State extends State<Widget2> {
#override
Widget build(BuildContext context) {
return Column(
children: [
Container(
height: 30,
child: Scrollbar(
child: ListView.builder(
padding: const EdgeInsets.all(8.0),
scrollDirection: Axis.horizontal,
itemCount: getWidgets2().length,
itemBuilder: (BuildContext context, int index) {
return Text('widget');
},
),
),
),
],
);
}
}
As you can see below this is how it currently works where the exercises is the parent list view and the sets are the child list view. Currently because the sets list is in a Container it takes up space when it's empty and also doesn't size to whatever makes up the list item. I want to change the sets list view so that it only takes up the space is needed by the list item.
Given that Listview takes all avaible height if you dont provide one it will result in failure.
In order to proovide alternative solutions I need you especify how is the design that you want. Coudl you give more details?
---- Edited:
This solution was found here: Flutter: Minimum height on horizontal list view
You can change the widget 2 from Listview to SingleChildScrollView:
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: [
const Padding(
padding: EdgeInsets.all(8.0),
child: TextField(
decoration: InputDecoration(label: Text('Workout Name')),
),
),
...List.generate(
exercises, // number of exercises
(index) => Column(
mainAxisSize: MainAxisSize.min,
children: [
DropdownButton<String>(items: const [
DropdownMenuItem(child: Text('Select Exercise'))
], onChanged: (value) {}),
SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Row(
children: List.generate(
sets, //number of sets
(index) => Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('Set $index'),
Text('reps'),
Text('rest')
],
)),
)),
TextButton.icon(
onPressed: () {
addSet();
},
icon: const Icon(Icons.add),
label: Text('Set'))
],
))
],
),
bottomNavigationBar: IconButton(
onPressed: () {
addExercise();
},
icon: const Icon(Icons.add)),
);
}
I've tried this code and works as you need, without setting the height of the 'Sets' scrollview.
I want to point the use of the bottomNavigationBar to the AddExercise button, instead of a Column>Listview structure. Separating the button in the bottomNavBar you can use the body atribute freely.
You want to use Flutter default widgets when posible.

ListView.Builder can't scroll to max extent

I'm trying to create a contact book page that will load recent contact and all of my contact list, I managed to get the data and build it into the widget.
Design that I want to achieve for the screen body are:
a search widget which always stay on top even when the contact book can be scrolled.
List of the contact that can be scrolled
The problem is when there are a lot of contact (let say 20 contacts). If the screen size is small, it can only scroll until 7-8 contacts, but I know there's more to it. But I guess my ListView.Builder won't let me scroll down anymore. Like it's constrained by some properties but I don't know what is the reason
Can anyone please tell me the problem and how to fix this ?
Here's my code:
/// BODY
body: WillPopScope(
onWillPop: () {
print('hello');
},
child: ListView(
physics: BouncingScrollPhysics(),
shrinkWrap: true,
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 20),
height: MediaQuery.of(context).size.height,
child: NestedScrollView(
controller: _scrollController,
headerSliverBuilder: (context, isScrolled) {
return [
SliverPersistentHeader(
pinned: true,
floating: false,
delegate: ChooseUsersHeaderDelegate(
widgetList: Container(
color: Colors.white,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// Build Search Box Function
buildSearch(),
],
),
),
minHeight: selectedContactId.isNotEmpty ? 135 : 60,
maxHeight: selectedContactId.isNotEmpty ? 135 : 60,
parentContext: context,
),
),
];
},
body: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
/// All Contacts Subtitle
Text(
'allContacts',
style: GoogleFonts.montserrat(
fontSize: 14,
fontWeight: FontWeight.w500,
color: primaryColor,
fontStyle: FontStyle.normal,
),
).tr(),
/// All Contacts
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
isLoading
? ReusableLoadingContact()
: NotificationListener<
OverscrollIndicatorNotification>(
onNotification: (overScroll) {
overScroll.disallowIndicator();
return true;
},
/// Call function to return the widget for building contact list
child: buildWidget(context, 'contacts')),
],
),
],
),
),
),
)
],
),
),
Here's the code for building the contact List:
Widget build(BuildContext context) {
return contact.isEmpty
? Text('')
: ListView.builder(
/// Force to build only 2 items if recent, otherwise build all
itemCount: status == 'recent' ? contact.length > 2 ? 2 : contact.length : contact.length,
scrollDirection: Axis.vertical,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
/// Return the widget of each contact
},
);
}
if you want to make your search bar stay on top here a very simple way
body : Column(
children:[
// this part will stay on top
Container() // your widget search bar
// listview widget will rendered after the Container
Expanded(
child: Listview.builder(
// this part will be scrollable
// return the widget contact
)
)
]
....
To attain this , use Container and Expanded as child widgets for Column
Format:
body : Column(
children:[
Container(
child: // Your search bar goes here
),
Expanded(
child: // Your list view goes here
)
)
]

how to set width of container based on text length in flutter

I have created list of cateogories using list listview.builder
here I want to highlight selected category by underlining category text with container and I want to apply width based on text length...same way like we do underline for text,
I know they are inbuilt some packages but I don't want to use it as I want to implement my logic.
here is my code
I have set comment where I want to dynamic width
class CatogoryList extends StatefulWidget {
#override
State<CatogoryList> createState() => _CatogoryListState();
}
class _CatogoryListState extends State<CatogoryList> {
List<String> categories=['HandBag','Jwellery','FootWear','Dresses','Pens','Jeans','Trousers'];
int selectedindex=2;
#override
Widget build(BuildContext context) {
return SizedBox(
height: 30,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categories.length,
itemBuilder: (context,index){
return buildCategory(index);
}),
);
}
Widget buildCategory(int index)
{
return GestureDetector(
onTap: (){
setState(() {
selectedindex=index;
});
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(categories[index],style: TextStyle(fontSize: 20,color: selectedindex==index?Colors.blue:Colors.grey),),
if(selectedindex==index)
Container(
// here I want to set widget of container based on text length
height: 3,width: 30,color: Colors.blue,),
],),
),
);
}
}
One way is to wrap the Column in an IntrinsicWidth and leave out the width from the Container. Like
Widget buildCategory(int index)
{
return GestureDetector(
onTap: (){
setState(() {
selectedindex=index;
});
},
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: IntrinsicWidth(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(categories[index],style: TextStyle(fontSize: 20,color: selectedindex==index?Colors.blue:Colors.grey),),
if(selectedindex==index)
Container(height: 3,color: Colors.blue,),
],),
),
),
);
}
I think the proper approach would be to wrap the selected Text widget with Container and apply the decoration there.
I'm answering your question as well:
final TextPainter textPainter = TextPainter(
text: TextSpan(text: text, style: yourTextStyle), /// apply your style here
textScaleFactor: MediaQuery.of(context).textScaleFactor,
textDirection: TextDirection.ltr,
)..layout();
final double width = textPainter.size.width;

Stack: cover bottom child with top child

I want overlay a widget over the bottom widget to hide it. hence I use stack. I don't know the bottom widget size so I can't give top widget fixed size.
what I have:
what I want to achieve:
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'Winner: ',
style: TextStyle(fontSize: 34),
),
Stack(
children: [
Text('YOU!!!', style: TextStyle(fontSize: 34)),
Container(
color: Colors.amber,
)
],
),
],
),
This is simplified version of what I want to achive. I have to use Row and assume Texts are any widget with any size.
Stack sizes its children based on the Constraint it receives from parent not based on children sizes.
how can I overlay top widget with exact size of bottom widget?
To know the size of your widget and apply things depending on this, you can use GlobalKey like this GlobalKey key;. Initialize it in your initSate and listen to it's size
GlobalKey key;
double yourWidgetWidth;
double yourWidgetHeight;
#override
initState() {
// .... other stuffs
key = GlobalKey();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
yourWidgetWidth = containerKey.currentContext.size.width;
yourWidgetHeight = containerKey.currentContext.size.height;
}
});
}
And with yourWidgetWidth and yourWidgetHeight you can achieve what you want
Edit
Here is a concept of what am trying to explain
// In variable declaration scope
GlobalKey youTextKey = GlobalKey();
ValueNotifier widthNotifier = ValueNotifier(0);
ValueNotifier heightNotifier = ValueNotifier(0);
// In initState
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
widthNotifier.value = youTextKey.currentContext.size.width - 1.0;
heightNotifier.value = youTextKey.currentContext.size.height - 5.0;
}
});
// Your row
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'Winner: ',
style: TextStyle(fontSize: 34),
),
Stack(
children: [
Text('YOU!!!', style: TextStyle(fontSize: 34), key: youTextKey,),
ValueListenableBuilder(
valueListenable: widthNotifier,
builder: (context, width, _) {
return ValueListenableBuilder(
valueListenable: heightNotifier,
builder: (context, height, _) {
return Container(
width: width,
height: height,
color: Colors.amber,
);
},
);
},
),
],
),
],
)
Using Positioned.Fill solved the problem:
Positioned.Fill Creates a Positioned object with [left], [top], [right], and [bottom] set to 0.0 unless a value for them is passed.
so the Positioned child get stretched to the edges of the stack's biggest child.
Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
'Winner: ',
style: TextStyle(fontSize: 34),
),
Stack(
children: [
Text('YOU!!!', style: TextStyle(fontSize: 34)),
Positioned.fill(
child: Container(
color: Colors.amber,
),
)
],
),
],
),

ListView or SingleChildScrollView of variable size

I want to have a widget of variable height that contains a ListView or a SingleChildScrollView or anything that scrolls.
I tried making it like this:
Container(
color: Colors.pink,
child: Column(
children: [
Container(color: Colors.orange, child: Text("Header")),
SingleChildScrollView(
child: Container(
height: 10000,
color: Colors.green,
child: Text("the height of this content could be anything")),
),
Container(color: Colors.blue, child: Text("Footer")),
],
),
)
This causes an overflow because the SingleChildScrollView expands to height of 10000 pixels. If I enclose it in an Expanded then it works fine but then if its child's height is for example 200 instead of 10000, it will still expand the parent widget to the entire height of the screen.
Is it possible to have the height of the scroll/list adjust itself to its content and only expand to the entire screen if it needs to?
You can do it if you know the size of the footer and header widget and using LayoutBuilder widget to get the constraints.
#override
Widget build(BuildContext newcontext) {
return Center(
child: Scaffold(
body: Container(
color: Colors.pink,
child: LayoutBuilder(
builder: (_, constraints) {
final sizeHeader = 150.0;
final sizeFooter = 150.0;
final sizeList = 1000.0;
final available =
constraints.maxHeight - (sizeHeader + sizeFooter);
Widget _buildCenterWidget() {
return Container(
height: sizeList,
color: Colors.green,
child: Text("the height of this content could be anything"),
);
}
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
height: sizeHeader,
color: Colors.orange,
child: Text("Header")),
available < sizeList
? Expanded(
child: _buildCenterWidget(),
)
: _buildCenterWidget(),
Container(
height: sizeFooter,
color: Colors.blue,
child: Text("Footer")),
],
);
},
)),
),
);
}
You can use ConstrainedBox, to specify minHeight, maxHeight for your widget. Remember that none of your widget should have infinite height/width, that spoils the UI, may also throw error