Skip to content

Instantly share code, notes, and snippets.

@mkiisoft
Created June 22, 2019 03:44
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save mkiisoft/0d5c013114a2101d69f5d4a780d936cd to your computer and use it in GitHub Desktop.
Save mkiisoft/0d5c013114a2101d69f5d4a780d936cd to your computer and use it in GitHub Desktop.
Fancy Button made for Flutter Games and Apps
import 'package:flutter/material.dart';
class FancyButton extends StatefulWidget {
const FancyButton({
Key key,
@required this.child,
@required this.size,
@required this.color,
this.duration = const Duration(milliseconds: 160),
this.onPressed,
}) : super(key: key);
final Widget child;
final Color color;
final Duration duration;
final VoidCallback onPressed;
final double size;
@override
_FancyButtonState createState() => _FancyButtonState();
}
class _FancyButtonState extends State<FancyButton> with TickerProviderStateMixin {
AnimationController _animationController;
Animation<double> _pressedAnimation;
TickerFuture _downTicker;
double get buttonDepth => widget.size * 0.2;
void _setupAnimation() {
_animationController?.stop();
final oldControllerValue = _animationController?.value ?? 0.0;
_animationController?.dispose();
_animationController = AnimationController(
duration: Duration(microseconds: widget.duration.inMicroseconds ~/ 2),
vsync: this,
value: oldControllerValue,
);
_pressedAnimation = Tween<double>(begin: -buttonDepth, end: 0.0).animate(
CurvedAnimation(parent: _animationController, curve: Curves.easeInOut),
);
}
@override
void didUpdateWidget(FancyButton oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.duration != widget.duration) {
_setupAnimation();
}
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
_setupAnimation();
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
void _onTapDown(_) {
if (widget.onPressed != null) {
_downTicker = _animationController.animateTo(1.0);
}
}
void _onTapUp(_) {
if (widget.onPressed != null) {
_downTicker.whenComplete(() {
_animationController.animateTo(0.0);
widget.onPressed?.call();
});
}
}
void _onTapCancel() {
if (widget.onPressed != null) {
_animationController.reset();
}
}
@override
Widget build(BuildContext context) {
final vertPadding = widget.size * 0.25;
final horzPadding = widget.size * 0.50;
final radius = BorderRadius.circular(horzPadding * 0.5);
return Container(
padding: widget.onPressed != null ? EdgeInsets.only(bottom: 2, left: 0.5, right: 0.5) : null,
decoration: BoxDecoration(
color: Colors.black87,
borderRadius: radius,
),
child: GestureDetector(
onTapDown: _onTapDown,
onTapUp: _onTapUp,
onTapCancel: _onTapCancel,
child: IntrinsicWidth(
child: IntrinsicHeight(
child: Stack(
children: <Widget>[
Container(
decoration: BoxDecoration(
color: _hslRelativeColor(s: -0.20, l: -0.20),
borderRadius: radius,
),
),
AnimatedBuilder(
animation: _pressedAnimation,
builder: (BuildContext context, Widget child) {
return Transform.translate(
offset: Offset(0.0, _pressedAnimation.value),
child: child,
);
},
child: Stack(
overflow: Overflow.visible,
children: <Widget>[
ClipRRect(
borderRadius: radius,
child: Stack(
children: <Widget>[
DecoratedBox(
decoration: BoxDecoration(
color: _hslRelativeColor(l: 0.06),
borderRadius: radius,
),
child: SizedBox.expand(),
),
Transform.translate(
offset: Offset(0.0, vertPadding * 2),
child: DecoratedBox(
decoration: BoxDecoration(
color: _hslRelativeColor(),
borderRadius: radius,
),
child: SizedBox.expand(),
),
),
],
),
),
Padding(
padding: EdgeInsets.symmetric(
vertical: vertPadding,
horizontal: horzPadding,
),
child: widget.child,
)
],
),
),
],
),
),
),
),
);
}
Color _hslRelativeColor({double h = 0.0, s = 0.0, l = 0.0}) {
final hslColor = HSLColor.fromColor(widget.color);
h = (hslColor.hue + h).clamp(0.0, 360.0);
s = (hslColor.saturation + s).clamp(0.0, 1.0);
l = (hslColor.lightness + l).clamp(0.0, 1.0);
return HSLColor.fromAHSL(hslColor.alpha, h, s, l).toColor();
}
}
// USAGE - Fancy Button
// Simple Text - Non Clickable
FancyButton(
child: Text(
"Your Amazing Text",
style: Utils.textStyle(18.0),
),
size: 18,
color: Color(0xFFCA3034),
),
// Complex Button - Clickable
FancyButton(
child: Text(
"X",
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontFamily: 'Gameplay',
),
),
size: 25,
color: Color(0xFFCA3034),
onPressed: () {
Navigator.of(context).pop();
},
),
@maheshmnj
Copy link

maheshmnj commented Aug 19, 2022

Thank you so much for sharing this 🙏🏻, Here's a complete example app, if you want to quickly see the result on dartpad.

code sample
import 'package:flutter/material.dart';

void main() => runApp(ButtonDemo());

class ButtonDemo extends StatefulWidget {
  @override
  _ButtonDemoState createState() => _ButtonDemoState();
}

class _ButtonDemoState extends State<ButtonDemo> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Fancy Button Demo',
      home: Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.spaceAround,
            children: [
              FancyButton(
                child: Text(
                  "Click me",
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 18,
                    fontFamily: 'Gameplay',
                  ),
                ),
                size: 35,
                color: Color(0xFFCA3034),
                onPressed: () {},
              ),
              FancyButton(
                child: Text(
                  "Your Amazing Text",
                ),
                size: 18,
                color: Color(0xFFCA3034),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class FancyButton extends StatefulWidget {
  const FancyButton({
    Key? key,
    required this.child,
    required this.size,
    required this.color,
    this.duration = const Duration(milliseconds: 160),
    this.onPressed,
  }) : super(key: key);

  final Widget child;
  final Color color;
  final Duration duration;
  final VoidCallback? onPressed;

  final double size;

  @override
  _FancyButtonState createState() => _FancyButtonState();
}

class _FancyButtonState extends State<FancyButton>
    with TickerProviderStateMixin {
  AnimationController? _animationController;
  Animation<double>? _pressedAnimation;

  late TickerFuture _downTicker;

  double get buttonDepth => widget.size * 0.2;

  void _setupAnimation() {
    _animationController?.stop();
    final oldControllerValue = _animationController!.value;
    _animationController!.dispose();
    _animationController = AnimationController(
      duration: Duration(microseconds: widget.duration.inMicroseconds ~/ 2),
      vsync: this,
      value: oldControllerValue,
    );
    _pressedAnimation = Tween<double>(begin: -buttonDepth, end: 0.0).animate(
      CurvedAnimation(parent: _animationController!, curve: Curves.easeInOut),
    );
  }

  @override
  void didUpdateWidget(FancyButton oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.duration != widget.duration) {
      _setupAnimation();
    }
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _setupAnimation();
  }

  @override
  void initState() {
    super.initState();
    _animationController = AnimationController(
      duration: Duration(microseconds: widget.duration.inMicroseconds ~/ 2),
      vsync: this,
    );
  }

  @override
  void dispose() {
    _animationController!.dispose();
    super.dispose();
  }

  void _onTapDown(_) {
    if (widget.onPressed != null) {
      _downTicker = _animationController!.animateTo(1.0);
    }
  }

  void _onTapUp(_) {
    if (widget.onPressed != null) {
      _downTicker.whenComplete(() {
        _animationController!.animateTo(0.0);
        widget.onPressed?.call();
      });
    }
  }

  void _onTapCancel() {
    if (widget.onPressed != null) {
      _animationController!.reset();
    }
  }

  @override
  Widget build(BuildContext context) {
    final vertPadding = widget.size * 0.25;
    final horzPadding = widget.size * 0.50;
    final radius = BorderRadius.circular(horzPadding * 0.5);

    return Container(
      padding: widget.onPressed != null
          ? EdgeInsets.only(bottom: 2, left: 0.5, right: 0.5)
          : null,
      decoration: BoxDecoration(
        color: Colors.black87,
        borderRadius: radius,
      ),
      child: GestureDetector(
        onTapDown: _onTapDown,
        onTapUp: _onTapUp,
        onTapCancel: _onTapCancel,
        child: IntrinsicWidth(
          child: IntrinsicHeight(
            child: Stack(
              children: <Widget>[
                Container(
                  decoration: BoxDecoration(
                    color: _hslRelativeColor(s: -0.20, l: -0.20),
                    borderRadius: radius,
                  ),
                ),
                AnimatedBuilder(
                  animation: _pressedAnimation!,
                  builder: (BuildContext context, Widget? child) {
                    return Transform.translate(
                      offset: Offset(0.0, _pressedAnimation!.value),
                      child: child,
                    );
                  },
                  child: Stack(
                    children: <Widget>[
                      ClipRRect(
                        borderRadius: radius,
                        child: Stack(
                          children: <Widget>[
                            DecoratedBox(
                              decoration: BoxDecoration(
                                color: _hslRelativeColor(l: 0.06),
                                borderRadius: radius,
                              ),
                              child: SizedBox.expand(),
                            ),
                            Transform.translate(
                              offset: Offset(0.0, vertPadding * 2),
                              child: DecoratedBox(
                                decoration: BoxDecoration(
                                  color: _hslRelativeColor(),
                                  borderRadius: radius,
                                ),
                                child: SizedBox.expand(),
                              ),
                            ),
                          ],
                        ),
                      ),
                      Padding(
                        padding: EdgeInsets.symmetric(
                          vertical: vertPadding,
                          horizontal: horzPadding,
                        ),
                        child: widget.child,
                      )
                    ],
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }

  Color _hslRelativeColor({double h = 0.0, s = 0.0, l = 0.0}) {
    final hslColor = HSLColor.fromColor(widget.color);
    h = (hslColor.hue + h).clamp(0.0, 360.0);
    s = (hslColor.saturation + s).clamp(0.0, 1.0);
    l = (hslColor.lightness + l).clamp(0.0, 1.0);
    return HSLColor.fromAHSL(hslColor.alpha, h, s, l).toColor();
  }
}

@IlmanNashran
Copy link

nice its help me a lot 😁

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