Last active
September 1, 2018 08:02
-
-
Save letsar/56e2a1b29d54e7515f09a58a543c49ab to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'package:flutter/material.dart'; | |
const _kDuration = const Duration(milliseconds: 4000); | |
const _kActionSize = 64.0; | |
const _kCount = 3; | |
const _kAnimInterval01 = const Interval(.00, .30); | |
const _kAnimInterval02 = const Interval(.00, .30); | |
const _kAnimInterval03 = const Interval(.30, .60); | |
const _kAnimInterval04 = const Interval(.60, .90); | |
const _kAnimInterval05 = const Interval(.60, .90); | |
const archiveAction = const IconSlideAction( | |
icon: Icons.archive, | |
color: Colors.blue, | |
caption: 'Archive', | |
); | |
const shareAction = const IconSlideAction( | |
icon: Icons.share, | |
color: Colors.indigo, | |
caption: 'Share', | |
); | |
void main() => runApp( | |
MaterialApp( | |
home: Material( | |
child: SlidableDrawerDelegateDemo(), | |
), | |
), | |
); | |
class SlidableDrawerDelegateDemo extends StatefulWidget { | |
@override | |
_SlidableDrawerDelegateDemoState createState() => | |
new _SlidableDrawerDelegateDemoState(); | |
} | |
class _SlidableDrawerDelegateDemoState extends State<SlidableDrawerDelegateDemo> | |
with TickerProviderStateMixin { | |
AnimationController _controller; | |
@override | |
void initState() { | |
super.initState(); | |
_controller = AnimationController(duration: _kDuration, vsync: this); | |
} | |
Future<Null> _playAnimation() async { | |
try { | |
await _controller.forward().orCancel; | |
await _controller.reverse().orCancel; | |
} on TickerCanceled { | |
// the animation got canceled, probably because we were disposed | |
} | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar( | |
title: Text('Slidable Drawer Delegate Animation'), | |
), | |
body: GestureDetector( | |
behavior: HitTestBehavior.opaque, | |
onTap: () => _playAnimation(), | |
child: Padding( | |
padding: const EdgeInsets.all(8.0), | |
child: Center( | |
child: SlidableDrawerDelegateAnimation( | |
controller: _controller.view, | |
), | |
), | |
), | |
), | |
); | |
} | |
} | |
class SlidableDrawerDelegateAnimation extends StatelessWidget { | |
SlidableDrawerDelegateAnimation({ | |
Key key, | |
this.controller, | |
}) : dxPositions = <Animation<Offset>>[ | |
Tween<Offset>( | |
begin: Offset(-1.0, 0.0), | |
end: Offset(1.0, 0.0), | |
).animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: _kAnimInterval03, | |
), | |
), | |
Tween<Offset>( | |
begin: Offset(-1.0, 0.0), | |
end: Offset(0.0, 0.0), | |
).animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: _kAnimInterval03, | |
), | |
), | |
Tween<Offset>( | |
begin: Offset(0.0, 0.0), | |
end: Offset(0.5, 0.0), | |
).animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: _kAnimInterval03, | |
), | |
), | |
], | |
dyPositions1 = List.generate( | |
_kCount, | |
(i) => Tween<Offset>( | |
begin: Offset.zero, | |
end: Offset(0.0, -i.toDouble() / 2), | |
).animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: _kAnimInterval01, | |
), | |
), | |
), | |
dyPositions2 = List.generate( | |
_kCount, | |
(i) => Tween<Offset>( | |
begin: Offset.zero, | |
end: Offset(0.0, i.toDouble() / 2), | |
).animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: _kAnimInterval04, | |
), | |
), | |
), | |
skewTransform1 = Tween<double>(begin: 0.0, end: 0.5).animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: _kAnimInterval02, | |
), | |
), | |
skewTransform2 = Tween<double>(begin: 0.0, end: -0.5).animate( | |
CurvedAnimation( | |
parent: controller, | |
curve: _kAnimInterval05, | |
), | |
), | |
super(key: key); | |
final Animation<double> controller; | |
final List<Animation<Offset>> dyPositions1; | |
final List<Animation<Offset>> dyPositions2; | |
final List<Animation<Offset>> dxPositions; | |
final Animation<double> skewTransform1; | |
final Animation<double> skewTransform2; | |
Widget _buildAnimation(BuildContext context, Widget child) { | |
return Container( | |
child: SkewTransition( | |
skew: skewTransform1, | |
child: SkewTransition( | |
skew: skewTransform2, | |
child: Container( | |
width: _kActionSize * 4, | |
height: 256.0, | |
color: Colors.black87, | |
child: Center( | |
child: Column( | |
mainAxisAlignment: MainAxisAlignment.center, | |
children: <Widget>[ | |
_buildListTile(context, 0), | |
Stack( | |
children: List.generate(_kCount, (i) { | |
return SlideTransition( | |
position: dyPositions1[i], | |
child: SlideTransition( | |
position: dxPositions[i], | |
child: SlideTransition( | |
position: dyPositions2[i], | |
child: _buildWidget(context, i), | |
), | |
), | |
); | |
}), | |
), | |
_buildListTile(context, 2), | |
], | |
), | |
), | |
), | |
), | |
), | |
); | |
} | |
Widget _buildWidget(BuildContext context, int index) { | |
switch (index) { | |
case 0: | |
return shareAction; | |
case 1: | |
return archiveAction; | |
case 2: | |
return _buildListTile(context, 1); | |
} | |
return null; | |
} | |
Widget _buildListTile(BuildContext context, int index) { | |
return Container( | |
color: Colors.grey.shade200, | |
width: _kActionSize * 4, | |
height: _kActionSize, | |
child: ListTile( | |
leading: CircleAvatar( | |
backgroundColor: Colors.green, | |
child: Text('$index'), | |
foregroundColor: Colors.white, | |
), | |
title: Text('Title'), | |
subtitle: Text('Subtitle'), | |
), | |
); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return AnimatedBuilder( | |
builder: _buildAnimation, | |
animation: controller, | |
); | |
} | |
} | |
/// Animates the skew of transformed widget. | |
class SkewTransition extends AnimatedWidget { | |
/// Creates a skew transition. | |
/// | |
/// The [skew] argument must not be null. | |
const SkewTransition({ | |
Key key, | |
@required Animation<double> skew, | |
this.child, | |
}) : super(key: key, listenable: skew); | |
/// The animation that controls the skew of the child. | |
Animation<double> get skew => listenable; | |
/// The widget below this widget in the tree. | |
/// | |
/// {@macro flutter.widgets.child} | |
final Widget child; | |
@override | |
Widget build(BuildContext context) { | |
final double skewValue = skew.value; | |
final Matrix4 transform = new Matrix4.skewX(skewValue); | |
return new Transform( | |
transform: transform, | |
child: child, | |
); | |
} | |
} | |
class IconSlideAction extends StatelessWidget { | |
/// Creates a slide action with an icon, a [caption] if set and a | |
/// background color. | |
/// | |
/// The [closeOnTap] argument must not be null. | |
const IconSlideAction({ | |
Key key, | |
@required this.icon, | |
this.caption, | |
this.color, | |
}) : super(key: key); | |
final IconData icon; | |
final String caption; | |
/// The background color. | |
/// | |
/// Defaults to true. | |
final Color color; | |
@override | |
Widget build(BuildContext context) { | |
final Color foregroundColor = | |
ThemeData.estimateBrightnessForColor(color) == Brightness.light | |
? Colors.black | |
: Colors.white; | |
final Text textWidget = new Text( | |
caption ?? '', | |
overflow: TextOverflow.ellipsis, | |
style: Theme.of(context) | |
.primaryTextTheme | |
.caption | |
.copyWith(color: foregroundColor), | |
); | |
return Container( | |
color: color, | |
width: _kActionSize, | |
height: _kActionSize, | |
child: new Center( | |
child: Column( | |
mainAxisSize: MainAxisSize.min, | |
children: <Widget>[ | |
new Flexible( | |
child: new Icon( | |
icon, | |
color: foregroundColor, | |
), | |
), | |
new Flexible(child: textWidget), | |
], | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment