Created
January 30, 2026 01:22
-
-
Save JaredEzz/c8507e50f73b5ca803f98a0f2bad0639 to your computer and use it in GitHub Desktop.
checkin-reminder
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import 'package:flutter/material.dart'; | |
| import 'dart:math' as math; | |
| import 'dart:async'; | |
| void main() { | |
| runApp(const MaterialApp( | |
| debugShowCheckedModeBanner: false, | |
| home: Scaffold( | |
| backgroundColor: Color(0xFF1E1E2C), // Dark theme background | |
| body: Center( | |
| child: FunCheckinReminder(), | |
| ), | |
| ), | |
| )); | |
| } | |
| class FunCheckinReminder extends StatefulWidget { | |
| const FunCheckinReminder({super.key}); | |
| @override | |
| State<FunCheckinReminder> createState() => _FunCheckinReminderState(); | |
| } | |
| class _FunCheckinReminderState extends State<FunCheckinReminder> | |
| with TickerProviderStateMixin { | |
| // Mouse position for the 3D tilt effect | |
| double x = 0.0; | |
| double y = 0.0; | |
| bool isHovering = false; | |
| // Animation controllers | |
| late AnimationController _colorController; | |
| late AnimationController _wobbleController; | |
| @override | |
| void initState() { | |
| super.initState(); | |
| // Cycle through colors continuously | |
| _colorController = AnimationController( | |
| vsync: this, | |
| duration: const Duration(seconds: 5), | |
| )..repeat(); | |
| // Controls the "wave" of the text | |
| _wobbleController = AnimationController( | |
| vsync: this, | |
| duration: const Duration(milliseconds: 1500), | |
| )..repeat(); | |
| } | |
| @override | |
| void dispose() { | |
| _colorController.dispose(); | |
| _wobbleController.dispose(); | |
| super.dispose(); | |
| } | |
| // Updates the x/y coordinates based on mouse position relative to center | |
| void _updateLocation(PointerEvent details) { | |
| setState(() { | |
| // We limit the rotation so it doesn't flip over completely | |
| // values usually range from -1 to 1 for full width | |
| // We divide by a factor to make the tilt subtle | |
| x = (details.localPosition.dx - 150) / 300; | |
| y = (details.localPosition.dy - 100) / 200; | |
| }); | |
| } | |
| @override | |
| Widget build(BuildContext context) { | |
| return MouseRegion( | |
| onEnter: (_) => setState(() => isHovering = true), | |
| onExit: (_) => setState(() { | |
| isHovering = false; | |
| x = 0; | |
| y = 0; | |
| }), | |
| onHover: _updateLocation, | |
| child: AnimatedBuilder( | |
| animation: Listenable.merge([_colorController, _wobbleController]), | |
| builder: (context, child) { | |
| // Dynamic Rainbow Color | |
| final Color primaryColor = | |
| HSVColor.fromAHSV(1.0, _colorController.value * 360, 0.8, 1.0) | |
| .toColor(); | |
| final Color secondaryColor = | |
| HSVColor.fromAHSV(1.0, (_colorController.value * 360 + 180) % 360, 0.8, 1.0) | |
| .toColor(); | |
| // Matrix for 3D Tilt | |
| final matrix = Matrix4.identity() | |
| ..setEntry(3, 2, 0.001) // Perspective | |
| ..rotateX(isHovering ? -y * 0.5 : 0) // Tilt X | |
| ..rotateY(isHovering ? x * 0.5 : 0); // Tilt Y | |
| return Transform( | |
| transform: matrix, | |
| alignment: Alignment.center, | |
| child: AnimatedContainer( | |
| duration: const Duration(milliseconds: 200), | |
| curve: Curves.easeOut, | |
| width: 350, | |
| height: 200, | |
| decoration: BoxDecoration( | |
| color: Colors.black.withOpacity(0.8), | |
| borderRadius: BorderRadius.circular(20), | |
| border: Border.all( | |
| color: isHovering ? primaryColor : Colors.white24, | |
| width: isHovering ? 3 : 1, | |
| ), | |
| boxShadow: [ | |
| BoxShadow( | |
| color: primaryColor.withOpacity(isHovering ? 0.6 : 0), | |
| blurRadius: isHovering ? 30 : 0, | |
| spreadRadius: isHovering ? 5 : 0, | |
| offset: Offset(x * 20, y * 20), | |
| ) | |
| ], | |
| ), | |
| child: Stack( | |
| children: [ | |
| // Animated Background shapes | |
| Positioned( | |
| top: -50, | |
| right: -50, | |
| child: Transform.rotate( | |
| angle: _colorController.value * 2 * math.pi, | |
| child: Container( | |
| width: 100, | |
| height: 100, | |
| decoration: BoxDecoration( | |
| shape: BoxShape.circle, | |
| gradient: LinearGradient(colors: [primaryColor, secondaryColor]) | |
| ), | |
| ), | |
| ), | |
| ), | |
| // Main Content | |
| Center( | |
| child: Column( | |
| mainAxisAlignment: MainAxisAlignment.center, | |
| children: [ | |
| Icon( | |
| Icons.check_circle_outline, | |
| color: secondaryColor, | |
| size: isHovering ? 50 : 40 | |
| ), | |
| const SizedBox(height: 20), | |
| WavyText( | |
| text: "Remember to do\nyour Checkins!", | |
| color: Colors.white, | |
| animValue: _wobbleController.value, | |
| isHovering: isHovering, | |
| ), | |
| ], | |
| ), | |
| ), | |
| ], | |
| ), | |
| ), | |
| ); | |
| }, | |
| ), | |
| ); | |
| } | |
| } | |
| // Helper Widget to split text into characters and animate them | |
| class WavyText extends StatelessWidget { | |
| final String text; | |
| final Color color; | |
| final double animValue; | |
| final bool isHovering; | |
| const WavyText({ | |
| super.key, | |
| required this.text, | |
| required this.color, | |
| required this.animValue, | |
| required this.isHovering, | |
| }); | |
| @override | |
| Widget build(BuildContext context) { | |
| // Split text into lines to handle newlines | |
| List<Widget> lines = []; | |
| List<String> textLines = text.split('\n'); | |
| int globalCharIndex = 0; | |
| for (var line in textLines) { | |
| List<Widget> chars = []; | |
| for (int i = 0; i < line.length; i++) { | |
| String char = line[i]; | |
| // Math for the wave effect | |
| // We use sine to create the up/down motion | |
| double offset = math.sin((animValue * 2 * math.pi) + (globalCharIndex * 0.4)) * 6; | |
| // If hovering, we make the wave bigger! | |
| double effectiveOffset = isHovering ? offset : offset * 0.3; | |
| chars.add( | |
| Transform.translate( | |
| offset: Offset(0, effectiveOffset), | |
| child: Text( | |
| char, | |
| style: TextStyle( | |
| color: color, | |
| fontSize: 24, | |
| fontWeight: FontWeight.bold, | |
| fontFamily: 'Courier', | |
| letterSpacing: 2, | |
| shadows: [ | |
| Shadow( | |
| blurRadius: 10, | |
| color: color.withOpacity(0.5), | |
| offset: const Offset(0, 5), | |
| ) | |
| ] | |
| ), | |
| ), | |
| ), | |
| ); | |
| globalCharIndex++; | |
| } | |
| lines.add(Row(mainAxisAlignment: MainAxisAlignment.center, children: chars)); | |
| } | |
| return Column( | |
| mainAxisSize: MainAxisSize.min, | |
| children: lines, | |
| ); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment