Initial commit: Xtream IPTV Player for Android TV
This commit is contained in:
159
lib/screens/player_screen.dart
Normal file
159
lib/screens/player_screen.dart
Normal file
@@ -0,0 +1,159 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
import 'package:chewie/chewie.dart';
|
||||
import '../models/xtream_models.dart';
|
||||
|
||||
class PlayerScreen extends StatefulWidget {
|
||||
final XtreamStream stream;
|
||||
final bool isLive;
|
||||
|
||||
const PlayerScreen({
|
||||
super.key,
|
||||
required this.stream,
|
||||
this.isLive = true,
|
||||
});
|
||||
|
||||
@override
|
||||
State<PlayerScreen> createState() => _PlayerScreenState();
|
||||
}
|
||||
|
||||
class _PlayerScreenState extends State<PlayerScreen> {
|
||||
late VideoPlayerController _videoController;
|
||||
ChewieController? _chewieController;
|
||||
bool _isLoading = true;
|
||||
String? _error;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initPlayer();
|
||||
}
|
||||
|
||||
Future<void> _initPlayer() async {
|
||||
try {
|
||||
final url = widget.stream.url;
|
||||
if (url == null || url.isEmpty) {
|
||||
throw Exception('No stream URL available');
|
||||
}
|
||||
|
||||
_videoController = VideoPlayerController.networkUrl(Uri.parse(url));
|
||||
|
||||
await _videoController.initialize();
|
||||
|
||||
_chewieController = ChewieController(
|
||||
videoPlayerController: _videoController,
|
||||
autoPlay: true,
|
||||
looping: widget.isLive,
|
||||
aspectRatio: _videoController.value.aspectRatio,
|
||||
allowFullScreen: true,
|
||||
allowMuting: true,
|
||||
showControls: true,
|
||||
placeholder: Container(
|
||||
color: Colors.black,
|
||||
child: const Center(
|
||||
child: CircularProgressIndicator(color: Colors.red),
|
||||
),
|
||||
),
|
||||
errorBuilder: (context, errorMessage) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.error, color: Colors.red, size: 48),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
errorMessage,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_isLoading = false;
|
||||
});
|
||||
|
||||
_videoController.addListener(() {
|
||||
setState(() {});
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_error = e.toString();
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_videoController.dispose();
|
||||
_chewieController?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.black,
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
title: Text(
|
||||
widget.stream.name,
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
iconTheme: const IconThemeData(color: Colors.white),
|
||||
),
|
||||
body: Center(
|
||||
child: _isLoading
|
||||
? const CircularProgressIndicator(color: Colors.red)
|
||||
: _error != null
|
||||
? _buildError()
|
||||
: _chewieController != null
|
||||
? Chewie(controller: _chewieController!)
|
||||
: const Text(
|
||||
'No video available',
|
||||
style: TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildError() {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.error_outline, color: Colors.red, size: 64),
|
||||
const SizedBox(height: 16),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Text(
|
||||
_error ?? 'Unknown error',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_isLoading = true;
|
||||
_error = null;
|
||||
});
|
||||
_initPlayer();
|
||||
},
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('Retry'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user