I am trying to implement a review page for products. I trying to show the products images in a expansionTile (see second image). Under the expansionTile I add my buttons.
My problem: To show the expansionTile list I must give the container a fix height. But if I have less images in my list the screen show a white space (second image).
How can I make the container height dynamic to hide the white space?
If I add some pictures my screen looks like this.
How I can hide the white space between list and buttons?
Here my Code:
Container(
height: MediaQuery.of(context).size.height * 0.5,
width: MediaQuery.of(context).size.width * 0.8,
child: ListView(
children: [
ExpansionTile(
title: Text('Pictures'),
onExpansionChanged: (value) {
setState(
() {},
);
},
children: List<Widget>.generate(
_imageList.length,
(index) => ListTile(
title: Text(_imageList[0]
.path
.split('/')
.last
.length >
25
? _imageList[0]
.path
.split('/')
.last
.substring(0, 25) +
'...'
: _imageList[index].path.split('/').last),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.delete_outline,
size: 20.0,
color: _selected![index]
? Colors.red
: Colors.red,
),
onPressed: () {
setState(() {
_selected![index] =
!_selected![index];
_imageList.removeAt(index);
});
},
),
],
),
),
),
),
],
),
)
Here my full code:
class ReviewPage1 extends StatefulWidget {
//passed paramter
final String _productID;
ReviewPage1(this._productID, {Key? key}) : super(key: key);
@override
_ReviewPage1 createState() => _ReviewPage1(_productID);
}
class _ReviewPage1 extends State<ReviewPage1> {
///passed paramter
final String _productID;
_ReviewPage1(this._productID);
//controller
final TextEditingController _controllerReviewTitle = TextEditingController();
final TextEditingController _controllerReviewDescription =
TextEditingController();
//image
List<File> _imageList = [];
var _image;
var imagePicker;
//list tile color
List<bool>? _selected = [];
//stars
var rating = 1;
@override
void initState() {
super.initState();
imagePicker = new ImagePicker();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: customSubAppBar("Create Review", context),
body: SingleChildScrollView(
child: Center(
child: Container(
width: MediaQuery.of(context).size.width * 0.8,
child: Column(
children: [
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.05,
MediaQuery.of(context).size.width * 0,
null,
),
//text
Row(
children: [
customText(
'Rate your expreince',
Colors.black,
20,
FontWeight.bold,
null,
),
],
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.02,
MediaQuery.of(context).size.width * 0,
null,
),
//stars rating
Row(
children: [
Row(
mainAxisSize: MainAxisSize.min,
children: List.generate(4, (index) {
return GestureDetector(
onTap: () {
setState(() {
// Toggle light when tapped.
print('star at index: ' + index.toString());
rating = index + 1;
});
},
child: Padding(
padding: const EdgeInsets.only(right: 8),
child: Icon(
//index < rating ? Icons.star : Icons.star_border,
Icons.star,
size: 30,
color: index < rating
? Theme.of(context).primaryColor
: Colors.black.withOpacity(0.5),
)));
}),
),
Spacer(),
],
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.08,
MediaQuery.of(context).size.width * 0,
null,
),
//text
Row(
children: [
customText(
'Review Title',
Colors.black,
20,
FontWeight.bold,
null,
),
],
),
//textfield review title
customDefaultTextField(
50,
1,
TextInputType.text,
_controllerReviewTitle,
1,
false,
Colors.black87,
Theme.of(context).primaryColor,
false,
Theme.of(context).primaryColor,
0,
0,
Theme.of(context).primaryColor,
0,
0,
'Title *',
'Enter Your Title',
Colors.black12,
2,
0,
Theme.of(context).primaryColor,
2,
0,
null,
0,
0,
0,
0,
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.08,
MediaQuery.of(context).size.width * 0,
null,
),
//text
Row(
children: [
customText(
'Write Review',
Colors.black,
20,
FontWeight.bold,
null,
),
],
),
//textfield review description
customDefaultTextField(
50,
1,
TextInputType.text,
_controllerReviewDescription,
1,
false,
Colors.black87,
Theme.of(context).primaryColor,
false,
Theme.of(context).primaryColor,
0,
0,
Theme.of(context).primaryColor,
0,
0,
'Write your Expreinces *',
'Enter your Expreinces',
Colors.black12,
2,
0,
Theme.of(context).primaryColor,
2,
0,
null,
0,
0,
0,
0,
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.08,
MediaQuery.of(context).size.width * 0,
null,
),
//text
Row(
children: [
customText(
'Enter your picture',
Colors.black,
20,
FontWeight.bold,
null,
),
],
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.03,
MediaQuery.of(context).size.width * 0,
null,
),
//take image
GestureDetector(
onTap: () async {
var source = ImageSource.camera;
XFile? image = await imagePicker.pickImage(
source: source,
imageQuality: 50,
preferredCameraDevice: CameraDevice.front);
setState(
() {
_image = File(image!.path);
_imageList.add(_image);
_selected!.add(false);
},
);
},
child: Stack(
children: [
//icon
Container(
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.1,
decoration: BoxDecoration(
color:
Theme.of(context).primaryColor.withOpacity(0.8),
borderRadius: BorderRadius.all(Radius.circular(8))),
child: Center(
child: customIcon(
Icons.camera_alt_rounded, Colors.black87, 30)),
),
//border
Container(
width: MediaQuery.of(context).size.width * 0.8,
height: MediaQuery.of(context).size.height * 0.1,
child: DashedRect(
color: Colors.black87,
strokeWidth: 2.0,
gap: 10.0,
),
),
],
),
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.03,
MediaQuery.of(context).size.width * 0,
null,
),
//show images
_imageList.length != 0
? //IntrinsicHeight(
Container(
height: MediaQuery.of(context).size.height * 0.5,
width: MediaQuery.of(context).size.width * 0.8,
child: ListView(
children: [
ExpansionTile(
title: Text('Pictures'),
onExpansionChanged: (value) {
setState(
() {},
);
},
children: List<Widget>.generate(
_imageList.length,
(index) => ListTile(
title: Text(_imageList[0]
.path
.split('/')
.last
.length >
25
? _imageList[0]
.path
.split('/')
.last
.substring(0, 25) +
'...'
: _imageList[index].path.split('/').last),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.delete_outline,
size: 20.0,
color: _selected![index]
? Colors.red
: Colors.red,
),
onPressed: () {
setState(() {
_selected![index] =
!_selected![index];
_imageList.removeAt(index);
});
},
),
],
),
),
),
),
],
),
)
//)
: Container(),
//text cancel
Container(
width: MediaQuery.of(context).size.width * 0.8,
child: TextButton(
child: Text(
'Send Review',
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
style: TextButton.styleFrom(
primary: Colors.black,
backgroundColor: Colors.greenAccent[400],
onSurface: Colors.grey,
),
onPressed: () {
if (_checkInputValuesEmpty(
context, _controllerReviewTitle.text, 'title') &&
_checkInputValuesEmpty(
context,
_controllerReviewDescription.text,
'description')) {
final String _userID =
FirebaseAuth.instance.currentUser!.uid;
_uploadFile(_userID, _productID, context, _imageList);
}
},
),
),
//size box
customSizedBox(
MediaQuery.of(context).size.height * 0.01,
MediaQuery.of(context).size.width * 0,
null,
),
//text send review
Container(
width: MediaQuery.of(context).size.width * 0.8,
child: TextButton(
child: Text(
'Cancel',
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold),
),
style: TextButton.styleFrom(
primary: Colors.black,
backgroundColor: Color.fromRGBO(170, 170, 170, 1),
onSurface: Colors.grey,
),
onPressed: () async {},
),
),
],
),
),
),
),
//sidebar
drawer: CustomSideBar(context),
);
}
//upload review images
Future _uploadFile(_userID, _productID, context, _imageList) async {
var downloadURLs = [];
for (var image in _imageList) {
String url;
String postId = DateTime.now().millisecondsSinceEpoch.toString();
String fileName = 'reviewImage_${_productID}_${postId}';
Reference ref = FirebaseStorage.instance
.ref()
.child("images/review/review_images/" + _productID + '/' + _userID)
.child(fileName);
await ref.putFile(image);
url = fileName;
downloadURLs.add(url);
}
//upload review data
_addReviewData(_productID, downloadURLs, _userID);
}
//add review data in db
_addReviewData(_productID, downloadURLs, _userID) async {
final String _userID = FirebaseAuth.instance.currentUser!.uid;
FirebaseFirestore.instance
.collection('review')
.doc('productID_' + _productID)
.collection("userID_" + _userID)
.doc('review_data')
.set({
'stars': rating,
'title': _controllerReviewTitle.text,
'description': _controllerReviewDescription.text,
'image_url': downloadURLs,
});
//Navigator.of(context).pushReplacement(
// MaterialPageRoute(builder: (context) => VerifyRegisterEmail()));
}
}
_checkInputValuesEmpty(context, text, field) {
if (text.toString().length == 0) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text('Enter text in field ${field}'),
));
return false;
} else {
return true;
}
}
Can I give the container a dynamic height which has always the same height how the listview with my image names?
try find this place and add after Colum mainAxisAlignment: MainAxisAlignment.start,
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: customSubAppBar("Create Review", context),
body: SingleChildScrollView(
child: Center(
child: Container(
width: MediaQuery.of(context).size.width * 0.8,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,