Flutter - A TextEditingController was used after being disposed running tests

Viewed 233

Still on the AlertDialog validation example I recently posted, I ran into another odd problem. I fixed the validation code as per the below and it works!

import "package:flutter/material.dart";

import "package:easy_wallet/resources/constants.dart";

class WalletApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(title: "EasyWallet", home: EasyWalletHomePage());
  }
}

class EasyWalletHomePage extends StatefulWidget {

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

class _EasyWalletState extends State<EasyWalletHomePage> {
  final TextEditingController _nameController = TextEditingController();

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

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

  @override
  Widget build(BuildContext context) {
    print("BUILD1!");
    return Scaffold(
      body: Center(
        child: Wrap(
          direction: Axis.vertical,
          crossAxisAlignment: WrapCrossAlignment.center,
          spacing: 15,
          children: [
            Text("No wallets yet..."),
            ElevatedButton(
                key: KEY_ADD_WALLET,
                child: const Text("➕"),
                style: ElevatedButton.styleFrom(
                  shape: CircleBorder(),
                ),
                onPressed: () {
                  _showAddWalletDialog(context);
                })
          ],
        ),
      ),
    );
  }

  Future<void> _showAddWalletDialog(BuildContext context) async {
    print("BUILD2!");
    return await showDialog(
        context: context,
        builder: (context) {
          return StatefulBuilder(
            builder: (context, setState) {
              return AlertDialog(
                title: const Text('Add wallet'),
                content: Wrap(
                  children: [
                    Text("Insert wallet name:"),
                    TextField(
                      controller: _nameController,
                      maxLength: 40,
                      onChanged: (value) {
                        setState(() {});
                      },
                      decoration: InputDecoration(
                          hintText: "eg: home"
                      ),
                    ),
                  ],
                ),
                actions: <Widget>[
                  TextButton(
                    child: const Text('CANCEL'),
                    onPressed: () {
                      setState(() {
                        Navigator.pop(context);
                      });
                    },
                  ),
                  TextButton(
                    child: const Text('OK'),
                    onPressed: (!_isValidName()) ? null : () {
                      Navigator.pop(context);
                    },
                  )
                ],
              );
            }
          );
        });
  }

  bool _isValidName() {
    return _nameController.value.text.isNotEmpty;
  }
}


void main() {
  runApp(WalletApp());
}

I want now to go back developing in TDD and started I wrote the test:

testWidgets('show add wallet dialog', (WidgetTester tester) async {
    await tester.pumpWidget(WalletApp());

    expect(find.byType(Dialog), findsNothing);

    var button = find.byKey(KEY_ADD_WALLET);

    await tester.tap(button);
    await tester.pumpAndSettle();

    expect(find.byType(Dialog), findsOneWidget);
  });

however, when I run it, I get the error:

The following assertion was thrown while finalizing the widget tree: A TextEditingController was used after being disposed. Once you have called dispose() on a TextEditingController, it can no longer be used.

When the exception was thrown, this was the stack: #0 ChangeNotifier._debugAssertNotDisposed. (package:flutter/src/foundation/change_notifier.dart:114:9) #1 ChangeNotifier._debugAssertNotDisposed (package:flutter/src/foundation/change_notifier.dart:120:6)

following, a long stack trace.

If I remove _nameController.dispose(); the the test runs...

What am I doing wrong?

1 Answers

For the (few at this point :) ) people that use TDD with flutter, this appears to be a bug. It is working on master channel (see https://github.com/flutter/flutter/issues/98965). Thanks to flutter developers for looking into this issue.

Related