feat: Murattal player enhancements & prayer schedule auto-scroll

- Murattal: Spotify-style 5-button controls [Shuffle, Prev, Play, Next, Playlist]
- Murattal: Animated 7-bar equalizer visualization in player circle
- Murattal: Unsplash API background with frosted glass player overlay
- Murattal: Transparent AppBar with backdrop blur
- Murattal: Surah playlist bottom sheet with full 114 Surah list
- Murattal: Auto-play disabled on screen open, enabled on navigation
- Murattal: Shuffle mode for random Surah playback
- Murattal: Photographer attribution per Unsplash guidelines
- Dashboard: Auto-scroll prayer schedule to next active prayer
- Fix: setState lifecycle errors on Reading & Murattal screens
- Setup: flutter_dotenv, cached_network_image, url_launcher deps
This commit is contained in:
dwindown
2026-03-13 15:42:17 +07:00
commit faadc1865d
189 changed files with 23834 additions and 0 deletions

View File

@@ -0,0 +1,58 @@
import 'package:flutter/material.dart';
import '../../app/theme/app_colors.dart';
/// Custom iOS-style toggle switch (51×31dp) per PRD §6.9.
/// Uses AnimatedContainer + GestureDetector for smooth animation.
class IosToggle extends StatelessWidget {
const IosToggle({
super.key,
required this.value,
required this.onChanged,
});
final bool value;
final ValueChanged<bool> onChanged;
static const double _width = 51.0;
static const double _height = 31.0;
static const double _thumbSize = 27.0;
static const double _thumbPadding = 2.0;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => onChanged(!value),
child: AnimatedContainer(
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
width: _width,
height: _height,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(_height / 2),
color: value ? AppColors.primary : AppColors.cream,
),
child: AnimatedAlign(
duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut,
alignment: value ? Alignment.centerRight : Alignment.centerLeft,
child: Container(
width: _thumbSize,
height: _thumbSize,
margin: const EdgeInsets.all(_thumbPadding),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.15),
blurRadius: 4,
offset: const Offset(0, 2),
),
],
),
),
),
),
);
}
}