您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
421 行
16 KiB
421 行
16 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using Unity.UIWidgets.animation;
|
|
using Unity.UIWidgets.foundation;
|
|
using Unity.UIWidgets.material;
|
|
using Unity.UIWidgets.painting;
|
|
using Unity.UIWidgets.rendering;
|
|
using Unity.UIWidgets.ui;
|
|
using Unity.UIWidgets.widgets;
|
|
|
|
namespace UIWidgetsGallery.demo.shrine
|
|
{
|
|
public class backdropUtils
|
|
{
|
|
public static readonly Cubic _kAccelerateCurve = new Cubic(0.548f, 0.0f, 0.757f, 0.464f);
|
|
public static readonly Cubic _kDecelerateCurve = new Cubic(0.23f, 0.94f, 0.41f, 1.0f);
|
|
public static readonly float _kPeakVelocityTime = 0.248210f;
|
|
public static readonly float _kPeakVelocityProgress = 0.379146f;
|
|
}
|
|
|
|
|
|
public class _TappableWhileStatusIs : StatefulWidget {
|
|
public _TappableWhileStatusIs(
|
|
AnimationStatus status,
|
|
Key key = null,
|
|
AnimationController controller = null,
|
|
Widget child = null
|
|
) : base(key: key)
|
|
{
|
|
this.status = status;
|
|
this.controller = controller;
|
|
this.child = child;
|
|
}
|
|
|
|
public readonly AnimationController controller;
|
|
public readonly AnimationStatus status;
|
|
public readonly Widget child;
|
|
|
|
public override State createState() => new _TappableWhileStatusIsState();
|
|
}
|
|
|
|
public class _TappableWhileStatusIsState : State<_TappableWhileStatusIs> {
|
|
bool _active;
|
|
|
|
public override void initState() {
|
|
base.initState();
|
|
widget.controller.addStatusListener(_handleStatusChange);
|
|
_active = widget.controller.status == widget.status;
|
|
}
|
|
|
|
|
|
public override void dispose() {
|
|
widget.controller.removeStatusListener(_handleStatusChange);
|
|
base.dispose();
|
|
}
|
|
|
|
void _handleStatusChange(AnimationStatus status) {
|
|
bool value = widget.controller.status == widget.status;
|
|
if (_active != value) {
|
|
setState(() => {
|
|
_active = value;
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
public override Widget build(BuildContext context) {
|
|
Widget child = new AbsorbPointer(
|
|
absorbing: !_active,
|
|
child: widget.child
|
|
);
|
|
|
|
if (!_active) {
|
|
child = new FocusScope(
|
|
canRequestFocus: false,
|
|
debugLabel: "_TappableWhileStatusIs",
|
|
child: child
|
|
);
|
|
}
|
|
return child;
|
|
}
|
|
}
|
|
|
|
public class _FrontLayer : StatelessWidget {
|
|
public _FrontLayer(
|
|
Key key = null,
|
|
VoidCallback onTap = null,
|
|
Widget child = null
|
|
) : base(key: key)
|
|
{
|
|
this.onTap = onTap;
|
|
this.child = child;
|
|
}
|
|
|
|
public readonly VoidCallback onTap;
|
|
public readonly Widget child;
|
|
|
|
|
|
public override Widget build(BuildContext context) {
|
|
return new Material(
|
|
elevation: 16.0f,
|
|
shape: new BeveledRectangleBorder(
|
|
borderRadius: BorderRadius.only(topLeft: Radius.circular(46.0f))
|
|
),
|
|
child: new Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: new List<Widget>{
|
|
new GestureDetector(
|
|
behavior: HitTestBehavior.opaque,
|
|
onTap: () => { onTap?.Invoke();},
|
|
child: new Container(
|
|
height: 40.0f,
|
|
alignment: AlignmentDirectional.centerStart
|
|
)
|
|
),
|
|
new Expanded(
|
|
child: child
|
|
),
|
|
}
|
|
)
|
|
);
|
|
}
|
|
}
|
|
public delegate void OnPress();
|
|
|
|
public class _BackdropTitle : AnimatedWidget {
|
|
public _BackdropTitle(
|
|
Key key = null,
|
|
Animation<float> listenable = null,
|
|
OnPress onPress = null,
|
|
Widget frontTitle = null,
|
|
Widget backTitle = null
|
|
) : base(key: key, listenable: listenable)
|
|
{
|
|
D.assert(frontTitle != null);
|
|
D.assert(backTitle != null);
|
|
this.backTitle = backTitle;
|
|
this.frontTitle = frontTitle;
|
|
this.onPress = onPress;
|
|
}
|
|
|
|
public readonly OnPress onPress;
|
|
public readonly Widget frontTitle;
|
|
public readonly Widget backTitle;
|
|
|
|
|
|
protected override Widget build(BuildContext context) {
|
|
Animation<float> animation = new CurvedAnimation(
|
|
parent: listenable as Animation<float>,
|
|
curve: new Interval(0.0f, 0.78f)
|
|
);
|
|
|
|
return new DefaultTextStyle(
|
|
style: Theme.of(context).primaryTextTheme.headline6,
|
|
softWrap: false,
|
|
overflow: TextOverflow.ellipsis,
|
|
child: new Row(children: new List<Widget>{
|
|
new SizedBox(
|
|
width: 72.0f,
|
|
child: new IconButton(
|
|
padding: EdgeInsets.only(right: 8.0f),
|
|
onPressed: ()=> { onPress?.Invoke();},
|
|
icon: new Stack(children: new List<Widget>{
|
|
new Opacity(
|
|
opacity: animation.value,
|
|
child: new Container(
|
|
width: 20.0f,
|
|
height: 20.0f,
|
|
decoration: new BoxDecoration(
|
|
image: new DecorationImage(
|
|
image: new FileImage(
|
|
file: "shrine_images/slanted_menu.png"
|
|
)
|
|
),
|
|
shape: BoxShape.circle
|
|
)
|
|
)
|
|
),
|
|
new FractionalTranslation(
|
|
translation: new OffsetTween(
|
|
begin: Offset.zero,
|
|
end: new Offset(1.0f, 0.0f)).evaluate(animation),
|
|
child: new Container(
|
|
width: 20.0f,
|
|
height: 20.0f,
|
|
decoration: new BoxDecoration(
|
|
image: new DecorationImage(
|
|
image: new FileImage(
|
|
file: "shrine_images/diamond.png"
|
|
)
|
|
),
|
|
shape: BoxShape.circle
|
|
)
|
|
)
|
|
)
|
|
})
|
|
)
|
|
),
|
|
new Stack(
|
|
children: new List<Widget>{
|
|
new Opacity(
|
|
opacity: new CurvedAnimation(
|
|
parent: new ReverseAnimation(animation),
|
|
curve: new Interval(0.5f, 1.0f)
|
|
).value,
|
|
child: new FractionalTranslation(
|
|
translation: new OffsetTween(
|
|
begin: Offset.zero,
|
|
end: new Offset(0.5f, 0.0f)).evaluate(animation),
|
|
child: backTitle
|
|
)
|
|
),
|
|
new Opacity(
|
|
opacity: new CurvedAnimation(
|
|
parent: animation,
|
|
curve: new Interval(0.5f, 1.0f)).value,
|
|
child: new FractionalTranslation(
|
|
translation: new OffsetTween(
|
|
begin: new Offset(-0.25f, 0.0f),
|
|
end: Offset.zero).evaluate(animation),
|
|
child: frontTitle
|
|
)
|
|
),
|
|
}
|
|
),
|
|
})
|
|
);
|
|
}
|
|
}
|
|
|
|
public class Backdrop : StatefulWidget {
|
|
public Backdrop(
|
|
Widget frontLayer,
|
|
Widget backLayer,
|
|
Widget frontTitle,
|
|
Widget backTitle,
|
|
AnimationController controller
|
|
) {
|
|
D.assert(frontLayer != null);
|
|
D.assert(backLayer != null);
|
|
D.assert(frontTitle != null);
|
|
D.assert(backTitle != null);
|
|
D.assert(controller != null);
|
|
this.frontLayer = frontLayer;
|
|
this.backLayer = backLayer;
|
|
this.frontTitle = frontTitle;
|
|
this.backTitle = backTitle;
|
|
this.controller = controller;
|
|
}
|
|
|
|
public readonly Widget frontLayer;
|
|
public readonly Widget backLayer;
|
|
public readonly Widget frontTitle;
|
|
public readonly Widget backTitle;
|
|
public readonly AnimationController controller;
|
|
|
|
|
|
public override State createState() => new _BackdropState();
|
|
}
|
|
|
|
public class _BackdropState : SingleTickerProviderStateMixin<Backdrop> {
|
|
public readonly GlobalKey _backdropKey = GlobalKey.key(debugLabel: "Backdrop");
|
|
AnimationController _controller;
|
|
Animation<RelativeRect> _layerAnimation;
|
|
|
|
|
|
public override void initState() {
|
|
base.initState();
|
|
_controller = widget.controller;
|
|
}
|
|
|
|
public override void dispose() {
|
|
_controller.dispose();
|
|
base.dispose();
|
|
}
|
|
|
|
bool _frontLayerVisible {
|
|
get
|
|
{
|
|
AnimationStatus status = _controller.status;
|
|
return status == AnimationStatus.completed || status == AnimationStatus.forward;
|
|
}
|
|
}
|
|
|
|
void _toggleBackdropLayerVisibility() { //[!!!]
|
|
setState(() => {
|
|
var visible = _frontLayerVisible ? _controller.reverse() : _controller.forward();
|
|
});
|
|
}
|
|
|
|
Animation<RelativeRect> _getLayerAnimation(Size layerSize, float layerTop) {
|
|
Curve firstCurve;
|
|
Curve secondCurve;
|
|
float firstWeight;
|
|
float secondWeight;
|
|
Animation<float> animation;
|
|
|
|
if (_frontLayerVisible) {
|
|
firstCurve = backdropUtils._kAccelerateCurve;
|
|
secondCurve = backdropUtils._kDecelerateCurve;
|
|
firstWeight = backdropUtils._kPeakVelocityTime;
|
|
secondWeight = 1.0f - backdropUtils._kPeakVelocityTime;
|
|
animation = new CurvedAnimation(
|
|
parent: _controller.view,
|
|
curve: new Interval(0.0f, 0.78f)
|
|
);
|
|
} else {
|
|
firstCurve = backdropUtils._kDecelerateCurve.flipped;
|
|
secondCurve = backdropUtils._kAccelerateCurve.flipped;
|
|
firstWeight = 1.0f - backdropUtils._kPeakVelocityTime;
|
|
secondWeight = backdropUtils._kPeakVelocityTime;
|
|
animation = _controller.view;
|
|
}
|
|
|
|
return new TweenSequence<RelativeRect>(
|
|
new List<TweenSequenceItem<RelativeRect>>{
|
|
new TweenSequenceItem<RelativeRect>(
|
|
tween: new RelativeRectTween(
|
|
begin: RelativeRect.fromLTRB(
|
|
0.0f,
|
|
layerTop,
|
|
0.0f,
|
|
layerTop - layerSize.height
|
|
),
|
|
end: RelativeRect.fromLTRB(
|
|
0.0f,
|
|
layerTop * backdropUtils._kPeakVelocityProgress,
|
|
0.0f,
|
|
(layerTop - layerSize.height) * backdropUtils._kPeakVelocityProgress
|
|
)
|
|
).chain(new CurveTween(curve: firstCurve)),
|
|
weight: firstWeight
|
|
),
|
|
new TweenSequenceItem<RelativeRect>(
|
|
tween: new RelativeRectTween(
|
|
begin: RelativeRect.fromLTRB(
|
|
0.0f,
|
|
layerTop * backdropUtils._kPeakVelocityProgress,
|
|
0.0f,
|
|
(layerTop - layerSize.height) * backdropUtils._kPeakVelocityProgress
|
|
),
|
|
end: RelativeRect.fill
|
|
).chain(new CurveTween(curve: secondCurve)),
|
|
weight: secondWeight
|
|
),
|
|
}
|
|
).animate(animation);
|
|
}
|
|
|
|
Widget _buildStack(BuildContext context, BoxConstraints constraints) {
|
|
float layerTitleHeight = 48.0f;
|
|
Size layerSize = constraints.biggest;
|
|
float layerTop = layerSize.height - layerTitleHeight;
|
|
|
|
_layerAnimation = _getLayerAnimation(layerSize, layerTop);
|
|
|
|
return new Stack(
|
|
key: _backdropKey,
|
|
children: new List<Widget>{
|
|
new _TappableWhileStatusIs(
|
|
AnimationStatus.dismissed,
|
|
controller: _controller,
|
|
child: widget.backLayer
|
|
),
|
|
new PositionedTransition(
|
|
rect: _layerAnimation,
|
|
child: new _FrontLayer(
|
|
onTap: _toggleBackdropLayerVisibility,
|
|
child: new _TappableWhileStatusIs(
|
|
AnimationStatus.completed,
|
|
controller: _controller,
|
|
child: widget.frontLayer
|
|
)
|
|
)
|
|
),
|
|
}
|
|
);
|
|
}
|
|
|
|
public override Widget build(BuildContext context) {
|
|
AppBar appBar = new AppBar(
|
|
brightness: Brightness.light,
|
|
elevation: 0.0f,
|
|
titleSpacing: 0.0f,
|
|
title: new _BackdropTitle(
|
|
listenable: _controller.view,
|
|
onPress: _toggleBackdropLayerVisibility,
|
|
frontTitle: widget.frontTitle,
|
|
backTitle: widget.backTitle
|
|
),
|
|
actions: new List<Widget>{
|
|
new IconButton(
|
|
icon: new Icon(Icons.search),
|
|
onPressed: () => {
|
|
Navigator.push<object>(
|
|
context,
|
|
new MaterialPageRoute(builder: (BuildContext context2) => new LoginPage())
|
|
);
|
|
}
|
|
),
|
|
new IconButton(
|
|
icon: new Icon(Icons.tune),
|
|
onPressed: () => {
|
|
Navigator.push<object>(
|
|
context,
|
|
new MaterialPageRoute(builder: (BuildContext context2) => new LoginPage())
|
|
);
|
|
}
|
|
),
|
|
}
|
|
);
|
|
return new Scaffold(
|
|
appBar: appBar,
|
|
body: new LayoutBuilder(
|
|
builder: _buildStack
|
|
)
|
|
);
|
|
}
|
|
}
|
|
|
|
}
|