I'm discovering Flutter and the bloc pattern and to practice I'm making an app about pizzas.
I am using a BlocProvider to access the blocks. It is from the generic_bloc_provider package. It is a basic implementation using an InheritedWidget combined with a StatelessWidget.
I have a page with two editable textfields, for the name and price of the pizza I want to create. It is backed by a bloc.
Here's the code :
AddPizzaPage.dart :
class AddPizzaPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
print("Building AddPizzaPage");
return Scaffold(
appBar: AppBar(
title: Text("Adding Pizza"),
),
body: BlocProvider(
bloc: AddPizzaBloc(),
child: ModifyPizzaWidget(),
),
);
}
}
ListPage.dart:
class ModifyPizzaWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final addPizzaBloc = BlocProvider.of<AddPizzaBloc>(context);
return Container(
margin: EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(hintText: "Nom de la pizza"),
onChanged: (name) {
addPizzaBloc.pizzaNameSink.add(name);
},
),
TextField(
decoration: InputDecoration(hintText: "Prix de la pizza"),
keyboardType: TextInputType.number,
onChanged: (price) {
addPizzaBloc.pizzaPriceSink.add(price);
},
),
IconButton(
icon: Icon(Icons.check),
iconSize: 40,
onPressed: () {
addPizzaBloc.evenSink.add(AddPizzaEvent.VALIDATE);
Navigator.of(context).pop();
},
)
],
),
);
}
}
AddPizzaBloc.dart :
enum AddPizzaEvent {
VALIDATE
}
class AddPizzaBloc extends Bloc {
final _pizza = Pizza.empty();
final _pizzaSubject = BehaviorSubject<Pizza>();
final _repository = PizzaRepository();
Sink<String> get pizzaNameSink => _pizzaNameController.sink;
final _pizzaNameController = StreamController<String>();
Sink<String> get pizzaPriceSink => _pizzaPriceController.sink;
final _pizzaPriceController = StreamController<String>();
Sink<AddPizzaEvent> get evenSink => _eventSink.sink;
final _eventSink = StreamController<AddPizzaEvent>();
AddPizzaBloc() {
print("Created");
_pizzaNameController.stream.listen(_addPizzaName);
_pizzaPriceController.stream.listen(_addPizzaPrice);
_eventSink.stream.listen(_onEventReceived);
}
dispose() {
print("Disposed");
_pizzaSubject.close();
_pizzaNameController.close();
_pizzaPriceController.close();
_eventSink.close();
}
void _addPizzaName(String pizzaName) {
_pizza.name = pizzaName;
print(_pizza);
}
void _addPizzaPrice(String price) {
var pizzaPrice = double.tryParse(price) ?? 0.0;
_pizza.price = pizzaPrice;
print(_pizza);
}
void _onEventReceived(AddPizzaEvent event) {
print("Received $event");
if (event == AddPizzaEvent.VALIDATE) {
print(_pizza);
_repository.addPizza(_pizza);
}
}
}
My issue is that I store the Pizza being built inside the block but the widget is rebuilt, and so the bloc is rebuilt and I lose the state.
The full code is available on gitlab
I don't know how to use the bloc to power the addPizza form.
This happens because you're creating the instance of your BLoC within the build method:
BlocProvider(
bloc: Bloc(),
child: ...
)
The consequence is that any rebuild would not reuse the previous instance (with some awful memory leaks too).
The solution would be to make a StatefulWidget and create that BLoC instance within initState, followed by a dispose override to clean things.
But since you're using a package already, you can use provider instead. It is a popular alternative that does everything listed above.
As such your BlocProvider usage becomes:
StatefulProvider(
valueBuilder: (_) => AddPizzaBloc(),
dispose: (_, bloc) => bloc.dispose(),
child: // ...
),
then obtained as such:
Provider.of<AddPizzaBloc>(context);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With