Skip to content

Instantly share code, notes, and snippets.

@theachoem
Last active January 5, 2022 16:41
Show Gist options
  • Save theachoem/071c1d441bd5c7021a3326129537a66f to your computer and use it in GitHub Desktop.
Save theachoem/071c1d441bd5c7021a3326129537a66f to your computer and use it in GitHub Desktop.
Tap Effect includes Scale down + Touchable Opacity (Flutter)
// Copyright 2021, Thea Choem, All rights reserved.
import 'package:flutter/material.dart';
enum TapEffectType {
touchableOpacity,
scaleDown,
}
class CustomTapEffect extends StatefulWidget {
const CustomTapEffect({
Key? key,
required this.child,
required this.onTap,
this.duration = const Duration(milliseconds: 100),
this.vibrate = false,
this.behavior = HitTestBehavior.opaque,
this.effects = const [
TapEffectType.touchableOpacity,
],
this.onLongPressed,
}) : super(key: key);
final Widget child;
final List<TapEffectType> effects;
final void Function()? onTap;
final void Function()? onLongPressed;
final Duration duration;
final bool vibrate;
final HitTestBehavior? behavior;
@override
State<CustomTapEffect> createState() => _CustomTapEffectState();
}
class _CustomTapEffectState extends State<CustomTapEffect> with SingleTickerProviderStateMixin {
final double scaleActive = 0.98;
final double opacityActive = 0.2;
late AnimationController controller;
late Animation<double> animation;
late Animation<double> animation2;
@override
void initState() {
controller = AnimationController(vsync: this, duration: widget.duration);
animation = Tween<double>(begin: 1, end: scaleActive).animate(controller);
animation2 = Tween<double>(begin: 1, end: opacityActive).animate(controller);
super.initState();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
void onTapCancel() => controller.reverse();
void onTapDown() => controller.forward();
void onTapUp() => controller.reverse().then((value) => widget.onTap!());
@override
Widget build(BuildContext context) {
if (widget.onTap != null) {
return GestureDetector(
behavior: widget.behavior,
onLongPress: widget.onLongPressed,
onTapDown: (detail) => onTapDown(),
onTapUp: (detail) => onTapUp(),
onTapCancel: () => onTapCancel(),
child: buildChild(controller, animation, animation2),
);
} else {
return buildChild(controller, animation, animation2);
}
}
AnimatedBuilder buildChild(
AnimationController controller,
Animation<double> animation,
Animation<double> animation2,
) {
return AnimatedBuilder(
child: widget.child,
animation: controller,
builder: (context, child) {
Widget result = child ?? const SizedBox();
for (var effect in widget.effects) {
switch (effect) {
case TapEffectType.scaleDown:
result = ScaleTransition(scale: animation, child: result);
break;
case TapEffectType.touchableOpacity:
result = Opacity(opacity: animation2.value, child: result);
break;
}
}
return result;
},
);
}
}
@theachoem
Copy link
Author

I use it in several apps include StoryPad.

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