import 'package:flutter/material.dart'; /// The bar screen title. Ten quick taps light it up as a flickering neon /// café sign for a few seconds. class VenueTitle extends StatefulWidget { final String text; const VenueTitle({super.key, required this.text}); @override State createState() => _VenueTitleState(); } class _VenueTitleState extends State with SingleTickerProviderStateMixin { static const _tapsNeeded = 10; static const _tapWindow = Duration(milliseconds: 600); static const _neonPink = Color(0xFFFF2BD6); /// Stretches of the animation (as fractions) where the tube is dark, /// like a sign that sputters on and then dies out. static const _darkSpans = [ (0.02, 0.05), (0.07, 0.08), (0.10, 0.14), (0.55, 0.56), (0.88, 0.89), (0.91, 0.93), ]; late final AnimationController _neon = AnimationController( vsync: this, duration: const Duration(seconds: 5), ); int _tapCount = 0; DateTime? _lastTap; @override void dispose() { _neon.dispose(); super.dispose(); } void _onTap() { final now = DateTime.now(); final last = _lastTap; _tapCount = last != null && now.difference(last) < _tapWindow ? _tapCount + 1 : 1; _lastTap = now; if (_tapCount < _tapsNeeded) return; _tapCount = 0; _neon.forward(from: 0); } @override Widget build(BuildContext context) { final baseStyle = DefaultTextStyle.of(context).style; return GestureDetector( behavior: HitTestBehavior.opaque, onTap: _onTap, child: AnimatedBuilder( animation: _neon, builder: (context, _) { final t = _neon.value; final isLit = _neon.isAnimating && !_darkSpans.any((span) => t >= span.$1 && t < span.$2); return Text( widget.text, style: isLit ? baseStyle.copyWith( color: const Color(0xFFFFD6F6), fontWeight: FontWeight.w800, shadows: const [ Shadow(color: _neonPink, blurRadius: 4), Shadow(color: _neonPink, blurRadius: 12), Shadow(color: _neonPink, blurRadius: 24), ], ) : _neon.isAnimating ? baseStyle.copyWith( color: baseStyle.color?.withValues(alpha: 0.35), fontWeight: FontWeight.w800, ) : baseStyle, ); }, ), ); } }