Remove arrow animation (better app pack UX incoming)

This commit is contained in:
franzap
2025-12-22 21:32:16 -03:00
parent ba937a7907
commit dbb758cac0
+13 -154
View File
@@ -53,91 +53,25 @@ class AppPackContainer extends HookConsumerWidget {
orElse: () => packs.first,
);
// Scroll controller and hint visibility state
final scrollController = useScrollController();
final showRightHint = useState(true);
final hasScrolledOnce = useState(false);
// Listen to scroll position to hide hint when near end
useEffect(() {
void listener() {
if (!scrollController.hasClients) return;
final maxScroll = scrollController.position.maxScrollExtent;
final currentScroll = scrollController.offset;
// Hide hint when scrolled past 20px or near the end
if (currentScroll > 20) {
hasScrolledOnce.value = true;
}
showRightHint.value =
!hasScrolledOnce.value && currentScroll < maxScroll - 20;
}
// Check initial state after first frame
WidgetsBinding.instance.addPostFrameCallback((_) {
if (scrollController.hasClients) {
final maxScroll = scrollController.position.maxScrollExtent;
// Hide hint if content doesn't overflow
showRightHint.value = maxScroll > 20;
}
});
scrollController.addListener(listener);
return () => scrollController.removeListener(listener);
}, [scrollController]);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 48,
child: Stack(
children: [
SingleChildScrollView(
controller: scrollController,
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.only(left: 12, right: 52),
child: Row(
children: packs
.map(
(pack) => _PackPill(
pack: pack,
isSelected: selectedId.value == pack.id,
onTap: () => selectedId.value = pack.id,
),
)
.toList(),
),
),
// Right fade gradient with animated chevron
if (showRightHint.value)
Positioned(
right: 0,
top: 0,
bottom: 0,
width: 56,
child: IgnorePointer(
child: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [
Colors.transparent,
AppColors.darkBackground,
AppColors.darkBackground,
],
stops: [0.0, 0.5, 1.0],
),
),
child: const Align(
alignment: Alignment(0.6, -0.1),
child: _AnimatedScrollHint(),
),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 12),
child: Row(
children: packs
.map(
(pack) => _PackPill(
pack: pack,
isSelected: selectedId.value == pack.id,
onTap: () => selectedId.value = pack.id,
),
),
),
],
)
.toList(),
),
),
),
const SizedBox(height: 16),
@@ -205,81 +139,6 @@ class _SkeletonPill extends StatelessWidget {
}
}
/// Animated chevron hint for horizontal scroll discovery
class _AnimatedScrollHint extends StatefulWidget {
const _AnimatedScrollHint();
@override
State<_AnimatedScrollHint> createState() => _AnimatedScrollHintState();
}
class _AnimatedScrollHintState extends State<_AnimatedScrollHint>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _offsetAnimation;
late Animation<double> _opacityAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: const Duration(milliseconds: 900),
vsync: this,
)..repeat(reverse: true);
_offsetAnimation = Tween<double>(
begin: 0,
end: 6,
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut));
_opacityAnimation = Tween<double>(
begin: 0.7,
end: 1.0,
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut));
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
builder: (context, child) => Transform.translate(
offset: Offset(_offsetAnimation.value, 0),
child: Opacity(
opacity: _opacityAnimation.value,
// Stack icons with slight offsets to create bold effect
child: SizedBox(
width: 20,
height: 20,
child: Stack(
children: [
Positioned(
left: 0.5,
child: Icon(
Icons.arrow_forward_ios_rounded,
color: Colors.white.withValues(alpha: 0.7),
size: 20,
),
),
const Icon(
Icons.arrow_forward_ios_rounded,
color: Colors.white,
size: 20,
),
],
),
),
),
),
);
}
}
/// Individual pack pill - watches its own author reactively
class _PackPill extends ConsumerWidget {
const _PackPill({