Skip to content

Conversation

@bleroux
Copy link
Contributor

@bleroux bleroux commented Sep 30, 2025

Description

This PR adds DropdownMenu.decorationBuilder.
The goal is to make DropdownMenu more flexible.

Before this PR, several fields are used by DropdownMenu to create an inner InputDecoration. This approach has several limitations:

  • InputDecoration has more fields that the ones that are exposed
  • DropdownMenu makes some choices that can't be change. Especially, it creates an IconButton (with hardcoded padding) which is passed to InputDecoration.suffixIcon. This inner IconButton introduces some difficulty related to focus management and UI customization.

The new DropdownMenu.decorationBuilder property offers users a way to take control on the inner InputDecoration in a non-breaking way.

In a future PR, this property will help replacing the default IconButton.
Currently users can replace the IconButton using this code sample:

DropdownMenu without IconButton
import 'package:flutter/material.dart';

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

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

  final List<DropdownMenuEntry<String>> menuEntries = [
    "Red",
    "Green",
    "Blue",
  ].map((t) => DropdownMenuEntry<String>(label: t, value: t)).toList();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: SizedBox(
            width: 220,
            child: DropdownMenu<String>(
              expandedInsets: EdgeInsets.zero,
              requestFocusOnTap: true,
              dropdownMenuEntries: menuEntries,
              decorationBuilder: (context, controller) {
                return InputDecoration(
                  labelText: 'Label text',
                  helperText: 'Select a color or enter one',
                  suffixIcon: controller.isOpen
                      ? const Icon(Icons.arrow_drop_up)
                      : const Icon(Icons.arrow_drop_down),
                );
              },
            ),
          ),
        ),
      ),
    );
  }
}

Related Issue

Fixes DropDownMenu secondary trailing widget
Will help for Make DropdownMenu's trailing icon not focusable by default

Related discussions

#175847 (comment)
#175558 (comment)

Tests

  • Adds 7 tests.

@github-actions github-actions bot added framework flutter/packages/flutter repository. See also f: labels. f: material design flutter/packages/flutter/material repository. labels Sep 30, 2025
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces DropdownMenu.decorationBuilder, a new property that provides a more flexible way to customize the InputDecoration of the DropdownMenu. This is a significant improvement over the previous method of exposing individual decoration properties. The implementation is robust, with assertions to prevent misuse and a clean refactoring that maintains backward compatibility. The accompanying tests are thorough. My review includes a couple of minor suggestions to fix typos in the documentation and a small correction in one of the new tests.

@bleroux bleroux force-pushed the add_DropdownMenu_decorationBuilder branch from 1242969 to bf2e959 Compare September 30, 2025 14:21
@bleroux bleroux changed the title Add InputDecoration.decorationBuilder Add DropdownMenu.decorationBuilder Oct 3, 2025
Copy link
Contributor

@QuncCccccc QuncCccccc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this approach! Thank you for the improvement. Overall this looks good to me:) Will take another look tomorrow.

@bleroux bleroux force-pushed the add_DropdownMenu_decorationBuilder branch from bf2e959 to c1fb293 Compare October 8, 2025 14:34
@bleroux bleroux requested a review from QuncCccccc October 14, 2025 07:17
@bleroux bleroux force-pushed the add_DropdownMenu_decorationBuilder branch from c1fb293 to 5da230e Compare October 14, 2025 07:47
final DropdownMenuDecorationBuilder decorationBuilder =
widget.decorationBuilder ?? _buildDefaultDecoration;
InputDecoration decoration = decorationBuilder(context, controller);
// If no suffixIcon is provided, the default IconButton is used for convenience.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think if there will be a case where the user doesn't want the sufficIcon at all? I'm thinking whether we should still give it a default value when suffixIcon is null.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting.
The property DropdownMenu.showTrailingIcon was added some time ago in #167782 for that purpose.

Because DropdownMenu.decorationBuilder is new we can decide to use a different solution. Nonetheless, If we don't add a default suffix when suffixIcon is null, most of the users will have to provide their implementation or we have to offer them an API to get the default Icon. It seems to add complexity for a use case which is somewhat specific.

Copy link
Contributor

@Gustl22 Gustl22 Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd propose to always add the default icon, also with a custom decoration (when the user adds an additinal trailing icon, then keep it in a Row). And the user can still disable it via the property showTrailingIcon. That way the dev does not need to implement it, e.g. also with providing more additional trailing actions, but still has the opportunity to remove it.
That way it has a purpose in all cases, while providing maximum flexibility and minimal effort for the dev.

The other way around: in the current implementation, if the user defines its own trailing icon, the showTrailingIcon does not have any effect, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd propose to always add the default icon, also with a custom decoration (when the user adds an additinal trailing icon, then keep it in a Row)

Adding a Row fits specific use cases and for these use cases it might be better for us to expose the default button construction if you think this would be interesting and let users add a Row (or something else) if needed (that way they can control padding for instance or anything matching their design).
My reasonning is to remove as much as possible custom rendering logic outside of DropdownMenu. In that particular case, InputDecorator already implements APIs that comply with M3 spec (padding, defaults colors, borders, etc) and offer flexibility (InputDecoration.suffix, InputDecoration.suffixIcon, etc).

The other way around: in the current implementation, if the user defines its own trailing icon, the showTrailingIcon does not have any effect, right?

Yes. It surely should be documented (in fact the best move would probably to rename showTrailingIcon to showDefaultTrailingIcon in that case). We can also decide to ignore InputDecoration.suffixIcon if showTrailingIcon is false, it might be easier to document/understand.

Copy link
Contributor

@Gustl22 Gustl22 Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a Row fits specific use cases and for these use cases it might be better for us to expose the default button construction if you think this would be interesting and let users add a Row

Good point.
Of course the Row was just meant as an internal helper to still be able to access the default trailing icon. But yes, it would then only serve in that (in my opinion most common) use case, and shrinks flexibility how (e.g. in which order) to use it.

I personally don't need the default trailing button, as the fuctionality can easily be recreated with the controller. Not sure if folks would notice / search for the constructor of the default icon.

Yes. It surely should be documented (in fact the best move would probably to rename showTrailingIcon to showDefaultTrailingIcon in that case). We can also decide to ignore InputDecoration.suffixIcon if showTrailingIcon is false, it might be easier to document/understand.

Sounds reasonable. Happy with both variants :)

Btw thank you for your efforts!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when suffixIcon is null, most of the users will have to provide their implementation or we have to offer them an API to get the default Icon. It seems to add complexity for a use case which is somewhat specific.

I see, it makes sense to me.

@bleroux bleroux force-pushed the add_DropdownMenu_decorationBuilder branch from 5da230e to 3dac456 Compare October 16, 2025 08:35
Copy link
Contributor

@QuncCccccc QuncCccccc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thank you for adding all the tests:)

@bleroux bleroux force-pushed the add_DropdownMenu_decorationBuilder branch from 3dac456 to 789e4ec Compare October 20, 2025 07:58
@bleroux bleroux force-pushed the add_DropdownMenu_decorationBuilder branch from 789e4ec to dc77ba7 Compare October 20, 2025 08:25
@bleroux bleroux added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 20, 2025
@auto-submit auto-submit bot added this pull request to the merge queue Oct 20, 2025
Merged via the queue into flutter:master with commit 6f743e3 Oct 20, 2025
77 checks passed
@flutter-dashboard flutter-dashboard bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 20, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Oct 20, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Oct 20, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Oct 20, 2025
auto-submit bot pushed a commit to flutter/packages that referenced this pull request Oct 20, 2025
flutter/flutter@891d7d5...2d34167

2025-10-20 vegorov@google.com Cleanup create_updated_flutter_deps.py a bit (flutter/flutter#177162)
2025-10-20 jessy.yameogo@gmail.com Fixed hot reload/restart crashes after closing browser tab on web-server device (flutter/flutter#177026)
2025-10-20 engine-flutter-autoroll@skia.org Roll Skia from 0a3ace6fde82 to ed4294faecde (2 revisions) (flutter/flutter#177249)
2025-10-20 bruno.leroux@gmail.com Add DropdownMenu.decorationBuilder (flutter/flutter#176264)
2025-10-20 engine-flutter-autoroll@skia.org Roll Skia from 05e2f42f533d to 0a3ace6fde82 (1 revision) (flutter/flutter#177242)
2025-10-20 engine-flutter-autoroll@skia.org Roll Skia from 89abc5393317 to 05e2f42f533d (1 revision) (flutter/flutter#177238)
2025-10-20 koji.wakamiya@gmail.com [ios][engine] Fix autofill context cleanup and view lifecycle management (flutter/flutter#173598)
2025-10-20 rajveer0malviya@gmail.com Fix Image.network not using cache when headers are specified (flutter/flutter#176831)
2025-10-19 engine-flutter-autoroll@skia.org Roll Dart SDK from a66f334fee2a to 2cd2106f2cef (4 revisions) (flutter/flutter#177190)
2025-10-19 engine-flutter-autoroll@skia.org Roll Skia from 2d424175a481 to 89abc5393317 (1 revision) (flutter/flutter#177235)
2025-10-19 ahmedsameha1@gmail.com Make sure that a ListTile doesn't crash in 0x0 environment (flutter/flutter#176176)
2025-10-19 ahmedsameha1@gmail.com Make sure that a DropdownButton doesn't crash in 0x0 environment (flutter/flutter#174880)
2025-10-19 engine-flutter-autoroll@skia.org Roll Skia from 899155871d29 to 2d424175a481 (1 revision) (flutter/flutter#177229)
2025-10-19 engine-flutter-autoroll@skia.org Roll Skia from b864c56efb66 to 899155871d29 (1 revision) (flutter/flutter#177227)
2025-10-19 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from M8WT2GMY46e_0fFho... to tKrvmvTOQITL81oOC... (flutter/flutter#177223)
2025-10-19 engine-flutter-autoroll@skia.org Roll Skia from 0992b560454f to b864c56efb66 (1 revision) (flutter/flutter#177222)
2025-10-18 matt.boetger@gmail.com Fix HEIF decoding (flutter/flutter#176860)
2025-10-18 engine-flutter-autoroll@skia.org Roll Skia from 74df18176924 to 0992b560454f (1 revision) (flutter/flutter#177217)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-packages
Please CC louisehsu@google.com,stuartmorgan@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
github-merge-queue bot pushed a commit that referenced this pull request Oct 24, 2025
…ion (#177488)

## Description

This PR adds some documentation to `DropdownMenu.showTrailingIcon` in
relation to `DropdownMenu.decorationBuilder`.

## Context

See
#176264 (comment)

## Tests

Documentation only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

f: material design flutter/packages/flutter/material repository. framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

DropDownMenu secondary trailing widget (e.g. for clear action)

3 participants