Direct Select In Flutter

Direct Select In Flutter :

Screenshot 1 :

Direct Select In Flutter

Screenshot 2 :

Widget Package :

direct_select_container.dart

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:rect_getter/rect_getter.dart';

import 'direct_select_list.dart';

class DirectSelectContainer extends StatefulWidget {
  //Actually content of screen
  final Widget child;

  //How fast list is scrolled
  final int dragSpeedMultiplier;

  const DirectSelectContainer({Key key,
    this.child,
    @Deprecated("Controls are now added from an InheritedWidget.") List<DirectSelectList> controls,
    this.dragSpeedMultiplier = 2})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return DirectSelectContainerState();
  }

  static DirectSelectGestureEventListeners of(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(_InheritedContainerListeners)
    as _InheritedContainerListeners)
        .listeners;
  }
}

class DirectSelectContainerState extends State<DirectSelectContainer>
    with SingleTickerProviderStateMixin
    implements DirectSelectGestureEventListeners {
  bool isOverlayVisible = false;

  ScrollController _scrollController;
  DirectSelectList _currentList =
  DirectSelectList(itemBuilder: (val) => null, values: []);
  double _currentScrollLocation = 0;

  double _adjustedTopOffset = 0.0;

  AnimationController fadeAnimationController;

  int lastSelectedItem = 0;

  double listPadding = 0.0;

  final scrollToListElementAnimationDuration = Duration(milliseconds: 200);
  final fadeAnimationDuration = Duration(milliseconds: 200);

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

    fadeAnimationController = AnimationController(
      duration: fadeAnimationDuration,
      vsync: this,
    );
  }

  @override
  Widget build(BuildContext context) {
    double topOffset = 0.0;
    RenderObject object = context.findRenderObject();
    if (object?.parentData is ContainerBoxParentData) {
      topOffset = (object.parentData as ContainerBoxParentData).offset.dy;
    }

    listPadding = MediaQuery.of(context).size.height;

    _adjustedTopOffset = _currentScrollLocation - topOffset;
    _scrollController = ScrollController(
        initialScrollOffset: listPadding -
            _currentScrollLocation +
            topOffset +
            _currentList.getSelectedItemIndex() * _currentList.itemHeight());

    return Stack(
      children: <Widget>[
        _InheritedContainerListeners(
          listeners: this,
          child: widget.child,
        ),
        Visibility(
            visible: isOverlayVisible,
            child: FadeTransition(
              opacity: fadeAnimationController
                  .drive(CurveTween(curve: Curves.easeOut)),
              child: Column(
                children: <Widget>[
                  Expanded(
                    child: Stack(
                      children: <Widget>[
                        _getListWidget(),
                        _getSelectionOverlayWidget(),
                      ],
                    ),
                  ),
                ],
              ),
            ))
      ],
    );
  }

  Widget _getListWidget() {
    var paddingLeft = 0.0;

    if (_currentList.items.isNotEmpty) {
      Rect rect = RectGetter.getRectFromKey(
          _currentList.paddingItemController.paddingGlobalKey);
      if (rect != null) {
        paddingLeft = rect.left;
      }
    }

    final theme = Theme.of(context);
    return Container(
        color: theme.scaffoldBackgroundColor,
        child: ListView.builder(
          padding: EdgeInsets.only(left: paddingLeft),
          controller: _scrollController,
          itemCount: _currentList.items.length + 2,
          itemBuilder: (BuildContext context, int index) {
            if (index == 0 || index == _currentList.items.length + 1) {
              return Container(height: listPadding);
            }
            final item = _currentList.items[index - 1];
            final normalScale = 1.0;
            if (lastSelectedItem == index - 1) {
              item.updateScale(_calculateNewScale(normalScale));
            } else {
              item.updateScale(normalScale);
            }
            return item;
          },
        ));
  }

  Widget _getSelectionOverlayWidget() {
    return Positioned(
        top: _adjustedTopOffset,
        left: 0,
        right: 0,
        height: _currentList.itemHeight(),
        child: Container(
            height: _currentList.itemHeight(),
            decoration: _currentList.focusedItemDecoration != null
                ? _currentList.focusedItemDecoration
                : BoxDecoration()));
  }

  void performListDrag(double dragDy) {
    try {
      if (_scrollController != null && _scrollController.position != null) {
        final currentScrollOffset = _scrollController.offset;
        double allowedOffset = _allowedDragDistance(
            currentScrollOffset + _adjustedTopOffset,
            dragDy * widget.dragSpeedMultiplier);
        if (allowedOffset != 0.0) {
          _scrollController.jumpTo(currentScrollOffset + allowedOffset);

          final scrollPixels =
              _scrollController.offset - listPadding + _adjustedTopOffset;
          final selectedItemIndex = _getCurrentListElementIndex(scrollPixels);
          lastSelectedItem = selectedItemIndex;

          _performScaleTransformation(scrollPixels, selectedItemIndex);
        }
      }
    } catch (e) {
      print(e);
    }
  }

  double _allowedDragDistance(double currentScrollOffset, double position) {
    double newPosition = currentScrollOffset + position;
    double endOfListPosition =
        (_currentList.items.length - 1) * _currentList.itemHeight() +
            listPadding;
    if (newPosition < listPadding) {
      return listPadding - currentScrollOffset;
    } else if (newPosition > endOfListPosition) {
      return endOfListPosition - currentScrollOffset;
    } else {
      return position;
    }
  }

  void _performScaleTransformation(double scrollPixels, int selectedItemIndex) {
    final neighbourDistance = _getNeighbourListElementDistance(scrollPixels);
    int neighbourIncrementDirection =
    neighbourScrollDirection(neighbourDistance);

    int neighbourIndex = lastSelectedItem + neighbourIncrementDirection;

    double neighbourDistanceToCurrentItem =
    _getNeighbourListElementDistanceToCurrentItem(neighbourDistance);

    if (neighbourIndex < 0 || neighbourIndex > _currentList.items.length - 1) {
      //incorrect neighbour index quit
      return;
    }
    _currentList.items[selectedItemIndex].updateOpacity(1.0);
    _currentList.items[neighbourIndex].updateOpacity(0.5);

    _currentList.items[selectedItemIndex]
        .updateScale(_calculateNewScale(neighbourDistanceToCurrentItem));
    _currentList.items[neighbourIndex]
        .updateScale(_calculateNewScale(neighbourDistance.abs()));
  }

  double _calculateNewScale(double distance) =>
      1.0 + distance / _currentList.items[lastSelectedItem].scaleFactor;

  int neighbourScrollDirection(double neighbourDistance) {
    int neighbourScrollDirection = 0;
    if (neighbourDistance > 0) {
      neighbourScrollDirection = 1;
    } else {
      neighbourScrollDirection = -1;
    }
    return neighbourScrollDirection;
  }

  double _getNeighbourListElementDistanceToCurrentItem(
      double neighbourDistance) {
    double neighbourDistanceToCurrentItem = (1 - neighbourDistance.abs());

    if (neighbourDistanceToCurrentItem > 1 ||
        neighbourDistanceToCurrentItem < 0) {
      neighbourDistanceToCurrentItem = 1.0;
    }
    return neighbourDistanceToCurrentItem;
  }

  int _getCurrentListElementIndex(double scrollPixels) {
    int selectedElement = (scrollPixels / _currentList.itemHeight()).round();
    final maxElementIndex = _currentList.items.length;

    if (selectedElement < 0) {
      selectedElement = 0;
    }
    if (selectedElement >= maxElementIndex) {
      selectedElement = maxElementIndex - 1;
    }
    return selectedElement;
  }

  double _getNeighbourListElementDistance(double scrollPixels) {
    double selectedElementDeviation =
    (scrollPixels / _currentList.itemHeight());
    int selectedElement = _getCurrentListElementIndex(scrollPixels);
    return selectedElementDeviation - selectedElement;
  }

  Future toggleListOverlayVisibility(DirectSelectList visibleList,
      double location) async {
    if (isOverlayVisible) {
      try {
        await _scrollController.animateTo(
          listPadding -
              _adjustedTopOffset +
              lastSelectedItem * _currentList.itemHeight(),
          duration: scrollToListElementAnimationDuration,
          curve: Curves.ease,
        );
      } catch (e) {} finally {
        _currentList.setSelectedItemIndex(lastSelectedItem);
        sleep(const Duration(milliseconds: 200));
        await fadeAnimationController.reverse();
        setState(() {
          _hideListOverlay();
        });
      }
    } else {
      setState(() {
        _showListOverlay(visibleList, location);
      });
    }
  }

  _showListOverlay(DirectSelectList visibleList, double location) async {
    _currentList = visibleList;
    _currentScrollLocation = location;
    lastSelectedItem = _currentList.getSelectedItemIndex();
    _currentList.items[lastSelectedItem].updateOpacity(1.0);
    isOverlayVisible = true;
    await fadeAnimationController.forward(from: 0.0);
  }

  void _hideListOverlay() {
    _scrollController.dispose();
    _scrollController = null;
    _currentList.items[lastSelectedItem].updateScale(1.0);
    _currentScrollLocation = 0;
    _adjustedTopOffset = 0;
    isOverlayVisible = false;
  }
}

class DirectSelectGestureEventListeners {
  toggleListOverlayVisibility(DirectSelectList list, double location) =>
      throw 'Not implemented.';

  performListDrag(double dragDy) => throw 'Not implemented';
}

/// Allows Direct Select List implementations to
class _InheritedContainerListeners extends InheritedWidget {
  final DirectSelectGestureEventListeners listeners;

  _InheritedContainerListeners({
    Key key,
    @required this.listeners,
    @required Widget child,
  }) : super(key: key, child: child);

  @override
  bool updateShouldNotify(_InheritedContainerListeners old) =>
      old.listeners != listeners;
}

direct_select_item.dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:rect_getter/rect_getter.dart';

class DirectSelectItem<T> extends StatefulWidget {
  //Value of item
  final T value;

  //Defines is this item is selected
  final isSelected;

  //height of items in list
  final double itemHeight;

  //initial item scale
  final scale = ValueNotifier<double>(1.0);

  //opacity unselected item
  final opacity = ValueNotifier<double>(0.5);

  //the more value the MORE max scale DECREASES
  final scaleFactor;

  final Widget Function(BuildContext context, T value) itemBuilder;

  DirectSelectItem(
      {Key key,
        this.scaleFactor = 4.0,
      @required this.value,
        @required this.itemBuilder,
      this.itemHeight = 48.0,
      this.isSelected = false})
      : super(key: key);

  @override
  State<StatefulWidget> createState() {
    return DirectSelectItemState<T>(isSelected: isSelected);
  }

  void updateScale(double scale) {
    this.scale.value = scale;
  }

  void updateOpacity(double opacity) {
    this.opacity.value = opacity;
  }

  Widget getSelectedItem(GlobalKey<DirectSelectItemState> animatedStateKey,
      GlobalKey paddingGlobalKey) {
    return RectGetter(
      key: paddingGlobalKey,
      child: DirectSelectItem<T>(
        value: value,
        key: animatedStateKey,
        itemHeight: itemHeight,
        itemBuilder: itemBuilder,
        isSelected: true,
      ),
    );
  }
}

class DirectSelectItemState<T> extends State<DirectSelectItem<T>>
    with SingleTickerProviderStateMixin {
  final bool isSelected;

  AnimationController animationController;
  Animation _animation;
  Tween<double> _tween;

  DirectSelectItemState({this.isSelected = false});

  bool isScaled = false;

  Future runScaleTransition({@required bool reverse}) {
    if (reverse) {
      return animationController.reverse();
    } else {
      return animationController.forward(from: 0.0);
    }
  }

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

    animationController =
        AnimationController(duration: Duration(milliseconds: 150), vsync: this);
    _tween = Tween(begin: 1.0, end: 1 + 1 / widget.scaleFactor);
    _animation = _tween.animate(animationController)
      ..addListener(() {
        setState(() {});
      });
  }

  @override
  Widget build(BuildContext context) {
    if (isSelected) {
      return Row(
        mainAxisSize: MainAxisSize.max,
        children: <Widget>[
          Expanded(
            child: Container(
              color: Colors.transparent,
              height: widget.itemHeight,
              alignment: AlignmentDirectional.centerStart,
              child: Transform.scale(
                  scale: _animation.value,
                  alignment: Alignment.topLeft,
                  child: widget.itemBuilder(context, widget.value)),
            ),
          ),
        ],
      );
    } else {
      return Material(
        child: Container(
          child: ValueListenableBuilder<double>(
            valueListenable: widget.scale,
            builder: (context, value, child) {
              return Opacity(
                opacity: widget.opacity.value,
                child: Container(
                    height: widget.itemHeight,
                    child: Transform.scale(
                        scale: value,
                        alignment: Alignment.topLeft,
                        child: widget.itemBuilder(context, widget.value)),
                    alignment: AlignmentDirectional.centerStart),
              );
            },
          ),
        ),
      );
    }
  }

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

direct_select_list.dart

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:rect_getter/rect_getter.dart';
import 'direct_select_container.dart';
import 'direct_select_item.dart';

typedef DirectSelectItemsBuilder<T> = DirectSelectItem<T> Function(T value);

class PaddingItemController {
  GlobalKey paddingGlobalKey = RectGetter.createGlobalKey();
}

typedef ItemSelected = Future<dynamic> Function(
    DirectSelectList owner, double location);

/// Widget that contains items and responds to user's interaction
/// Usage Example
///
///     final dsl2 = DirectSelectList<String>(
///        values: _numbers,
///        itemBuilder: (String value) => getDropDownMenuItem(value),
///        focusedItemDecoration: _getDslDecoration());
///
class DirectSelectList<T> extends StatefulWidget {
  //Item widgets
  final List<DirectSelectItem<T>> items;

  //Current focused item overlay
  final Decoration focusedItemDecoration;

  //Default selected item index
  final int defaultItemIndex;

  //Notifies state about new item selected
  final ValueNotifier<int> selectedItem;

  //Function to execute when item selected
  final Function(T value, int selectedIndex, BuildContext context)
  onItemSelectedListener;

  final PaddingItemController paddingItemController = PaddingItemController();

  DirectSelectList({
    Key key,
    @required List<T> values,
    @required DirectSelectItemsBuilder<T> itemBuilder,
    this.onItemSelectedListener,
    this.focusedItemDecoration,
    this.defaultItemIndex = 0,
  })
      : items = values.map((val) => itemBuilder(val)).toList(),
        selectedItem = ValueNotifier<int>(defaultItemIndex),
        assert(defaultItemIndex + 1 <= values.length + 1),
        super(key: key);

  @override
  State<StatefulWidget> createState() {
    return DirectSelectState<T>();
  }

  //todo pass item height in this class and build items with that height
  double itemHeight() {
    if (items != null && items.isNotEmpty) {
      return items.first.itemHeight;
    }
    return 0.0;
  }

  int getSelectedItemIndex() {
    if (selectedItem != null) {
      return selectedItem.value;
    } else {
      return 0;
    }
  }

  void setSelectedItemIndex(int index) {
    if (selectedItem != null && index != selectedItem.value) {
      selectedItem.value = index;
    }
  }

  T getSelectedItem() {
    return items[selectedItem.value].value;
  }
}

class DirectSelectState<T> extends State<DirectSelectList<T>> {
  final GlobalKey<DirectSelectItemState> animatedStateKey =
  GlobalKey<DirectSelectItemState>();

  Future Function(DirectSelectList, double) onTapEventListener;
  void Function(double) onDragEventListener;

  bool isOverlayVisible = false;
  int lastSelectedItem;

  bool _isShowUpAnimationRunning = false;

  Map<int, Widget> selectedItemWidgets;

  @override
  void initState() {
    super.initState();
    lastSelectedItem = widget.defaultItemIndex;
    selectedItemWidgets = Map();
    for (int index = 0; index < widget.items.length; index++) {
      selectedItemWidgets.putIfAbsent(
        index,
            () =>
            widget.items[index].getSelectedItem(
              animatedStateKey,
              widget.paddingItemController.paddingGlobalKey,
            ),
      );
    }
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    final dsListener = DirectSelectContainer.of(context);
    assert(dsListener != null,
    "A DirectSelectList must inherit a DirectSelectContainer!");

    this.onTapEventListener = dsListener.toggleListOverlayVisibility;
    this.onDragEventListener = dsListener.performListDrag;
  }

  @override
  Widget build(BuildContext context) {
    widget.selectedItem.addListener(() {
      if (widget.onItemSelectedListener != null) {
        widget.onItemSelectedListener(
            widget.items[widget.selectedItem.value].value,
            widget.selectedItem.value,
            this.context);
      }
    });

    bool transitionEnded = false;

    return ValueListenableBuilder<int>(
        valueListenable: widget.selectedItem,
        builder: (context, value, child) {
          final selectedItem = selectedItemWidgets[value];
          return GestureDetector(
              child: selectedItem,
              onTapDown: (tapDownDetails) async {
                if (!isOverlayVisible) {
                  transitionEnded = false;
                  _isShowUpAnimationRunning = true;
                  await animatedStateKey.currentState
                      .runScaleTransition(reverse: false);
                  if (!transitionEnded) {
                    await _showListOverlay(_getItemTopPosition(context));
                    _isShowUpAnimationRunning = false;
                    lastSelectedItem = value;
                  }
                }
              },
              onTapUp: (tapUpDetails) async {
                await _hideListOverlay(_getItemTopPosition(context));
                animatedStateKey.currentState.runScaleTransition(reverse: true);
              },
              onVerticalDragEnd: (dragDetails) async {
                transitionEnded = true;
                _dragEnd();
              },
              onHorizontalDragEnd: (horizontalDetails) async {
                transitionEnded = true;
                _dragEnd();
              },
              onVerticalDragUpdate: (dragInfo) {
                if (!_isShowUpAnimationRunning) {
                  _showListOverlay(dragInfo.primaryDelta);
                }
              });
        });
  }

  void _dragEnd() async {
    await _hideListOverlay(_getItemTopPosition(context));
    animatedStateKey.currentState.runScaleTransition(reverse: true);
  }

  double _getItemTopPosition(BuildContext context) {
    final RenderBox itemBox = context.findRenderObject();
    final Rect itemRect = itemBox.localToGlobal(Offset.zero) & itemBox.size;
    return itemRect.top;
  }

  _hideListOverlay(double dy) async {
    if (isOverlayVisible) {
      isOverlayVisible = false;
      //fix to prevent stuck scale if selected item is the same as previous
      await onTapEventListener(widget, dy);
      if (lastSelectedItem == widget.selectedItem.value) {
        animatedStateKey.currentState.runScaleTransition(reverse: true);
      }
    }
  }

  @override
  void didUpdateWidget(DirectSelectList oldWidget) {
    widget.paddingItemController.paddingGlobalKey =
        oldWidget.paddingItemController.paddingGlobalKey;
    super.didUpdateWidget(oldWidget);
  }

  _showListOverlay(double dy) {
    if (!isOverlayVisible) {
      isOverlayVisible = true;
      onTapEventListener(widget, _getItemTopPosition(context));
    } else {
      onDragEventListener(dy);
    }
  }
}

Outside Widget Package :

main.dart

import 'package:flutter/material.dart';
import 'widget/direct_select_container.dart';
import 'widget/direct_select_item.dart';
import 'widget/direct_select_list.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

List<String> _meals = [
  "Breakfast1",
  "Breakfast2",
  "Lunch1",
  "Lunch2",
  "Dinner1",
  "Dinner2",
];

class _MyHomePageState extends State<MyHomePage> {
  List<String> _food = ["Chicken", "Pork", "Vegetables", "Cheese", "Bread"];

  List<String> _foodVariants = [
    "Chicken grilled",
    "Pork grilled",
    "Vegetables as is",
    "Cheese as is",
    "Bread tasty"
  ];

  List<String> _portionSize = [
    "Small portion",
    "Medium portion",
    "Large portion",
    "Huge portion"
  ];

  List<String> _numbers = ["1.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0"];

  int selectedFoodVariants = 0;
  int selectedPortionCounts = 0;
  int selectedPortionSize = 0;

  DirectSelectItem<String> getDropDownMenuItem(String value) {
    return DirectSelectItem<String>(
        itemHeight: 56,
        value: value,
        itemBuilder: (context, value) {
          return Text(value);
        });
  }

  _getDslDecoration() {
    return BoxDecoration(
      border: BorderDirectional(
        bottom: BorderSide(width: 1, color: Colors.black12),
        top: BorderSide(width: 1, color: Colors.black12),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    final appBar = PreferredSize(
        child: Container(
          decoration: BoxDecoration(
              color: Color.fromRGBO(246, 247, 249, 1),
              border: BorderDirectional(
                  bottom: BorderSide(width: 1, color: Colors.black12))),
          child: Padding(
              padding: EdgeInsets.only(left: 16, bottom: 24),
              child: Column(
                  verticalDirection: VerticalDirection.up,
                  children: <Widget>[
                    Container(
                        alignment: AlignmentDirectional.centerStart,
                        child: Text("Add Food",
                            style: TextStyle(
                                fontSize: 26,
                                color: Colors.black,
                                fontWeight: FontWeight.bold))),
                    Container(
                        alignment: AlignmentDirectional.centerStart,
                        child: Text("Journal",
                            style: TextStyle(
                                fontSize: 12,
                                color: Colors.black38,
                                fontWeight: FontWeight.bold)))
                  ])),
        ),
        preferredSize: Size.fromHeight(90));

    return Scaffold(
      appBar: appBar,
      body: DirectSelectContainer(
        child: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              verticalDirection: VerticalDirection.down,
              children: <Widget>[
                SizedBox(height: 20.0),
                Container(
                    padding: const EdgeInsets.only(left: 8.0),
                    alignment: AlignmentDirectional.centerStart,
                    margin: EdgeInsets.only(left: 4),
                    child: Column(
                      children: <Widget>[
                        Text(_foodVariants[selectedFoodVariants],
                            style: TextStyle(
                                fontSize: 26, fontWeight: FontWeight.bold))
                      ],
                    )),
                Container(
                    padding: const EdgeInsets.only(left: 8.0),
                    alignment: AlignmentDirectional.centerStart,
                    margin: EdgeInsets.only(left: 4),
                    child: Column(
                      children: <Widget>[
                        Text(_numbers[selectedPortionCounts] +
                            "   " +
                            _portionSize[selectedPortionSize])
                      ],
                    )),
                SizedBox(height: 5.0),
                _getFoodContainsRow(),
                SizedBox(height: 20.0),
                Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Column(
                    children: <Widget>[
                      MealSelector(data: _meals, label: "To which meal?"),
                      SizedBox(height: 20.0),
                      MealSelector(
                          data: _food, label: "Search our database by name"),
                      Padding(
                        padding: EdgeInsets.fromLTRB(0, 8, 0, 0),
                        child: Container(
                          decoration: _getShadowDecoration(),
                          child: Card(
                              child: Row(
                            mainAxisSize: MainAxisSize.max,
                            children: <Widget>[
                              Expanded(
                                  child: Padding(
                                      child: DirectSelectList<String>(
                                          values: _foodVariants,
                                          defaultItemIndex:
                                              selectedFoodVariants,
                                          itemBuilder: (String value) =>
                                              getDropDownMenuItem(value),
                                          focusedItemDecoration:
                                              _getDslDecoration(),
                                          onItemSelectedListener:
                                              (item, index, context) {
                                            setState(() {
                                              selectedFoodVariants = index;
                                            });
                                          }),
                                      padding: EdgeInsets.only(left: 22))),
                              Padding(
                                padding: EdgeInsets.only(right: 8),
                                child: _getDropdownIcon(),
                              )
                            ],
                          )),
                        ),
                      ),
                      SizedBox(height: 15.0),
                      Container(
                          padding: const EdgeInsets.fromLTRB(0, 16, 0, 0),
                          margin: EdgeInsets.only(left: 4),
                          alignment: AlignmentDirectional.centerStart,
                          child: Text("How Much?")),
                      Row(children: <Widget>[
                        Expanded(
                            flex: 2,
                            child: Container(
                              decoration: _getShadowDecoration(),
                              child: Card(
                                  child: Row(
                                crossAxisAlignment: CrossAxisAlignment.center,
                                mainAxisSize: MainAxisSize.max,
                                children: <Widget>[
                                  Expanded(
                                      child: Padding(
                                          child: DirectSelectList<String>(
                                              values: _numbers,
                                              defaultItemIndex:
                                                  selectedPortionCounts,
                                              itemBuilder: (String value) =>
                                                  getDropDownMenuItem(value),
                                              focusedItemDecoration:
                                                  _getDslDecoration(),
                                              onItemSelectedListener:
                                                  (item, index, context) {
                                                setState(() {
                                                  selectedPortionCounts = index;
                                                });
                                              }),
                                          padding: EdgeInsets.only(left: 22))),
                                ],
                              )),
                            )),
                        Expanded(
                            flex: 8,
                            child: Container(
                              decoration: _getShadowDecoration(),
                              child: Card(
                                  child: Row(
                                mainAxisSize: MainAxisSize.max,
                                children: <Widget>[
                                  Expanded(
                                      child: Padding(
                                          child: DirectSelectList<String>(
                                              values: _portionSize,
                                              defaultItemIndex:
                                                  selectedPortionSize,
                                              itemBuilder: (String value) =>
                                                  getDropDownMenuItem(value),
                                              focusedItemDecoration:
                                                  _getDslDecoration(),
                                              onItemSelectedListener:
                                                  (item, index, context) {
                                                setState(() {
                                                  selectedPortionSize = index;
                                                });
                                              }),
                                          padding: EdgeInsets.only(left: 22))),
                                  Padding(
                                    padding: EdgeInsets.only(right: 8),
                                    child: _getDropdownIcon(),
                                  )
                                ],
                              )),
                            )),
                      ]),
                      Row(children: <Widget>[
                        Expanded(
                            child: RaisedButton(
                          child: const Text('ADD TO JOURNAL',
                              style: TextStyle(color: Colors.blueAccent)),
                          onPressed: () {},
                        ))
                      ]),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Icon _getDropdownIcon() {
    return Icon(
      Icons.unfold_more,
      color: Colors.blueAccent,
    );
  }

  BoxDecoration _getShadowDecoration() {
    return BoxDecoration(
      boxShadow: <BoxShadow>[
        new BoxShadow(
          color: Colors.black.withOpacity(0.06),
          spreadRadius: 4,
          offset: new Offset(0.0, 0.0),
          blurRadius: 15.0,
        ),
      ],
    );
  }
}

class MealSelector extends StatelessWidget {
  final buttonPadding = const EdgeInsets.fromLTRB(0, 8, 0, 0);

  final List<String> data;
  final String label;

  MealSelector({@required this.data, @required this.label});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Container(
            alignment: AlignmentDirectional.centerStart,
            margin: EdgeInsets.only(left: 4),
            child: Text(label)),
        Padding(
          padding: buttonPadding,
          child: Container(
            decoration: _getShadowDecoration(),
            child: Card(
                child: Row(
              mainAxisSize: MainAxisSize.max,
              children: <Widget>[
                Expanded(
                    child: Padding(
                        child: DirectSelectList<String>(
                          values: data,
                          defaultItemIndex: 0,
                          itemBuilder: (String value) =>
                              getDropDownMenuItem(value),
                          focusedItemDecoration: _getDslDecoration(),
                        ),
                        padding: EdgeInsets.only(left: 12))),
                Padding(
                  padding: EdgeInsets.only(right: 8),
                  child: _getDropdownIcon(),
                )
              ],
            )),
          ),
        ),
      ],
    );
  }

  DirectSelectItem<String> getDropDownMenuItem(String value) {
    return DirectSelectItem<String>(
        itemHeight: 56,
        value: value,
        itemBuilder: (context, value) {
          return Text(value);
        });
  }

  _getDslDecoration() {
    return BoxDecoration(
      border: BorderDirectional(
        bottom: BorderSide(width: 1, color: Colors.black12),
        top: BorderSide(width: 1, color: Colors.black12),
      ),
    );
  }

  BoxDecoration _getShadowDecoration() {
    return BoxDecoration(
      boxShadow: <BoxShadow>[
        new BoxShadow(
          color: Colors.black.withOpacity(0.06),
          spreadRadius: 4,
          offset: new Offset(0.0, 0.0),
          blurRadius: 15.0,
        ),
      ],
    );
  }

  Icon _getDropdownIcon() {
    return Icon(
      Icons.unfold_more,
      color: Colors.blueAccent,
    );
  }
}

Widget _getFoodContainsRow() {
  final cardSize = 80.0;
  final cardColor = Colors.blueGrey[100];
  return Padding(
    padding: EdgeInsets.only(left: 8, right: 8),
    child: Row(
      children: <Widget>[
        Expanded(
          child: Container(
              child: Center(child: Text("226")),
              height: cardSize,
              margin: EdgeInsets.only(right: 3),
              decoration: BoxDecoration(
                  color: cardColor,
                  borderRadius: BorderRadius.only(
                      topLeft: const Radius.circular(10.0),
                      bottomLeft: const Radius.circular(10.0)))),
        ),
        Expanded(
          child: Container(
              child: Center(child: Text("41")),
              height: cardSize,
              margin: EdgeInsets.only(right: 3),
              decoration: BoxDecoration(color: cardColor)),
        ),
        Expanded(
          child: Container(
              child: Center(child: Text("0")),
              height: cardSize,
              margin: EdgeInsets.only(right: 3),
              decoration: BoxDecoration(color: cardColor)),
        ),
        Expanded(
          child: Container(
              child: Center(child: Text("4.5")),
              height: cardSize,
              decoration: BoxDecoration(
                  color: cardColor,
                  borderRadius: BorderRadius.only(
                      topRight: const Radius.circular(10.0),
                      bottomRight: const Radius.circular(10.0)))),
        ),
      ],
    ),
  );
}

The flutter tutorial  is a website that bring you the latest and amazing resources of code. All the languages codes are included in this website. The languages like flutter, android, java,kotlin etc.with the help of this languages any user can develop the beautiful application

For more information about Flutter. visit www.fluttertutorial.in