Created
September 28, 2024 21:21
-
-
Save Rahiche/b9f07eb1906caa7273788efd27f26307 to your computer and use it in GitHub Desktop.
lyrics
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 'dart:ui'; | |
import 'package:flutter/material.dart'; | |
void main() { | |
runApp(const MusicPlayerApp()); | |
} | |
class MusicPlayerApp extends StatelessWidget { | |
const MusicPlayerApp({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return MaterialApp( | |
title: 'Music Player', | |
theme: ThemeData( | |
primarySwatch: Colors.blue, | |
visualDensity: VisualDensity.adaptivePlatformDensity, | |
), | |
home: const MusicPlayerUI(), | |
); | |
} | |
} | |
class MusicPlayerUI extends StatelessWidget { | |
const MusicPlayerUI({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
body: Container( | |
decoration: BoxDecoration( | |
gradient: LinearGradient( | |
begin: Alignment.topLeft, | |
end: Alignment.bottomRight, | |
colors: [Colors.brown.shade300, Colors.grey.shade700], | |
), | |
), | |
child: const Column( | |
children: [ | |
Expanded(child: LyricsDisplay()), | |
ControlsSection(), | |
], | |
), | |
), | |
); | |
} | |
} | |
class LyricsDisplay extends StatefulWidget { | |
const LyricsDisplay({Key? key}) : super(key: key); | |
@override | |
State<LyricsDisplay> createState() => _LyricsDisplayState(); | |
} | |
class _LyricsDisplayState extends State<LyricsDisplay> { | |
final List<String> _lyrics = [ | |
"This is where the first line would go", | |
"And here's where you'd put the second", | |
"The third line comes right after", | |
"Fourth line to complete the verse", | |
"", | |
"This is the catchy chorus part", | |
"That everyone likes to sing along", | |
"It usually repeats a couple times", | |
"To really stick in your mind", | |
"", | |
"Now we're back to telling the story", | |
"With new lyrics in the second verse", | |
"But keeping the same melody", | |
"As we did in the first", | |
"", | |
"Sometimes there's a bridge", | |
"With a different feel", | |
"To add variety", | |
"Before the final chorus", | |
"", | |
"This is the catchy chorus part", | |
"That everyone likes to sing along", | |
"It usually repeats a couple times", | |
"To really stick in your mind", | |
"", | |
"And finally we wrap it up", | |
"With a line or two to close", | |
"Leaving listeners with a lasting thought", | |
"As the music fades away", | |
]; | |
final ScrollController _scrollController = ScrollController(); | |
final List<GlobalKey> _textKeys = []; | |
final List<double> _textBlurLevels = []; | |
@override | |
void initState() { | |
super.initState(); | |
_textKeys.addAll(List.generate(_lyrics.length, (_) => GlobalKey())); | |
_textBlurLevels.addAll(List.filled(_lyrics.length, 0.0)); | |
_scrollController.addListener(_updateBlurLevels); | |
WidgetsBinding.instance.addPostFrameCallback((_) => _updateBlurLevels()); | |
} | |
@override | |
void dispose() { | |
_scrollController.removeListener(_updateBlurLevels); | |
_scrollController.dispose(); | |
super.dispose(); | |
} | |
void _updateBlurLevels() { | |
final RenderBox? scrollViewBox = context.findRenderObject() as RenderBox?; | |
if (scrollViewBox == null) return; | |
setState(() { | |
for (int i = 0; i < _textKeys.length; i++) { | |
final RenderBox? textBox = | |
_textKeys[i].currentContext?.findRenderObject() as RenderBox?; | |
if (textBox != null) { | |
final Offset textPosition = textBox.localToGlobal(Offset.zero); | |
if (textPosition.dy < 400 && textPosition.dy > 0.0) { | |
final double normalizedValue = 200.0 / textPosition.dy; | |
_textBlurLevels[i] = normalizedValue.clamp(0.0, 4.0); | |
} | |
} | |
} | |
}); | |
} | |
@override | |
Widget build(BuildContext context) { | |
return Padding( | |
padding: const EdgeInsets.only(top: 8.0), | |
child: SingleChildScrollView( | |
controller: _scrollController, | |
clipBehavior: Clip.none, | |
child: Padding( | |
padding: const EdgeInsets.all(26.0), | |
child: Column( | |
crossAxisAlignment: CrossAxisAlignment.center, | |
children: _lyrics.asMap().entries.map((entry) { | |
final int index = entry.key; | |
final String line = entry.value; | |
return Padding( | |
padding: const EdgeInsets.symmetric(vertical: 4.0), | |
child: Container( | |
key: _textKeys[index], | |
child: ImageFiltered( | |
imageFilter: ImageFilter.blur( | |
sigmaX: _textBlurLevels[index], | |
sigmaY: _textBlurLevels[index], | |
), | |
child: Text( | |
line, | |
style: TextStyle( | |
fontSize: 28, | |
color: Colors.white70.withOpacity( | |
(5 - (_textBlurLevels[index] / 5)).clamp(0.0, 1.0), | |
), | |
), | |
), | |
), | |
), | |
); | |
}).toList(), | |
), | |
), | |
), | |
); | |
} | |
} | |
class ControlsSection extends StatelessWidget { | |
const ControlsSection({Key? key}) : super(key: key); | |
@override | |
Widget build(BuildContext context) { | |
return Column( | |
mainAxisSize: MainAxisSize.min, | |
children: [ | |
Slider( | |
value: 0.17, | |
onChanged: (value) {}, | |
), | |
Padding( | |
padding: const EdgeInsets.symmetric(horizontal: 20), | |
child: Row( | |
mainAxisAlignment: MainAxisAlignment.spaceBetween, | |
children: const [ | |
Text('0:17', style: TextStyle(color: Colors.white70)), | |
Text('-3:24', style: TextStyle(color: Colors.white70)), | |
], | |
), | |
), | |
const SizedBox(height: 20), | |
Row( | |
mainAxisAlignment: MainAxisAlignment.spaceEvenly, | |
children: const [ | |
Icon(Icons.skip_previous, color: Colors.white, size: 40), | |
Icon(Icons.play_arrow, color: Colors.white, size: 60), | |
Icon(Icons.skip_next, color: Colors.white, size: 40), | |
], | |
), | |
const SizedBox(height: 40), | |
], | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment