skip to Main Content

I would like to discuss about a code.
I am using autoDispose in my application but there is a point that confuses me.
I am using this autoDispose with ConsumerWidget and I am not sure if it is disposed properly.
I am sharing the code below, could you please take a look at it if you have a chance to review the code?
Note: I prefer to use ConsumerWidget for efficient performance and I thought I could use autoDispose in this way since I am using riverpod.
I used static for security reasons but I am not sure if it causes any issues.

Also, I used ref.onDispose() inside autoDispose, maybe this code is excessive and I already disposed it manually…
Thank you in advance for your help…

I am using this autoDispose with ConsumerWidget and I want to make sure that it is properly disposed when dispose() is called.

class ControlTextField extends ConsumerWidget {
  const ControlTextField({super.key});

  static final _textController = Provider.autoDispose<TextEditingController>(
    (ref) {
      final controller = TextEditingController();

      ref.onDispose(() {
        controller.dispose();
      });

      return controller;
    },
  );

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final FocusNode firstFocusNode = FocusNode();
    return Row(
      mainAxisAlignment: MainAxisAlignment.end,
      children: [
        const Icon(Icons.receipt, size: 16),
        const SizedBox(width: 8),
        Expanded(
            flex: 2,
            child: Text('Work List',
                style: Theme.of(context).textTheme.labelMedium)),
        Expanded(
          flex: 2,
          child: SizedBox(
            height: 20,
            child: Consumer(
              builder: (context, watch, _) {
                final textEditingController = ref.watch(_textController);

                return TextField(
                  controller: textEditingController,
                  onChanged: (event) {
                    final textEditingController = ref.read(_textController);

                    debugPrint(textEditingController.value.text);
                  },
                  style: Theme.of(context).textTheme.bodySmall,
                  autocorrect: false,
                  onTapOutside: (b) {
                    FocusManager.instance.primaryFocus?.unfocus();
                  },
                  focusNode: firstFocusNode,
                  enableSuggestions: true,
                  decoration: const InputDecoration(
                    hintText: 'Ara...',
                    suffixIcon: Icon(Icons.search, size: 12),
                  ),
                  keyboardType: TextInputType.text,
                  autofocus: false,
                  inputFormatters: <TextInputFormatter>[
                    FilteringTextInputFormatter.singleLineFormatter
                  ],
                );
              },
            ),
          ),
        ),
      ],
    );
  }
}

2

Answers


  1. The code you shared seems to be using Riverpod and Flutter to create a ControlTextField widget that extends ConsumerWidget. The purpose of this widget is to display a text field with some associated functionality.

    Regarding the disposal of the TextEditingController, using Provider.autoDispose is a good choice to ensure that the controller is disposed of when it’s no longer needed. The ref.onDispose callback is used to dispose of the controller manually when the provider is disposed of, which is also correct.

    However, there are a few points I would like to address:

    The static modifier on the _textController provider:
    In your code, _textController is declared as static. While this may have been done for security reasons, it’s worth noting that using static can cause potential issues with testability and maintainability. If you intend to use this provider in multiple instances of ControlTextField widgets, it would be better to remove the static modifier and create a separate provider outside the widget.

    The usage of Consumer widget:
    Within the ControlTextField widget, you’re using Consumer to access the TextEditingController provided by _textController. However, since you’re already using ConsumerWidget, you can directly access the value of the provider within the build method using ref.watch. This avoids the need for the Consumer widget and makes the code more concise. Here’s an updated version of the code with this change:

    class ControlTextField extends ConsumerWidget {
      const ControlTextField({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final FocusNode firstFocusNode = FocusNode();
        final textEditingController = ref.watch(_textController);
    
        return Row(
          mainAxisAlignment: MainAxisAlignment.end,
          children: [
            const Icon(Icons.receipt, size: 16),
            const SizedBox(width: 8),
            Expanded(
              flex: 2,
              child: Text('Work List', style: Theme.of(context).textTheme.labelMedium),
            ),
            Expanded(
              flex: 2,
              child: SizedBox(
                height: 20,
                child: TextField(
                  controller: textEditingController,
                  onChanged: (event) {
                    debugPrint(textEditingController.value.text);
                  },
                  style: Theme.of(context).textTheme.bodySmall,
                  autocorrect: false,
                  onTapOutside: (b) {
                    FocusManager.instance.primaryFocus?.unfocus();
                  },
                  focusNode: firstFocusNode,
                  enableSuggestions: true,
                  decoration: const InputDecoration(
                    hintText: 'Ara...',
                    suffixIcon: Icon(Icons.search, size: 12),
                  ),
                  keyboardType: TextInputType.text,
                  autofocus: false,
                  inputFormatters: <TextInputFormatter>[
                    FilteringTextInputFormatter.singleLineFormatter,
                  ],
                ),
              ),
            ),
          ],
        );
      }
    }
    

    With this updated code, you can be confident that the TextEditingController will be properly disposed when the ControlTextField widget is disposed.

    Login or Signup to reply.
  2. Exactly for these purposes, you can use a couple of packages:

    And your code will look like this:

    class ControlTextField extends HookConsumerWidget {
      const ControlTextField({super.key});
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final firstFocusNode = useFocusNode();
        final textEditingController = useTextEditingController();
        
        // .. lots of other code
        return TextField(
          controller: textEditingController,
          onChanged: (event) {
            // do something
            
            debugPrint(textEditingController.text);
          },
          onTapOutside: (_) {
            FocusScope.of(context).unfocus();
          },
          focusNode: firstFocusNode,
        );
      }
    }
    

    In this case, we are using the HookConsumerWidget class, which allows the use of hooks in the build method. Hooks are things that will be disposed of on their own if necessary. Also, you can add some parameters to useTextEditingController.fromValue(...), such as initial text.

    I also changed your FocusNode object, which also needs to be properly disposed of.


    If you don’t like hooks for whatever reason, use the ConsumerStatefulWidget widget and the initState() and dispose() methods to define any of your controllers and dispose of them afterwards. This way you better show your intentions in the code.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search