feat: add more easter eggs
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
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<VenueTitle> createState() => _VenueTitleState();
|
||||
}
|
||||
|
||||
class _VenueTitleState extends State<VenueTitle>
|
||||
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,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user