title: Flutter Navigation and the requirement for dispose() in the mean time
body: <p>Trying to use <a href="https://pub.dartlang.org/packages/image_picker" rel="nofollow noreferrer"><code>image_picker</code></a> in Flutter, I have the following issue:</p>
  
  <p>When navigation is supposed to pop back to Widget Nr1, I can no longer call <code>setState()</code> inside Widget Nr1. This is due to the fact that the <code>dispose()</code>method was called once the <code>Navigation.push</code> from Widget-Nr1 to Widget-Nr2 happened.</p>
  
  <p>It turns out that I absolutely need to call this <code>dispose()</code>method in order for the <code>image_picker</code> plugin to work correctly. (if I don't then the error <code>...was disposed with an active Ticker...</code> happens, probably due to the fact that the <code>image_picker</code> plugin does something under the hood that desperately needs dispose() beforehand.</p>
  
  <p>Anyway, I feel like the snake bites its tail.</p>
  
  <p>As a summary I do the following (also see code below):</p>
  
  <ul>
  <li>inside Widget Nr1: Pressing a FloatingAction-Button, pushes the Navigator to a Widget Nr2</li>
  <li>both Widgets (Nr1 and Nr2) are Stateful Widgets</li>
  <li>they both have a dispose-method (needed otherwise image_picker does not work)</li>
  <li>Widget-Nr2 calls the <code>image_picker</code> plugin (letting the user take a photo with the camera and asking the user for some String-text describing the image)</li>
  <li>the result (i.e. imageFile and some String-text) needs to be given back to Widget-Nr1 (using <code>Navigation.pop</code>)</li>
  <li>the Widget-Nr1 actually does get this data (i.e. image plus some String-text)</li>
  <li>but: it cannot call <code>setState()</code> anymore after the Navigation.pop most likely due to the fact that both Widgets had already called their <code>dispose()</code> method</li>
  </ul>
  
  <p>I get the error inside Widget-Nr1:</p>
  
  <pre><code>Dart Error: Unhandled exception:
  setState() called after dispose()
  </code></pre>
  
  <p>What can I do in order to make this work ?</p>
  
  <p>How can I use the result-data of the <code>image_picker</code> (that requires <code>dispose()</code> in Widget-1) as a Navigation.pop result again in Widget-1 and this in a way where <code>setState()</code> is still possible after all the Navigation ??</p>
  
  <p>Or is there another approach to go with ?</p>
  
  <p>Here is my code:</p>
  
  <p>StatefulWidget Nr1 (excerpt of it):</p>
  
  <pre class="lang-dart prettyprint-override"><code>    child: FloatingActionButton(
        onPressed: () async {
          _imagePickerResult = await navigateToImagePicker(context);
          setState(() async {
              this.itemBins.add(ItemBin(
                  _imagePickerResult.locationName,
                  _imagePickerResult.locationImage));
          });
        },
        child: Icon(Icons.add),
      ),
  
      // ...
  
      Future&lt;ImagePickerResult&gt; navigateToImagePicker(BuildContext context) async {
        return await Navigator.push(
          context, MaterialPageRoute(builder: (context) =&gt; MyImagePickerView())
        );
      }
  
      // ...
  
      class ImagePickerResult {
        String locationName;
        Image locationImage;
  
        ImagePickerResult({this.locationName, this.locationImage});
      }
  </code></pre>
  
  <p>StatefulWidget Nr2:</p>
  
  <pre class="lang-dart prettyprint-override"><code>    import 'package:flutter/material.dart';
      import 'dart:io';
      import 'package:image_picker/image_picker.dart';
      import './../../models/image_picker_location.dart';
  
      class MyImagePickerView extends StatefulWidget {
        _MyImagePickerViewState createState() =&gt; _MyImagePickerViewState();
      }
  
      class _MyImagePickerViewState extends State&lt;MyImagePickerView&gt; {
        TextEditingController _myController = TextEditingController();
        File _imageFile;
        bool _pickImage = true;
  
        @override
        Widget build(BuildContext context) {
          if (_pickImage) {
            return FutureBuilder&lt;File&gt;(
              future: ImagePicker.pickImage(source: ImageSource.camera),
              builder: (BuildContext context, AsyncSnapshot&lt;File&gt; snapshot) {
                if (snapshot.hasData) {
                  _pickImage = false;
                  _imageFile = snapshot.data;
                  return _showImage(snapshot.data);
                } else {
                  return Scaffold(
                    body: Center(
                      child: Text('no image picker availalbe'),
                    ),
                  );
                }
              },
            );
          } else {
            return _showImage(_imageFile);
          }
        }
  
        Widget _showImage(File imgFile) {
          return Scaffold(
            body: Stack(
              alignment: AlignmentDirectional.topStart,
              children: &lt;Widget&gt;[
                Positioned(
                  left: 0.0,
                  bottom: 0.0,
                  width: MediaQuery.of(context).size.width,
                  height: MediaQuery.of(context).size.height,
                  child: Center(
                    child: imgFile == null
                        ? Text('No image selected.')
                        : Image.file(imgFile),
                  ),
                ),
                Positioned(
                  left: 16.0,
                  bottom: 70.0,
                  width: MediaQuery.of(context).size.width - 32.0,
                  height: 50.0,
                  child: Container(
                    color: Colors.grey[100],
                    child: TextField(
                      autofocus: false,
                      keyboardType: TextInputType.text,
                      autocorrect: false,
                      style: TextStyle(
                          color: Colors.black,
                          fontSize: 22.0,
                          fontWeight: FontWeight.w600),
                      decoration: InputDecoration(
                        hintStyle: TextStyle(
                            color: Colors.black38,
                            fontSize: 22.0,
                            fontWeight: FontWeight.normal),
                        hintText: "depart From :",
                        contentPadding: const EdgeInsets.fromLTRB(6.0, 13.0, 0, 12.0),
                        enabledBorder: UnderlineInputBorder(
                          borderSide: BorderSide(color: Colors.red, width: 2.0),
                        ),
                      ),
                      maxLines: 1,
                      textAlign: TextAlign.left,
                      controller: _myController,
                      onEditingComplete: () {
                        FocusScope.of(context)
                            .requestFocus(FocusNode()); // dismiss keyboard
                        Navigator.pop(
                          context,
                          ImagePickerResult(
                            locationName: _myController.text,
                            locationImage: Image.file(imgFile),
                          ),
                        );
                      },
                    ),
                  ),
                ),
              ],
            ),
          );
        }
      }
  </code></pre>
  
  <p>dispose-method of Widget Nr1:</p>
  
  <pre class="lang-dart prettyprint-override"><code>  @override
    void dispose() {
      if (_debounce?.isActive ?? false) {
        _debounce.cancel(); // if _debounce is active cancel it...
      }
      _debounce = Timer(const Duration(milliseconds: 200), () {
        // security wait due to the fact that there are animations still running during setState()
      });
      // dispose AnimationController
      controller.dispose();
      _debounce.cancel();
      super.dispose();
    }
  </code></pre>
  
  <p>dispose method of Widget-Nr2:</p>
  
  <pre class="lang-dart prettyprint-override"><code>   @override
     void dispose() {
       _myController.dispose();
       super.dispose();
     }
  </code></pre>
  
  <p>Here is the error-message if I don't make view1 do the dispose() before image_picker starts... (please note that there is an animation running at the moment the user wants to start image_picker and therefore the dispose() makes an artificial "wait" of 200ms before the segue to the image_picker takes place)....</p>
  
  <pre class="lang-dart prettyprint-override"><code>flutter: ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
  flutter: The following assertion was thrown while finalizing the widget tree:
  flutter: _HistoryViewState#a8eac(ticker active but muted) was disposed with an active Ticker.
  flutter: _HistoryViewState created a Ticker via its SingleTickerProviderStateMixin, but at the time dispose()
  flutter: was called on the mixin, that Ticker was still active. The Ticker must be disposed before calling
  flutter: super.dispose(). Tickers used by AnimationControllers should be disposed by calling dispose() on the
  flutter: AnimationController itself. Otherwise, the ticker will leak.
  flutter: The offending ticker was: Ticker(created by _HistoryViewState#a8eac(lifecycle state: created))
  flutter: The stack trace when the Ticker was actually created was:
  flutter: #0      new Ticker.&lt;anonymous closure&gt; 
  package:flutter/…/scheduler/ticker.dart:64
  flutter: #1      new Ticker 
  package:flutter/…/scheduler/ticker.dart:66
  flutter: #2      __HistoryViewState&amp;State&amp;SingleTickerProviderStateMixin.createTicker 
  package:flutter/…/widgets/ticker_provider.dart:93
  flutter: #3      new AnimationController 
  </code></pre>