如何在Flutter中自定义DropdownButtons和DropdownMenuItems?

63
默认的DropdownButton和DropdownMenuItems会返回一个浅灰色的下拉菜单。我该如何自定义下拉菜单(例如背景颜色、下拉宽度)?我可以在DropdownButton和DropdownMenuItem中都改变style属性,就像这样:
return new DropdownButton(
      value: ...,
      items: ...,
      onChanged: ...,
      style: new TextStyle(
        color: Colors.white,
      ),
    );

但这不会改变下拉菜单的背景颜色。

我应该复制DropdownMenu并进行扩展吗?Flutter是否计划在不久的将来为此小部件添加自定义功能?

11个回答

90

您可以通过将 DropdownButton 包装在 Theme widget 中,并覆盖 canvasColor 来实现此目的。

screenshot

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  State createState() => new MyHomePageState();
}

class MyHomePageState extends State<MyHomePage> {
  int _value = 42;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Center(
        child: new Theme(
          data: Theme.of(context).copyWith(
            canvasColor: Colors.blue.shade200,
          ),
          child: new DropdownButton(
            value: _value,
            items: <DropdownMenuItem<int>>[
              new DropdownMenuItem(
                child: new Text('Foo'),
                value: 0,
              ),
              new DropdownMenuItem(
                child: new Text('Bar'),
                value: 42,
              ),
            ],
            onChanged: (int value) {
              setState(() {
                _value = value;
              });
            },
          ),
        ),
      ),
    );
  }
}

1
谢谢。在找到这个答案之前,我一直卡在这个问题上! - Deborah
1
感谢这个解决方法。看起来这是一个长期存在的问题:https://github.com/flutter/flutter/issues/17414 - Wak
1
你也可以在主题中使用data: ThemeData.dark(),这将让你拥有白色/黑色下拉菜单或者使用data: ThemeData.light() - Zuriel
@Zuriel:你甚至不需要这样做。你可以在MaterialApp中更改深色或浅色主题。 - Thomas Weller

66

通过将下拉列表包装在设置了color属性的Container中,我能够改变其背景。

之前:

enter image description here

之后:

enter image description here

以下是代码:

在小部件状态中定义这些值:

final items = ['One', 'Two', 'Three', 'Four'];
String selectedValue = 'Four';

然后使用这段代码

Container(
  padding: EdgeInsets.symmetric(horizontal: 10, vertical: 5),
  decoration: BoxDecoration(
      color: Colors.white, borderRadius: BorderRadius.circular(10)),

  // dropdown below..
  child: DropdownButton<String>(
      value: selectedValue,
      onChanged: (String newValue) =>
        setState(() => selectedValue = newValue),
      items: items
          .map<DropdownMenuItem<String>>(
              (String value) => DropdownMenuItem<String>(
                    value: value,
                    child: Text(value),
                  ))
          .toList(),

      // add extra sugar..
      icon: Icon(Icons.arrow_drop_down),
      iconSize: 42,
      underline: SizedBox(),
  ),
);

2
这是我正在寻找的最佳示例。 - Rahul Kushwaha
1
这几乎拯救了我的一天,但在DropdownButtonFormField中,它也覆盖了验证部分,而不仅仅是字段。 - Antonycx
1
这是我能找到的唯一真正的解决方案。首先,我找到了这个:https://stackoverflow.com/questions/66135853/how-to-create-a-rounded-corner-of-dropdownbuttonformfield-flutter/66136773,但这比那1500行代码要优雅得多。将其包装在容器中,我可以在按钮和下拉菜单列表上获得圆角边缘。它肯定有些笨重,因为我正在模拟本应该原生支持的功能,但已足够。谢谢! - MetaStack
需要定义变量 - dropdownValue 吗? - CrazyMind
1
@CrazyMind 如果你不定义它,当选择一个项目时,下拉菜单不会改变。 - FloatingRock

38

正如Collin所说,你的DropdownMenuItem将遵循你的ThemeData类。它不仅会与你的ThemeData类中的canvasColor相匹配,而且还会遵循相同的TextStyle

因此,以下是一个快速的示例:

new ThemeData(
        fontFamily: "Encode Sans", //my custom font
        canvasColor: _turquoise, //my custom color
//other theme data)
此外,如果您想控制菜单的宽度,可以为其child属性提供新的Container并添加所需的width。请查看以下GIF,我从width: 100.0开始,然后在更改为width: 200.0后进行了热重载,注意如何操作宽度。只需确保使用适当的宽度,以便在稍后将菜单用于更复杂的布局时不会出现溢出问题。 enter image description here
class TestPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title:new Text ("Test"),
      ),
      body: new Center(
        child: new DropdownButton(items: new List.generate(20, (int index){
          return new DropdownMenuItem(child: new Container(
            child: new Text ("Item#$index"),
            width: 200.0, //200.0 to 100.0
          ));
        })
            , onChanged: null)
      ),
    );
  }
}

你知道是否有一种方法可以覆盖每个DropdownMenuItem的填充(左和右)吗?ThemeData没有padding属性。我尝试将我的DropdownMenuItem的填充设置为负数,但是不被允许。谢谢! - Mary
@Mary ThemeData 是关于视觉效果(颜色,文本样式等)而不是关于定位的。我相信您想要在下拉菜单中居中您的项目,所以您只需要将答案中的Text widget包装在Center widget中即可。然而,如果您需要自定义项目的定位使它们不居中,则需要将Text widget包装在Row中,该Row再被包装在Padding widget中,然后按您的要求自定义padding属性。 - Shady Aziza
你可以将上一个评论中的 Row 替换为 Container,同样可以达到相同的效果。如果有任何不清楚的地方,请告诉我。 - Shady Aziza

16

在最新版的Flutter中,你可以做一些非常简单的事情。

DropdownButton类有一个内置变量叫做“dropdownColor”,可以直接分配任何颜色,而不需要更改任何“ThemeData”。它会自动更改下拉菜单项的颜色。


我有一个大型应用程序,其中有50多个下拉按钮表单字段。所以我不能逐个更改它们。这是非常耗时的工作。我们能否通过添加全局主题数据来解决这个问题?如果可以,请建议如何操作。谢谢。 - undefined

15

使用此选项进行着色

 DropdownButtonFormField(
              items: null,
              onChanged: null,
              dropdownColor: Colors.red,
            ),

我有一个大型应用程序,其中有50多个下拉按钮表单字段。所以我不能一个一个地更改它们。这是非常耗时的工作。我们可以通过添加全局主题数据来完成吗?如果可以,请建议如何操作。谢谢。 - undefined
@Kamlesh 你也可以创建一个可重复使用的自定义下拉菜单。 - undefined
1
非常感谢您的快速回复。是的,我们可以开发可重复使用的自定义下拉菜单,但是我们仍然需要在应用程序中替换超过50个现有的下拉按钮表单字段元素。感谢您的建议 :) - undefined

15
如果您想让DropdownButton填充其所在的空间,请使用属性isExpanded并将其设置为true
DropdownButton<String>(
   isExpanded: true,
)

16
与问题无关。 - Manikandan Selvanathan
我用一个容器包装了下拉按钮,并设置了容器的颜色。 - Golden Lion

4

https://api.flutter.dev/flutter/material/DropdownButton/style.html可以帮助你找到一些样式。

DropdownButton(
  dropdownColor: Colors.grey,
  value: this.repeatType,
  onChanged: (String? value) {
    print(value);
    setState(() {
      this.repeatType = value!;
    });
  },
  selectedItemBuilder: (BuildContext context) {
    return this.repeatTypes.map((String value) {
      return Text(
        this.repeatType,
        style: const TextStyle(color: Colors.white),
      );
    }).toList();
  },
  items: this
      .repeatTypes
      .map((item) => DropdownMenuItem(
            child: Text(
              item,
              style: TextStyle(color: Colors.green),
            ),
            value: item,
          ))
      .toList())

3
你可以像这样使用容器来包装它:

Container(
  margin: const EdgeInsets.all(15.0),
  padding: const EdgeInsets.only(left: 10.0, right: 10.0),
  decoration: BoxDecoration(
      color: Colors.white,
      border: Border.all(color: Colors.white)
  ),
  child: DropdownButton(
    dropdownColor: Colors.white,
    style: TextStyle(
      color: Colors.black,
      backgroundColor: Colors.white,
    ),
    value: 'ar',
    items: [
      DropdownMenuItem(child: Text('English'), value: 'en'),
      DropdownMenuItem(child: Text('العربية'), value: 'ar'),
    ],
  ),
)

输出结果:

在这里输入图片说明


1

现在非常简单,只需在DropdownButton小部件中使用dropdownColor属性,就像这样:

DropdownButton(
    dropdownColor: Colors.red,  // here you change the background color
    value: 'Your value',
    items: [ ],
)

1

你可以使用dropdown_button2包。 你可以使用dropdownDecoration属性自定义下拉菜单的外观。这是我找到的最好的完全自定义DropdownButton的包。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接