I'm using the youtube_player_flutter package to play youtube embedded videos. I'm facing problems with the package. I tried almost everything as per my understanding using init method, function to assign the controller, etc none works.
Issues
YoutubeController only take a single id and I have a list of URLs, so I assign the controller in the listview builder method with the videoId assigned as per the index. The issue is when the app launch it works fine, but if I hot reload or save the play button turns into infinite loading, if I hot reload again it stops and turns back to the play button. If I play the video, the player plays but it still shows the thumbnail. If hot reloaded, the thumbnail changes to the playing video. After the initial launch if I hot reload or save. It will always need a hot reload to change the state.
Using onEnd() property to reset the video to the initial state i.e video thumbnail with the play button. But using
onEnded: () {_ytController.reset(); }
shows the infinite loading with the video thumbnail. PS. states change only after hot reload.When the video is playing I don't want to show the headline container so I'm using
_isPlaying
bool to change the state. The value is changing but the container doesn't disappear even with thesetstate()
. I think it needs to rebuild again.Metadata Title is not displayed but works when the controller is assigned in
init()
Code:
import 'package:flutter/material.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
class HomeScreen extends StatefulWidget {
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
// YT Controller
// late YoutubePlayerController _youtubePlayerController;
// Video Title
late String videoTitle;
// Url List
final List<String> _videoUrlList = [
'https://youtu.be/dWs3dzj4Wng',
'https://youtu.be/S3npWREXr8s',
];
/*
YoutubePlayerController _ytFN({String? url}) {
return YoutubePlayerController(
initialVideoId: YoutubePlayer.convertUrlToId(url!)!,
flags: const YoutubePlayerFlags(
autoPlay: false,
enableCaption: true,
),
);
}
//
@override
void initState() {
_ytFN(url: _videoUrlList.first);
super.initState();
}
//
@override
void dispose() {
_ytFN().dispose();
_youtubePlayerController.dispose();
super.dispose();
}
*/
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Tubeloid'),
centerTitle: true,
actions: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.menu_outlined),
)
],
),
body: Padding(
padding: const EdgeInsets.all(10.0),
child: ListView.builder(
itemCount: _videoUrlList.length,
shrinkWrap: true,
itemBuilder: (context, index) {
///-------------------------- ISSUE NO. 1
// YT Controller
final _ytController = YoutubePlayerController(
initialVideoId:
YoutubePlayer.convertUrlToId(_videoUrlList[index])!,
flags: const YoutubePlayerFlags(
autoPlay: false,
enableCaption: true,
captionLanguage: 'en',
),
);
// for container visibility
bool _isPlaying = false;
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Stack(
alignment: Alignment.bottomCenter,
children: [
// Youtube Player
Container(
height: 220.0,
decoration: const BoxDecoration(
color: Color(0xfff5f5f5),
borderRadius: BorderRadius.all(Radius.circular(12)),
),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(12)),
child: YoutubePlayer(
controller: _ytController
..addListener(() {
if (_ytController.value.isPlaying) {
setState(() {
_isPlaying = true;
});
} else {
_isPlaying = false;
}
}),
showVideoProgressIndicator: true,
progressIndicatorColor: Colors.lightBlueAccent,
bottomActions: [
CurrentPosition(),
ProgressBar(isExpanded: true),
FullScreenButton(),
],
onEnded: (YoutubeMetaData _md) {
///--------------------------- ISSUE NO. 2
_ytController.reset();
// _ytController.seekTo(const Duration(seconds: 1));
// _ytController.pause();
_md.videoId;
print(_md.title);
},
),
),
),
///-------------------------- ISSUE NO. 3
// Headline
_isPlaying
? Container()
: Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.9),
borderRadius: const BorderRadius.only(
bottomRight: Radius.circular(12),
bottomLeft: Radius.circular(12),
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
///-------------------------- ISSUE NO. 4
_ytController.metadata.title,
style: const TextStyle(
fontSize: 20.0,
color: Colors.black,
),
),
),
),
],
),
);
},
),
),
);
}
}
CodePudding user response:
Some comments before code)
- don't put this final _ytController = YoutubePlayerController... into builder - you recreate all controllers every time, when run setstate. You have to make some list of controllers for every video and fill it into init override
- reset to begin you should do with _ytController.seekTo command.
- save you isPlayng states into global list and check it into the builder
- I got Metadata title from contrller ONLY when concrete video is playing. So better way - load titles for your videos previously through usual http.get request (to something like such https://noembed.com/embed?url=https://www.youtube.com/watch?v=668nUCeBHyY)
my code:
import 'package:flutter/material.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';
class YT extends StatefulWidget {
const YT({Key? key}) : super(key: key);
@override
_YTState createState() => _YTState();
}
class _YTState extends State<YT> {
late String videoTitle;
// Url List
final List<String> _videoUrlList = [
'https://youtu.be/dWs3dzj4Wng',
'https://www.youtube.com/watch?v=668nUCeBHyY',
'https://youtu.be/S3npWREXr8s',
];
List <YoutubePlayerController> lYTC = [];
Map<String, dynamic> cStates = {};
@override
void initState() {
super.initState();
fillYTlists();
}
fillYTlists(){
for (var element in _videoUrlList) {
String _id = YoutubePlayer.convertUrlToId(element)!;
YoutubePlayerController _ytController = YoutubePlayerController(
initialVideoId: _id,
flags: const YoutubePlayerFlags(
autoPlay: false,
enableCaption: true,
captionLanguage: 'en',
),
);
_ytController.addListener(() {
print('for $_id got isPlaying state ${_ytController.value.isPlaying}');
if (cStates[_id] != _ytController.value.isPlaying) {
if (mounted) {
setState(() {
cStates[_id] = _ytController.value.isPlaying;
});
}
}
});
lYTC.add(_ytController);
}
}
@override
void dispose() {
for (var element in lYTC) {
element.dispose();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Tubeloid'),
centerTitle: true,
actions: [
IconButton(
onPressed: () {},
icon: const Icon(Icons.menu_outlined),
)
],
),
body: Padding(
padding: const EdgeInsets.all(10.0),
child: ListView.builder(
itemCount: _videoUrlList.length,
shrinkWrap: true,
itemBuilder: (context, index) {
YoutubePlayerController _ytController = lYTC[index];
String _id = YoutubePlayer.convertUrlToId(_videoUrlList[index])!;
String curState = 'undefined';
if (cStates[_id] != null) {
curState = cStates[_id]? 'playing':'paused';
}
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Stack(
alignment: Alignment.bottomCenter,
children: [
Container(
height: 220.0,
decoration: const BoxDecoration(
color: Color(0xfff5f5f5),
borderRadius: BorderRadius.all(Radius.circular(12)),
),
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(12)),
child: YoutubePlayer(
controller: _ytController,
showVideoProgressIndicator: true,
progressIndicatorColor: Colors.lightBlueAccent,
bottomActions: [
CurrentPosition(),
ProgressBar(isExpanded: true),
FullScreenButton(),
],
onReady: (){
print('onReady for $index');
},
onEnded: (YoutubeMetaData _md) {
_ytController.seekTo(const Duration(seconds: 0));
},
),
),
),
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0.9),
borderRadius: const BorderRadius.only(
bottomRight: Radius.circular(12),
bottomLeft: Radius.circular(12),
),
),
child: Text(curState, textScaleFactor: 1.5,),
)
],
),
);
},
),
),
);
}
}