Skip to content

[DropdownMenu] Improve document on how to reset selectedValue #177599

Closed
flutter/packages
#13266

Description

@UriDevzera

Steps to reproduce

1 - Create a simple statefull home page;
2 - Add a DropdownMenu with some options;
3 - Inform initialSelection with a selectedValue;
4 - Create one button with a pressed function that should change the value of selectedValue for null;
5 - Select one item;
6 - Press the button;

OBS: I think that I know why it happens. I checked the DropdownMenu flutter implementation and I saw that in didUpdateWidget function do not implement something when widget.initialSelection is null, of course there is a check to see if oldWidget.initialSelection != widget.initialSelection but it is just trying to find the current option in the list and effective change the value if there is. Of course I don't know if there is a reason for it, I'm just wondering that maybe this function should check if initialSelection is null to clear the controller text;

Expected results

The expected results is clean the dropdown selection UI;

Actual results

Don't clean selection.

Code sample

Code sample
import 'package:flutter/material.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String? selectedValue1;
  String? selectedValue2;

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text('Welcome to Flutter!'),
            const SizedBox(height: 20),
            DropdownMenu<String>(
              width: 200,
              label: const Text('First Dropdown'),
              onSelected: (String? value) {
                setState(() {
                  selectedValue1 = value;
                });
              },
              initialSelection: selectedValue1,
              dropdownMenuEntries: const <DropdownMenuEntry<String>>[
                DropdownMenuEntry<String>(value: 'option1', label: 'Option 1'),
                DropdownMenuEntry<String>(value: 'option2', label: 'Option 2'),
                DropdownMenuEntry<String>(value: 'option3', label: 'Option 3'),
              ],
            ),
            const SizedBox(height: 20),
            DropdownMenu<String>(
              width: 200,
              label: const Text('Second Dropdown'),
              initialSelection: selectedValue2,
              onSelected: (String? value) {
                setState(() {
                  selectedValue2 = value;
                });
              },
              dropdownMenuEntries: const <DropdownMenuEntry<String>>[
                DropdownMenuEntry<String>(value: 'choice1', label: 'Choice 1'),
                DropdownMenuEntry<String>(value: 'choice2', label: 'Choice 2'),
                DropdownMenuEntry<String>(value: 'choice3', label: 'Choice 3'),
              ],
            ),
            const SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                setState(() {
                  selectedValue2 = null;
                });
              },
              child: const Text('Clear Second Dropdown'),
            ),
          ],
        ),
      ),
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
[Paste your logs here]

Flutter Doctor output

Doctor output
Doctor summary (to see all details, run flutter doctor -v):
[√] Flutter (Channel stable, 3.35.7, on Microsoft Windows [Version 10.0.22631.6060], locale en-US)
[√] Windows Version (11 Enterprise 64-bit, 23H2, 2009)
[√] Android toolchain - develop for Android devices (Android SDK version 36.0.0)
[√] Chrome - develop for the web
[√] Visual Studio - develop Windows apps (Visual Studio Professional 2022 17.13.3)
[√] Android Studio (version 2024.1)
[√] VS Code (version 1.105.1)
[√] Connected device (5 available)
[√] Network resources

• No issues found!

Activity

  1. UriDevzera commented on Oct 27, 2025

    @UriDevzera
    Author

    I can solve this using controller for the DropdownMenu. Again, I don't know if it is a issue, cause it don't crash the application and don't throw any exception. But at least is a unexpected behavior.

    Thanks!

  2. tirth-patel-nc commented on Oct 28, 2025

    @tirth-patel-nc
    Member

    @UriDevzera thanks for the report. as noted in #165877 (comment), this behavior is working as intended. however, the documentation could be improved to make this clearer and help avoid confusion.

  3. added
    d: api docsIssues with https://api.flutter.dev/
    team-designOwned by Design Languages team
    frameworkflutter/packages/flutter repository. See also f: labels.
    p: material_uimaterial_ui package in flutter/packages
    c: proposalA detailed proposal for a change to Flutter
    and removed
    in triagePresently being triaged by the triage team
    on Oct 28, 2025
  4. UriDevzera commented on Oct 28, 2025

    @UriDevzera
    Author

    @tirth-patel-nc Oh there is one item for it, I didn't found when I was looking for. Well that's it hehe, I will use controller to handle this as recommended. Thanks for your time.

  5. added
    P3Issues that are less important to the Flutter project
    triaged-designTriaged by Design Languages team
    on Oct 29, 2025
  6. changed the title [-][DropdownMenu][initialSelection] doesn't change the UI if I change for null[/-] [+][DropdownMenu] Improve document on how to reset `selectedValue`[/+] on Oct 29, 2025
  7. kazbeksultanov commented on Dec 10, 2025

    @kazbeksultanov
    Contributor

    @tirth-patel-nc I added PR for enhancing the documentation.
    Can you take a look 🙏 ?

  8. QuncCccccc commented on Dec 15, 2025

    @QuncCccccc
    Contributor

    Adding documentation sounds good to me! Maybe we can introduce a new property value or selectedValue and gradually deprecate initialSelection? @bleroux

  9. bleroux commented on Dec 17, 2025

    @bleroux
    Contributor

    Maybe we can introduce a new property value or selectedValue and gradually deprecate initialSelection?

    This is something to consider but I'm not sure yet if it is the best option. For instance, we can also consider introducing a specific DropdownMenuController which would be a single point to manage the text field state and the dropdown menu selected value.

  10. ishaquehassan commented on Feb 27, 2026

    @ishaquehassan
    Contributor

    The docs should mention that DropdownMenu has a controller parameter (TextEditingController) which can be used to programmatically reset the selection.

    Workaround until this is documented/fixed:

    class _MyHomePageState extends State<MyHomePage> {
      String? selectedValue;
      final TextEditingController _controller = TextEditingController();
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Column(
            children: [
              DropdownMenu<String>(
                controller: _controller,
                initialSelection: selectedValue,
                onSelected: (value) => setState(() => selectedValue = value),
                dropdownMenuEntries: const [
                  DropdownMenuEntry(value: 'option1', label: 'Option 1'),
                  DropdownMenuEntry(value: 'option2', label: 'Option 2'),
                ],
              ),
              ElevatedButton(
                onPressed: () {
                  setState(() {
                    selectedValue = null;
                    _controller.clear(); // This clears the displayed text
                  });
                },
                child: const Text('Clear'),
              ),
            ],
          ),
        );
      }
    }

    The key point is using controller.clear() alongside setting your state variable to null. Without passing a controller, there's no way to imperatively clear the displayed text since initialSelection only applies on first render and on subsequent didUpdateWidget calls when the value differs — it doesn't handle the null reset case.

    Ideally the docs for DropdownMenu should have an example showing how to reset/clear the selection, and didUpdateWidget should handle null as a signal to clear the controller text.

  11. added
    packageflutter/packages repository. See also p: labels.
    and removed
    frameworkflutter/packages/flutter repository. See also f: labels.
    on Aug 17, 2026
  12. Snonky commented on Sep 24, 2026

    @Snonky
    Contributor

    For those who liked the old way of controlling the state of the dropdown with a value parameter, here's an emulation of that with the new DropdownButton:

    /// This widget keeps the value of the given [TextEditingController] pinned to a given value.
    ///
    /// It is useful to control a [DropdownMenu] when its internal changes of the text are undesired.
    /// For example when the selection of a value should not result in the immediate change of the text to that value but
    /// wait until some async process (loading/saving) has finished.
    class TextEditingControllerPin extends StatefulWidget {
      const TextEditingControllerPin({
        super.key,
        required this.controller,
        required this.value,
        required this.child,
      });
    
      final TextEditingController controller;
    
      /// While this is not null, the widget keeps the [controller]'s text value at this value, preventing any changes.
      final String? value;
    
      final Widget child;
    
      @override
      State<TextEditingControllerPin> createState() => _TextEditingControllerPinState();
    }
    
    class _TextEditingControllerPinState extends State<TextEditingControllerPin> {
      @override
      void initState() {
        widget.controller.addListener(_onChange);
        _onChange();
        super.initState();
      }
    
      @override
      void didUpdateWidget(TextEditingControllerPin oldWidget) {
        if (oldWidget.controller != widget.controller) {
          oldWidget.controller.removeListener(_onChange);
          widget.controller.addListener(_onChange);
          _onChange();
        } else if (oldWidget.value != widget.value) {
          _onChange();
        }
        super.didUpdateWidget(oldWidget);
      }
    
      @override
      void dispose() {
        widget.controller.removeListener(_onChange);
        super.dispose();
      }
    
      void _onChange() {
        if (widget.value != null) {
          widget.controller.text = widget.value!;
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return widget.child;
      }
    }
    

    To use, pass the same controller to TextEditingControllerPin and DropdownMenu. The menu will always reflect the state value not its internal state. In this demo the selection is delayed by 1 second:

    class Demo extends StatefulWidget {
      const Demo({super.key});
    
      @override
      State<Demo> createState() => _DemoState();
    }
    
    class _DemoState extends State<Demo> {
      final _controller = TextEditingController();
    
      String _value = 'Hello';
    
      void delayedSet(String value) {
        Future.delayed(
          const Duration(seconds: 1),
          () => setState(() {
            _value = value;
          }),
        );
      }
    
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return TextEditingControllerPin(
          controller: _controller,
          value: _value,
          child: DropdownMenu<String>(
            controller: _controller,
            selectOnly: true,
            initialSelection: 'Hello',
            onSelected: (value) => delayedSet(value ?? ''),
            dropdownMenuEntries: [
              for (final str in ['Hello', 'World', '!']) DropdownMenuEntry(value: str, label: str),
            ],
          ),
        );
      }
    }
    
  13. added a commit that references this issue on Oct 14, 2026
    b204a50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

P3Issues that are less important to the Flutter projectc: proposalA detailed proposal for a change to Flutterd: api docsIssues with https://api.flutter.dev/p: material_uimaterial_ui package in flutter/packagespackageflutter/packages repository. See also p: labels.team-designOwned by Design Languages teamtriaged-designTriaged by Design Languages team

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions