Repository: flutter
Issue: Decomposing DropdownButton and Autocomplete
Labels: framework,f: material design,c: proposal,P3,team-design,triaged-design

I recently spent some time recreating behaviors that `DropdownButton` and `Autocomplete` already implement. I'd like to share this story to explain why these widgets should probably be decomposed, and also to help demonstrate to the Flutter team what happens when generic capabilities are kept private and over-specified.

## What I was working on
I'm implementing a number of custom form fields based on a design team's specs. Among these specs was an autocomplete text field, and a single-select dropdown field.

The go-to Flutter widget for autocomplete is [Autocomplete](https://api.flutter.dev/flutter/material/Autocomplete-class.html), and the go-to widget for a single-select dropdown field is [DropdownButton](https://api.flutter.dev/flutter/material/DropdownButton-class.html).

## Implementing an autocomplete field
First, I worked on the custom autocomplete field. The `Autocomplete` widget is fairly well decomposed in terms of the properties that it accepts. It allows completely custom widgets for the field and the popup. For some reason, it doesn't take a `FocusNode` or ` TextEditingController`, which I needed to pass in. For that reason, I had to switch to `RawAutocomplete`. The `RawAutocomplete` provided a sufficient level of configuration for my use-cases.

However, to achieve the design spec, I still needed to create the widget that appears as a dropdown list. I didn't notice any obvious existing widgets to serve this purpose. Instead, I ended up copying the implementation for `_AutocompleteOptions` in `material/autocomplete.dart`. Most of that widget is just an accumulation of simple layout and visual details, but there are some parts that most people wouldn't think about, including the use of `AutocompleteHighlightOption.of()` and `Scrollable.ensureVisible()` in a post-frame callback.

Overall, I achieved my goal by using `RawAutocomplete` instead of `Autocomplete`, and by copying the implementation for `_AutocompleteOptions`. The team might consider providing a public widget that makes it easy to assemble a keyboard navigable, autoscrolling list that can be used as a small popup.

## Implementing a dropdown select field
Things got a lot messier when I got to the custom dropdown select field.

Unlike `Autocomplete`, Flutter's `DropdownButton` imposes a very specific UI for the field, itself. `DropdownButton` doesn't accept a builder for the field, but rather accepts properties for things like `icon`, `iconSize`, `isDense`, and `underline`. But my spec doesn't look anything like this particular visual configuration for a dropdown button, so I couldn't use the `DropdownButton` widget at all.

My hope was that `DropdownButton` was a Material Design Component that composed other, generic widgets into a specialized widget. I checked the docs for `DropdownButton` expecting to find a link to other widgets that would make it easy to build my own, but no such references were found.

I jumped into the `DropdownButton` source code, hoping to find other widgets that I could compose for my own purposes. What I found was that pretty much everything `DropdownButton` implemented was custom and private. I couldn't use any other publicly available widgets.

Next, I started copying large chunks of code from `DropdownButton` into my own file. Pretty soon, I had copied the majority of the `DropdownButton` implementation because every private class that I copied, referenced another private class that I had to copy over. There were also private constants that I had to copy over. Before I managed to even get the copied code to compile, I noticed that it seemed significantly longer than the code in `Autocomplete`, and `Autocomplete` accomplishes pretty much the same thing in terms of a dropdown list.

I deleted all the code that I copied from `DropdownButton` and instead started copying code from `Autocomplete`. Eventually, by copying the `Autocomplete` dropdown implementation, I was able to build my own version of a dropdown button field. It ended up taking me about a day to work through this process with `DropdownButton`.

## Conclusion
As I went through this process, I got the distinct impression that Flutter could have broken out additional, generic widgets and tools that could have saved most/all of this time investment.

There are clearly some concepts involved with `Autocomplete` and `DropdownButton` that are completely generic:
 * Anchored positioning and sizing
 * Keyboard focus traversal and item selection
 * List autoscrolling
 * Popup routes

Additionally, comparing `Autocomplete` with `DropdownButton`, it seems like it would make a lot of sense to have a version of `DropdownButton` that accepts builders like `Autocomplete`. `Autocomplete` was far more useful for me as someone who was implementing a fully custom form field, as opposed to `DropdownButton` which I completely ruled out immediately.

My guess is that whoever worked on `DropdownButton` took the Material Design Component spec and focused heavily on directly implementing that spec. What the `DropdownButton` implementation shows us is that there were clearly some generic behaviors for which Flutter had no public solution. Had development focused on identifying and building those generic tools, first, instead of jumping straight to the MDC implementation, then not only would `DropdownButton` still exist, but so would a number of tools with utility far beyond this one component, or even Material Design at large.