Merge pull request #803 from abarth/dropdown
Material gallery crashes when you press the drop-down button
This commit is contained in:
commit
8dd8c203e2
@ -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<T> extends StatusTransitionComponent {
|
||||
|
||||
final AnimatedValue<double> menuOpacity = new AnimatedValue<double>(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<double> menuTop = new AnimatedValue<double>(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<double> menuBottom = new AnimatedValue<double>(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: <AnimatedValue<double>>[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: <AnimatedValue<double>>[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<T> extends PopupRoute<T> {
|
||||
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<T> extends StatelessComponent {
|
||||
}
|
||||
}
|
||||
|
||||
class DropDownButton<T> extends StatelessComponent {
|
||||
class DropDownButton<T> extends StatefulComponent {
|
||||
DropDownButton({
|
||||
Key key,
|
||||
this.items,
|
||||
@ -212,40 +215,62 @@ class DropDownButton<T> extends StatelessComponent {
|
||||
final ValueChanged<T> onChanged;
|
||||
final int elevation;
|
||||
|
||||
void _showDropDown(BuildContext context, int selectedIndex, GlobalKey indexedStackKey) {
|
||||
_DropDownButtonState<T> createState() => new _DropDownButtonState<T>();
|
||||
}
|
||||
|
||||
class _DropDownButtonState<T> extends State<DropDownButton<T>> {
|
||||
final GlobalKey indexedStackKey = new GlobalKey(debugLabel: 'DropDownButton.IndexedStack');
|
||||
|
||||
void initState() {
|
||||
super.initState();
|
||||
_updateSelectedIndex();
|
||||
}
|
||||
|
||||
void didUpdateConfig(DropDownButton<T> 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<T>();
|
||||
Navigator.push(context, new _DropDownRoute<T>(
|
||||
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(<Widget>[
|
||||
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<T> extends StatelessComponent {
|
||||
],
|
||||
justifyContent: FlexJustifyContent.collapse
|
||||
)
|
||||
),
|
||||
onTap: () {
|
||||
_showDropDown(context, selectedIndex, indexedStackKey);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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<MaterialType, double> kMaterialEdges = const <MaterialType, double>{
|
||||
@ -33,6 +36,7 @@ const Map<MaterialType, double> kMaterialEdges = const <MaterialType, double>{
|
||||
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<Material> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -123,6 +123,10 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
|
||||
final List<PopupMenuItem<T>> items;
|
||||
final int elevation;
|
||||
|
||||
ModalPosition getPosition(BuildContext context) {
|
||||
return position;
|
||||
}
|
||||
|
||||
PerformanceView createPerformance() {
|
||||
return new CurvedPerformance(
|
||||
super.createPerformance(),
|
||||
|
@ -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<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
||||
|
||||
// The API for subclasses to override - used by _ModalScope
|
||||
|
||||
ModalPosition get position => 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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user