Port PopupMenu to fn3
This changes how PopupMenu works slightly because there's no long an onPressed callback on PopupMenuItem. Instead, callers should use showPopupMenu.
This commit is contained in:
parent
82dd9d65df
commit
0206f1a65d
206
packages/flutter/lib/src/fn3/popup_menu.dart
Normal file
206
packages/flutter/lib/src/fn3/popup_menu.dart
Normal file
@ -0,0 +1,206 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:sky' as sky;
|
||||
|
||||
import 'package:sky/animation.dart';
|
||||
import 'package:sky/painting.dart';
|
||||
import 'package:sky/material.dart';
|
||||
import 'package:sky/src/fn3/basic.dart';
|
||||
import 'package:sky/src/fn3/focus.dart';
|
||||
import 'package:sky/src/fn3/framework.dart';
|
||||
import 'package:sky/src/fn3/gesture_detector.dart';
|
||||
import 'package:sky/src/fn3/navigator.dart';
|
||||
import 'package:sky/src/fn3/popup_menu_item.dart';
|
||||
import 'package:sky/src/fn3/scrollable.dart';
|
||||
import 'package:sky/src/fn3/transitions.dart';
|
||||
|
||||
const Duration _kMenuDuration = const Duration(milliseconds: 300);
|
||||
const double _kMenuCloseIntervalEnd = 2.0 / 3.0;
|
||||
const double _kMenuWidthStep = 56.0;
|
||||
const double _kMenuMargin = 16.0; // 24.0 on tablet
|
||||
const double _kMenuMinWidth = 2.0 * _kMenuWidthStep;
|
||||
const double _kMenuMaxWidth = 5.0 * _kMenuWidthStep;
|
||||
const double _kMenuHorizontalPadding = 16.0;
|
||||
const double _kMenuVerticalPadding = 8.0;
|
||||
|
||||
class PopupMenu extends StatefulComponent {
|
||||
PopupMenu({
|
||||
Key key,
|
||||
this.items,
|
||||
this.level: 4,
|
||||
this.navigator,
|
||||
this.performance
|
||||
}) : super(key: key) {
|
||||
assert(items != null);
|
||||
assert(performance != null);
|
||||
}
|
||||
|
||||
final List<PopupMenuItem> items;
|
||||
final int level;
|
||||
final NavigatorState navigator;
|
||||
final WatchableAnimationPerformance performance;
|
||||
|
||||
PopupMenuState createState() => new PopupMenuState(this);
|
||||
}
|
||||
|
||||
class PopupMenuState extends ComponentState<PopupMenu> {
|
||||
PopupMenuState(PopupMenu config) : super(config) {
|
||||
_updateBoxPainter();
|
||||
config.performance.addListener(_performanceChanged);
|
||||
}
|
||||
|
||||
BoxPainter _painter;
|
||||
|
||||
void _updateBoxPainter() {
|
||||
_painter = new BoxPainter(
|
||||
new BoxDecoration(
|
||||
backgroundColor: Colors.grey[50],
|
||||
borderRadius: 2.0,
|
||||
boxShadow: shadows[config.level]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void didUpdateConfig(PopupMenu oldConfig) {
|
||||
if (config.level != config.level)
|
||||
_updateBoxPainter();
|
||||
if (config.performance != oldConfig.performance) {
|
||||
oldConfig.performance.removeListener(_performanceChanged);
|
||||
config.performance.addListener(_performanceChanged);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
config.performance.removeListener(_performanceChanged);
|
||||
}
|
||||
|
||||
void _performanceChanged() {
|
||||
setState(() {
|
||||
// the performance changed, and our state is tied up with the performance
|
||||
});
|
||||
}
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
double unit = 1.0 / (config.items.length + 1.5); // 1.0 for the width and 0.5 for the last item's fade.
|
||||
List<Widget> children = [];
|
||||
for (int i = 0; i < config.items.length; ++i) {
|
||||
double start = (i + 1) * unit;
|
||||
double end = (start + 1.5 * unit).clamp(0.0, 1.0);
|
||||
children.add(new FadeTransition(
|
||||
performance: config.performance,
|
||||
opacity: new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(start, end)),
|
||||
child: new GestureDetector(
|
||||
onTap: () { config.navigator.pop(config.items[i].value); },
|
||||
child: config.items[i]
|
||||
))
|
||||
);
|
||||
}
|
||||
final width = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit));
|
||||
final height = new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, unit * config.items.length));
|
||||
return new FadeTransition(
|
||||
performance: config.performance,
|
||||
opacity: new AnimatedValue<double>(0.0, end: 1.0, interval: new Interval(0.0, 1.0 / 3.0)),
|
||||
child: new Container(
|
||||
margin: new EdgeDims.all(_kMenuMargin),
|
||||
child: new BuilderTransition(
|
||||
performance: config.performance,
|
||||
variables: [width, height],
|
||||
builder: (BuildContext context) {
|
||||
return new CustomPaint(
|
||||
callback: (sky.Canvas canvas, Size size) {
|
||||
double widthValue = width.value * size.width;
|
||||
double heightValue = height.value * size.height;
|
||||
_painter.paint(canvas, new Rect.fromLTWH(size.width - widthValue, 0.0, widthValue, heightValue));
|
||||
},
|
||||
child: new ConstrainedBox(
|
||||
constraints: new BoxConstraints(
|
||||
minWidth: _kMenuMinWidth,
|
||||
maxWidth: _kMenuMaxWidth
|
||||
),
|
||||
child: new IntrinsicWidth(
|
||||
stepWidth: _kMenuWidthStep,
|
||||
child: new ScrollableViewport(
|
||||
child: new Container(
|
||||
padding: const EdgeDims.symmetric(
|
||||
horizontal: _kMenuHorizontalPadding,
|
||||
vertical: _kMenuVerticalPadding
|
||||
),
|
||||
child: new BlockBody(children)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MenuPosition {
|
||||
const MenuPosition({ this.top, this.right, this.bottom, this.left });
|
||||
final double top;
|
||||
final double right;
|
||||
final double bottom;
|
||||
final double left;
|
||||
}
|
||||
|
||||
class MenuRoute extends RouteBase {
|
||||
MenuRoute({ this.completer, this.position, this.builder, this.level });
|
||||
|
||||
final Completer completer;
|
||||
final MenuPosition position;
|
||||
final PopupMenuItemsBuilder builder;
|
||||
final int level;
|
||||
|
||||
AnimationPerformance createPerformance() {
|
||||
AnimationPerformance result = super.createPerformance();
|
||||
AnimationTiming timing = new AnimationTiming();
|
||||
timing.reverseInterval = new Interval(0.0, _kMenuCloseIntervalEnd);
|
||||
result.timing = timing;
|
||||
return result;
|
||||
}
|
||||
|
||||
Duration get transitionDuration => _kMenuDuration;
|
||||
bool get isOpaque => false;
|
||||
Widget build(Key key, NavigatorState navigator, WatchableAnimationPerformance performance) {
|
||||
return new Positioned(
|
||||
top: position?.top,
|
||||
right: position?.right,
|
||||
bottom: position?.bottom,
|
||||
left: position?.left,
|
||||
child: new Focus(
|
||||
key: new GlobalObjectKey(this),
|
||||
autofocus: true,
|
||||
child: new PopupMenu(
|
||||
key: key,
|
||||
items: builder != null ? builder(navigator) : const <PopupMenuItem>[],
|
||||
level: level,
|
||||
navigator: navigator,
|
||||
performance: performance
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void popState([dynamic result]) {
|
||||
completer.complete(result);
|
||||
}
|
||||
}
|
||||
|
||||
typedef List<PopupMenuItem> PopupMenuItemsBuilder(NavigatorState navigator);
|
||||
|
||||
Future showMenu({ NavigatorState navigator, MenuPosition position, PopupMenuItemsBuilder builder, int level: 4 }) {
|
||||
Completer completer = new Completer();
|
||||
navigator.push(new MenuRoute(
|
||||
completer: completer,
|
||||
position: position,
|
||||
builder: builder,
|
||||
level: level
|
||||
));
|
||||
return completer.future;
|
||||
}
|
37
packages/flutter/lib/src/fn3/popup_menu_item.dart
Normal file
37
packages/flutter/lib/src/fn3/popup_menu_item.dart
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:sky/src/fn3/basic.dart';
|
||||
import 'package:sky/src/fn3/framework.dart';
|
||||
import 'package:sky/src/fn3/ink_well.dart';
|
||||
import 'package:sky/src/fn3/theme.dart';
|
||||
|
||||
const double _kMenuItemHeight = 48.0;
|
||||
const double _kBaselineOffsetFromBottom = 20.0;
|
||||
|
||||
class PopupMenuItem extends StatelessComponent {
|
||||
PopupMenuItem({
|
||||
Key key,
|
||||
this.value,
|
||||
this.child
|
||||
}) : super(key: key);
|
||||
|
||||
final Widget child;
|
||||
final dynamic value;
|
||||
|
||||
Widget build(BuildContext context) {
|
||||
return new InkWell(
|
||||
child: new Container(
|
||||
height: _kMenuItemHeight,
|
||||
child: new DefaultTextStyle(
|
||||
style: Theme.of(context).text.subhead,
|
||||
child: new Baseline(
|
||||
baseline: _kMenuItemHeight - _kBaselineOffsetFromBottom,
|
||||
child: child
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user