Search code examples
flutter

Error that failed to call super.dispose in Flutter - despite calling super.dipose()?


I am getting an error that super.dispose failed to call, even though I am calling super.dipose in the overridden method and unsure why this error is occuring.

I've tried different approaches such as wrap super.dispose in WidgetsBinding.instance!.addPostFrameCallback((timeStamp) { }) but that didnt work.

Any ideas why this might occur and is there a way I can force this / fix it?

(Below I am selecting random words from a sqlite database, which all runs fine).

Thank you!

import 'dart:math';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:learn_to_read/Pages/WordMatch/answerManager.dart';
import 'package:learn_to_read/Pages/WordMatch/pageProgressBar.dart';
import 'package:learn_to_read/Pages/WordMatch/pageProgressManager.dart';
import 'package:learn_to_read/animations/colorChange.dart';
import 'package:learn_to_read/animations/customScrollPhysics.dart';
import 'package:learn_to_read/database/databaseManager.dart';
import 'package:provider/provider.dart';
import 'package:sqflite/sqflite.dart';
import 'contents.dart';
import 'homeBar.dart';

class WordMatch extends StatefulWidget {
  @override
  _WordMatchState createState() => _WordMatchState();
}

class _WordMatchState extends State<WordMatch> with ChangeNotifier {
  List<String> _originalList = [];
  List<String> _selectedList = [];

  void newPage(int i) {
    context.read<AnswerManager>().currentPage = i;
    context.read<PageProgressManager>().incrementProgressDots(i);
  }

  Future<List<String>> generateData() async {
    DatabaseManager _databaseManager = DatabaseManager();
    Database _database = await _databaseManager.initDatabase();
    List<Map> _list = await _databaseManager.createList(_database);
    return List.generate(_list.length, (index) => _list[index]["Word"]);
  }

  Future<bool> populateLocalLists(List<String> _list) async {
    _originalList = _list;
    context.read<AnswerManager>().setTotalCards(_originalList.length);
    context.read<AnswerManager>().calculateTotalPages();

    return true;
  }

  Future<void> selectInitialWords(bool _listsPopulated) async {
    randomWordSelector();
    setState(() {});
  }

  void selectSubsequentWords(int i) async {
    _selectedList.clear();
    randomWordSelector();
  }

  void setUp() async {
    final _list = await generateData();
    final _listsPopulated = await populateLocalLists(_list);
    await selectInitialWords(_listsPopulated);
  }

  void initState() {
    context.read<PageProgressManager>().progressDotTracker = 0;
    context.read<AnswerManager>().resetVariables();
    bool accepted = false;

    setUp();
    super.initState();
  }

  @override
  void didChangeDependencies() {
    if (context.read<AnswerManager>().currentPage != Provider.of<AnswerManager>(context).itemCount &&
        Provider.of<AnswerManager>(context).cardsUpdated == false) {
      WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
        selectSubsequentWords(0);
        context.read<AnswerManager>().resetPoints();
      });
    }
    super.didChangeDependencies();
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        toolbarHeight: 0,
        elevation: 0,
        title: Text('Match'),
        backgroundColor: Colors.blueAccent,
        centerTitle: true,
      ),
      body: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          HomeBar(),
          Expanded(
            flex: 5,
            child: PageView.builder(
                itemCount: context.watch<AnswerManager>().itemCount,
                pageSnapping: true,
                physics: CustomScrollPhysics(parent: null),
                onPageChanged: newPage,
                scrollDirection: Axis.vertical,
                itemBuilder: (context, int) {
                  return Padding(
                    padding: const EdgeInsets.all(0),
                    child: ColorChangeUser(
                      duration: 500,
                      colorBegin: Colors.orange,
                      colorEnd: Colors.green,
                      widget: Container(
                        child: Contents(
                          words: _selectedList,
                        ),
                      ),
                    ),
                  );
                }),
          ),
          PageProgressBar(),
        ],
      ),
    );
  }

  void randomWordSelector() {
    if (_originalList.length == 0) {
      return;
    }
    Random rn = Random();
    for (int i = 0; i < 4; i++) {
      int j = rn.nextInt(_originalList.length);
      _selectedList.add(_originalList[j]);
      _originalList.removeAt(j);
    }
  }
}

error message

The following assertion was thrown while finalizing the widget tree:
_WordMatchState.dispose failed to call super.dispose.

dispose() implementations must always call their superclass dispose() method, to ensure that all the resources used by the widget are fully released.
When the exception was thrown, this was the stack: 
#0      StatefulElement.unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:4804:7)
#1      StatefulElement.unmount (package:flutter/src/widgets/framework.dart:4811:6)
#2      _InactiveElements._unmount (package:flutter/src/widgets/framework.dart:1847:13)
#3      _InactiveElements._unmount.<anonymous closure> (package:flutter/src/widgets/framework.dart:1845:7)
#4      SingleChildRenderObjectElement.visitChildren (package:flutter/src/widgets/framework.dart:6070:14)

Solution

  • OK - so I finally figured what I was doing wrong in case anyone else makes the same mistake.

    I had left ChangeNotifier (from Provider) on _WordMatchState (which I no longer wanted) and forgot to remove. Yay.