I have a ListView where I want to implement a nice way to delete list items using a bottom sheet with actions on. Initially I went down the path of simply calling showBottomSheet() in the onLongPress event handler for my list items, which would successfully open a bottom sheet with my action buttons on. However, this would automatically add a back button to the AppBar which is not what I want.
I then went down the route of trying out animations, such as SlideTransition and AnimatedPositioned:
class FoldersListWidget extends StatefulWidget {
#override
_FoldersListWidgetState createState() => _FoldersListWidgetState();
}
class _FoldersListWidgetState extends State<FoldersListWidget>
with SingleTickerProviderStateMixin {
double _bottomPosition = -70;
#override
Widget build(BuildContext context) {
return Stack(
children: [
FutureBuilder<List<FolderModel>>(
future: Provider.of<FoldersProvider>(context).getFolders(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, i) {
final folder = snapshot.data[i];
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
elevation: 1,
child: ListTile(
title: Text(folder.folderName),
leading: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
width: 50,
child: Consumer<FoldersProvider>(
builder:
(BuildContext context, value, Widget child) {
return value.deleteFolderMode
? CircularCheckBox(
value: false,
onChanged: (value) {},
)
: Icon(
Icons.folder,
color: Theme.of(context).accentColor,
);
},
),
),
],
),
subtitle: folder.numberOfLists != 1
? Text('${folder.numberOfLists} items')
: Text('${folder.numberOfLists} item'),
onTap: () {},
onLongPress: () {
Provider.of<FoldersProvider>(context, listen: false)
.toggleDeleteFolderMode(true); // removes fab from screen
setState(() {
_bottomPosition = 0;
});
},
),
);
},
);
},
),
AnimatedPositioned(
bottom: _bottomPosition,
duration: Duration(milliseconds: 100),
child: ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(25),
topRight: Radius.circular(25),
),
child: Container(
height: 70,
width: MediaQuery.of(context).size.width,
color: Theme.of(context).colorScheme.surface,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
Expanded(
child: IconAboveTextButton(
icon: Icon(Icons.cancel),
text: 'Cancel',
textColour: Colors.black,
opacity: 0.65,
onTap: () => setState(() {
_bottomPosition = -70;
}),
),
),
VerticalDivider(
color: Colors.black26,
),
Expanded(
child: IconAboveTextButton(
icon: Icon(Icons.delete),
text: 'Delete',
textColour: Colors.black,
opacity: 0.65,
),
),
],
),
),
),
),
],
);
}
}
This slides the bottom Container on and off the screen but my issue is that it covers the last list item:
Could anyone suggest a better way of doing this or simply a way to adjust the height of the ListView so that when the Container slides up, the ListView also slides up.
Wrap ListView.builder inside a container and set its bottom padding as (70+16)
70 (height of bottom sheet), 16 (some default padding to make it look better if you like).
return Container(
padding: EdgetInset.ony(bottom: (70+16)),
child:ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, i) {
.....
.....
Related
I am trying to make a list view only occupy part of the screen, but it keeps growing till the end not respecting the contianer constraints. I tried to use a sizedbox too but it didn' work. List tiles outside the container are shown without any widget inside, but the background is shown anyways
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: pedidos,
builder: (context, AsyncSnapshot<List<Pedido>> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
height: MediaQuery.of(context).size.height * 0.6,
child: ListView.builder(
itemCount: snapshot.data!.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(8.0),
child: Hero(
tag:
"pedidos_card${snapshot.data![index].idPedido}",
child: ListTile(
tileColor: Colors.white,
leading: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle),
child: Center(
child: Text(
style: Theme.of(context)
.textTheme
.headlineSmall,
"${snapshot.data![index].idPedido}"),
),
),
title: Text(
'Pedido: ${snapshot.data![index].idPedido}'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 10),
Text(
'Estado: ${snapshot.data![index].estadoPedido.last.tipoEstadoPedido.name}'),
SizedBox(height: 10),
Text(
"Cliente: ${snapshot.data![index].cliente.nombre}")
],
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
trailing: Checkbox(
value: pedidosSeleccion
.contains(snapshot.data![index]),
onChanged: (bool? value) {
// value = checkboxList[index];
// setState(() {});
},
),
onTap: () {
bool isSelected = pedidosSeleccion
.contains(snapshot.data![index]);
if (isSelected) {
pedidosSeleccion
.remove(snapshot.data![index]);
} else {
pedidosSeleccion.add(snapshot.data![index]);
}
setState(() {});
},
),
));
}),
),
ElevatedButton(
onPressed: () {}, child: Text('Ver ultima milla')),
],
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
});
}
}
example
you can use Expanded instead of Sizedbox
eg:-
Column(
children:[
Expanded(flex:9,
child: ListView(
padding: const EdgeInsets.only(top: 10.0),
children: snapshot.map((data) => _buildListItem(context, data)).toList(),
),
),
Expanded(flex:1,
child:
ElevatedButton(
// fill in required params
),
)
])
I want to have a modal bottom sheet that I can scroll through as normal without closing the modal bottom sheet. Then, when I'm at the top of my list, I want to be able to close the Modal Bottom Sheet with a scroll gesture.
I would like to add the YouTube style modal bottom sheet shown in the video to my app, but unfortunately I don't know how to do it.
This is the link to the video that shows what I mean: https://drive.google.com/file/d/1977Ptq4Sox6WKGQkiTbCH_zydn8Mu05o/view?usp=sharing
This is inside my Profile Screen:
#override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: Container(
height: MediaQuery.of(context).size.height * 0.95,
margin: EdgeInsets.only(bottom: 5, left: 10, right: 10),
decoration: BoxDecoration(
border: Border.all(color: Colors.black, width: 1.5),
borderRadius: BorderRadius.circular(25),
color: Colors.white,
),
child: ClipRRect(
borderRadius: BorderRadius.circular(23),
child: DraggableScrollableSheet(
initialChildSize: 1,
minChildSize: 0.5,
maxChildSize: 1,
expand: false,
builder: (BuildContext context, ScrollController _scrollController) {
return SingleChildScrollView(
controller: _scrollController,
child: Column(
children: [
Container(
height: 590,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(width: 1.5, color:
Colors.black),
),
color: Colors.white,
),
child: Stack(
children: [
InteractiveViewer(
transformationController:
_zoomProfilePictureController,
minScale: 1,
maxScale: 3,
onInteractionEnd: (ScaleEndDetails endDetails) {
setState(() {
_zoomProfilePictureController.value =
Matrix4.identity();
});
},
child: Swiper(
physics: NeverScrollableScrollPhysics(),
itemBuilder:
(BuildContext context, int imageIndex) {
return Hero(
tag: "Profile",
child: CachedNetworkImage(
imageUrl: widget.user.imageUrl[imageIndex],
fit: BoxFit.cover,
useOldImageOnUrlChange: true,
placeholder: (context, url) => Center(
child: CircularProgressIndicator(
strokeWidth: 3,
valueColor: AlwaysStoppedAnimation<Color>(
primaryColor.withOpacity(0.7),
),
),
),
errorWidget: (context, url, error) => Icon(
Icons.error_outline,
size: 30,
color: Colors.red,
),
),
);
},
itemCount: widget.user.imageUrl.length,
pagination: SwiperPagination(
alignment: Alignment.topCenter,
margin: EdgeInsets.all(0),
builder: DotSwiperPaginationBuilder(
color: Colors.white.withOpacity(0.7),
size: 8,
activeColor: widget.user.imageUrl.length > 1
? primaryColor
: Colors.transparent,
activeSize: 10,
),
),
control: widget.user.imageUrl.length > 1
? SwiperControl(
padding: const EdgeInsets.only(top: 250),
color: Colors.transparent,
disableColor: Colors.transparent,
size: 228,
)
: null,
loop: false,
),
),
This is how I call the BottomSheet:
ListTile(
onTap: () => showModalBottomSheet(
backgroundColor: Colors.transparent,
context: context,
builder: (context) {
return ProfileInformationHomeScreen(
secondUser,
widget.currentUser,
);
},
),
it's DraggableScrollableSheet.
create a widget for bottomSheet.
#override
Widget build(BuildContext context) {
return ClipRRect(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(12),
topRight: Radius.circular(12),
),
child: DraggableScrollableSheet(
///* it will be always visible 95% of screen,
///* if we drag down at 50% it will close the sheet
initialChildSize: 0.95,
minChildSize: 0.5,
maxChildSize: .95,
expand: false,
builder: (BuildContext context, c) {
return Container(
color: Colors.white,
child: ListView(
controller: c,
children: [
/// your widgets
call a method to showModalBottomSheet.
_showBottomSheet(BuildContext context) {
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => DestinationBottomSheetWidget(),
);
}
To close the buttomSheet you can drag or use a button inside DraggableScrollableSheet builder and use Navigator.of(context).pop().
I do like this and add item of ListView
/// heading
Padding(
padding: EdgeInsets.only(
left: 20,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Icon(
Icons.close,
color: Colors.grey,
),
),
Text(
"Title",
textAlign: TextAlign.center,
),
///* this empty widget will handle the spaces
Icon(
Icons.close,
color: Colors.grey.withOpacity(0.0),
),
],
),
),
for more flutter doc youtube
I'm trying to create a listview of cards whose images get displayed in the listview only if the card is selected. The selection widget is the PopupSubscription() where I'm choosing which cards (SubscriptionCard) to display by setting the bool of that particular image to be true. But when the selections are applied, the selected images don't appear immediately even after doing setState().
However, they do appear when I switch tabs and return the screen again. What should I do in order to change the state of an object that's not in my current state class? I tried using Provider but it left me confused as to what I'm supposed to do.
This is the SubscriptionCard where the bool is set on tapping it:
return InkWell(
radius: 1.0,
borderRadius: BorderRadius.circular(8.0),
highlightColor: buttonBackground,
onTap: () {
setState(() {
widget.currentSubscription.selected = !widget.currentSubscription.selected;
});
},
child: Card(
elevation: 1.0,
borderOnForeground: true,
shape: widget.currentSubscription.selected ? RoundedRectangleBorder(
borderRadius: BorderRadius.circular(3.0),
side: BorderSide(color: buttonBackground, width: 2.0),
) : ContinuousRectangleBorder(),
color: bgDarkColor,
child: SizedBox(
width: SizeConfig.blockSizeHorizontal * 30,
child: Stack(
alignment: Alignment.topRight,
children: [
Row(
mainAxisSize: MainAxisSize.max,
children: [
Image.asset(this.widget.currentSubscription.logo, height: 35.0,),
Text(
' ${this.widget.currentSubscription.name}',
style: TextStyle(fontFamily: 'Muli', fontSize: 16.0)
),
],
),
widget.currentSubscription.selected ? Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
color: buttonBackground,
),
child: Padding(
padding: const EdgeInsets.all(2.0),
child: Icon(
Icons.check,
size: 10.0,
color: Colors.white,
),
),
) : Container()
],
),
),
),
);
This is the ListView where the selected cards' images are rendered:
Container(
height: 50,
width: 350,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
IconButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return PopupSubscription();
}
);
},
icon: Icon(Icons.add_box_rounded, size: 30.0,),
),
StatefulBuilder(
builder: (context, setState) {
return ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: subscriptionsList.length,
itemBuilder: (context, index) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 5.0),
child: ClipRRect(
borderRadius: BorderRadius.circular(25.0),
child: Image.asset(
subscriptionsList[index].selected ? subscriptionsList[index].logo
: "assets/elements/streaming-services/netflix.jpeg",
),
),
);
},
);
}
),
],
)
),
Based on your current code, I'm guessing you've added the currentSubscription variable as final in the StatefulWidget above the actual State, like:
class MyClass extends StatefulWidget{
final currentSubscription;
// Rest of the code
}
class _MyClassState extends State<MyClass> {
// Your ListView Code and other stuff.
}
This wont work when you want to change the state onTap, I recommend making the variable you use in setState within the _MyClassState class and use setState in that. Something like:
class _MyClassState extends State<MyClass> {
bool _isSelected = false;
// And in your onTap method, something like:
setState(() {
_isSelected = !_isSelected;
});
}
i set two buttons(left and right Button) on top of ListView. buttons work for scrolling on click. now i want to hide the left button when index is 0 and the right button when index is last. more explain to clear, the left button will be hidden in first index and the right button will be hidden in last index. please help me.
class ScrollingLeftAndRightButtonHide extends StatefulWidget {
#override
_ScrolllingOnClickButtonState createState() =>
_ScrolllingOnClickButtonState();}
class _ScrolllingOnClickButtonState
extends State<ScrollingLeftAndRightButtonHide> {
final _controller = ScrollController();
var _width = 100.0;
#override
Widget build(BuildContext context) {
var sizeDevice = MediaQuery.of(context).size;
this._width = sizeDevice.width;
var recentIndexIncrease = 0;
var recentIndexDecrease = 0;
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Column(
children: <Widget>[
Expanded(
flex: 1,
child: Container(
color: Colors.green,
)),
Expanded(
flex: 2,
child: Row(
children: <Widget>[
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: ClipOval(
child: Material(
color: Colors.blue, // button color
child: InkWell(
splashColor: Colors.red, // inkwell color
child: SizedBox(
width: 56,
height: 56,
child: Icon(Icons.arrow_back)),
onTap: () {
var recentIndexDecreaseMinus =
recentIndexDecrease--;
_animateToIndex(recentIndexDecrease);
},
),
),
),
),
Expanded(
flex: 2,
child: Container(
color: Colors.transparent,
)),
Padding(
padding: const EdgeInsets.only(right: 8),
child: ClipOval(
child: Material(
color: Colors.blue, // button color
child: InkWell(
splashColor: Colors.red, // inkwell color
child: SizedBox(
width: 56,
height: 56,
child: Icon(Icons.arrow_forward)),
onTap: () {
_animateToIndex(recentIndexIncrease);
},
),
),
),
),
],
)),
Expanded(
flex: 16,
child: Container(
// height: 400,
child: ListView.builder(
controller: _controller,
scrollDirection: Axis.horizontal,
physics: PageScrollPhysics(),
itemCount: word_data.drink.length,
itemBuilder: (BuildContext context, int index) {
recentIndexIncrease = index;
recentIndexDecrease = index;
var wordDataReplace = word_data.drink[index]
.replaceAll(" ", "_")
.toLowerCase();
return Container(
child: Column(
children: <Widget>[
Expanded(
flex: 6,
child: GestureDetector(
child: Container(
color: Colors.purple,
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Image.asset(
"asset/drink_images/" +
wordDataReplace +
".png",
fit: BoxFit.contain,
),
),
),
)),
],
),
width: sizeDevice.width,
);
}),
color: Colors.yellow,
),
),
],
),
),
);
}
_animateToIndex(i) => _controller.animateTo(_width * i,
duration: Duration(seconds: 1), curve: Curves.fastOutSlowIn);
}
this image of (ListView with top two Button)
I think it might be easier for you to replace ListView.builder by a Flutter_Swiper it will make your life a lot easier. Or maybe you can add a listner to your ScrollController in the initState where it handles the value of two Boolean variables leftButtonEnabled and rightButtonEnabled and set them to true or false depending on the position of the Controller
EDIT :
here's an example of using Flutter swiper in your code, I tried to make it simple and in the same time adding multiple features that can help you ( like SwiperControl ) I hope it helps you a little bit.
void main() {
runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: ScrollingLeftAndRightButtonHide(),
),
);
}
class ScrollingLeftAndRightButtonHide extends StatefulWidget {
#override
_ScrolllingOnClickButtonState createState() =>
_ScrolllingOnClickButtonState();
}
class _ScrolllingOnClickButtonState
extends State<ScrollingLeftAndRightButtonHide> {
SwiperController _controller = SwiperController();
SwiperControl _control = SwiperControl(color: Colors.white);
double get _width => MediaQuery.of(context).size.width;
double get _height => MediaQuery.of(context).size.height;
bool inFirstPage = true;
bool inLastPage = false;
List<String> word_data = [
"First",
"Second",
"Third",
"Fourth",
"Fifth",
"Sixth",
"Last",
];
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: SafeArea(
child: Column(
children: <Widget>[
Container(
color: Colors.white,
child: Row(
children: <Widget>[
if (!inFirstPage)
IconButton(
color: Colors.indigoAccent,
onPressed: () {
_controller.previous();
},
icon: Icon(Icons.arrow_back),
),
Spacer(),
if (!inLastPage)
IconButton(
color: Colors.indigoAccent,
onPressed: () {
_controller.next();
},
icon: Icon(Icons.arrow_forward),
),
],
),
),
Expanded(
child: Container(
color: Colors.white,
child: Swiper(
controller: _controller,
control: _control,
loop: false,
scrollDirection: Axis.horizontal,
itemCount: word_data.length,
onIndexChanged: (value) {
if (value == word_data.length - 1)
setState(() {
inLastPage = true;
});
else if (value == 0)
setState(() {
inFirstPage = true;
});
else {
setState(() {
inFirstPage = false;
inLastPage = false;
});
}
},
itemBuilder: (BuildContext context, int index) {
return Container(
child: Column(
children: <Widget>[
Expanded(
child: GestureDetector(
child: Container(
width: _width,
alignment: Alignment.center,
color: Colors.indigoAccent,
child: Text(word_data[index]),
),
),
),
],
),
);
},
),
),
),
],
),
),
);
}
}
Add two variables in your state as
class _ScrolllingOnClickButtonState
extends State<ScrollingLeftAndRightButtonHide> {
bool leftEnabled = false; //this is initial visibility of left button
bool rightEnabled = true; //this is initial visibility of right button
........
Then in your build function where you are displaying left and right button add if statement
#override
Widget build(BuildContext context) {
var sizeDevice = MediaQuery.of(context).size;
this._width = sizeDevice.width;
var recentIndexIncrease = 0;
var recentIndexDecrease = 0;
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Column(
.............
if(leftEnabled)
Padding(
padding: const EdgeInsets.only(left: 8.0),
child: ClipOval(
child: Material(
color: Colors.blue, // button color
child: InkWell(
splashColor: Colors.red, // inkwell color
child: SizedBox(
width: 56,
height: 56,
child: Icon(Icons.arrow_back)),
onTap: () {
var recentIndexDecreaseMinus =
recentIndexDecrease--;
_animateToIndex(recentIndexDecrease);
if (index == 0) { //Your logic to detect start of the list.
leftEnabled = false; //if it is the start make left invisible
}
if(list.size > 1)
rightEnabled = true; //whenever left button is pressed and your data has more than 1 element make right visible
setState(() {});
},
),
),
),
),
...............
Same code for the right button.
You cannot do it through your current structure of code. To achieve it you will have to move those arrow button Icons inside of the listView like this:
return ListView.builder(
scrollDirection: Axis.horizontal,
physics: PageScrollPhysics(),
itemCount: 5,
itemBuilder: (BuildContext context, int index) {
recentIndexIncrease = index;
recentIndexDecrease = index;
var wordDataReplace = word_data.drink[index].replaceAll(" ", "_").toLowerCase();
return Column(
children: <Widget>[
Row(
children: [
//Left arrow is the button indicating left arrow
if (index != 0) LeftArrow,
//Rightarrow is the button indicating right arrow
if (index != 4) RightArrow
],
),
Expanded(
child: GestureDetector(
child: Container(
color: Colors.purple,
padding: const EdgeInsets.all(10.0),
child: Image.asset("asset/drink_images/" +
wordDataReplace +
".png",
fit: BoxFit.contain,
),
),
)),
],
);
});
I have a home screen where at the top I declared a listview, with scrolldirection taking information from a list.dart file. This horizontal scrolling screen brings me 5 images and a text in each of them. I would like to insert an onpress directing to other screens according to the information passed in this list. Example: Chat, direct to chat.screen.
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Stack(
children: <Widget>[
Container(
width: double.infinity,
height: MediaQuery.of(context).size.height * 4 / 7,
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Color(0xff40dedf), Color(0xff0fb2ea)],
),
),
),
Positioned(
top: 100,
left: 20,
child: Container(
height: 100,
width: MediaQuery.of(context).size.width,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: categoryData.length,
itemBuilder: (context, index) {
bool isSelected = true;
if (index == 0) {
isSelected = true;
}
Navigator.push<dynamic>(
context,
MaterialPageRoute<dynamic>(
builder: (BuildContext
context) =>
HomeList[index].navigateScreen,
),
);
return Row(
children: <Widget>[
Column(
children: <Widget>[
Container(
width: 65,
height: 65,
decoration: BoxDecoration(
color: isSelected
? Colors.transparent
: Colors.transparent,
borderRadius:
BorderRadius.circular(16),
border: Border.all(
color: Colors.white,
width: 1,
),
boxShadow: isSelected
? [
BoxShadow(
color: Color(0x14000000),
blurRadius: 10)
]
: null),
child: Center(
child: Image.asset(categoryData[index].imageUrl),
),
),
SizedBox(
height: 10,
),
Text(
categoryData[index].name,
style: TextStyle(color: Colors.white, fontSize: 15),
),
],
),
SizedBox(
width: 20,
)
],
);
},
),
),
),
Homelist
import 'package:flutter/material.dart';
import 'package:projeto/pages/chat.screen.dart';
class HomeList {
HomeList({
this.navigateScreen,
this.imagePath = '',
});
Widget navigateScreen;
String imagePath;
static List<HomeList> homeList = [
HomeList(
imagePath: 'assets/page1/usuario.png',
navigateScreen: ChatScreen(),
),
HomeList(
imagePath: 'assets/page1/entregas.png',
navigateScreen: ChatScreen(),
),
HomeList(
imagePath: 'assets/page1/msg.png',
navigateScreen: ChatScreen(),
),
HomeList(
imagePath: 'assets/page1/configurações.png',
navigateScreen: ChatScreen(),
),
HomeList(
imagePath: 'assets/page1/sair.png',
navigateScreen: ChatScreen(),
),
];
}
from what i understand, you want to transform the data in your HomeList file to a listview where clicking on one of its items takes you to its related page, you could use a ListView.builder with an itemBuilder and itemCount, the code below shows how you can achieve a listView where items are images with text in them and an onTap function:
class HomePage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Container(
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return GestureDetector(
child: Stack(
children: <Widget>[
Image.asset(
homeList[index].imagePath,
),
Positioned(child: Text())
],
),
onTap: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => homeList[index].navigateScreen),
),
);
},
itemCount: homeList.length,
),
);
}
}