Issue
I used a provider to display data from firestore
in one page and in turn I passed the data to second page and I called the changenotifier
in main.dart
.
But whenever I am trying to navigate to second page it will show me this error :
“I/flutter ( 1763): Another exception was thrown: setState() or markNeedsBuild() called during build.”
I do not what to do.
- Note:in the
main.dart
I already have onechangenotifier
before.
First page code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shopping/pages/product_details.dart';
import 'package:shopping/provider/app_provider.dart';
import 'package:shopping/models/product.dart';
class Products extends StatefulWidget {
@override
ProductsState createState() => ProductsState();
}
class ProductsState extends State<Products> {
List<Product> products;
String img;
@override
Widget build(BuildContext context) {
final productProvider = Provider.of<CRUDModel>(context);
// productProvider.changeProductPage(PAGESTATUS.PRODUCTS);
return StreamBuilder<QuerySnapshot>(
stream: productProvider.fetchProductsAsStream(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
products = snapshot.data.documents
.map((doc) => Product.fromMap(doc.data, doc.documentID))
.toList();
return GridView.builder(
itemCount: products.length,
gridDelegate:new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2),
itemBuilder: (BuildContext context, index){
return Padding(
padding:const EdgeInsets.all(4.0),
child:SingleProd(
product:products[index]
// prodPicture: productList[index]['picture'],
//prodOldPrice: productList[index]['oldPrice'],
//prodPrice: productList[index]['price'],
),
);
}
);
}
else {
return Text('fetching');
}
}
);
}
}
class SingleProd extends StatelessWidget {
//final prodName;
//final prodPicture;
//final prodOldPrice;
//final prodPrice;
final Product product;
SingleProd({ @required this.product});
//SingleProd({product.picture});
@override
Widget build(BuildContext context) {
//final appProvider=Provider.of<CRUDModel>(context);
return Card(
child: Hero(tag: product.id,
child:
Material( child: InkWell(
onTap: (){
// if(appProvider.selectedPage==PAGESTATUS.PRODUCTDETAILS){
// appProvider.changeProductPage(PAGESTATUS.PRODUCTDETAILS);
Navigator.of(context).push(new MaterialPageRoute(builder: (context)=>ProductDetails(
//here we are passing the value of the products to Product detail page
productDetailName:product.name,
productDetailNewPrice:product.price,
productDetailPicture:product.picture,
//productDetailOldPrice:prodOldPrice,
//productDetailNewPrice:prodPrice,
//productDetailPicture: prodPicture,
)
)
);
//}
},
child:GridTile(
footer: Container(
color: Colors.white,
child: new Row(
children: <Widget>[
new Expanded(
child: new Text(product.name, style: TextStyle(fontWeight: FontWeight.bold, fontSize:16.0),),
),
new Text(
'\$${product.price} ', style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),)
],
)
),
child:Image.network('${product.picture}.jpg', //Image.asset('assets/${product.picture}.jpg',
fit: BoxFit.cover,),
),
),
),
),
);
}
}
Future<Widget>_displayImages(){
}
Second page code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shopping/provider/app_provider.dart';
//import 'package:shopping/main.dart';
import './home.dart';
class ProductDetails extends StatefulWidget {
final productDetailName;
final productDetailNewPrice;
final productDetailOldPrice;
final productDetailPicture;
ProductDetails(
{
this.productDetailName,
this.productDetailNewPrice,
this.productDetailOldPrice,
this.productDetailPicture,
}
);
@override
_ProductDetailsState createState() => _ProductDetailsState();
}
class _ProductDetailsState extends State<ProductDetails> {
@override
Widget build(BuildContext context) {
ChangeNotifierProvider<CRUDModel>(create: (context) => CRUDModel());
return Scaffold(
appBar: new AppBar(
elevation: 0.1 ,
backgroundColor: Colors.red,
title: InkWell(
onTap: (){Navigator.push(context, MaterialPageRoute(builder:(context)=>new HomePage()));},
child: Text("MyShop")),
actions: <Widget>[
new IconButton(icon: Icon(Icons.search, color: Colors.white,), onPressed:(){},),
],
),
body: new ListView(
children: <Widget>[
new Container(
height: 300.0,
child: GridTile(
child: Container(
color: Colors.white,
child: Image.network(widget.productDetailPicture),
),
footer: Container(
color: Colors.white70,
child: ListTile(
leading: Text(widget.productDetailName, style: TextStyle(
fontWeight: FontWeight.bold, fontSize: 15.00,
),
),
title: new Row(
children: <Widget>[
Expanded(
child: new Text("\$${widget.productDetailOldPrice}",style: TextStyle(
color: Colors.grey, decoration: TextDecoration.lineThrough,
),
)
),
Expanded(
child: new Text("\$${widget.productDetailNewPrice}",style: TextStyle(
fontWeight: FontWeight.bold, color: Colors.red
),
)
)
],
),
),
),
),
),
//The First button
Row(
children: <Widget>[
//The Size Button
Expanded(
child: MaterialButton(
onPressed: (){
showDialog(context:context,
builder:(context){
return new AlertDialog(
title: new Text("Size"),
content: new Text("Choose the Size"),
actions: <Widget>[
new MaterialButton(onPressed: (){
Navigator.of(context).pop();
},
child: new Text("Close"),
),
],
);
}
);
},
color: Colors.white,
textColor: Colors.grey,
elevation: 0.2,
child: Row(
children: <Widget>[
Expanded(
child: new Text("Size")
),
Expanded(
child:new Icon(Icons.arrow_drop_down),
),
],
),
),
),
Expanded(
child: MaterialButton(
onPressed: (){
showDialog(context:context,
builder:(context){
return new AlertDialog(
title: new Text("Colors"),
content: new Text("Choose a Color"),
actions: <Widget>[
new MaterialButton(onPressed: (){
Navigator.of(context).pop();
},
child: new Text("Close"),
),
],
);
}
);
},
color: Colors.white,
textColor: Colors.grey,
elevation: 0.2,
child: Row(
children: <Widget>[
Expanded(
child: new Text("Color")
),
Expanded(
child:new Icon(Icons.arrow_drop_down),
),
],
),
),
),
Expanded(
child: MaterialButton(
onPressed: (){
showDialog(context:context,
builder:(context){
return new AlertDialog(
title: new Text("Quantity"),
content: new Text("Choose the Quantity"),
actions: <Widget>[
new MaterialButton(onPressed: (){
Navigator.of(context).pop();
},
child: new Text("Close"),
),
],
);
}
);
},
color: Colors.white,
textColor: Colors.grey,
elevation: 0.2,
child: Row(
children: <Widget>[
Expanded(
child: new Text("QTY")
),
Expanded(
child:new Icon(Icons.arrow_drop_down),
),
],
),
),
),
],
),
//The Second button
Row(
children: <Widget>[
//The Size Button
Expanded(
child: MaterialButton(
onPressed: (){},
color: Colors.red,
textColor: Colors.white,
elevation: 0.2,
child: new Text("Buy Now"),
),
),
new IconButton(icon: Icon(Icons.add_shopping_cart, color: Colors.red,), onPressed: null,),
new IconButton(icon: Icon(Icons.favorite_border, color: Colors. red,), onPressed: null,),
],
),
new Divider(),
new ListTile(
title: Text("Product Description", style: TextStyle(fontWeight: FontWeight.bold),),
subtitle: new Text("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 survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets",),
),
new Divider(),
new Row(
children: <Widget>[
new Padding(padding: const EdgeInsets.fromLTRB(12.0, 5.0, 5.0, 5.0),
child: new Text("Product Name", style: TextStyle(color: Colors.grey),),),
new Padding(padding: EdgeInsets.all(5.0),
child: new Text(widget.productDetailName),)
],
),
new Row(
children: <Widget>[
//remember to create product brand
new Padding(padding: const EdgeInsets.fromLTRB(12.0, 5.0, 5.0, 5.0),
child: new Text("Product Brand", style: TextStyle(color: Colors.grey),),),
new Padding(padding: EdgeInsets.all(5.0),
child: new Text("Brand X"),),
],
),
new Row(
children: <Widget>[
//Add the product condition
new Padding(padding: const EdgeInsets.fromLTRB(12.0, 5.0, 5.0, 5.0),
child: new Text("Product Condition", style: TextStyle(color: Colors.grey),),),
new Padding(padding: EdgeInsets.all(5.0),
child: new Text("New"),)
],
),
new Divider(),
Padding(padding: const EdgeInsets.all(8.0),
child: new Text("Similar Products"),),
//Similar Products
new Container(
height: 360.0,
child: SimilarProducts(),
)
],
),
);
}
}
class SimilarProducts extends StatefulWidget {
@override
_SimilarProductsState createState() => _SimilarProductsState();
}
class _SimilarProductsState extends State<SimilarProducts> {
var productList=[
{
"name":"Black Dress",
"picture":"images/products/dress2.jpeg",
"oldPrice":150,
"price": 80,
},
{
"name":" M Pant1",
"picture":"images/products/pants1.jpg",
"oldPrice":120,
"price": 50,
},
{
"name":"Hill",
"picture":"images/products/hills1.jpeg",
"oldPrice":130,
"price": 80,
},
{
"name":"W Skirt",
"picture":"images/products/skt1.jpeg",
"oldPrice":150,
"price": 100,
},
];
@override
Widget build(BuildContext context) {
return GridView.builder(
itemCount: productList.length,
gridDelegate:new SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:2),
itemBuilder: (BuildContext context, int index){
return SimilarSingleProd(
prodName: productList[index]['name'],
prodPicture: productList[index]['picture'],
prodOldPrice: productList[index]['oldPrice'],
prodPrice: productList[index]['price'],
);
}
);
}
}
class SimilarSingleProd extends StatelessWidget {
final prodName;
final prodPicture;
final prodOldPrice;
final prodPrice;
SimilarSingleProd({this.prodName, this.prodPicture,this.prodOldPrice,this.prodPrice});
@override
Widget build(BuildContext context) {
return Card(
child: Hero(tag: new Text("hero 1"),
child:
Material( child: InkWell(
onTap: ()=>Navigator.of(context).push(new MaterialPageRoute(builder: (context)=>ProductDetails(
//here we are passing the value of the products to Product detail page
productDetailName:prodName,
productDetailOldPrice:prodOldPrice,
productDetailNewPrice:prodPrice,
productDetailPicture: prodPicture,
)
)
),
child:GridTile(
footer: Container(
color: Colors.white,
child: new Row(
children: <Widget>[
new Expanded(
child: new Text(prodName, style: TextStyle(fontWeight: FontWeight.bold, fontSize:16.0),),
),
new Text("\$$prodPrice", style: TextStyle(color: Colors.red, fontWeight: FontWeight.bold),)
],
)
),
child: Image.asset(prodPicture,
fit: BoxFit.cover,),
),
),
),
),
);
}
}
main.dart code:
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shopping/pages/home.dart';
import 'package:shopping/pages/splash.dart';
import 'package:shopping/provider/app_provider.dart';
import 'provider/user_provider.dart';
import './pages/login.dart';
//import 'package:path_provider/path_provider.dart';
/*void main() {
runApp(
new MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.blue.shade900
),
home: Login(),
)
);
}*/
void main() {
WidgetsFlutterBinding.ensureInitialized();
runApp(MultiProvider(providers: [
ChangeNotifierProvider.value(value: UserProvider.initialize()),
//second notifier that is given me problem
ChangeNotifierProvider.value(value: CRUDModel()),
],
child:new MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
primaryColor: Colors.blue.shade900
),
home:ScreenController(),
),
),
);
}
class ScreenController extends StatelessWidget {
@override
Widget build(BuildContext context) {
final user=Provider.of<UserProvider>(context);
switch(user.status){
case Status.Uninitialized:
return Splash();
case Status.UnAuthenticated:
case Status.Authenticating:
return Login();
case Status.Authenticated:
return HomePage();
default:
return Login();
}
}
}
The provider itself
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:shopping/db/Api.dart';
import 'package:shopping/models/product.dart';
enum PAGESTATUS { INITIAL, PRODUCTS, PRODUCTDETAILS, PRODUCTFETCHFAIL }
class CRUDModel extends ChangeNotifier {
PAGESTATUS _pageStatus = PAGESTATUS.INITIAL;
PAGESTATUS get pageStatus => _pageStatus;
//Api _api = locator<Api>();
String path = "Products";
Api _api = Api();
List<Product> products;
Future<List<Product>> fetchProducts() async {
try {
//notifyListeners();
var result = await _api.getDataCollection();
products = result.documents
.map((doc) => Product.fromMap(doc.data, doc.documentID))
.toList();
} catch (e) {
notifyListeners();
print(e.toString());
}
return products;
}
Stream<QuerySnapshot> fetchProductsAsStream() {
notifyListeners();
return _api.streamDataCollection();
}
}
Solution
Your fetchProductsAsStream
method calls notifyListeners()
as soon as it is invoked, which is probably what’s causing the issue. This method is called from the build
function at the point of creating the StreamBuilder
, but notifyListeners
will trigger an additional build
, therefore the error. You should be able to simply remove notifyListeners
from fetchProductsAsStream
as there are no changes to be notified of anyway.
Answered By – Ovidiu
Answer Checked By – Marilyn (FlutterFixes Volunteer)