Skip to content

Instantly share code, notes, and snippets.

@Rahiche
Created September 28, 2024 21:21
Show Gist options
  • Save Rahiche/b9f07eb1906caa7273788efd27f26307 to your computer and use it in GitHub Desktop.
Save Rahiche/b9f07eb1906caa7273788efd27f26307 to your computer and use it in GitHub Desktop.
lyrics
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