Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter BLoC being recreated

Tags:

flutter

dart

bloc

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.

like image 580
Yutsa Avatar asked Nov 18 '25 04:11

Yutsa


1 Answers

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);
like image 198
Rémi Rousselet Avatar answered Nov 20 '25 06:11

Rémi Rousselet



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!