Search code examples
flutterdartdispose

Why we need to call `dispose` method while member is property of the disposed class?


I am looking at the flutter example below;

https://docs.flutter.dev/cookbook/forms/text-field-changes#interactive-example

it has code like;

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Retrieve Text Input',
      home: MyCustomForm(),
    );
  }
}

// Define a custom Form widget.
class MyCustomForm extends StatefulWidget {
  const MyCustomForm({super.key});

  @override
  State<MyCustomForm> createState() => _MyCustomFormState();
}

// Define a corresponding State class.
// This class holds data related to the Form.
class _MyCustomFormState extends State<MyCustomForm> {
  // Create a text controller and use it to retrieve the current value
  // of the TextField.
  final myController = TextEditingController();

  @override
  void initState() {
    super.initState();

    // Start listening to changes.
    myController.addListener(_printLatestValue);
  }

  @override
  void dispose() {
    // Clean up the controller when the widget is removed from the widget tree.
    // This also removes the _printLatestValue listener.
    myController.dispose();
    super.dispose();
  }

  void _printLatestValue() {
    print('Second text field: ${myController.text}');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Retrieve Text Input'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              onChanged: (text) {
                print('First text field: $text');
              },
            ),
            TextField(
              controller: myController,
            ),
          ],
        ),
      ),
    );
  }
}

and following method within;

  @override
  void dispose() {
    // Clean up the controller when the widget is removed from the widget tree.
    // This also removes the _printLatestValue listener.
    myController.dispose();
    super.dispose();
  }

What I see here myController is already instance of _MyCustomFormState class. So my assumption is once MyCustomForm widget removed from widget tree, all it's instance members should be automatically disposed right? But it is recommended to call dispose method of those members explicitly.

Can I learn why framework does not do it itself and we need to handle it? I would understand if that holds a handle to a file system which is outside of that class scope, but what I see in there there is no handle of this controller outside of the scope.


Solution

  • When an instance is no longer used, (like your _MyCustomFormState), it is automatically garbage-collected, together with all the references it contains, like your myController.

    However, a TextEditingController instance may have more resources attached to it than can be reclaimed by simple garbage collection. Therefore is is recommended to call its dispose method before the memory is reclaimed.

    Unlike for instance C++, Dart does not have a concept of destructors for its classes, so the call to dispose methods is needed.