ListView.Builder can't scroll to max extent - flutter

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
)
)
]

Related

The argument type 'dynamic Function(BuildContext, int)' can't be assigned to the parameter type 'List<Widget>'

I am trying to make a scrollable screen where I could have each different images vertically with a few functions like a star rating bar and maybe a few buttons. I am first trying to insert a photo into the scrollable screen. Here is my code:
`
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
expandedHeight:250,
pinned: true,
floating: true,
flexibleSpace: FlexibleSpaceBar(
background: Container(
padding: EdgeInsets.symmetric(horizontal: AppLayout.getWidth(20)),
child: Column(
children: [
Gap(AppLayout.getHeight(20)),
Row( // text
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"review", style: Styles.headLineStyle1,
),
],
)
],
),
Gap(AppLayout.getHeight(25)),
Container( //search bar
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(AppLayout.getHeight(10)),
color: const Color(0xFFF4F6FD)
),
padding: EdgeInsets.symmetric(horizontal: AppLayout.getWidth(12), vertical: AppLayout.getHeight(12)),
child: Row(
children: [
const Icon(FluentSystemIcons.ic_fluent_search_regular, color: Color(0xFFBFC205)),
Text(
"place, restaurant",
style: Styles.headLineStyle4,
)
],
),
),
],
),
),
),
),
`
The code that gets an error:
`
SliverList(
delegate: SliverChildListDelegate(
(BuildContext context, int index) {
return _buildListItem(foodList2[index]);
},
),
),
`
The foodList2 List:
`
static const List<Tuple4> foodList2 = [
Tuple4<String, String, String, String> (
'assets/images/eel.jpg',
'assets/images/aburasoba.jpg',
'assets/images/soba.jpg',
'assets/images/okonomiyaki.jpg',
)
];
`
I tried to find information about SliverList and more information about SliverChildListDelegate and I figured out that this error dissapears when I delete the body. I do want to keep this body so would there be a way to fix this? (I do know that I don't have a _buildListItem in my code right now)
If you want to use a builder method, you need to use SliverChildBuilderDelegate instead of SliverChildListDelegate.
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return _buildListItem(foodList2[index]);
},
),
),
Also remember that your _buildListItem needs to return a Widget

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 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

Flutter TextField overflow bottom on keyboard up

This is the problem:
I have a grid (a crossword) and a list of questions below it. I need the list of questions to scroll indipendently from the grid. When I click on the grid to type the answer and the keyboard comes up I get an overflow error AND the grid doesn't move so the focused textfield that I use to input the answer is not visible (if it's low enough on the grid to be covered by the keyboard).
I tried all kind of combinations with ListView and Expanded widgets...no solution...I cannot use resizeToAvoidBottomInset: false because I need the grid to move up and keep the focused TextField visible.
Here is the code:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Column(
children:[
FutureBuilder(
future: grid,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Align(
alignment: Alignment.topCenter,
child: Container(
width: MediaQuery.of(context).size.width-8,
margin: EdgeInsets.zero,
padding: EdgeInsets.zero,
child: GridView.count(
padding: EdgeInsets.only(left:0.0,top:5.0,right:0.0,bottom: 0.0),
shrinkWrap: true,
crossAxisCount: 15,
mainAxisSpacing: 2,
crossAxisSpacing: 2,
children: List.generate(snapshot.data.length, (index) {
if(gridFilled.length < snapshot.data.length) {
gridFilled.add(snapshot.data[index]);
}
return FocusScope(
node: _node,
child: Container(
decoration: BoxDecoration(
color: snapshot.data[index] == "#" ? Colors.black : Colors.white,
border: Border.all(color: Colors.black)),
child:
snapshot.data[index] == "#"
? Text('#')
: _isTextField(gridFilled[index], index),
));
}),
),
),
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
),
SizedBox(width: MediaQuery.of(context).size.width, height: 15.0,),
Expanded(
child: QPanel(dir: widget.dir, dbName: widget.xwordnumber),//this is a ListView with the questions
),
]),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children:<Widget>[
//just menu items
]
),
),
);
}
Unfortunately I'm not allowed to post pictures yet...not enough reputation..please, any help would be greatly appreciated!
wrap your Column inside SingleChildScrollView.
for example:
SingleChildScrollView(
child:Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text('We move under cover and we move as one'),
Text('Through the night, we have one shot to live another day'),
Text('We cannot let a stray gunshot give us away'),
Text('We will fight up close, seize the moment and stay in it'),
Text('It’s either that or meet the business end of a bayonet'),
Text('The code word is ‘Rochambeau,’ dig me?'),
],
)
)
you can wrap your entire column inside SingleChildScrollView()

Horizontal ListView inside a Vertical ScrollView in Flutter

I am trying to achieve a very common behavior nowadays which is to have a horizontal List within another widget that is at the same time scrollable. Think something like the home screen of the IMDb app:
So I want to have a widget that scrolls vertically with few items on them. At the top of it, there should be a horizontal ListView, followed up with some items called motivationCard. There are some headers in between the list and the cards as well.
I got something like this on my Widget:
#override
Widget build(BuildContext context) => BlocBuilder<HomeEvent, HomeState>(
bloc: _homeBloc,
builder: (BuildContext context, HomeState state) => Scaffold(
appBar: AppBar(),
body: Column(
children: <Widget>[
Text(
Strings.dailyTasks,
),
ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: tasks.length,
itemBuilder: (BuildContext context, int index) =>
taskCard(
taskNumber: index + 1,
taskTotal: tasks.length,
task: tasks[index],
),
),
Text(
Strings.motivations,
),
motivationCard(
motivation: Motivation(
title: 'Motivation 1',
description:
'this is a description of the motivation'),
),
motivationCard(
motivation: Motivation(
title: 'Motivation 2',
description:
'this is a description of the motivation'),
),
motivationCard(
motivation: Motivation(
title: 'Motivation 3',
description:
'this is a description of the motivation'),
),
],
),
),
);
this is the error I get:
I/flutter (23780): ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
I/flutter (23780): The following assertion was thrown during performResize():
I/flutter (23780): Horizontal viewport was given unbounded height.
I/flutter (23780): Viewports expand in the cross axis to fill their container and constrain their children to match
I/flutter (23780): their extent in the cross axis. In this case, a horizontal viewport was given an unlimited amount of
I/flutter (23780): vertical space in which to expand.
I have tried:
Wrapping the ListView with an Expanded widget
Wrapping the Column with SingleChildScrollView > ConstrainedBox > IntrinsicHeight
Having CustomScrollView as a parent, with a SliverList and the List within a SliverChildListDelegate
None of these work and I continue getting the same kind of error. This is a very common thing and shouldn't be any hard, somehow I just cannot get it to work :(
Any help would be much appreciated, thanks!
Edit:
I thought this could help me but it didn't.
Well, Your Code Work Fine with wrapping your- ListView.builder with Expanded Widget &
setting mainAxisSize: MainAxisSize.min, of Column Widget.
E.x Code of what you Have.
body: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'Headline',
style: TextStyle(fontSize: 18),
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: 15,
itemBuilder: (BuildContext context, int index) => Card(
child: Center(child: Text('Dummy Card Text')),
),
),
),
Text(
'Demo Headline 2',
style: TextStyle(fontSize: 18),
),
Expanded(
child: ListView.builder(
shrinkWrap: true,
itemBuilder: (ctx,int){
return Card(
child: ListTile(
title: Text('Motivation $int'),
subtitle: Text('this is a description of the motivation')),
);
},
),
),
],
),
Update:
Whole page Is Scroll-able with - SingleChildScrollView.
body: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text(
'Headline',
style: TextStyle(fontSize: 18),
),
SizedBox(
height: 200.0,
child: ListView.builder(
physics: ClampingScrollPhysics(),
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: 15,
itemBuilder: (BuildContext context, int index) => Card(
child: Center(child: Text('Dummy Card Text')),
),
),
),
Text(
'Demo Headline 2',
style: TextStyle(fontSize: 18),
),
Card(
child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
),
Card(
child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
),
Card(
child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
),
Card(
child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
),
Card(
child: ListTile(title: Text('Motivation $int'), subtitle: Text('this is a description of the motivation')),
),
],
),
),
Screenshot:
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView.builder(
itemCount: 7,
itemBuilder: (_, i) {
if (i < 2)
return _buildBox(color: Colors.blue);
else if (i == 3)
return _horizontalListView();
else
return _buildBox(color: Colors.blue);
},
),
);
}
Widget _horizontalListView() {
return SizedBox(
height: 120,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (_, __) => _buildBox(color: Colors.orange),
),
);
}
Widget _buildBox({Color color}) => Container(margin: EdgeInsets.all(12), height: 100, width: 200, color: color);
}
We have to use SingleScrollView inside another SingleScrollView, using ListView will require fixed height
SingleChildScrollView(
child: Column(
children: <Widget>[
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [Text('H1'), Text('H2'), Text('H3')])),
Text('V1'),
Text('V2'),
Text('V3')]))
If someone gets the renderview port was exceeded error. warp your ListView in a Container widget and give it the height and width property to fix the issue
Column(
children: <Widget>[
Text(
Strings.dailyTasks,
),
Container(
height: 60,
width: double.infinity,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: tasks.length,
itemBuilder: (BuildContext context, int index) =>
taskCard(
taskNumber: index + 1,
taskTotal: tasks.length,
task: tasks[index],
),
),
)
]
)
I tried in this code and I fixed my problem I hope solved your want it.
SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: [
item(),
item(),
item(),
item(),
],
),
),
Horizontal ListView inside Vertical ListView using Builder
None of the answers proved to solve my issue, which was to have a horizontal ListView inside a Vertical ListView while still using ListBuilder (which is more performant than simply rendering all child elements at once).
Turned out it was rather simple. Simply wrap your vertical list child inside a Column, and check if index is 0 (or index % 3 == 0) then render the horizontal list.
Seems to work fine:
final verticalListItems = [];
final horizontalListItems = [];
ListView.builder(
shrinkWrap: true,
itemCount: verticalListItems.length,
itemBuilder: (context, vIndex) {
final Chat chat = verticalListItems[vIndex];
return Column( // Wrap your child inside this column
children: [
// And then conditionally render your Horizontal list
if (vIndex == 0) ListView.builder(itemCount: horizontalListItems.length itemBuilder: (context, hIndex) => Text('Horizontal List $hIndex')),
// Vertical list
Text('Item No. $vIndex')
],
);
},
),
for Web Chome you have to add MaterialScrollBehavior for horizontal scrolling to work. see(Horizontal listview not scrolling on web but scrolling on mobile) I demonstrate how to use the scrollcontroller to animate the list both left and right.
import 'package:flutter/gestures.dart';
class MyCustomScrollBehavior extends MaterialScrollBehavior {
// Override behavior methods and getters like dragDevices
#override
Set<PointerDeviceKind> get dragDevices => {
PointerDeviceKind.touch,
PointerDeviceKind.mouse,
};
}
return MaterialApp(
title: 'Flutter Demo',
scrollBehavior: MyCustomScrollBehavior(),
)
class TestHorizontalListView extends StatefulWidget {
TestHorizontalListView({Key? key}) : super(key: key);
#override
State<TestHorizontalListView> createState() => _TestHorizontalListViewState();
}
class _TestHorizontalListViewState extends State<TestHorizontalListView> {
List<String> lstData=['A','B','C','D','E','F','G'];
final ScrollController _scrollcontroller = ScrollController();
_buildCard(String value)
{
return Expanded(child:Container(
margin: const EdgeInsets.symmetric(vertical: 20.0),
width:300,height:400,child:Card(child: Expanded(child:Text(value,textAlign: TextAlign.center, style:TextStyle(fontSize:30))),)));
}
void _scrollRight() {
_scrollcontroller.animateTo(
_scrollcontroller.position.maxScrollExtent,
duration: Duration(seconds: 1),
curve: Curves.fastOutSlowIn,
);
}
void _scrollLeft() {
_scrollcontroller.animateTo(
0,
duration: Duration(seconds: 1),
curve: Curves.fastOutSlowIn,
);
}
_segment1()
{
return SingleChildScrollView(child:
Expanded(child:
Container(height:300,
width:MediaQuery.of(context).size.width,
child:Row(children: [
FloatingActionButton.small(onPressed: _scrollRight, child: const Icon(Icons.arrow_right),),
Expanded(child:Scrollbar(child:ListView.builder(
itemCount: lstData.length,
controller: _scrollcontroller,
scrollDirection: Axis.horizontal,
itemBuilder:(context,index)
{
return _buildCard(lstData[index]);
})
,),
),
FloatingActionButton.small(onPressed: _scrollLeft, child: const Icon(Icons.arrow_left),),
]))
,
)
);
}
#override
void initState() {
// TODO: implement initState
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(appBar: AppBar(title: Text("horizontal listview",)),body:
segment1(),
);
}
}
You just have to fix your height of your Listview (by wrapping it in a SizedBox for example).
This is because the content of your listview can't be known before the frame is drawn. Just imagine a list of hundreds of items.. There is no way to directly know the maximum height among all of them.