Hola, Amigos! Меня зовут Вова Зевеке, я Flutter-разработчик в Amiga. В одном из проектов передо мной стояла задача — интегрировать видеоплеер во Flutter- приложение, с которого можно было бы смотреть видео с YouTube. Казалось бы, подключаем пакет youtube_player_flutter и всё готово. Но не тут-то было, я столкнулся с рядом проблем, о решении которых рассказываю в статье.

Немного о самом проекте
NL Store — мобильное приложение с функционалом интернет-магазина и личным кабинетом для покупателя. NL международная торговая марка, известна своими протеиновыми коктейлями и снеками, но ассортимент выходит далеко за продукты питания и представляет собой косметические средства, одежду, средства для мытья и уборки.
Мобильное приложение для компании мы разрабатывали с нуля на Flutter. Функционал приложения: каталог, карточка товара, профиль, личный кабинет.
Интернет-магазин доступен всей России и СНГ, а также поддерживается оплата в разной валюте.
Моей задачей на проекте было интегрировать видеоплеер, чтобы подтягивались видео с Youtube и корректно воспроизводились.
Проблемы при реализации
Долгая загрузка видео
Вначале я подключил пакет youtube_player_flutter, настроил. «Задача решена». Но спустя некоторое время возникла проблема — буферизация видео занимала очень много времени.
Я переключил у контроллера видеоплеера флаг «autoPlay» в состояние «true». Видео перестало буферизоваться, время ожидания загрузки видео срезалось на порядок. Однако возникла новая проблема: со включенным флагом «autoPlay» пользователь не может взаимодействовать с видео. Нельзя перемотать, поставить на паузу. Разрешалось только переключать полноэкранный режим.
Казалось, тупик — ограничения пакета не позволяли решить задачу. Стал копаться в пакете. Через некоторое время обнаружил, что у контроллера при включенном «autoPlay» остаётся возможность воздействовать на поле controller.value. Сделав отдельный метод, который ставил контроллеру настройки «playerState: PlayerState.paused, isPlaying: false», мне удалось добиться от видеоплеера возможности ставить паузу даже с включенным «autoPlay». Однако, редактировать кнопку плеера «играть/пауза» я не мог. Пришлось делать свою.
if (isShowInterface && !isLoading) Center( child: InkWell( splashColor: Colors.transparent, highlightColor: Colors.transparent, onTap: playPauseVideo, child: Icon( isPaused ? Icons.play_arrow : Icons.pause, color: const Color(0xFFFFFFFF), size: 40, ), ), ),
Future<void> playPauseVideo() async{ setState(() { isPaused = !isPaused; }); if (isPaused) { pausedMoment = controllerVideo.value.position; controllerVideo.value = controllerVideo.value.copyWith( playerState: PlayerState.paused, isPlaying: false, ); } else { controllerVideo.value = controllerVideo.value.copyWith( playerState: PlayerState.playing, isPlaying: true, ); changeVideoPosition(seconds: pausedMoment.inSeconds); } }
Перемотка видео
Решил воспользоваться тем, что у контроллера можно задавать флаг «startAt», определяющий, с какого момента видео начинает проигрываться. На основе этого сделал метод, который при попытке перематывания видео, пересоздает плеер с новой точкой старта видео.
Future<void> changeVideoPosition({required int seconds}) async { controllerVideo.removeListener(update); setState(() { isUpdate = true; isLoading = true; }); await Future.delayed(const Duration(milliseconds: 10)); controllerVideo = YoutubePlayerController( initialVideoId: widget.videoId.toString(), flags: YoutubePlayerFlags( autoPlay: true, hideControls: true, showLiveFullscreenButton: false, startAt: seconds, ), ); setState(() { isUpdate = false; }); oldMoment = const Duration(); controllerVideo.addListener(update); }
Я использовал ProgressBar, предлагаемый пакетом. И тут тоже был свой подвох с включенным флагом «autoPlay». Он тоже не желал работать, поскольку методы контроллера, которые он вызывал, были заморожены. Пришлось у этого класса дополнить метод _dragEndActions(), дописав вызов метода changeVideoPosition(), функционал которого описан в виджете самого плеера.
void _dragEndActions() { _controller.updateValue( _controller.value.copyWith(isControlsVisible: false, isDragging: false), ); _controller.seekTo(_position, allowSeekAhead: true); setState(() { _touchDown = false; }); _controller.play(); if (widget.changeVideoPosition != null) { widget.changeVideoPosition!(); } }
ProgressBar( isExpanded: true, colors: const ProgressBarColors(), controller: controllerVideo, changeVideoPosition: () { changeVideoPosition(seconds: controllerVideo.value.position.inSeconds); }, ),
Обратил также внимание на то, что мобильное приложение YouTube пролистывало видео немного вперёд/назад, когда пользователь нажимал два раза подряд на правую/левую половину экрана во время просмотра видео. У этого плеера такой фишки не было, поэтому я её дописал.
if (!isLoading) Row( children: [ Expanded( child: InkWell( splashColor: Colors.transparent, highlightColor: Colors.transparent, onTap: showInterface, onDoubleTap: () { changeVideoPosition(seconds: controllerVideo.value.position.inSeconds - 5); }, child: const SizedBox(height: double.infinity), ), ), Expanded( child: InkWell( splashColor: Colors.transparent, highlightColor: Colors.transparent, onTap: showInterface, onDoubleTap: () { changeVideoPosition(seconds: controllerVideo.value.position.inSeconds + 5); }, child: const SizedBox(height: double.infinity), ), ), ], ),
Таким образом, я постепенно весь интерфейс видеоплеера заменил своим, спрятав старый.
Итог: получился всё тот же плеер, что и дефолтный YoutubePlayer, но с куда меньшей задержкой загрузки видео.
После эти изменения вынес в форк репозитория пакета: https://github.com/Necro73/youtube_player_flutter
Аудио быстрее видео
Но, как оказалось, счастье длилось недолго – спустя время посыпались баги. Их было много, но, тем не менее, работая через контроллер, я их исправлял, один за другим.
Однако, один баг мне так и не дался – у плеера появилась вредная привычка проигрывать аудиодорожку на пару секунд раньше, чем видео. Пытаясь решить его, упёрся в возможности пакета.
Пришлось искать альтернативный пакет, хотя свой эксперимент бросать было грустно. Покопавшись на просторах интернета, нашёл пакет pod_player. Очень смущал номер версии – 0.2.1. Однако, решил испытать. Сказать, что я был доволен результатом – это ничего не сказать.
Виджет плеера от этого пакета был крайне простой в настройке при этом работал корректно, без багов, видео грузил быстро. Поддерживал как id видео на ютубе, так и url видео в сети, а кроме того, видео в ассетах. В общем, превосходил youtube_player_flutter, даже с учётом моих оптимизационных трюков, по всем параметрам.
Спасибо за внимание! Буду рад вашим комментариям, делитесь, был ли у вас подобный опыт? Как решили проблему?
