Instantly share code, notes, and snippets.
This file contains 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 'dart:io'; | |
import 'package:flutter/foundation.dart'; | |
import 'package:flutter/material.dart'; | |
class AppButton extends StatefulWidget { | |
final Widget child; | |
final TargetPlatform platform; | |
final double height; | |
final double width; | |
final VoidCallback onPressed; | |
final Border border; | |
final BorderRadius borderRadius; | |
final Color color; | |
final Color splashColor; | |
final Color highlightColor; | |
final EdgeInsets padding; | |
final bool enabled; | |
AppButton({ | |
@required this.child, | |
@required this.onPressed, | |
this.platform, | |
this.height = 55.0, | |
this.width, | |
this.color = const Color(0x00FFFFFF), | |
this.highlightColor = const Color(0x22000000), | |
this.splashColor = const Color(0x33000000), | |
this.border, | |
this.borderRadius, | |
this.padding, | |
this.enabled = true, | |
}); | |
@override | |
AppButtonState createState() => new AppButtonState(); | |
} | |
class AppButtonState extends State<AppButton> | |
with SingleTickerProviderStateMixin { | |
static const Duration fadeOutDuration = const Duration(milliseconds: 120); | |
static const Duration fadeInDuration = const Duration(milliseconds: 1200); | |
static const Duration quickFadeInDuration = const Duration(milliseconds: 300); | |
static const Duration quickFadeOutDuration = | |
const Duration(milliseconds: 200); | |
Tween<double> _opacityTween; | |
AnimationController _animationController; | |
void _setTween() { | |
_opacityTween = new Tween<double>( | |
begin: 0.0, | |
end: 1.0, | |
); | |
} | |
@override | |
void initState() { | |
super.initState(); | |
_animationController = new AnimationController( | |
duration: const Duration(milliseconds: 800), | |
value: 0.0, | |
vsync: this, | |
); | |
_setTween(); | |
} | |
@override | |
Widget build(BuildContext context) { | |
final enabled = widget.enabled && widget.onPressed != null; | |
return new SizedBox( | |
height: widget.height, | |
width: widget.width, | |
child: new Semantics( | |
container: true, | |
button: true, | |
enabled: enabled, | |
child: new ClipRRect( | |
borderRadius: widget.borderRadius ?? new BorderRadius.circular(0.0), | |
child: new Material( | |
color: widget.color, | |
child: new Container( | |
constraints: | |
const BoxConstraints(minWidth: 88.0, minHeight: 36.0), | |
decoration: new BoxDecoration( | |
border: widget.border, | |
borderRadius: widget.borderRadius, | |
), | |
child: _platformButton(context, enabled), | |
), | |
), | |
), | |
), | |
); | |
} | |
static TargetPlatform _currentPlatform({BuildContext context}) { | |
if (context != null) { | |
return Theme.of(context).platform; | |
} | |
if (Platform.isIOS) return TargetPlatform.iOS; | |
if (Platform.isAndroid) return TargetPlatform.android; | |
if (Platform.isFuchsia) return TargetPlatform.fuchsia; | |
throw Exception("Unsupported platform"); | |
} | |
// widget generation | |
_platformButton(BuildContext context, bool isEnabled) { | |
final ButtonThemeData buttonTheme = ButtonTheme.of(context); | |
final targetPlatform = | |
widget.platform ?? _currentPlatform(context: context); | |
if (targetPlatform == TargetPlatform.iOS) { | |
return _iOSButton(context, buttonTheme, isEnabled); | |
} else { | |
return _androidButton(context, buttonTheme, isEnabled); | |
} | |
} | |
_androidButton( | |
BuildContext context, ButtonThemeData buttonTheme, bool isEnabled) { | |
return new InkWell( | |
highlightColor: widget.highlightColor, | |
splashColor: widget.splashColor, | |
onTap: widget.enabled ? widget.onPressed : null, | |
child: new Container( | |
child: new Center( | |
widthFactor: 1.0, | |
heightFactor: 1.0, | |
child: new Padding( | |
padding: widget.padding ?? new EdgeInsets.all(0.0), | |
child: widget.child, | |
), | |
), | |
), | |
); | |
} | |
_iOSButton( | |
BuildContext context, ButtonThemeData buttonTheme, bool isEnabled) { | |
return GestureDetector( | |
behavior: HitTestBehavior.opaque, | |
onTapDown: isEnabled ? _handleTapDown : null, | |
onTapUp: isEnabled ? _handleTapUp : null, | |
onTapCancel: isEnabled ? _handleTapCancel : null, | |
onTap: isEnabled ? _handleTap : null, | |
child: new Stack( | |
fit: StackFit.passthrough, | |
children: <Widget>[ | |
new Positioned.fill( | |
child: new FadeTransition( | |
opacity: _opacityTween.animate(new CurvedAnimation( | |
parent: _animationController, | |
curve: Curves.decelerate, | |
)), | |
child: new Material(color: widget.splashColor), | |
), | |
), | |
new Container( | |
child: new Center( | |
widthFactor: 1.0, | |
heightFactor: 1.0, | |
child: new Padding( | |
padding: widget.padding ?? new EdgeInsets.all(0.0), | |
child: widget.child, | |
), | |
), | |
), | |
], | |
), | |
); | |
} | |
// animation | |
_animate({bool quickHit = false, bool quickOut = false}) { | |
if (quickHit) { | |
final Future<Null> ticker = !quickOut | |
? _animationController.animateTo(1.0, duration: quickFadeInDuration) | |
: _animationController.animateTo(0.0, duration: quickFadeOutDuration); | |
ticker.then((Null value) { | |
if (mounted && !quickOut) { | |
_animate(quickHit: true, quickOut: true); | |
} | |
}); | |
return; | |
} | |
final bool wasHeldDown = _buttonHeldDown; | |
if (_animationController.isAnimating) return; | |
final Future<Null> ticker = _buttonHeldDown | |
? _animationController.animateTo(1.0, duration: fadeInDuration) | |
: _animationController.animateTo(0.0, duration: fadeOutDuration); | |
ticker.then((Null value) { | |
if (mounted && wasHeldDown != _buttonHeldDown) { | |
_animate(); | |
} | |
}); | |
} | |
// gesture detection | |
bool _buttonHeldDown = false; | |
void _handleTapDown(TapDownDetails event) { | |
_animationController.reset(); | |
if (!_buttonHeldDown) { | |
_buttonHeldDown = true; | |
_animate(); | |
} | |
} | |
void _handleTapUp(TapUpDetails event) { | |
if (_buttonHeldDown) { | |
_buttonHeldDown = false; | |
if (_animationController.value <= 0.2) { | |
_animate(quickHit: true); | |
} else { | |
_animationController.reset(); | |
} | |
} | |
} | |
void _handleTap() { | |
widget.onPressed(); | |
} | |
void _handleTapCancel() { | |
if (_buttonHeldDown) { | |
_buttonHeldDown = false; | |
_animate(); | |
} | |
} | |
// clean up | |
@override | |
void dispose() { | |
_animationController.dispose(); | |
_animationController = null; | |
super.dispose(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
how about change
_animate
to this code? i cast it because i get error