diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart index 18b3e144d3..0d0657488a 100644 --- a/packages/flutter/lib/src/material/dropdown.dart +++ b/packages/flutter/lib/src/material/dropdown.dart @@ -13,6 +13,7 @@ import 'icon.dart'; import 'ink_well.dart'; import 'shadows.dart'; import 'theme.dart'; +import 'material.dart'; const Duration _kDropDownMenuDuration = const Duration(milliseconds: 300); const double _kMenuItemHeight = 48.0; @@ -100,51 +101,42 @@ class _DropDownMenu extends StatusTransitionComponent { final AnimatedValue menuOpacity = new AnimatedValue(0.0, end: 1.0, - curve: new Interval(0.0, 0.25), - reverseCurve: new Interval(0.75, 1.0) + curve: const Interval(0.0, 0.25), + reverseCurve: const Interval(0.75, 1.0) ); final AnimatedValue menuTop = new AnimatedValue(route.rect.top, end: route.rect.top - route.selectedIndex * route.rect.height, - curve: new Interval(0.25, 0.5), + curve: const Interval(0.25, 0.5), reverseCurve: const Interval(0.0, 0.001) ); final AnimatedValue menuBottom = new AnimatedValue(route.rect.bottom, end: menuTop.end + route.items.length * route.rect.height, - curve: new Interval(0.25, 0.5), + curve: const Interval(0.25, 0.5), reverseCurve: const Interval(0.0, 0.001) ); - final RenderBox renderBox = route.navigator.context.findRenderObject(); - final Size navigatorSize = renderBox.size; - final RelativeRect menuRect = new RelativeRect.fromSize(route.rect, navigatorSize); - - return new Positioned( - top: menuRect.top - (route.selectedIndex * route.rect.height), - right: menuRect.right, - left: menuRect.left, - child: new Focus( - key: new GlobalObjectKey(route), - child: new FadeTransition( - performance: route.performance, - opacity: menuOpacity, - child: new BuilderTransition( - performance: route.performance, - variables: >[menuTop, menuBottom], - builder: (BuildContext context) { - return new CustomPaint( - painter: new _DropDownMenuPainter( - color: Theme.of(context).canvasColor, - elevation: route.elevation, - menuTop: menuTop.value, - menuBottom: menuBottom.value, - renderBox: context.findRenderObject() - ), - child: new Block(children) - ); - } - ) - ) + return new FadeTransition( + performance: route.performance, + opacity: menuOpacity, + child: new BuilderTransition( + performance: route.performance, + variables: >[menuTop, menuBottom], + builder: (BuildContext context) { + return new CustomPaint( + painter: new _DropDownMenuPainter( + color: Theme.of(context).canvasColor, + elevation: route.elevation, + menuTop: menuTop.value, + menuBottom: menuBottom.value, + renderBox: context.findRenderObject() + ), + child: new Material( + type: MaterialType.transparency, + child: new Block(children) + ) + ); + } ) ); } @@ -168,6 +160,17 @@ class _DropDownRoute extends PopupRoute { bool get barrierDismissable => true; Color get barrierColor => null; + ModalPosition getPosition(BuildContext context) { + RenderBox overlayBox = Overlay.of(context).context.findRenderObject(); + Size overlaySize = overlayBox.size; + RelativeRect menuRect = new RelativeRect.fromSize(rect, overlaySize); + return new ModalPosition( + top: menuRect.top - selectedIndex * rect.height, + left: menuRect.left, + right: menuRect.right + ); + } + Widget buildPage(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance) { return new _DropDownMenu(route: this); } @@ -198,7 +201,7 @@ class DropDownMenuItem extends StatelessComponent { } } -class DropDownButton extends StatelessComponent { +class DropDownButton extends StatefulComponent { DropDownButton({ Key key, this.items, @@ -212,40 +215,62 @@ class DropDownButton extends StatelessComponent { final ValueChanged onChanged; final int elevation; - void _showDropDown(BuildContext context, int selectedIndex, GlobalKey indexedStackKey) { + _DropDownButtonState createState() => new _DropDownButtonState(); +} + +class _DropDownButtonState extends State> { + final GlobalKey indexedStackKey = new GlobalKey(debugLabel: 'DropDownButton.IndexedStack'); + + void initState() { + super.initState(); + _updateSelectedIndex(); + } + + void didUpdateConfig(DropDownButton oldConfig) { + if (config.items[_selectedIndex].value != config.value) + _updateSelectedIndex(); + } + + int _selectedIndex; + + void _updateSelectedIndex() { + for (int itemIndex = 0; itemIndex < config.items.length; itemIndex++) { + if (config.items[itemIndex].value == config.value) { + _selectedIndex = itemIndex; + return; + } + } + } + + void _handleTap() { final RenderBox renderBox = indexedStackKey.currentContext.findRenderObject(); final Rect rect = renderBox.localToGlobal(Point.origin) & renderBox.size; final Completer completer = new Completer(); Navigator.push(context, new _DropDownRoute( completer: completer, - items: items, - selectedIndex: selectedIndex, + items: config.items, + selectedIndex: _selectedIndex, rect: _kMenuHorizontalPadding.inflateRect(rect), - elevation: elevation + elevation: config.elevation )); completer.future.then((T newValue) { - if (onChanged != null) - onChanged(newValue); + if (!mounted) + return; + if (config.onChanged != null) + config.onChanged(newValue); }); } Widget build(BuildContext context) { - GlobalKey indexedStackKey = new GlobalKey(debugLabel: 'DropDownButton.IndexedStack'); - int selectedIndex = 0; - for (int itemIndex = 0; itemIndex < items.length; itemIndex++) { - if (items[itemIndex].value == value) { - selectedIndex = itemIndex; - break; - } - } return new GestureDetector( + onTap: _handleTap, child: new Container( decoration: new BoxDecoration(border: _kDropDownUnderline), child: new Row([ new IndexedStack( - items, + config.items, key: indexedStackKey, - index: selectedIndex, + index: _selectedIndex, alignment: const FractionalOffset(0.5, 0.0) ), new Container( @@ -255,10 +280,7 @@ class DropDownButton extends StatelessComponent { ], justifyContent: FlexJustifyContent.collapse ) - ), - onTap: () { - _showDropDown(context, selectedIndex, indexedStackKey); - } + ) ); } } diff --git a/packages/flutter/lib/src/material/material.dart b/packages/flutter/lib/src/material/material.dart index e1a81a4258..b20bd447d4 100644 --- a/packages/flutter/lib/src/material/material.dart +++ b/packages/flutter/lib/src/material/material.dart @@ -25,7 +25,10 @@ enum MaterialType { circle, /// Rounded edges, no color by default (used for MaterialButton buttons). - button + button, + + /// A transparent piece of material that draws ink splashes and highlights. + transparency } const Map kMaterialEdges = const { @@ -33,6 +36,7 @@ const Map kMaterialEdges = const { MaterialType.card: 2.0, MaterialType.circle: null, MaterialType.button: 2.0, + MaterialType.transparency: null, }; abstract class InkSplash { @@ -141,17 +145,19 @@ class _MaterialState extends State { child: contents ); } - contents = new AnimatedContainer( - curve: Curves.ease, - duration: kThemeChangeDuration, - decoration: new BoxDecoration( - backgroundColor: backgroundColor, - borderRadius: kMaterialEdges[config.type], - boxShadow: config.elevation == 0 ? null : elevationToShadow[config.elevation], - shape: config.type == MaterialType.circle ? Shape.circle : Shape.rectangle - ), - child: contents - ); + if (config.type != MaterialType.transparency) { + contents = new AnimatedContainer( + curve: Curves.ease, + duration: kThemeChangeDuration, + decoration: new BoxDecoration( + backgroundColor: backgroundColor, + borderRadius: kMaterialEdges[config.type], + boxShadow: config.elevation == 0 ? null : elevationToShadow[config.elevation], + shape: config.type == MaterialType.circle ? Shape.circle : Shape.rectangle + ), + child: contents + ); + } return contents; } } diff --git a/packages/flutter/lib/src/material/popup_menu.dart b/packages/flutter/lib/src/material/popup_menu.dart index ca9189550d..6ef058839a 100644 --- a/packages/flutter/lib/src/material/popup_menu.dart +++ b/packages/flutter/lib/src/material/popup_menu.dart @@ -123,6 +123,10 @@ class _PopupMenuRoute extends PopupRoute { final List> items; final int elevation; + ModalPosition getPosition(BuildContext context) { + return position; + } + PerformanceView createPerformance() { return new CurvedPerformance( super.createPerformance(), diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart index fd7356cca2..c4cc04d6e2 100644 --- a/packages/flutter/lib/src/widgets/routes.dart +++ b/packages/flutter/lib/src/widgets/routes.dart @@ -359,7 +359,7 @@ class _ModalScopeState extends State<_ModalScope> { ); } contents = new RepaintBoundary(child: contents); - ModalPosition position = config.route.position; + ModalPosition position = config.route.getPosition(context); if (position == null) return contents; return new Positioned( @@ -398,7 +398,7 @@ abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute null; + ModalPosition getPosition(BuildContext context) => null; Widget buildPage(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance); Widget buildTransitions(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance, Widget child) { return child;