I'm new to flutter, and I'm trying to build an Online Shopping app as my graduation project.
Every time I run the app it goes straight the "Item Card" method and through it to "Details Screen", even though it's supposed to only go there on pressing through the Navigator widget.
It's also marking the Item Card as a dirty child (I don't quite understand what that means and how to revert it to being a normal child).
Error message: The following assertion was thrown building ItemCard(dirty):
setState() or markNeedsBuild() called during build.
I hope I explained the error well enough.. here is the code, First is the Body class, then Item Card class, and then Details Screen class:
class Body extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(horizontal: kDefaultPaddin),
child: Text(
"Mobiles",
style: Theme.of(context)
.textTheme
.headline5!
.copyWith(fontWeight: FontWeight.bold),
),
),
Categories(),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: kDefaultPaddin),
child: GridView.builder(
itemCount: productz.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: kDefaultPaddin,
crossAxisSpacing: kDefaultPaddin,
childAspectRatio: 0.75,
),
itemBuilder: (context, index) => ItemCard(
productz: productz[index],
press: () => Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailsScreen(
productz: productz[index],
),
)),
)),
),
),
],
);
}
}
class ItemCard extends StatelessWidget {
final Productz productz;
final Function press;
const ItemCard({
Key? key,
required this.productz,
required this.press,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: press(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.all(kDefaultPaddin),
decoration: BoxDecoration(
color: Colors.white12,
borderRadius: BorderRadius.circular(16),
),
child: Hero(
tag: "${productz.id}",
child: Image.asset(productz.item_image),
),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: kDefaultPaddin / 4),
child: Text(
// products is out demo list
productz.item_name,
style: TextStyle(color: kTextLightColor),
),
),
Text(
"\$${productz.item_price}",
style: TextStyle(fontWeight: FontWeight.bold),
)
],
),
);
}
}
class DetailsScreen extends StatelessWidget {
final Productz productz;
const DetailsScreen({Key? key, required this.productz}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
// each product have a color
backgroundColor: Colors.white12,
appBar: buildAppBar(context),
body: Body(productz: productz),
);
}
AppBar buildAppBar(BuildContext context) {
return AppBar(
backgroundColor: Colors.white12,
elevation: 0,
leading: IconButton(
icon: Icon(Icons.arrow_back,
size: 30,
color: Colors.white,
),
onPressed: () => Navigator.pop(context),
),
actions: <Widget>[
IconButton(
icon: Icon(FontAwesomeIcons.search),
onPressed: () {},
),
IconButton(
icon: Icon(FontAwesomeIcons.shoppingCart),
onPressed: () {},
),
SizedBox(width: kDefaultPaddin / 2)
],
);
}
}
The onPress
function in the GestureDetector
is being invoked immediately.
There are two ways you can go about fixing the problem.
()
onChanged
handler of TextField
or TextFormField
Check the code snippets below.
Example of Method 1.
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: press,
child: ...
)
}
Example of Method 2:
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => press(),
child: ...
)
}