import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:media_kit/media_kit.dart'; import '../app/tema.dart'; /// Uygulama geneli tek ses çalar — galeri kartları bunu kullanır. final sesCalarProvider = NotifierProvider(SesCalarNotifier.new); class SesCalarDurumu { const SesCalarDurumu({ this.dosya, this.caliyor = false, this.konum = Duration.zero, this.sure = Duration.zero, }); final String? dosya; final bool caliyor; final Duration konum; final Duration sure; } class SesCalarNotifier extends Notifier { late final Player _player; @override SesCalarDurumu build() { _player = Player(); final abonelikler = [ _player.stream.playing.listen((v) => state = _kopyala(caliyor: v)), _player.stream.position.listen((v) => state = _kopyala(konum: v)), _player.stream.duration.listen((v) => state = _kopyala(sure: v)), _player.stream.completed.listen((bitti) { if (bitti) state = _kopyala(caliyor: false, konum: Duration.zero); }), ]; ref.onDispose(() { for (final a in abonelikler) { a.cancel(); } _player.dispose(); }); return const SesCalarDurumu(); } SesCalarDurumu _kopyala( {String? dosya, bool? caliyor, Duration? konum, Duration? sure}) => SesCalarDurumu( dosya: dosya ?? state.dosya, caliyor: caliyor ?? state.caliyor, konum: konum ?? state.konum, sure: sure ?? state.sure, ); Future calDurdur(String dosya) async { if (state.dosya == dosya && state.caliyor) { await _player.pause(); return; } if (state.dosya == dosya) { await _player.play(); return; } state = _kopyala(dosya: dosya, konum: Duration.zero); await _player.open(Media(dosya)); } Future atla(Duration konum) => _player.seek(konum); Future durdur() async { await _player.pause(); } } /// Galeri kartı içi kompakt çalar şeridi. class SesCalarSeridi extends ConsumerWidget { const SesCalarSeridi({super.key, required this.dosyaYolu}); final String dosyaYolu; @override Widget build(BuildContext context, WidgetRef ref) { final durum = ref.watch(sesCalarProvider); final benimki = durum.dosya == dosyaYolu; final caliyor = benimki && durum.caliyor; final varMi = File(dosyaYolu).existsSync(); return Row( children: [ IconButton( icon: Icon( caliyor ? Icons.pause_circle_filled : Icons.play_circle_fill, size: 32, color: varMi ? FikoRenkler.neonCyan : Colors.grey, ), onPressed: varMi ? () => ref.read(sesCalarProvider.notifier).calDurdur(dosyaYolu) : null, ), Expanded( child: benimki && durum.sure > Duration.zero ? Slider( value: durum.konum.inMilliseconds .clamp(0, durum.sure.inMilliseconds) .toDouble(), max: durum.sure.inMilliseconds.toDouble(), onChanged: (v) => ref .read(sesCalarProvider.notifier) .atla(Duration(milliseconds: v.round())), ) : const SizedBox(height: 20), ), if (benimki && durum.sure > Duration.zero) Text( '${_fmt(durum.konum)} / ${_fmt(durum.sure)}', style: const TextStyle(fontSize: 10.5), ), ], ); } static String _fmt(Duration d) => '${d.inMinutes}:${(d.inSeconds % 60).toString().padLeft(2, '0')}'; }