|
|
|
|
|
|
borderRadius: BorderRadius.circular(2.0f), |
|
|
|
boxShadow: ShadowConstants.kElevationToShadow[elevation ?? 0] |
|
|
|
).createBoxPainter(); |
|
|
|
this.color = color; |
|
|
|
this.elevation = elevation; |
|
|
|
this.selectedIndex = selectedIndex; |
|
|
|
this.resize = resize; |
|
|
|
public readonly int elevation; |
|
|
|
public readonly int selectedIndex; |
|
|
|
public readonly int? elevation; |
|
|
|
public readonly int? selectedIndex; |
|
|
|
float selectedItemOffset = this.selectedIndex * DropdownConstants._kMenuItemHeight + |
|
|
|
float selectedItemOffset = this.selectedIndex ?? 0 * DropdownConstants._kMenuItemHeight + |
|
|
|
Constants.kMaterialListPadding.top; |
|
|
|
FloatTween top = new FloatTween( |
|
|
|
begin: selectedItemOffset.clamp(0.0f, size.height - DropdownConstants._kMenuItemHeight), |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class _DropdownMenu<T> : StatefulWidget where T : Diagnosticable { |
|
|
|
class _DropdownMenu<T> : StatefulWidget where T : class { |
|
|
|
public _DropdownMenu( |
|
|
|
Key key = null, |
|
|
|
EdgeInsets padding = null, |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class _DropdownMenuState<T> : State<_DropdownMenu<T>> where T : Diagnosticable { |
|
|
|
class _DropdownMenuState<T> : State<_DropdownMenu<T>> where T : class { |
|
|
|
CurvedAnimation _fadeOpacity; |
|
|
|
CurvedAnimation _resize; |
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public override Widget build(BuildContext context) { |
|
|
|
// The menu is shown in three stages (unit timing in brackets):
|
|
|
|
// [0s - 0.25s] - Fade in a rect-sized menu container with the selected item.
|
|
|
|
// [0.25s - 0.5s] - Grow the otherwise empty menu container from the center
|
|
|
|
// until it's big enough for as many items as we're going to show.
|
|
|
|
// [0.5s - 1.0fs] Fade in the remaining visible items from top to bottom.
|
|
|
|
//
|
|
|
|
// When the menu is dismissed we just fade the entire thing out
|
|
|
|
// in the first 0.25s.
|
|
|
|
D.assert(MaterialD.debugCheckHasMaterialLocalizations(context)); |
|
|
|
MaterialLocalizations localizations = MaterialLocalizations.of(context); |
|
|
|
_DropdownRoute<T> route = this.widget.route; |
|
|
|
|
|
|
opacity = new CurvedAnimation(parent: route.animation, curve: new Interval(start, end)); |
|
|
|
} |
|
|
|
|
|
|
|
var index = itemIndex; |
|
|
|
children.Add(new FadeTransition( |
|
|
|
opacity: opacity, |
|
|
|
child: new InkWell( |
|
|
|
|
|
|
), |
|
|
|
onTap: () => Navigator.pop( |
|
|
|
context, |
|
|
|
new _DropdownRouteResult<T>(route.items[itemIndex].value) |
|
|
|
new _DropdownRouteResult<T>(route.items[index].value) |
|
|
|
) |
|
|
|
) |
|
|
|
)); |
|
|
|
|
|
|
float menuHeight, |
|
|
|
TextDirection textDirection |
|
|
|
) { |
|
|
|
this.buttonRect = buttonRect; |
|
|
|
this.menuTop = menuTop; |
|
|
|
this.menuHeight = menuHeight; |
|
|
|
this.textDirection = textDirection; |
|
|
|
} |
|
|
|
|
|
|
|
public readonly Rect buttonRect; |
|
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
}); |
|
|
|
D.assert(this.textDirection != null); |
|
|
|
float left; |
|
|
|
switch (this.textDirection) { |
|
|
|
case TextDirection.rtl: |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class _DropdownRouteResult<T> { |
|
|
|
class _DropdownRouteResult<T> where T: class { |
|
|
|
public _DropdownRouteResult(T result) { |
|
|
|
this.result = result; |
|
|
|
} |
|
|
|
|
|
|
public static bool operator ==(_DropdownRouteResult<T> left, dynamic right) { |
|
|
|
if (!(right is _DropdownRouteResult<T>)) { |
|
|
|
return false; |
|
|
|
} |
|
|
|
|
|
|
|
_DropdownRouteResult<T> typedOther = right as _DropdownRouteResult<T>; |
|
|
|
return left.result.Equals(typedOther.result); |
|
|
|
public static bool operator ==(_DropdownRouteResult<T> left, _DropdownRouteResult<T> right) { |
|
|
|
return left.result == right.result; |
|
|
|
public static bool operator !=(_DropdownRouteResult<T> left, dynamic right) { |
|
|
|
return !(left == right); |
|
|
|
public static bool operator !=(_DropdownRouteResult<T> left, _DropdownRouteResult<T> right) { |
|
|
|
return left.result != right.result; |
|
|
|
} |
|
|
|
|
|
|
|
public override int GetHashCode() { |
|
|
|
|
|
|
|
|
|
|
class _DropdownRoute<T> : PopupRoute where T : Diagnosticable { |
|
|
|
class _DropdownRoute<T> : PopupRoute where T : class { |
|
|
|
public _DropdownRoute( |
|
|
|
List<DropdownMenuItem<T>> items = null, |
|
|
|
EdgeInsets padding = null, |
|
|
|
|
|
|
|
|
|
|
if (this.scrollController == null) { |
|
|
|
float scrollOffset = preferredMenuHeight > maxMenuHeight |
|
|
|
? Mathf.Max(0.0f, selectedItemOffset - (buttonTop - menuTop) ?? 0.0f) |
|
|
|
? Mathf.Max(0.0f, selectedItemOffset ?? 0.0f - (buttonTop - (menuTop ?? 0.0f))) |
|
|
|
: 0.0f; |
|
|
|
this.scrollController = new ScrollController(initialScrollOffset: scrollOffset); |
|
|
|
} |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public class DropdownMenuItem<T> : StatelessWidget where T : Diagnosticable { |
|
|
|
/// Creates an item for a dropdown menu.
|
|
|
|
///
|
|
|
|
/// The [child] argument is required.
|
|
|
|
public class DropdownMenuItem<T> : StatelessWidget where T : class { |
|
|
|
public DropdownMenuItem( |
|
|
|
Key key = null, |
|
|
|
T value = null, |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
public class DropdownButtonHideUnderline : InheritedWidget { |
|
|
|
/// Creates a [DropdownButtonHideUnderline]. A non-null [child] must
|
|
|
|
/// be given.
|
|
|
|
public DropdownButtonHideUnderline( |
|
|
|
Key key = null, |
|
|
|
Widget child = null |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public class DropdownButton<T> : StatefulWidget where T : Diagnosticable { |
|
|
|
public class DropdownButton<T> : StatefulWidget where T : class { |
|
|
|
public DropdownButton( |
|
|
|
Key key = null, |
|
|
|
List<DropdownMenuItem<T>> items = null, |
|
|
|
|
|
|
D.assert(items == null || value == null || |
|
|
|
items.Where<DropdownMenuItem<T>>((DropdownMenuItem<T> item) => item.value.Equals(value)).ToList() |
|
|
|
.Count == 1); |
|
|
|
this.items = items; |
|
|
|
this.value = value; |
|
|
|
this.hint = hint; |
|
|
|
this.disabledHint = disabledHint; |
|
|
|
this.onChanged = onChanged; |
|
|
|
this.elevation = elevation; |
|
|
|
this.style = style; |
|
|
|
this.iconSize = iconSize; |
|
|
|
this.isDense = isDense; |
|
|
|
this.isExpanded = isExpanded; |
|
|
|
} |
|
|
|
|
|
|
|
public readonly List<DropdownMenuItem<T>> items; |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
class _DropdownButtonState<T> : State<DropdownButton<T>>, WidgetsBindingObserver where T : Diagnosticable { |
|
|
|
class _DropdownButtonState<T> : State<DropdownButton<T>>, WidgetsBindingObserver where T : class { |
|
|
|
int? _selectedIndex; |
|
|
|
_DropdownRoute<T> _dropdownRoute; |
|
|
|
|
|
|
|
|
|
|
_DropdownRouteResult<T> value = newValue as _DropdownRouteResult<T>; |
|
|
|
this._dropdownRoute = null; |
|
|
|
if (!this.mounted || newValue == null) { |
|
|
|
return null; |
|
|
|
return; |
|
|
|
|
|
|
|
return null; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
D.assert(MaterialD.debugCheckHasMaterial(context)); |
|
|
|
D.assert(MaterialD.debugCheckHasMaterialLocalizations(context)); |
|
|
|
|
|
|
|
// The width of the button and the menu are defined by the widest
|
|
|
|
// item and the width of the hint.
|
|
|
|
List<Widget> items = this._enabled ? new List<Widget>(this.widget.items) : new List<Widget>(); |
|
|
|
int hintIndex = 0; |
|
|
|
if (this.widget.hint != null || (!this._enabled && this.widget.disabledHint != null)) { |
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public class DropdownButtonFormField<T> : FormField<T> where T : Diagnosticable { |
|
|
|
public class DropdownButtonFormField<T> : FormField<T> where T : class { |
|
|
|
public DropdownButtonFormField( |
|
|
|
Key key = null, |
|
|
|
T value = null, |
|
|
|
|
|
|
this.onChanged = onChanged; |
|
|
|
} |
|
|
|
|
|
|
|
/// Called when the user selects an item.
|
|
|
|
public State createState() { |
|
|
|
public override State createState() { |
|
|
|
class _DropdownButtonFormFieldState<T> : FormFieldState<T> where T : Diagnosticable { |
|
|
|
class _DropdownButtonFormFieldState<T> : FormFieldState<T> where T : class { |
|
|
|
public DropdownButtonFormField<T> widget { |
|
|
|
get { return base.widget as DropdownButtonFormField<T>; } |
|
|
|
} |
|
|
|