7

Im trying to implement a DropdownButton that can separate specific items from each other with a title (see desired output on image below, the titles are in green)

The green title separates the items of the dropdown

Like in the example I would like to add these green titles to my existing DropdownButton, but did not find anything that showed me how to add something else than items to a DropdownButton

I also tried to switch from DropdownButton to a ListView with ExpansionTile but I would like to keep the behaviour of the DropdownButton...

Is it possible to add these titles to the DropdownButton items? Or shourd I try to achive it by a other way? Im stuck at the moment and dont know how to proceed

1
  • 1
    I believe there is no Widget that does exactly what you are looking for. The currently available Flutter Widgets were created for Mobile devices and this kind of title inside a DropDown is something that takes a lot of space and tends to have difficult usability. I haven't found any packages available to reproduce this either. You will most likely have to build it yourself. Commented Dec 18, 2019 at 14:49

3 Answers 3

4

try this code in dartpad:

 import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      
      home: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return HomeScreen();
  }
}

class HomeScreen extends StatefulWidget {
  @override
  HomeScreenState createState() => HomeScreenState();
}

class HomeScreenState extends State<HomeScreen> {
  String dropdownValue;
  List<Product> products = [
    Product(name: 'sep1', type: 'sep'),
    Product(name: 'milk', type: 'data'),
    Product(name: 'oil', type: 'data'),
    Product(name: 'sep2', type: 'sep'),
    Product(name: 'suger', type: 'data'),
    Product(name: 'salt', type: 'data'),
    Product(name: 'sep3', type: 'sep'),
    Product(name: 'potatoe', type: 'data'),
    Product(name: 'tomatoe', type: 'data'),
    Product(name: 'apple', type: 'data'),
  ];

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('text')),
      body: Column(
        children: [
          Text('test'),
          Expanded(
            child: DropdownButton<String>(
              value: dropdownValue,
              items: products.map((value) {
                return DropdownMenuItem(
                  value: value.name,
                  child: value.type == 'data'
                      ? Text(value.name)
                      : Divider(
                          color: Colors.red,
                          thickness: 3,
                        ),
                );
              }).toList(),
              onChanged: (newValue) {
                
                setState(() {
                  
                    dropdownValue = newValue;
                  
                });
                print('$newValue $dropdownValue');
                
              },
            ),
          ),
        ],
      ),
    );
  }
}

class Product {
  String name;
  String type;

  Product({this.name, this.type});
}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your effort! Nice workaround but a integrated seperator for dropdown items would be very nice to have! But as it is one year since I asked, I suggest this would be the best way to implement dropdown seperators... I'll mark as answer
i hope it help you
Flutter 2.5 adds enabled parameter to DropdownMenuItem. Use it to disable your separators so they are not tappable.
2

In addition to Ali Tenni's answer you can also subclass DropdownMenuItem like this:

class DropdownMenuItemSeparator<T> extends DropdownMenuItem<T> {
  DropdownMenuItemSeparator() : super(
    enabled: false,     // As of Flutter 2.5.
    child: Container(), // Trick the assertion.
  ); 

  @override
  Widget build(BuildContext context) {
    return Divider(thickness: 3);
  }
}

This will make the separator more narrow because otherwise the default build() adds some min height.

UPD 2021-09-14 Below is no longer relevant as the feature is implemented in Flutter 2.5. The code above updated accordingly.

This solves one of the two problems. Unfortunately, the second and the larger problem is that the separator is still tappable, and the tap closes the dropdown. To address this, I submitted a feature request to Flutter: https://github.com/flutter/flutter/issues/75487

3 Comments

Thanks for your answer & for filling the feature request to the Flutter team! It hope they thought about this and add a solution for making sub menus in dropdowns, which arent selectable hehe ^^
how to use it and how about DropdownMenuItem variables like onTap, child etc?
i think for separator menu item to be more narrow, DropdownButton must have itemHeight: null,
0

This is my code from a closed question: How to create a dropdown menu with sections?

The y position of the accepted anwser change. I don't understand why it's an accepted anwser.

import 'package:flutter/material.dart';

class CustomDropDownMenu extends StatefulWidget {
  final Widget child;

  const CustomDropDownMenu({Key? key, required this.child}) : super(key: key);

  @override
  _CustomDropDownMenu createState() => _CustomDropDownMenu();
}

class _CustomDropDownMenu extends State<CustomDropDownMenu>
    with SingleTickerProviderStateMixin {
  OverlayEntry? overlayEntry;

  late AnimationController _animationController;

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 500));
  }

  @override
  void dispose() {
    _animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MouseRegion(
      onExit: (onExit) {
        setOnExitMenu(context, onExit.delta.dy);
      },
      child: GestureDetector(
          onTap: () {
            insertOverlay(context);
            _animationController.forward();
          },
          child: widget.child),
    );
  }

  void setOnExitMenu(BuildContext context, double delta) {
    if (delta >= 0) {
      return;
    }
    closeDropDown();
  }

  void insertOverlay(BuildContext context) {
    closeDropDown();
    final rect = findParamsData(context);
    overlayEntry = _createOverlay(rect: rect);
    Overlay.of(context)!.insert(overlayEntry!);
  }

  OverlayEntry _createOverlay({required Rect rect}) {
    return OverlayEntry(builder: (context) {
      return Material(
        color: Colors.transparent,
        child: Stack(
          alignment: Alignment.center,
          clipBehavior: Clip.none,
          children: [
            Positioned(
              child: IgnorePointer(
                child: Container(
                  color: Colors.black45,
                  width: MediaQuery.of(context).size.width,
                  height: MediaQuery.of(context).size.height,
                ),
              ),
            ),
            Positioned.fromRect(
                rect: Rect.fromLTWH(
                    rect.left, rect.top, rect.width * 3, rect.height * 10),
                child: SlideTransition(
                    position: Tween(
                            begin: const Offset(0, 0.15),
                            end: const Offset(0, 0))
                        .animate(_animationController),
                    child: MouseRegion(
                      onExit: (onExit) {
                        setOnExitMenu(context, -1);
                      },
                      child: const ContentDropDown(),
                    )
                )),
          ],
        ),
      );
    });
  }

  Rect findParamsData(BuildContext context) {
    final RenderBox renderBox = context.findRenderObject()! as RenderBox;

    final size = renderBox.size;
    final offset = renderBox.localToGlobal(Offset.zero);

    return Rect.fromLTWH(
        offset.dx, offset.dy + size.height, size.width, size.height);
  }

  void closeDropDown() {
    if (overlayEntry != null) {
      overlayEntry!.remove();
      overlayEntry = null;
    }
  }
}

class ContentDropDown extends StatelessWidget {
  const ContentDropDown({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
          color: Colors.white,
          border: Border.all(
              color: Theme.of(context).colorScheme.secondary,
              width: 0.5)),
      child: ListView(
        shrinkWrap: true,
        children: [
          TextFormField(),
          ListView.builder(
              shrinkWrap: true,
              itemCount: 10,
              itemBuilder: (context, index) {
                return ListTile(
                  leading: Image.network(
                      "https://upload.wikimedia.org/wikipedia/nap/thumb/f/f3/Logo_UEFA_Champions_League.png/250px-Logo_UEFA_Champions_League.png"),
                  title: const Text("Champions League"),
                );
              }),
        ],
      ),
    );
  }
}

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.