POLISHING STUFF and Smarter AI, and alot i mean.. ALOT OF BUG FIX AND OPTIMIZE
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
export 'bounce_tap.dart';
|
||||
export 'fade_slide_wrapper.dart';
|
||||
export 'stagger_wrapper.dart';
|
||||
@@ -0,0 +1,90 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class BounceTap extends StatefulWidget {
|
||||
final Widget child;
|
||||
final VoidCallback? onTap;
|
||||
final HitTestBehavior behavior;
|
||||
final Duration pressDuration;
|
||||
final Duration releaseDuration;
|
||||
|
||||
const BounceTap({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.onTap,
|
||||
this.behavior = HitTestBehavior.opaque,
|
||||
this.pressDuration = const Duration(milliseconds: 80),
|
||||
this.releaseDuration = const Duration(milliseconds: 120),
|
||||
});
|
||||
|
||||
@override
|
||||
State<BounceTap> createState() => _BounceTapState();
|
||||
}
|
||||
|
||||
class _BounceTapState extends State<BounceTap>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _controller;
|
||||
late final Animation<double> _scale;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
value: 1,
|
||||
lowerBound: 0.94,
|
||||
upperBound: 1,
|
||||
duration: widget.pressDuration,
|
||||
reverseDuration: widget.releaseDuration,
|
||||
);
|
||||
_scale = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.easeOut,
|
||||
reverseCurve: Curves.elasticOut,
|
||||
);
|
||||
}
|
||||
|
||||
void _press() {
|
||||
if (widget.onTap == null) return;
|
||||
_controller.animateTo(
|
||||
0.94,
|
||||
duration: widget.pressDuration,
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
}
|
||||
|
||||
void _release({bool triggerTap = false}) {
|
||||
if (widget.onTap == null) return;
|
||||
_controller.animateTo(
|
||||
1,
|
||||
duration: widget.releaseDuration,
|
||||
curve: Curves.elasticOut,
|
||||
);
|
||||
if (triggerTap) {
|
||||
widget.onTap?.call();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Listener(
|
||||
behavior: widget.behavior,
|
||||
onPointerDown: (_) => _press(),
|
||||
onPointerCancel: (_) => _release(),
|
||||
onPointerUp: (_) => _release(triggerTap: true),
|
||||
child: AnimatedBuilder(
|
||||
animation: _scale,
|
||||
child: widget.child,
|
||||
builder: (context, child) => Transform.scale(
|
||||
scale: _scale.value,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class FadeSlideWrapper extends StatefulWidget {
|
||||
final Widget child;
|
||||
final Duration delay;
|
||||
final Duration duration;
|
||||
|
||||
const FadeSlideWrapper({
|
||||
super.key,
|
||||
required this.child,
|
||||
this.delay = Duration.zero,
|
||||
this.duration = const Duration(milliseconds: 400),
|
||||
});
|
||||
|
||||
@override
|
||||
State<FadeSlideWrapper> createState() => _FadeSlideWrapperState();
|
||||
}
|
||||
|
||||
class _FadeSlideWrapperState extends State<FadeSlideWrapper>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _controller;
|
||||
late final Animation<double> _opacity;
|
||||
late final Animation<Offset> _offset;
|
||||
Timer? _delayTimer;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(
|
||||
vsync: this,
|
||||
duration: widget.duration,
|
||||
);
|
||||
final curved = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.easeOutCubic,
|
||||
);
|
||||
_opacity = Tween<double>(begin: 0, end: 1).animate(curved);
|
||||
_offset = Tween<Offset>(
|
||||
begin: const Offset(0, 30),
|
||||
end: Offset.zero,
|
||||
).animate(curved);
|
||||
|
||||
if (widget.delay == Duration.zero) {
|
||||
_controller.forward();
|
||||
} else {
|
||||
_delayTimer = Timer(widget.delay, () {
|
||||
if (mounted) _controller.forward();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_delayTimer?.cancel();
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedBuilder(
|
||||
animation: _controller,
|
||||
child: widget.child,
|
||||
builder: (context, child) => Opacity(
|
||||
opacity: _opacity.value,
|
||||
child: Transform.translate(
|
||||
offset: _offset.value,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class StaggerWrapper extends StatefulWidget {
|
||||
final List<Widget> children;
|
||||
final Axis direction;
|
||||
final Duration duration;
|
||||
final Duration stagger;
|
||||
final MainAxisAlignment mainAxisAlignment;
|
||||
final CrossAxisAlignment crossAxisAlignment;
|
||||
final MainAxisSize mainAxisSize;
|
||||
final TextDirection? textDirection;
|
||||
final VerticalDirection verticalDirection;
|
||||
final TextBaseline? textBaseline;
|
||||
|
||||
const StaggerWrapper({
|
||||
super.key,
|
||||
required this.children,
|
||||
this.direction = Axis.vertical,
|
||||
this.duration = const Duration(milliseconds: 400),
|
||||
this.stagger = const Duration(milliseconds: 60),
|
||||
this.mainAxisAlignment = MainAxisAlignment.start,
|
||||
this.crossAxisAlignment = CrossAxisAlignment.center,
|
||||
this.mainAxisSize = MainAxisSize.max,
|
||||
this.textDirection,
|
||||
this.verticalDirection = VerticalDirection.down,
|
||||
this.textBaseline,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StaggerWrapper> createState() => _StaggerWrapperState();
|
||||
}
|
||||
|
||||
class _StaggerWrapperState extends State<StaggerWrapper>
|
||||
with SingleTickerProviderStateMixin {
|
||||
late AnimationController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = _createController()..forward();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant StaggerWrapper oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.children.length != widget.children.length ||
|
||||
oldWidget.duration != widget.duration ||
|
||||
oldWidget.stagger != widget.stagger) {
|
||||
_controller.dispose();
|
||||
_controller = _createController()..forward();
|
||||
}
|
||||
}
|
||||
|
||||
AnimationController _createController() {
|
||||
final totalDuration = widget.duration +
|
||||
widget.stagger *
|
||||
(widget.children.isEmpty ? 0 : widget.children.length - 1);
|
||||
return AnimationController(vsync: this, duration: totalDuration);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final animatedChildren = List<Widget>.generate(
|
||||
widget.children.length,
|
||||
(index) => _StaggeredChild(
|
||||
controller: _controller,
|
||||
duration: widget.duration,
|
||||
stagger: widget.stagger,
|
||||
index: index,
|
||||
child: widget.children[index],
|
||||
),
|
||||
);
|
||||
|
||||
if (widget.direction == Axis.horizontal) {
|
||||
return Row(
|
||||
mainAxisAlignment: widget.mainAxisAlignment,
|
||||
crossAxisAlignment: widget.crossAxisAlignment,
|
||||
mainAxisSize: widget.mainAxisSize,
|
||||
textDirection: widget.textDirection,
|
||||
verticalDirection: widget.verticalDirection,
|
||||
textBaseline: widget.textBaseline,
|
||||
children: animatedChildren,
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: widget.mainAxisAlignment,
|
||||
crossAxisAlignment: widget.crossAxisAlignment,
|
||||
mainAxisSize: widget.mainAxisSize,
|
||||
textDirection: widget.textDirection,
|
||||
verticalDirection: widget.verticalDirection,
|
||||
textBaseline: widget.textBaseline,
|
||||
children: animatedChildren,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _StaggeredChild extends StatelessWidget {
|
||||
final AnimationController controller;
|
||||
final Duration duration;
|
||||
final Duration stagger;
|
||||
final int index;
|
||||
final Widget child;
|
||||
|
||||
const _StaggeredChild({
|
||||
required this.controller,
|
||||
required this.duration,
|
||||
required this.stagger,
|
||||
required this.index,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final totalMs =
|
||||
controller.duration?.inMilliseconds ?? duration.inMilliseconds;
|
||||
final beginMs = stagger.inMilliseconds * index;
|
||||
final endMs = beginMs + duration.inMilliseconds;
|
||||
final begin = totalMs <= 0 ? 0.0 : (beginMs / totalMs).clamp(0.0, 1.0);
|
||||
final end = totalMs <= 0 ? 1.0 : (endMs / totalMs).clamp(begin, 1.0);
|
||||
final curve = CurvedAnimation(
|
||||
parent: controller,
|
||||
curve: Interval(begin, end, curve: Curves.easeOutCubic),
|
||||
);
|
||||
final opacity = Tween<double>(begin: 0, end: 1).animate(curve);
|
||||
final offset = Tween<Offset>(
|
||||
begin: const Offset(0, 30),
|
||||
end: Offset.zero,
|
||||
).animate(curve);
|
||||
|
||||
return AnimatedBuilder(
|
||||
animation: controller,
|
||||
child: child,
|
||||
builder: (context, child) => Opacity(
|
||||
opacity: opacity.value,
|
||||
child: Transform.translate(
|
||||
offset: offset.value,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user