Flutter Dropdown Different Selected Item Style than Items

1.1k Views Asked by At

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: flag only, dropdown closed What I want: This for opened dropdown: flag and text, dropdown opened

Unwanted: flag only, dropdown opened Unwanted: flag and text, dropdown closed

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.

1

There are 1 best solutions below

0
Clevino Alrin On BEST ANSWER

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.