androidflutteruser-experience

Flutter: I want to show dropdown list under dropdown flutter


I want to create dropdown which look like I mention but I am not able to achieve my aspected results

I try to use render box for make custom dropdown but it want feel like actual dropdown

Can anyone help me to get this type of result

I want Result like this:-

enter image description here

My curent ui look like this:-

enter image description here

Here is my code:-


class AppDropDown extends StatefulWidget {
  AppDropDown({
    Key? key,
    required this.dropDownList,
    required this.selected,
    this.text = "",
  }) : super(key: key);
  final List<String> dropDownList;
  String selected;
  final String text;

  @override
  State<PerytonDropDown> createState() => _PerytonDropDownState();
}

class _PerytonDropDownState extends State<PerytonDropDown> {
  @override
  Widget build(BuildContext context) {
    return Card(
      color: Colors.white,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(15.sp),
      ),
      child: Material(
        borderRadius: BorderRadius.circular(15.sp),
        clipBehavior: Clip.antiAlias,
        color: Colors.transparent,
        child: Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            if (widget.text.isNotEmpty) (8.0).addHSpace(),
            if (widget.text.isNotEmpty) "${widget.text}".grayText(),
            SizedBox(
              height: 40,
              child: DropdownButton<String>(
                isExpanded: true,
                value: widget.selected,
                underline: SizedBox(),
                onChanged: (String? value) {
                  print(value);
                  setState(() {
                    widget.selected = value!;
                  });
                },
                alignment: Alignment.bottomRight,
                borderRadius: BorderRadius.circular(15),
                items: widget.dropDownList
                    .map((item) => DropdownMenuItem(
                          child: Text(
                            item,
                          ),
                          value: item,
                        ))
                    .toList(),
              ),
            )
          ],
        ).pSymmetricOnly(horizontal: 10),
      ),
    );
  }
}


Solution

  • I have made a widget now play with this code.

    This is dropdown widget

    class CustDropDown<T> extends StatefulWidget {
      final List<CustDropdownMenuItem> items;
      final Function onChanged;
      final String hintText;
      final double borderRadius;
      final double maxListHeight;
      final double borderWidth;
      final int defaultSelectedIndex;
      final bool enabled;
    
      const CustDropDown(
          {required this.items,
          required this.onChanged,
          this.hintText = "",
          this.borderRadius = 0,
          this.borderWidth = 1,
          this.maxListHeight = 100,
          this.defaultSelectedIndex = -1,
          Key? key,
          this.enabled = true})
          : super(key: key);
    
      @override
      _CustDropDownState createState() => _CustDropDownState();
    }
    
    class _CustDropDownState extends State<CustDropDown>
        with WidgetsBindingObserver {
      bool _isOpen = false, _isAnyItemSelected = false, _isReverse = false;
      late OverlayEntry _overlayEntry;
      late RenderBox? _renderBox;
      Widget? _itemSelected;
      late Offset dropDownOffset;
      final LayerLink _layerLink = LayerLink();
    
      @override
      void initState() {
        WidgetsBinding.instance!.addPostFrameCallback((_) {
          if (mounted) {
            setState(() {
              dropDownOffset = getOffset();
            });
          }
          if (widget.defaultSelectedIndex > -1) {
            if (widget.defaultSelectedIndex < widget.items.length) {
              if (mounted) {
                setState(() {
                  _isAnyItemSelected = true;
                  _itemSelected = widget.items[widget.defaultSelectedIndex];
                  widget.onChanged(widget.items[widget.defaultSelectedIndex].value);
                });
              }
            }
          }
        });
        WidgetsBinding.instance!.addObserver(this);
        super.initState();
      }
    
      void _addOverlay() {
        if (mounted) {
          setState(() {
            _isOpen = true;
          });
        }
    
        _overlayEntry = _createOverlayEntry();
        Overlay.of(context)!.insert(_overlayEntry);
      }
    
      void _removeOverlay() {
        if (mounted) {
          setState(() {
            _isOpen = false;
          });
          _overlayEntry.remove();
        }
      }
    
      @override
      dispose() {
        WidgetsBinding.instance!.removeObserver(this);
        super.dispose();
      }
    
      OverlayEntry _createOverlayEntry() {
        _renderBox = context.findRenderObject() as RenderBox?;
    
        var size = _renderBox!.size;
    
        dropDownOffset = getOffset();
    
        return OverlayEntry(
            maintainState: false,
            builder: (context) => Align(
                  alignment: Alignment.center,
                  child: CompositedTransformFollower(
                    link: _layerLink,
                    showWhenUnlinked: false,
                    offset: dropDownOffset,
                    child: SizedBox(
                      height: widget.maxListHeight,
                      width: size.width,
                      child: Column(
                        mainAxisSize: MainAxisSize.max,
                        mainAxisAlignment: _isReverse
                            ? MainAxisAlignment.end
                            : MainAxisAlignment.start,
                        children: <Widget>[
                          Padding(
                            padding: const EdgeInsets.only(top: 10),
                            child: Container(
                              constraints: BoxConstraints(
                                  maxHeight: widget.maxListHeight,
                                  maxWidth: size.width),
                              decoration: BoxDecoration(
                                  color: Colors.white,
                                  borderRadius: BorderRadius.circular(12)),
                              child: ClipRRect(
                                borderRadius: BorderRadius.all(
                                  Radius.circular(widget.borderRadius),
                                ),
                                child: Material(
                                  elevation: 0,
                                  shadowColor: Colors.grey,
                                  child: ListView(
                                    padding: EdgeInsets.zero,
                                    shrinkWrap: true,
                                    children: widget.items
                                        .map((item) => GestureDetector(
                                              child: Padding(
                                                padding: const EdgeInsets.all(8.0),
                                                child: item.child,
                                              ),
                                              onTap: () {
                                                if (mounted) {
                                                  setState(() {
                                                    _isAnyItemSelected = true;
                                                    _itemSelected = item.child;
                                                    _removeOverlay();
                                                    if (widget.onChanged != null)
                                                      widget.onChanged(item.value);
                                                  });
                                                }
                                              },
                                            ))
                                        .toList(),
                                  ),
                                ),
                              ),
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                ));
      }
    
      Offset getOffset() {
        RenderBox? renderBox = context.findRenderObject() as RenderBox?;
        double y = renderBox!.localToGlobal(Offset.zero).dy;
        double spaceAvailable = _getAvailableSpace(y + renderBox.size.height);
        if (spaceAvailable > widget.maxListHeight) {
          _isReverse = false;
          return Offset(0, renderBox.size.height);
        } else {
          _isReverse = true;
          return Offset(
              0,
              renderBox.size.height -
                  (widget.maxListHeight + renderBox.size.height));
        }
      }
    
      double _getAvailableSpace(double offsetY) {
        double safePaddingTop = MediaQuery.of(context).padding.top;
        double safePaddingBottom = MediaQuery.of(context).padding.bottom;
    
        double screenHeight =
            MediaQuery.of(context).size.height - safePaddingBottom - safePaddingTop;
    
        return screenHeight - offsetY;
      }
    
      @override
      Widget build(BuildContext context) {
        return CompositedTransformTarget(
          link: _layerLink,
          child: GestureDetector(
            onTap: widget.enabled
                ? () {
                    _isOpen ? _removeOverlay() : _addOverlay();
                  }
                : null,
            child: Container(
              decoration: _getDecoration(),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: <Widget>[
                  Flexible(
                    flex: 3,
                    child: _isAnyItemSelected
                        ? Padding(
                            padding: const EdgeInsets.only(left: 4.0),
                            child: _itemSelected!,
                          )
                        : Padding(
                            padding:
                                const EdgeInsets.only(left: 4.0), // change it here
                            child: Text(
                              widget.hintText,
                              maxLines: 1,
                              overflow: TextOverflow.clip,
                            ),
                          ),
                  ),
                  const Flexible(
                    flex: 1,
                    child: Icon(
                      Icons.arrow_drop_down,
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      }
    
      Decoration? _getDecoration() {
        if (_isOpen && !_isReverse) {
          return BoxDecoration(
              borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(widget.borderRadius),
                  topRight: Radius.circular(
                    widget.borderRadius,
                  )));
        } else if (_isOpen && _isReverse) {
          return BoxDecoration(
              borderRadius: BorderRadius.only(
                  bottomLeft: Radius.circular(widget.borderRadius),
                  bottomRight: Radius.circular(
                    widget.borderRadius,
                  )));
        } else if (!_isOpen) {
          return BoxDecoration(
              borderRadius: BorderRadius.all(Radius.circular(widget.borderRadius)));
        }
      }
    }
    
    class CustDropdownMenuItem<T> extends StatelessWidget {
      final T value;
      final Widget child;
    
      const CustDropdownMenuItem({required this.value, required this.child});
    
      @override
      Widget build(BuildContext context) {
        return child;
      }
    }
    

    Using dropdown in ui part

    class DropDownTest extends StatelessWidget {
      const DropDownTest({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          backgroundColor: const Color(0xFFF2F3F7),
          body: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Center(
                child: Container(
                  width: 200,
                  height: 40,
                  decoration: BoxDecoration(
                      color: Colors.white, borderRadius: BorderRadius.circular(12)),
                  child: CustDropDown(
                    items: const [
                      CustDropdownMenuItem(
                        value: 0,
                        child: Text("Day"),
                      ),
                      CustDropdownMenuItem(
                        value: 0,
                        child: Text("Week"),
                      )
                    ],
                    hintText: "DropDown",
                    borderRadius: 5,
                    onChanged: (val) {
                      print(val);
                    },
                  ),
                ),
              )
            ],
          ),
        );
      }
    }
    

    enter image description here