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 'ink_well.dart';
|
||||||
import 'shadows.dart';
|
import 'shadows.dart';
|
||||||
import 'theme.dart';
|
import 'theme.dart';
|
||||||
|
import 'material.dart';
|
||||||
|
|
||||||
const Duration _kDropDownMenuDuration = const Duration(milliseconds: 300);
|
const Duration _kDropDownMenuDuration = const Duration(milliseconds: 300);
|
||||||
const double _kMenuItemHeight = 48.0;
|
const double _kMenuItemHeight = 48.0;
|
||||||
@ -100,32 +101,22 @@ class _DropDownMenu<T> extends StatusTransitionComponent {
|
|||||||
|
|
||||||
final AnimatedValue<double> menuOpacity = new AnimatedValue<double>(0.0,
|
final AnimatedValue<double> menuOpacity = new AnimatedValue<double>(0.0,
|
||||||
end: 1.0,
|
end: 1.0,
|
||||||
curve: new Interval(0.0, 0.25),
|
curve: const Interval(0.0, 0.25),
|
||||||
reverseCurve: new Interval(0.75, 1.0)
|
reverseCurve: const Interval(0.75, 1.0)
|
||||||
);
|
);
|
||||||
|
|
||||||
final AnimatedValue<double> menuTop = new AnimatedValue<double>(route.rect.top,
|
final AnimatedValue<double> menuTop = new AnimatedValue<double>(route.rect.top,
|
||||||
end: route.rect.top - route.selectedIndex * route.rect.height,
|
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)
|
reverseCurve: const Interval(0.0, 0.001)
|
||||||
);
|
);
|
||||||
final AnimatedValue<double> menuBottom = new AnimatedValue<double>(route.rect.bottom,
|
final AnimatedValue<double> menuBottom = new AnimatedValue<double>(route.rect.bottom,
|
||||||
end: menuTop.end + route.items.length * route.rect.height,
|
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)
|
reverseCurve: const Interval(0.0, 0.001)
|
||||||
);
|
);
|
||||||
|
|
||||||
final RenderBox renderBox = route.navigator.context.findRenderObject();
|
return new FadeTransition(
|
||||||
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,
|
performance: route.performance,
|
||||||
opacity: menuOpacity,
|
opacity: menuOpacity,
|
||||||
child: new BuilderTransition(
|
child: new BuilderTransition(
|
||||||
@ -140,12 +131,13 @@ class _DropDownMenu<T> extends StatusTransitionComponent {
|
|||||||
menuBottom: menuBottom.value,
|
menuBottom: menuBottom.value,
|
||||||
renderBox: context.findRenderObject()
|
renderBox: context.findRenderObject()
|
||||||
),
|
),
|
||||||
|
child: new Material(
|
||||||
|
type: MaterialType.transparency,
|
||||||
child: new Block(children)
|
child: new Block(children)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,6 +160,17 @@ class _DropDownRoute<T> extends PopupRoute<T> {
|
|||||||
bool get barrierDismissable => true;
|
bool get barrierDismissable => true;
|
||||||
Color get barrierColor => null;
|
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) {
|
Widget buildPage(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance) {
|
||||||
return new _DropDownMenu(route: this);
|
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({
|
DropDownButton({
|
||||||
Key key,
|
Key key,
|
||||||
this.items,
|
this.items,
|
||||||
@ -212,40 +215,62 @@ class DropDownButton<T> extends StatelessComponent {
|
|||||||
final ValueChanged<T> onChanged;
|
final ValueChanged<T> onChanged;
|
||||||
final int elevation;
|
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 RenderBox renderBox = indexedStackKey.currentContext.findRenderObject();
|
||||||
final Rect rect = renderBox.localToGlobal(Point.origin) & renderBox.size;
|
final Rect rect = renderBox.localToGlobal(Point.origin) & renderBox.size;
|
||||||
final Completer completer = new Completer<T>();
|
final Completer completer = new Completer<T>();
|
||||||
Navigator.push(context, new _DropDownRoute<T>(
|
Navigator.push(context, new _DropDownRoute<T>(
|
||||||
completer: completer,
|
completer: completer,
|
||||||
items: items,
|
items: config.items,
|
||||||
selectedIndex: selectedIndex,
|
selectedIndex: _selectedIndex,
|
||||||
rect: _kMenuHorizontalPadding.inflateRect(rect),
|
rect: _kMenuHorizontalPadding.inflateRect(rect),
|
||||||
elevation: elevation
|
elevation: config.elevation
|
||||||
));
|
));
|
||||||
completer.future.then((T newValue) {
|
completer.future.then((T newValue) {
|
||||||
if (onChanged != null)
|
if (!mounted)
|
||||||
onChanged(newValue);
|
return;
|
||||||
|
if (config.onChanged != null)
|
||||||
|
config.onChanged(newValue);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget build(BuildContext context) {
|
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(
|
return new GestureDetector(
|
||||||
|
onTap: _handleTap,
|
||||||
child: new Container(
|
child: new Container(
|
||||||
decoration: new BoxDecoration(border: _kDropDownUnderline),
|
decoration: new BoxDecoration(border: _kDropDownUnderline),
|
||||||
child: new Row(<Widget>[
|
child: new Row(<Widget>[
|
||||||
new IndexedStack(
|
new IndexedStack(
|
||||||
items,
|
config.items,
|
||||||
key: indexedStackKey,
|
key: indexedStackKey,
|
||||||
index: selectedIndex,
|
index: _selectedIndex,
|
||||||
alignment: const FractionalOffset(0.5, 0.0)
|
alignment: const FractionalOffset(0.5, 0.0)
|
||||||
),
|
),
|
||||||
new Container(
|
new Container(
|
||||||
@ -255,10 +280,7 @@ class DropDownButton<T> extends StatelessComponent {
|
|||||||
],
|
],
|
||||||
justifyContent: FlexJustifyContent.collapse
|
justifyContent: FlexJustifyContent.collapse
|
||||||
)
|
)
|
||||||
),
|
)
|
||||||
onTap: () {
|
|
||||||
_showDropDown(context, selectedIndex, indexedStackKey);
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,10 @@ enum MaterialType {
|
|||||||
circle,
|
circle,
|
||||||
|
|
||||||
/// Rounded edges, no color by default (used for MaterialButton buttons).
|
/// 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>{
|
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.card: 2.0,
|
||||||
MaterialType.circle: null,
|
MaterialType.circle: null,
|
||||||
MaterialType.button: 2.0,
|
MaterialType.button: 2.0,
|
||||||
|
MaterialType.transparency: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
abstract class InkSplash {
|
abstract class InkSplash {
|
||||||
@ -141,6 +145,7 @@ class _MaterialState extends State<Material> {
|
|||||||
child: contents
|
child: contents
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (config.type != MaterialType.transparency) {
|
||||||
contents = new AnimatedContainer(
|
contents = new AnimatedContainer(
|
||||||
curve: Curves.ease,
|
curve: Curves.ease,
|
||||||
duration: kThemeChangeDuration,
|
duration: kThemeChangeDuration,
|
||||||
@ -152,6 +157,7 @@ class _MaterialState extends State<Material> {
|
|||||||
),
|
),
|
||||||
child: contents
|
child: contents
|
||||||
);
|
);
|
||||||
|
}
|
||||||
return contents;
|
return contents;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,6 +123,10 @@ class _PopupMenuRoute<T> extends PopupRoute<T> {
|
|||||||
final List<PopupMenuItem<T>> items;
|
final List<PopupMenuItem<T>> items;
|
||||||
final int elevation;
|
final int elevation;
|
||||||
|
|
||||||
|
ModalPosition getPosition(BuildContext context) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
PerformanceView createPerformance() {
|
PerformanceView createPerformance() {
|
||||||
return new CurvedPerformance(
|
return new CurvedPerformance(
|
||||||
super.createPerformance(),
|
super.createPerformance(),
|
||||||
|
@ -359,7 +359,7 @@ class _ModalScopeState extends State<_ModalScope> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
contents = new RepaintBoundary(child: contents);
|
contents = new RepaintBoundary(child: contents);
|
||||||
ModalPosition position = config.route.position;
|
ModalPosition position = config.route.getPosition(context);
|
||||||
if (position == null)
|
if (position == null)
|
||||||
return contents;
|
return contents;
|
||||||
return new Positioned(
|
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
|
// 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 buildPage(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance);
|
||||||
Widget buildTransitions(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance, Widget child) {
|
Widget buildTransitions(BuildContext context, PerformanceView performance, PerformanceView forwardPerformance, Widget child) {
|
||||||
return child;
|
return child;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user