Flutter Dynamic Spacing between Widgets in ListView/ListView Builder.

Lavkant Kachhwaha
speakX.ai Product & Engineering
3 min readAug 31, 2021

Many times we encounter this scenario where we need to add dynamic spacing between widgets; for Row and Columns its pretty easy but in case of ListView this can be challenging.

Yellow Class

In this scenario my goal was to fill remaining space between widgets in ListView with Sizedbox with height proportional to screen size.

I have two components ListItem and BottomCard if ListItem.length >3 then this Sizedbox has height = 0 otherwise :

height = (ScreenHeight — (ListItemHeight*ListItem.length + BottomCardHeight) )

OUR GOAL IS TO KEEP BOTTOMCARD AT THE BOTTOM IRRESPECTIVE TO LIST ITEM LENGTH

We can achieve this by using Bottom Navigation but it would unnecessary overlap other widgets.

for eg. if ListItem.length > 4 its covering most screen and so spacing is not needed here, bottom card will always be at bottom but if ListItem.length < 4 some space will always be there in between list and bottomCard so we will use that much height spacing for obataining our goal.

Implementaion

import 'package:flutter/material.dart';
import 'package:rect_getter/rect_getter.dart';
class Listing extends StatefulWidget {
@override
_ListingState createState() => _ListingState();
}
class _ListingState extends State<Listing> {ScrollController controller = ScrollController();
ScrollPhysics physics = ClampingScrollPhysics();
var rectGlobalKey = RectGetter.createGlobalKey();
double screenHeight = SharedViews.getScreenHeight();
var customHeight = 0.0;
//LISTINFS
void newListings() {
//IMP RESET CONTAINER, SO AFTER SETSTATE IT WILL AUTO ADJUST SIZE
customHeight = 0.0;
this.setState(() {});
//FETCH EMPTY SIZE
Future.delayed(Duration(milliseconds: 150), () {
getBottomSpace();
});
}
@override
void initState() {
super.initState();
//GET NEW HEIGHT OF CONTAINER AFTER CERT LOADED
newListings();
WidgetsBinding.instance!.addPostFrameCallback((_) {
Future.delayed(Duration(milliseconds: 100), () {
getBottomSpace();
});
});
}
Widget bottomCard() { return RectGetter(
key: rectGlobalKey,
child: Container(
height: 250,
width: double.infinity,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: [
Color(0xffdbf5ff),
Color.fromRGBO(241, 251, 255, 0.24),
])),
),
);
}
getBottomSpace() {
if (data.length != 0) {
var rect;
rect = RectGetter.getRectFromKey(rectGlobalKey);
var x = screenHeight - rect.bottom - MediaQuery.of(context).padding.vertical;
if (x > 1.0) {
setState(() {
customHeight = x;
});
}
}
}
Widget ListWidget(context, data) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
Container(
width: SharedViews.getScreenWidth(),
child: Padding(
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
],)
,)
,),
ListView.separated(
physics: ClampingScrollPhysics(),
controller: controller,
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: data.length,
itemBuilder: (BuildContext context, i) {
return ListItem(title: data[i].title);
},
separatorBuilder: (BuildContext context, i) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 20),
child: Divider(
color: AppColors.cBLACK_10,
thickness: 1,
),
),),],),
data.length != 0
?
AnimatedContainer(
duration: Duration(milliseconds: 100),
curve: Curves.easeIn,
height: customHeight,
)
:
Container(),
data.length != 0 ? bottomCard() : Container(),
SizedBox(height: customHeight == 0 ? MediaQuery.of(context).padding.bottom : 0,),
],);
}

@override
Widget build(BuildContext context) {
var data = [
{"name": "test1"},
{"name": "test2"},
{"name": "test3"}
];
return ListWidget(context, data);
}
}

End Result

Using Rect Getter we are passing rectGetterGloabal key to Our BottomCard Widget and we are calling getBottomSpace() function to fetches data from rectGetterKey and calculate the height available, after all ListItems and BottomCard is Rendered, and if height ≥ 0px we change height of Container inside Animated Container and call SetState(), it again rebuild widgets and now we will get height =0, so now getBottomSpace() function wont do anything and bottom card will be appended at end of List inside ListView.

--

--