Search code examples
flutterscopedisposeblocinherited-widget

Flutter: How Do I Properly Seat a Provider.of Method into a StatefulWidget so Dispose() Can Be Used in My Code?


Introduction: I have spent over an hour looking through StackOverflow seeking this answer; so, I have hesitantly decided to ask this question which I believe is unanswered. I hope that it am not repeating something that has been answered Ad nauseam.

I have been spending the last five weeks immersing myself in the study of the BLoC pattern (yes, I know that I'm about a year behind the curve). I have been watching several videos and reading a couple of books on the topic. I have learned that memory leaks occur if no dispose method is called. In many recent YouTube videos that I have watched, a stateful widget is recommended instead of a stateless widget, so that the dispose method can be used during the lifecycle of the app for preventing memory leaks.

Problem: what is the proper way to seat an Inherited Widget, via a Provide.of method, to gain the scope for the context of the stateful widget, so the dispose() method can be used during the lifecycle?

Relevant Code:

import 'package:flutter/material.dart';

import 'package:testingblock6/counter_provider.dart';
import 'counter_event.dart';
import 'model.dart';

class Home extends StatefulWidget {
  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  @override
  Widget build(BuildContext context) {
    final _bloc = Provider.of(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Testing Bloc 6'),
      ),
      body: Center(
          child: StreamBuilder<Model>(
        stream: _bloc.modelStream,
        initialData: Model(),
        builder: (BuildContext context, AsyncSnapshot<Model> snapshot) {
          return Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'You have pushed the button this many times:',
              ),
              Text(
                'Counter: ${snapshot.data.counter}',
                style: Theme.of(context).textTheme.display1,
              ),
              Text(
                'ThisGuy: ${snapshot.data.thisGuy}',
                style: Theme.of(context).textTheme.display1,
              ),
            ],
          );
        },
      )),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          FloatingActionButton(
            onPressed: () => _bloc.counterEventSink.add(IncrementEvent()),
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
          SizedBox(width: 10),
          FloatingActionButton(
            onPressed: () => _bloc.counterEventSink.add(DecrementEvent()),
            tooltip: 'Decrement',
            child: Icon(Icons.remove),
          ),
          SizedBox(width: 10),
          FloatingActionButton(
            onPressed: () => _bloc.counterEventSink.add(ThisGuy()),
            tooltip: 'ThisGuy',
            child: Icon(Icons.golf_course),
          ),
        ],
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

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

Issue: I believe that the Provider.of method must be called inside of the _HomeState region of the code because this is where the context is available. However, by doing this, it removes it from the scope of the dispose method.

Question: What am I doing wrong regarding the availability of the scope of the dispose() method, is there an answer to this that I am not seeing?


Solution

  • import 'package:flutter/material.dart';
    
    import 'counter_event.dart';
    import 'counter_provider.dart';
    import 'model.dart';
    
    class Home extends StatefulWidget {
      @override
      _HomeState createState() => _HomeState();
    }
    
    class _HomeState extends State<Home> {
      var _bloc;
    
      @override
      Widget build(BuildContext context) {
        _bloc = Provider.of(context);
    
        return Scaffold(
          appBar: AppBar(
            title: Text('Testing Bloc 6'),
          ),
          body: Center(
              child: StreamBuilder<Model>(
            stream: _bloc.modelStream,
            initialData: Model(),
            builder: (BuildContext context, AsyncSnapshot<Model> snapshot) {
              return Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  Text(
                    'You have pushed the button this many times:',
                  ),
                  Text(
                    'Counter: ${snapshot.data.counter}',
                    style: Theme.of(context).textTheme.display1,
                  ),
                  Text(
                    'ThisGuy: ${snapshot.data.thisGuy}',
                    style: Theme.of(context).textTheme.display1,
                  ),
                ],
              );
            },
          )),
          floatingActionButton: Row(
            mainAxisAlignment: MainAxisAlignment.end,
            children: <Widget>[
              FloatingActionButton(
                onPressed: () => _bloc.counterEventSink.add(IncrementEvent()),
                tooltip: 'Increment',
                child: Icon(Icons.add),
              ),
              SizedBox(width: 10),
              FloatingActionButton(
                onPressed: () => _bloc.counterEventSink.add(DecrementEvent()),
                tooltip: 'Decrement',
                child: Icon(Icons.remove),
              ),
              SizedBox(width: 10),
              FloatingActionButton(
                onPressed: () => _bloc.counterEventSink.add(ThisGuy()),
                tooltip: 'ThisGuy',
                child: Icon(Icons.golf_course),
              ),
            ],
          ), // This trailing comma makes auto-formatting nicer for build methods.
        );
      }
    
      @override
      void dispose() {
        super.dispose();
        _bloc.dispose();
      }
    }