I want to showing list of items with ListView.builder, which wrap with BlocBuilder. My code is success connect to API, but the problem is I do not know how to display the items, instead the length of the items like picture below.
Here I attach the code:
SizedBox(
height: 350,
width: 290,
child: Padding(
padding: const EdgeInsets.only(left: 30, top: 20),
child: BlocBuilder<ExcavatorBloc, ExcavatorState>(
builder: (context, state) {
return ListView.builder(
itemCount: state.excavator.length,
itemBuilder: (context, index) {
return Row(
children: [
const SizedBox(
height: 10,
width: 10,
child: CircleAvatar(
foregroundColor:
ColorName.brandSecondaryGreen,
backgroundColor:
ColorName.brandSecondaryGreen,
),
),
const SizedBox(
width: 5,
),
Text(
state.excavator.length.toString(), //The problem is here----------
style: subtitle1(),
),
],
);
},
);
},
),
),
),
Instead of this
Text(
state.excavator.length.toString(),
style: subtitle1(),
),
use the index of the list builder
Text(
state.excavator?[index].attributeName ?? 'No value',
style: subtitle1(),
),
Related
I have success to display the items with ListView.builder, now I want to display the items based on their ACTIVE or INACTIVE status at API. So when I want to display ACTIVE, it only shows the active items, and goes the same with INACTIVE.
The API is like this:
I don't have to attach identity 1100, because it is ACTIVE like identity 1200
And my code is like this:
BlocBuilder<ExcavatorBloc, ExcavatorState>(
builder: (context, state) {
return ListView.builder(
itemCount: state.excavator.length,
itemBuilder: (context, index) {
return Row(
children: [
const SizedBox(
height: 10,
width: 10,
child: CircleAvatar(
foregroundColor:
ColorName.brandSecondaryGreen,
backgroundColor:
ColorName.brandSecondaryGreen,
),
),
const SizedBox(
width: 5,
),
Text(
state.excavator[index].identity,
style: subtitle1(),
),
],
);
},
);
},
),
I'd do it like this without having to create an extra counter for active devices:
return ListView.builder(
itemCount: state.excavator.length,
itemBuilder: (context, index) {
return Visibility(
visible: state.excavator[index].status == "ACTIVE" ? true : false,
child: Row(
children: [
const SizedBox(
height: 10,
width: 10,
child: CircleAvatar(
foregroundColor: ColorName.brandSecondaryGreen,
backgroundColor: ColorName.brandSecondaryGreen,
),
),
const SizedBox(
width: 5,
),
Text(
state.excavator[index].identity,
style: subtitle1(),
),
),
],
);
I want the cards built in a listview.builder, to have a height of 150.
What currently happens:
Currently, with my code, here's what gets built. Instead of the default height, I want to explicitly set my own card height, say, 150
What I have tried:
Isn't using SizedBox enough to get the height I want in a listview?
class GamesListState extends State<GamesList> {
#override
Widget build(BuildContext context) {
return MyScaffold(
body: Container(
padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 32),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(height: 32),
const MyHeaderTitle(title: 'Games'),
const SizedBox(height: 40),
Flexible(
child: ListView.builder(
itemCount: list.length,
prototypeItem: ListTile(
title: Text(list.first.values.first),
),
itemBuilder: (context, index) {
return Card(
elevation: 5,
child: SizedBox(
height: 150,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Image.asset('assets/images/logo.png'),
const Text(
'Game name',
style: TextStyle(
fontSize: 10, fontWeight: FontWeight.w700),
),
const Icon(Icons.keyboard_arrow_right)
],
),
),
);
},
)),
],
),
),
);
}
}
Will appreciate any insights as to what I'm doing wrong.
Specify the height of prototypeItem of listView.builder by wrapping it with SizedBox
prototypeItem: ListTile(
title: SizedBox(height: 150, child: Text(list.first.values.first)),
),
In my home screen my app shows carousel first then a vertical list of challenges cards retrieved from Cloud Firestore using GridView.builder as follows:
GridView.builder(
scrollDirection: Axis.vertical,
itemCount: _challenges.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 1,
childAspectRatio: MediaQuery.of(context).size.width /
(MediaQuery.of(context).size.height / 4),
),
itemBuilder: (context, index) {
return InkWell(
onTap: () {
if (_challenges[index]["isLocked"] == "true") {
showLockedDialog();
} else {
checkParticipation(index);
if (checkPart == true) {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
ChallengeDetails(_challenges[index])));
}
checkPart = true;
}
},
child: Stack(
children: [
Center(
child: ClipRRect(
borderRadius: BorderRadius.circular(15),
child: Image(
image: NetworkImage(_challenges[index]["image-path"]),
fit: BoxFit.cover,
height: 150,
width: 350,
opacity: _challenges[index]["isLocked"] == "true"
? AlwaysStoppedAnimation(.4)
: null,
),
),
),
Center(
child: Text(
"${_challenges[index]["name"]}\n",
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
),
),
Center(
child: Text(
"\n${_challenges[index]["date"]}",
style: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.bold),
textDirection: TextDirection.ltr,
)),
Center(
child: SizedBox(
height: 130,
width: 130,
child: _challenges[index]["isLocked"] == "true"
? Image.asset("assets/lock-icon.jpg")
: null,
),
)
],
),
);
});
Everything retrieving fine and it is rendered in my home_screen as follows:
body: Column(
children: [
AdsBanner(),
SizedBox(
height: 30,
),
Padding(
padding: const EdgeInsets.only(right: 8, left: 8, bottom: 5),
child: Row(
children: [
Text(
AppLocalizations.of(context)!.challenges + " ",
style: TextStyle(fontSize: 20),
),
Text(
AppLocalizations.of(context)!.clickToParticipate,
style: TextStyle(fontSize: 15),
)
],
),
),
Expanded(child: ChallengeCard()),
],
),
The problem is that only the GridView area is scrolling and what am seeking for is to scroll the whole screen with the GridView area, I was trying to use the CustomScrollView() but its not working properly.
I'll be thankful for any help.
First in your GridView.builder add these:
GridView.builder(
physics: NeverScrollableScrollPhysics(),
shrinkWrap: true,
...
)
then in your home_screen wrap your column with SingleChildScrollView:
SingleChildScrollView(
child: Column(
children: [
AdsBanner(),
SizedBox(
height: 30,
),
Padding(
...
),
),
You can provide physics: NeverScrollableScrollPhysics() on GridView to disable scroll effect. If you want scrollable as secondary widget use primary: false, to have Full Page scrollable, you can use body:SingleChildScrollView(..) or better using body:CustomScrollView(..)
I would like to create a generic dialog but I have some issue with the callback... Here is my code :
The dialog :
class ConfirmDialog {
static Future show<T>(BuildContext context, String message,
void Function(T) onConfirm, VoidCallback? onRefuse) {
return showDialog(
context: context,
builder: (context) {
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.grey[300],
),
child: Container(
padding: EdgeInsets.fromLTRB(0, 20, 0, 0),
width: 400,
height: 200,
child: Card(
child: Column(
children: [
Text(
message,
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30),
),
Container(
margin: EdgeInsets.fromLTRB(0, 20, 0, 0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
child: Container(
height: 70,
padding: EdgeInsets.fromLTRB(0, 0, 20, 0),
child: Image.asset("assets/images/confirm.png"),
),
onTap: () => {
onConfirm,
print("accept"),
Navigator.pop(context),
}),
GestureDetector(
child: Container(
height: 70,
padding: EdgeInsets.fromLTRB(20, 0, 0, 0),
child: Image.asset("assets/images/cancel.png"),
),
onTap: () => {
if (onRefuse != null)
{
onRefuse,
}
else
{
Navigator.pop(context),
}
print("refuse")
},
),
],
),
),
],
),
),
),
);
},
);
}
The widget :
Widget listView(List<Product> products) {
return ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
Product product = products[index];
return GestureDetector(
child: Container(
height: 150,
child: Card(
child: Row(
children: [
Container(
alignment: Alignment.center,
padding: EdgeInsets.fromLTRB(5, 10, 5, 10),
width: 150,
height: 150,
child: Image.network(product.small),
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
product.name,
overflow: TextOverflow.ellipsis,
maxLines: 1,
softWrap: false,
style: TextStyle(
fontSize: 30,
),
),
Text(
product.getBrand(),
style: TextStyle(
color: Colors.grey,
fontSize: 25,
fontFamily: "Antonio",
),
)
],
),
)
],
),
),
),
onTap: () => {},
onLongPress: () => {
ConfirmDialog.show<Product>(
context,
"Are you sure you want to delete\n${product.name} ?",
deleteSelectedProduct(product),
null,
)
},
);
},
);
}
Everytime I "longpress", I got this error message :
The following _TypeError was thrown while handling a gesture: type
'Null' is not a subtype of type '(Product) => dynamic'
The product I pass as parameter is not null and the dialog don't show up but the method "deleteSelectedProduct" is triggered.
I can't figure out where I got it wrong?
If someone can help, it would be great!
Thank you in advance
The function is being called before it is passed in, so it will return null and pass that value in. You would need to use deleteSelectedProduct instead of deleteSelectedProduct(product)
In the code below, I am trying to pass data from the home screen to the detail screen when user clicks on any of the product listed on the home screen. I seem to be having difficulty here.
Similar Question but does not solve my issue
Excerpt of the code at the home screen.
StreamBuilder(
stream: FirebaseFirestore.instance
.collection("name")
.limit(12)
.orderBy("Published Date", descending: true)
.snapshots(),
builder: (context, snapshot){
if (!snapshot.hasData) {
return Center(
child: spinkit,
);
}
return GridView.builder(
physics: ScrollPhysics(),
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: snapshot.data.docs.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 10.0,
mainAxisSpacing: 10,
),
itemBuilder: (BuildContext context, int index){
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: spinkit,
);
}
print("${snapshot.data.documents[index].get('Product Title')}");
return GestureDetector(
onTap: (){
// ===> SEND USER TO THE DETAILS SCREEN <===
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProductDetailsScreen()),
);
},
child: ClipRRect(
borderRadius: BorderRadius.circular(5),
child: Stack(
children: [
Container(
height: 150,
width: 150,
child: Image.network(
snapshot.data.documents[index].get('image') ?? spinkit,
fit: BoxFit.cover,
width: double.infinity,
height: double.infinity,
),
),
Positioned(
left: 0,
bottom: 0,
child: Container(
height: 20,
width: 150,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.black38,
Colors.black38,
],
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
)),
),
),
Positioned(
left: 4,
bottom: 5,
child: Text(
snapshot.data.documents[index].get('name') ?? "Product Name",
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: Palette.whiteColor,
fontSize: 11,
fontWeight: FontWeight.bold),
),
)
],
),
),
);
}
);
},
)
And excerpt of the code at the Details Screen. what you see at the detail screen is data that I have populated manually. it is not coming from firestore.
class ProductDetailsScreen extends StatefulWidget {
#override
_ProductDetailsScreenState createState() => _ProductDetailsScreenState();
}
class _ProductDetailsScreenState extends State<ProductDetailsScreen> {
final productDb = FirebaseFirestore.instance.collection("name");
User user = FirebaseAuth.instance.currentUser;
final spinkit = SpinKitHourGlass(
color: Colors.white,
size: 50.0,
);
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: _onBackPressed,
child: Scaffold(
body: SafeArea(
child: Padding(
padding: EdgeInsets.only(left: 15, right: 15, top: 3, bottom: 10),
child: FutureBuilder(
future: productDb.get(),
builder: (context, snapshot){
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: spinkit,
);
}
return Container(
child: ListView(
children: [
Column(
children: [
Center(
child: Container(
width: 350,
child: Card(
elevation: 5,
child: Container(
padding: EdgeInsets.all(8),
child: Container(
height: 220,
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.fill,
image: AssetImage("asset/images/headphone.jpg",)
)
),
),
),
),
),
),
SizedBox(height: 10,),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Item Name",
style: TextStyle(
color: Palette.blackColor,
fontSize: 18,
fontWeight: FontWeight.w300
),),
Text("Item Price",
style: TextStyle(
color: Palette.blackColor,
fontSize: 18,
fontWeight: FontWeight.w300
),),
],
),
SizedBox(height: 10,),
Text("Item Description",
style: TextStyle(
color: Palette.blackColor,
fontSize: 18,
fontWeight: FontWeight.w300
),),
Container(
height: 1,
width: 100,
color: Colors.black12,
),
SizedBox(height: 10,),
Container(
height: 140,
width: 350,
child: SingleChildScrollView(
child: Wrap(
children: [
Text(
"What is Lorem Ipsum Lorem Ipsum is simply dummy "
"text of the printing and typesetting industry"
" Lorem Ipsum has been the industry's standard"
" dummy text ever since the 1500s when an "
"unknown printer took a galley of type and "
"scrambled it to make a type specimen book "
"it has?",
textAlign: TextAlign.justify,
style: TextStyle(
color: Palette.blackColor,
fontSize: 16,
),),
]
),
),
),
],
),
],
),
);
},
),
)),
),
);
}
}
In your GestureDetector onTap
// ===> SEND USER TO THE DETAILS SCREEN WITH DOC<===
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProductDetailsScreen(snapshot.data.documents[index])),
);
Then
class ProductDetailsScreen extends StatefulWidget {
ProductDetailsScreen(this.doc);
QueryDocumentSnapshot doc;
#override
_ProductDetailsScreenState createState() => _ProductDetailsScreenState();
}
Access in _ProductDetailsScreenState as widget.doc.
The document data will be found at widget.doc.data as a Map<String, dynamic>.