0

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 2

1

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'),
                      ),
                    ],
                  ),
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

Sign up to request clarification or add additional context in comments.

4 Comments

I aggree with your answer. I think that the first Expanded is not necessary, it has its own size.
Thanks alot for your answer. But it is not a row with columns I need, it is an dropdown. I´ve tried using your row setup with flexibles as a child of my DropdownMenuItem. But it throws different layout render exceptions I cannot fix. Like 'computeDistanceToActualBaseline' error and "RenderFlex children have non-zero flex but incoming width constraints are unbounded."
@Farsen can you please include an update of how you tried the answer I provided, or else include a complete sample?
Thanks, but I am far from that state now 😅 I have found another solution (see below)
0

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,
    );
  }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.