I am developping an UI for the user to change the app language.
When showing the currently selected language I want to only display the flag with a dropdown icon. When the user clicks on the dropdown button I want the full string representation of the language to be added.
I can't seem to find a way to make the the selected item to look different than the menu items.
What I want: This for unopened dropdown: What I want: This for opened dropdown:
I only get text for both or just flag for both, but never as desired.
The Code so far:
DropdownButton(
items: LanguageUtils.getSupportedLanguagesAsStringList()
.map<DropdownMenuItem<String>>((String langCodeString) {
return DropdownMenuItem<String>(
value: langCodeString,
child: buildLanguageRowForDropDownButton(
langCode:
LanguageUtils.getLangCodeFromItsStringRep(langCodeString)),
);
}).toList(),
value: LanguageUtils.getStringRepForLangCode(_chosenLangCode),
onChanged: (String? value) {
changeLanguage();
},
);
/// Build row to be displayed in DropDownMenu.
Row buildLanguageRowForDropDownButton({LangCode? langCode}) {
// Use currently _chosenLangCode as default.
langCode = langCode ?? _chosenLangCode;
// Build row from settings.
List<Widget> rowElements = [];
if (showFlag) {
rowElements.add(getImageOfLangCode(langCode: langCode));
}
if (showWrittenLanguage) {
rowElements
.add(Text(LanguageUtils.getFullNameForLangCode(langCode)));
}
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: rowElements,
);
}
// LanguageUtils
/// Functions around setting app translation language.
class LanguageUtils {
LanguageUtils() {}
/// Get written rep of language to display to user.
static String getFullNameForLangCode(LangCode langCodeToGetStringFor) {
switch (langCodeToGetStringFor) {
case LangCode.en:
return "English";
break;
case LangCode.de:
return "Deutsch";
break;
}
}
/// Get short rep of language code to use programmatically when exporting state.
static String getStringRepForLangCode(LangCode langCodeToGetStringFor) {
switch (langCodeToGetStringFor) {
case LangCode.en:
return "en";
break;
case LangCode.de:
return "de";
break;
}
}
/// Get all supported LangCodes as string rep to create dropdown menu from.
static List<String> getSupportedLanguagesAsStringList() {
return LangCode.values.map((e) => e.name).toList();
}
/// To return LangCode from its converted rep.
///
/// Defaults to en => Failsafe, always returns valid LangCode.
static LangCode getLangCodeFromItsStringRep(String langCodeAsString) {
switch (langCodeAsString) {
case "de":
return LangCode.de;
break;
case "en":
default:
return LangCode.en;
break;
}
}
}
/// Supported Languages.
enum LangCode { en, de }
I looked at flutter dev, searched stackoverflow, googled and attempted to change things on my own.
Looks like you need to use the selectedItemBuilder
of DropdownButton
.
Here's an example you can try and plug in your data to get the desired output:
import 'package:flutter/material.dart';
const List<String> list = <String>['One ', 'Two ', 'Three', 'Four'];
void main() => runApp(const DropdownButtonApp());
class DropdownButtonApp extends StatelessWidget {
const DropdownButtonApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('DropdownButton Sample')),
body: const Center(
child: DropdownButtonExample(),
),
),
);
}
}
class DropdownButtonExample extends StatefulWidget {
const DropdownButtonExample({super.key});
@override
State<DropdownButtonExample> createState() => _DropdownButtonExampleState();
}
class _DropdownButtonExampleState extends State<DropdownButtonExample> {
String dropdownValue = list.first;
@override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropdownValue,
//icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(color: Colors.deepPurple),
underline: Container(
height: 2,
color: Colors.deepPurpleAccent,
),
onChanged: (String? value) {
// This is called when the user selects an item.
setState(() {
dropdownValue = value!;
});
},
selectedItemBuilder: (context) =>
List.generate(1,
(i) =>
const SizedBox(
width: 50,
child: Icon(Icons.flag)
)
),
items: list.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: SizedBox(
width: 100,
child: Row(
mainAxisSize: MainAxisSize.min,
children: [const Icon(Icons.flag), Text(value)],
),
),
);
}).toList(),
);
}
}
Note: SizedBox is important so it doesn't throw viewport errors.