Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter - InheritedWidget - dispose

Tags:

flutter

I was wondering if anybody knew a way to know when an InheritedWidget was disposed?

The reason of this question is that I am doing some experiments and I am using an InheritedWidget as a provider of a BLoC. This BLoC is initialized at the InheritedWidget level, and uses a StreamController.

As it is more than recommended to close a StreamController, I am trying to find a solution.

Here is a piece of code (silly code just for experiments) to illustrate the question:

///
/// ApplicationProvider
/// 
/// A provider of ApplicationBloc
/// 
class ApplicationProvider extends InheritedWidget {
  //
  // Initialization of the BLoC
  //
  final ApplicationBloc bloc = new ApplicationBloc();

  ApplicationProvider({Key key, Widget child}) : super(key: key, child: child);

  @override
  bool updateShouldNotify(_) => true;

  static ApplicationBloc of(BuildContext context, [bool redraw = true]) {
    return redraw ? (context.inheritFromWidgetOfExactType(ApplicationProvider) as ApplicationProvider).bloc
                  : (context.ancestorWidgetOfExactType(ApplicationProvider) as ApplicationProvider).bloc;
  }
}

//
// The BLoC
//   
class ApplicationBloc {

  int _counter;
  StreamController<int> _counterController = new StreamController<int>.broadcast();
  Sink get inCounter => _counterController;

  Stream<int> get outCounter => _counterController.stream;

  ApplicationBloc(){
    _counter = 0;
  }

  void increment(){
    _counter++;
    inCounter.add(_counter);
  }

  int get counter => _counter;

  //
  // How could I call this method ???
  //
  void dispose(){
    _counterController.close();
  }
}

So the main question is "how can I call the dispose() method of my BLoC" ?

Many thanks for your help.

like image 895
boeledi Avatar asked Aug 04 '18 23:08

boeledi


People also ask

How does dispose work in flutter?

Dispose is a method triggered whenever the created object from the stateful widget is removed permanently from the widget tree. It is generally overridden and called only when the state object is destroyed. Dispose releases the memory allocated to the existing variables of the state.

Does Dispose get called automatically flutter?

dispose() method called automatically from stateful if not defined. In some cases dispose is required for example in CameraPreview , Timer etc.. you have to close the stream.


2 Answers

InheritedWidget behaves the same way as other Widget do. Their lifetime is really short: Usually not longer than one build call.

If you want to store data for longer, InheritedWidget is not what you want. You'll need a State for that.

Which also means that ultimately, you can use State's dispose for your bloc dispose.

class BlocHolder extends StatefulWidget {
  final Widget child;

  BlocHolder({this.child});

  @override
  _BlocHolderState createState() => _BlocHolderState();
}

class _BlocHolderState extends State<BlocHolder> {
  final _bloc = new MyBloc();

  @override
  Widget build(BuildContext context) {
    return MyInherited(bloc: _bloc, child: widget.child,);
  }


  @override
  void dispose() {
    _bloc.dispose();
    super.dispose();
  }
}

class MyInherited extends InheritedWidget {
  final MyBloc bloc;

  MyInherited({this.bloc, Widget child}): super(child: child);

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return oldWidget != this;
  }
}


class MyBloc {
  void dispose() {

  }
}
like image 117
Rémi Rousselet Avatar answered Oct 19 '22 07:10

Rémi Rousselet


Inherited widgets behave very much like stateless widgets, which also do not have a dispose method. Inherited widgets are getting rebuilt frequently, and all the values stored inside of it would be lost (and without a proper updateShouldNotify implementation, the dependent widget trees will also be rebuilt frequently!).

To solve this problem, you can utilize a StatefulWidget:

import 'dart:async';

import 'package:flutter/widgets.dart';

class ApplicationProvider extends StatefulWidget {
  const ApplicationProvider({Key key, this.child}) : super(key: key);

  final Widget child;

  @override
  State<StatefulWidget> createState() => _ApplicationProviderState();
}

class _ApplicationProviderState extends State<ApplicationProvider> {
  final ApplicationBloc bloc = new ApplicationBloc();

  @override
  void dispose() {
    bloc.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return _ApplicationProvider(
      bloc: bloc,
      child: widget.child,
    );
  }
}

class _ApplicationProvider extends InheritedWidget {
  _ApplicationProvider({
    Key key,
    this.bloc,
    Widget child,
  }) : super(key: key, child: child);

  final ApplicationBloc bloc;

  @override
  bool updateShouldNotify(_ApplicationProvider oldWidget) {
    return bloc != oldWidget.bloc;
  }
}

class ApplicationBloc {
  ApplicationBloc of(BuildContext context) {
    final _ApplicationProvider provider = context.inheritFromWidgetOfExactType(_ApplicationProvider);
    return provider.bloc;
  }

  int _counter;
  StreamController<int> _counterController = new StreamController<int>.broadcast();
  Sink get inCounter => _counterController;

  Stream<int> get outCounter => _counterController.stream;

  ApplicationBloc() {
    _counter = 0;
  }

  void increment() {
    _counter++;
    inCounter.add(_counter);
  }

  int get counter => _counter;

  void dispose() {
    _counterController.close();
  }
}
like image 25
boformer Avatar answered Oct 19 '22 08:10

boformer