skip to Main Content

I need a custom dropdown that when opened shows an image in a column, and in the second column three lines of text. When an item is selected, the image and only the first row of text should be visible.
The three lines of text is dynamic and can be long, so I need to handle overflowing.
I cannot get it to work. This is how it currently looks.

Opened:

enter image description here

Closed:

enter image description here

As one can see, the opened state has overflow problems. The closed state has overflow problems and the other two lines are shown.

This is the current component code:

class BDImageDropdownButton extends StatelessWidget {
  final bool disabled;
  final String? labelText;
  final IconData? icon;
  final String? Function(dynamic)? validator;
  final FormFieldSetter<dynamic>? onSaved;
  final List<DropdownMenuItem<dynamic>> items;
  final dynamic value;
  final void Function(dynamic value)? onChanged;

  const BDImageDropdownButton({required this.labelText, this.icon, this.disabled = false, this.onChanged, this.validator, this.onSaved, required this.items, this.value, Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    items.add(
      DropdownMenuItem(
        value: 1,
        child: SizedBox(
          height: 100.0,
          child: Row(
            mainAxisAlignment: MainAxisAlignment.start,
            children: <Widget>[
              Image.asset('assets/radiatorTypes/testImage.png'),
              Padding(
                padding: const EdgeInsets.only(left: 10),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium',
                      style: Theme.of(context).textTheme.headline6?.copyWith(fontWeight: FontWeight.bold),
                      overflow: TextOverflow.fade,
                      softWrap: false,
                    ),
                    SizedBox(height: 5),
                    Text(
                      'odit aut fugit, sed quia consequuntur magni dolores eos qui ratione',
                      overflow: TextOverflow.fade,
                      softWrap: false,
                    ),
                    SizedBox(height: 5),
                    Text('iquid ex ea commodi consequatur? Quis autem vel eum'),
                  ],
                ),
              )
            ],
          ),
        ),
      ),
    );
   
    return DropdownButtonFormField<dynamic>(
      validator: validator,
      decoration: InputDecoration(
        filled: true,
        prefixIcon: icon != null ? Icon(icon) : null,
        labelText: labelText,
        labelStyle: TextStyle(color: disabled ? Colors.grey : Color(0xFFAFAFAF)),
        contentPadding: EdgeInsets.only(left: 12, top: 9, bottom: 6, right: 12),
        errorBorder: UnderlineInputBorder(
          borderSide: BorderSide(
            color: Color(0xffba0000),
            width: 3,
          ),
        ),
      ),
      items: disabled ? [] : items,
      onChanged: (value) => {
        if (onChanged != null) {onChanged!(value)}
      },
      onSaved: onSaved,
      value: value,
    );
  }
}

I´ve googled alot, and tried many different things (isExpanded, itemHeight etc.), but nothing seems to fit this situation.
Regarding the need for only showing the first line I could work it out with some variable that keeps track of open/closed state and then use it for showing/hiding the other two lines. But I guess there is a cleaner solution for this?

Thanks!

2

Answers


  1. Chosen as BEST ANSWER

    After fiddling around with Rows, columns, flexibles and containers for some time, I found this solution.

    This allows text overflow in multiple columns in the dropdown when its opened. When the dropdown is closed I use the selectedItemBuilder to create a "custom" representation of the selected Item. The only problem here is that I have to set a width on the container of the row (MediaQuery.of(context).size.width * 0.85) - else I get constrain errors because of the row. I have not managed to find another solution to this unfortunately - so suggestions to this problem is very welcome!

    enter image description here

    enter image description here

    enter image description here

    (In the screenshots I have two items in the list, but not in the code sample for the sake of saving space)

    Code: (static code and values, shall of course be dynamic)

    Widget build(BuildContext context) {
        ValueItem item = ValueItem(
            title: 'Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque lau ',
            subTitle1: 'commodo consequat. Duis aute irure dolor ',
            subTitle2: 'fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proi',
            imagePath: 'assets/radiatorTypes/test.png');
    
        items.add(
          DropdownMenuItem(
            value: item,
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                Image.asset(
                  item.imagePath,
                  width: 100,
                ),
                Flexible(
                  child: Padding(
                    padding: const EdgeInsets.only(
                      left: 10,
                    ),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          item.title,
                          style: Theme.of(context).textTheme.headline6?.copyWith(fontWeight: FontWeight.bold),
                          overflow: TextOverflow.fade,
                          softWrap: false,
                        ),
                        SizedBox(height: 5),
                        Text(
                          item.subTitle1,
                          overflow: TextOverflow.fade,
                          softWrap: false,
                        ),
                        SizedBox(height: 5),
                        Text(
                          item.subTitle2,
                          overflow: TextOverflow.fade,
                          softWrap: false,
                        ),
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ),
        );
    
        return DropdownButtonFormField<dynamic>(
          selectedItemBuilder: (context) => items.map((item) {
            var selectedItem = item.value as ValueItem;
            return SizedBox(
              width: MediaQuery.of(context).size.width * 0.85,
              child: Row(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Image.asset(
                    selectedItem.imagePath,
                  ),
                  Flexible(
                    child: Padding(
                      padding: const EdgeInsets.only(
                        left: 10,
                      ),
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.start,
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            selectedItem.title,
                            style: Theme.of(context).textTheme.headline6?.copyWith(fontWeight: FontWeight.bold),
                            overflow: TextOverflow.fade,
                            softWrap: false,
                          ),
                        ],
                      ),
                    ),
                  ),
                ],
              ),
            );
          }).toList(),
          validator: validator,
          decoration: InputDecoration(
            filled: true,
            prefixIcon: icon != null ? Icon(icon) : null,
            labelText: labelText,
            labelStyle: TextStyle(color: disabled ? Colors.grey : Color(0xFFAFAFAF)),
            contentPadding: EdgeInsets.only(left: 12, top: 9, bottom: 6, right: 12),
            errorBorder: UnderlineInputBorder(
              borderSide: BorderSide(
                color: Color(0xffba0000),
                width: 3,
              ),
            ),
          ),
          items: disabled ? [] : items,
          onChanged: (value) => {
            if (onChanged != null) {onChanged!(value)}
          },
          onSaved: onSaved,
          value: value,
        );
      }
    

  2. To solve overflowing text problem you need to use SingleChildScrollView, to fit two widgets horizontally within the screen space inside a Row, you need to wrap the row’s children with Expanded widgets, this is full example (change the values of the flex parameter as you see fit), and I used your code in the example, just made few changes to the values of the text widgets, you can copy it again and put it inside any other widget (in your case put it in the drop down button):

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            body: Container(
              color: Colors.red,
              height: 200,
              child: Row(
                mainAxisAlignment: MainAxisAlignment.start,
                children: <Widget>[
                  Expanded(
                    flex: 20,
                    child: FlutterLogo(size: 300),
                  ),
                  Expanded(
                    flex: 80,
                    child: Padding(
                      padding: const EdgeInsets.only(left: 10),
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          SingleChildScrollView(
                            scrollDirection: Axis.horizontal,
                            child: Text(
                              'line 1111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111',
                              style: TextStyle(
                                  fontWeight: FontWeight.bold, fontSize: 16),
                              overflow: TextOverflow.fade,
                              softWrap: false,
                            ),
                          ),
                          SizedBox(height: 5),
                          SingleChildScrollView(
                            scrollDirection: Axis.horizontal,
                            child: Text(
                              'line 22222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222',
                              overflow: TextOverflow.fade,
                              softWrap: false,
                            ),
                          ),
                          SizedBox(height: 5),
                          SingleChildScrollView(
                            scrollDirection: Axis.horizontal,
                            child: Text(
                                'line 333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333'),
                          ),
                        ],
                      ),
                    ),
                  )
                ],
              ),
            ),
          ),
        );
      }
    }
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search