import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:video_player_win/video_player_win.dart'; /// 2-9 video varyantını esnek grid içinde yan yana karşılaştırır. class AbKarsilastirma extends StatefulWidget { final List videoYollari; final List? etiketler; final String? mevcutSecilenYol; const AbKarsilastirma({ super.key, required this.videoYollari, this.etiketler, this.mevcutSecilenYol, }); /// Karşılaştırma diyaloğunu açar. Kullanıcı "Bunu seç" derse seçilen yolu döndürür. static Future goster( BuildContext context, { required List videoYollari, List? etiketler, String? mevcutSecilenYol, }) async { if (videoYollari.length < 2) return null; return await showDialog( context: context, builder: (_) => Dialog( child: SizedBox( width: 1200, height: 720, child: AbKarsilastirma( videoYollari: videoYollari, etiketler: etiketler, mevcutSecilenYol: mevcutSecilenYol, ), ), ), ); } @override State createState() => _AbKarsilastirmaState(); } class _AbKarsilastirmaState extends State { late List _ctrls; late List _favoriler; bool _senkron = true; int _masterIndex = 0; final FocusNode _odakNode = FocusNode(); @override void initState() { super.initState(); _ctrls = widget.videoYollari .map((y) => WinVideoPlayerController.file(File(y))) .toList(); _favoriler = List.filled(_ctrls.length, false); for (final c in _ctrls) { c.initialize().then((_) => mounted ? setState(() {}) : null); } WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) _odakNode.requestFocus(); }); } @override void dispose() { for (final c in _ctrls) { c.dispose(); } _odakNode.dispose(); super.dispose(); } ({int satir, int sutun}) _gridHesapla(int n) { final satir = (n <= 2) ? 1 : (n <= 6) ? 2 : 3; final sutun = (n / satir).ceil(); return (satir: satir, sutun: sutun); } void _toplOynatDurakla() { final hepsiOynuyor = _ctrls.every((c) => c.value.isPlaying); for (final c in _ctrls) { hepsiOynuyor ? c.pause() : c.play(); } setState(() {}); } void _basaSar() { for (final c in _ctrls) { c.seekTo(Duration.zero); } } void _tekOynat(int i) { final c = _ctrls[i]; if (_senkron) { _masterIndex = i; final hedefPoz = c.value.position; final oynuyor = c.value.isPlaying; for (var k = 0; k < _ctrls.length; k++) { _ctrls[k].seekTo(hedefPoz); oynuyor ? _ctrls[k].pause() : _ctrls[k].play(); } } else { c.value.isPlaying ? c.pause() : c.play(); } setState(() {}); } void _favoriDegistir(int i) { setState(() => _favoriler[i] = !_favoriler[i]); } void _bunuSec(int i) { Navigator.pop(context, widget.videoYollari[i]); } void _klavyeOlay(KeyEvent e) { if (e is! KeyDownEvent) return; final key = e.logicalKey; if (key == LogicalKeyboardKey.escape) { Navigator.pop(context); return; } if (key == LogicalKeyboardKey.space) { _toplOynatDurakla(); return; } for (var i = 1; i <= 9; i++) { if (key == LogicalKeyboardKey(0x30 + i) || key == LogicalKeyboardKey(0x00100000030 + i)) { if (i - 1 < _ctrls.length) _bunuSec(i - 1); return; } } } @override Widget build(BuildContext context) { final theme = Theme.of(context); final n = _ctrls.length; final grid = _gridHesapla(n); final hepsiOynuyor = _ctrls.every((c) => c.value.isPlaying); return KeyboardListener( focusNode: _odakNode, autofocus: true, onKeyEvent: _klavyeOlay, child: Column( children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), color: theme.colorScheme.surface, child: Row( children: [ Text('A/B Karşılaştırma ($n varyant)', style: theme.textTheme.titleMedium), const SizedBox(width: 16), IconButton( icon: Icon(hepsiOynuyor ? Icons.pause : Icons.play_arrow), tooltip: hepsiOynuyor ? 'Hepsini duraklat' : 'Hepsini oynat', onPressed: _toplOynatDurakla, ), IconButton( icon: const Icon(Icons.replay), tooltip: 'Hepsini başa sar', onPressed: _basaSar, ), const Spacer(), Text('1-9: seç • ESC: kapat • Boşluk: oynat', style: theme.textTheme.bodySmall ?.copyWith(color: theme.hintColor)), const SizedBox(width: 12), Row( children: [ const Text('Senkron', style: TextStyle(fontSize: 12)), Switch( value: _senkron, onChanged: (v) => setState(() => _senkron = v), ), ], ), IconButton( icon: const Icon(Icons.close), tooltip: 'Kapat (ESC)', onPressed: () => Navigator.pop(context), ), ], ), ), Expanded( child: Padding( padding: const EdgeInsets.all(6), child: GridView.count( crossAxisCount: grid.sutun, mainAxisSpacing: 8, crossAxisSpacing: 8, childAspectRatio: 16 / 11, children: List.generate(n, (i) => _varyantHucre(i, theme)), ), ), ), ], ), ); } Widget _varyantHucre(int i, ThemeData theme) { final c = _ctrls[i]; final yol = widget.videoYollari[i]; final etiket = (widget.etiketler != null && i < widget.etiketler!.length) ? widget.etiketler![i] : 'v${i + 1}'; final secili = widget.mevcutSecilenYol == yol; final master = _senkron && _masterIndex == i; return Container( decoration: BoxDecoration( border: Border.all( color: secili ? theme.colorScheme.primary : (master ? theme.colorScheme.tertiary : Colors.white.withValues(alpha: 0.08)), width: secili || master ? 2 : 1, ), borderRadius: BorderRadius.circular(6), ), child: Column( children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( color: theme.colorScheme.primary.withValues(alpha: 0.18), borderRadius: const BorderRadius.vertical(top: Radius.circular(4)), ), child: Row( children: [ Container( padding: const EdgeInsets.symmetric( horizontal: 6, vertical: 1), decoration: BoxDecoration( color: theme.colorScheme.primary, borderRadius: BorderRadius.circular(3), ), child: Text('${i + 1}', style: const TextStyle( fontSize: 10, color: Colors.white, fontWeight: FontWeight.bold)), ), const SizedBox(width: 6), Expanded( child: Text(etiket, overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 12, fontWeight: FontWeight.bold)), ), if (secili) const Padding( padding: EdgeInsets.only(left: 4), child: Icon(Icons.check_circle, size: 14, color: Colors.greenAccent), ), ], ), ), Expanded( child: GestureDetector( onTap: () => _tekOynat(i), child: Container( color: Colors.black, child: c.value.isInitialized ? Center( child: AspectRatio( aspectRatio: c.value.aspectRatio, child: WinVideoPlayer(c), ), ) : const Center(child: CircularProgressIndicator()), ), ), ), Container( padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), color: theme.colorScheme.surface.withValues(alpha: 0.6), child: Row( children: [ IconButton( iconSize: 18, padding: const EdgeInsets.all(4), constraints: const BoxConstraints(), icon: Icon( _favoriler[i] ? Icons.star : Icons.star_border, color: _favoriler[i] ? Colors.amber : null, ), tooltip: 'Favori', onPressed: () => _favoriDegistir(i), ), const Spacer(), TextButton.icon( style: TextButton.styleFrom( padding: const EdgeInsets.symmetric(horizontal: 8), minimumSize: const Size(0, 28), visualDensity: VisualDensity.compact, ), icon: const Icon(Icons.check, size: 14), label: const Text('Bunu seç', style: TextStyle(fontSize: 11)), onPressed: () => _bunuSec(i), ), ], ), ), ], ), ); } }