Skip to content

Instantly share code, notes, and snippets.

@lukef
Created Jun 24, 2018
Embed
What would you like to do?
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();
}
}
@pishguy
Copy link

pishguy commented May 9, 2019

how about change _animate to this code? i cast it because i get error

  _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)) as Future<Null>;
      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)) as Future<Null>;
    ticker.then((Null value) {
      if (mounted && wasHeldDown != _buttonHeldDown) {
        _animate();
      }
    });
  }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment