Hi,
I am following your todo app tutorial to implement flutter bloc. I am using bloc listener to listen to the state change. but it only listens to the initial state and does not show changes based on state change. would you please tell me what is the mistake in the following code. i have tried and searched to find a solution but not a clear one.
this is where I am providing the bloc in my home screen:
List _pages = [
MultiBlocProvider(
child: ProductPage(),
providers: [
BlocProvider
create: (context) => ProductListBloc()..add(ProductLoaded()),
),
BlocProvider
create: (context) => CartBloc()..add(CartLoaded()),
),
]
),
//ProductPage(), // all products
CartPage(), // all carts
ProfilePage(), // profile page
];
This is my ProductPage:
class ProductPage extends StatefulWidget {
@override
_ProductPageState createState() => _ProductPageState();
}
class _ProductPageState extends State
int x = 1;
List
List
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Products"),
),
body: BlocListener
listener: (context, state) {
if (state is ProductLoadedSuccess) {
products = state.productList;
}
},
child: BlocBuilder
builder: (context, state) {
return BlocListener
listener: (context, state) {
THIS IS BEING CALLED
if (state is CartLoadedSuccess) {
debugPrint('CartLoadedSuccess is triggering');
cartItems = state.cartList;
}
THIS IS NOT BEING CALLED
else if (state is CartAddedFailure) {
debugPrint('CartAddedFailure is triggering');
}
},
child: BlocBuilder
builder: (context, state) {
// debugPrint('cartItems after listen $cartItems');
// debugPrint('products after listen $products');
return Container(
color: Color(0xf8f8ff),
padding: EdgeInsets.all(10),
child: buildProductListing(products, context, cartItems),
);
},
),
);
},
),
));
}
ListView buildProductListing(List
List
return new ListView.builder(
itemCount: products.length,
itemBuilder: (BuildContext ctxt, int index) {
CartDbModel cartProd = cartItems.firstWhere(
(cart) => cart.id == products[index].id,
orElse: () => CartDbModel());
//debugPrint('cartProd $cartProd');
return Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
child: new ListTile(
title: Text(products[index].name),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
margin: EdgeInsets.only(top: 5),
child: Row(
children: [
Container(
width: MediaQuery.of(context).size.width / 1.5,
child: Text(
products[index].description,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
),
GestureDetector(
onTap: () => debugPrint('remove'),
child: Icon(Icons.remove, color: Colors.black)),
Text(cartProd.id == null ? '0' : cartProd.dbQty),
GestureDetector(
onTap: () {
THIS IS WHERE I AM TRYING TO MUTATE THE STATE
CartBloc()..add(CartAdded(products[index]));
},
child: Icon(Icons.add, color: Colors.black)),
],
),
),
Container(
margin: EdgeInsets.only(top: 5),
child: Text(
'MYR ${products[index].price.toString()}',
style: TextStyle(
color: Colors.purple, fontWeight: FontWeight.bold),
),
)
],
),
),
);
});
}
}
this is the bloc
class CartBloc extends Bloc
// final int id;
// final Products product;
// final List
DatabaseCart databaseCart = DatabaseCart();
List
CartDbModel cartItem = CartDbModel();
CartBloc() : super(CartLoadedSuccess());
@override
Stream
// debugPrint(currentState)
if (event is CartLoaded) {
yield* _mapCartLoadedState();
} else if (event is CartAdded) {
debugPrint('state CartAdded${state}');
yield* _mapCartAddedState(event);
}else if(event is CartAddFailed){
debugPrint('this is being called for fail');
yield* _mapCartAddeFailedState();
} else {
throw UnimplementedError();
}
}
Stream
yield CartLoadedFailure();
yield CartAddedFailure();
}
Stream
try {
databaseCart.getAllCarts().then((value) {
// if (value != null) {
// print('bloc get all: $value');
value.forEach((element) {
// print('cart element ${element.id}');
cartProducts.add(element);
});
// }
});
yield CartLoadedSuccess(cartProducts);
} catch (_) {
yield CartLoadedFailure();
}
}
Stream
//print('added state ${event.props[0]}');
Products product = event.props[0];
//print(product.id);
databaseCart.getAllCarts().then((value) {
// debugPrint('bloc add to cart value: $value');
cartItem.id = product.id;
cartItem.name = product.name;
cartItem.availableQuantity = product.availableQuantity;
cartItem.price = product.price;
cartItem.description = product.description;
debugPrint('value length check ${value.length}');
if (value.length > 0) {
List
value.where((element) => element.id == product.id).toList();
// debugPrint('findcart ${findCart.length}');
if (findCart.length > 0) {
cartItem.dbQty = (int.parse(findCart[0].dbQty) + 1).toString();
if (int.parse(cartItem.dbQty) <= product.availableQuantity) {
if (product.availableQuantity > 0) {
databaseCart
.updateCart(cartItem)
.then((value) =>
//_mapCartLoadedState()
CartBloc()..add(CartLoaded()));
}
}
} else {
cartItem.dbQty = "1";
databaseCart
.saveToCart(cartItem)
.then((value) =>
// _mapCartLoadedState())
CartBloc()..add(CartLoaded()));
}
} else {
if (product.availableQuantity > 0) {
cartItem.dbQty = "1";
databaseCart
.saveToCart(cartItem)
.then((value) =>
// _mapCartLoadedState()
CartBloc()..add(CartLoaded()));
}else{
debugPrint('it is checking 0 items');
CartBloc()..add(CartAddFailed());
}
// debugPrint('db quantity: ${int.parse(cartItem.dbQty)}');
}
//cartProducts = value == null ? [] : value;
});
}
}
this is the event class:
abstract class CartEvent extends Equatable {
const CartEvent();
@override
List
class CartLoaded extends CartEvent {
}
class CartAddFailed extends CartEvent{
}
class CartAdded extends CartEvent {
final Products product;
const CartAdded( this.product);
@override
List
@override
String toString() => 'Product to add updatedCart: $product ';
}
this is the state class:
abstract class CartState extends Equatable{
const CartState();
@override
List
class CartLoadedInProgress extends CartState{}
class CartLoadedSuccess extends CartState{
final List
const CartLoadedSuccess([this.cartList = const []]);
@override
List
// @override
// String toString() => 'CartLoadedSuccess { carts state: $cartList }';
}
class CartAddedSuccess extends CartState{}
class CartAddedFailure extends CartState{}
class CartLoadedFailure extends CartState{
@override
String toString() => 'Cart cannot be loaded';
}
Hi @ZAT90 馃憢
Thanks for opening an issue!
Have you taken a look at the FAQs? You should not be maintaining internal state in your bloc (cartProducts) because you are just mutating the same list of products and yielding it which results in the bloc ignoring the state (since it is equal to the previous state).
You should also provide a type for the final List cartList; because right now it's inferred to be List<dynamic> which is not Equatable.
Instead prefer to have something like:
final List<Product> cartList;
Where the class Product extends Equatable like:
class Product extends Equatable {
...
}
Closing for now but if you're still having trouble can you please share a link to a sample app which illustrates the issue so I can run and debug the issue locally? Thanks! 馃憤
I have made the changes as per you said. Still nothing changed. I will provide a link of the project to you and maybe you can check it out and guide me towards the right direction. Thanks for your constant help brother.
https://drive.google.com/drive/folders/1OsS8XaHCnWnvHjD6fDY4Uft7j9-mLiXV?usp=sharing
this is the link to the project @felangel
I have solved it. In place of CartBloc()..add(CartAdded(products[index])), i replaced it with blocprovider.of
@ZAT90 if you are adding to a new CartBloc then there are no widgets reacting to it so your app won't rebuild any parts of the UI. That's why using BlocProvider to access the existing bloc works 馃憤