Amiga

Вернуться к блогу

Интеграция видеоплеера YouTube во Flutter

18.01.2024

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 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 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);

                   },

              ),

           ),

        ],

   ), 

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, но с куда меньшей задержкой загрузки видео.

После эти изменения вынес в форк репозитория пакета.

Аудио быстрее видео

Но, как оказалось, счастье длилось недолго – спустя время посыпались баги. Их было много, но, тем не менее, работая через контроллер, я их исправлял, один за другим. 

Однако, один баг мне так и не дался – у плеера появилась вредная привычка проигрывать аудиодорожку на пару секунд раньше, чем видео. Пытаясь решить его, упёрся в возможности пакета. 

Пришлось искать альтернативный пакет, хотя свой эксперимент бросать было грустно. Покопавшись на просторах интернета, нашёл пакет pod_player. Очень смущал номер версии – 0.2.1. Однако, решил испытать. Сказать, что я был доволен результатом – это ничего не сказать. 

Виджет плеера от этого пакета был крайне простой в настройке при этом работал корректно, без багов, видео грузил быстро. Поддерживал как id видео на ютубе, так и url видео в сети, а кроме того, видео в ассетах. В общем, превосходил youtube_player_flutter, даже с учётом моих оптимизационных трюков, по всем параметрам.

Web | Minicase

Интернет-магазин мебели Трио

Интернет-магазин мебели Трио

Mobile

Мобильное приложение для сети аптек «Ваша №1»

Мобильное приложение для сети аптек «Ваша №1»

Mobile

Приложение для пекарен Хлеб Хмельницкого

Приложение для пекарен Хлеб Хмельницкого

Web | Minicase

Аникура

Аникура

Mobile | NDA

Rockwool

Rockwool

Web | NDA

Ecco

Ecco

Web | NDA

НЛМК

НЛМК

Mobile | NDA

Бизнес-приложение Жёлтая печать

Бизнес-приложение Жёлтая печать

Web

B2B-сервис по отработке обращений «Авеста»

B2B-сервис по отработке обращений «Авеста»

Mobile | NDA

Сбер

Сбер

Web | NDA

Mercedes-Benz

Mercedes-Benz

Mobile | NDA

М.Видео

М.Видео

Web | NDA

Nike

Nike

Mobile

Airspector

Airspector

Web

Маркетплейс специалистов Gigoo

Маркетплейс специалистов Gigoo

Web | В работе

Русплитка

Русплитка

Web | В работе

Makita

Makita

Web

Транспортная компания №1

Транспортная компания №1

Web | NDA

Casio

Casio

Mobile

Мобильное приложение для АЗС ХТК

Мобильное приложение для АЗС ХТК

Mobile | NDA

DHL Express

DHL Express

Mobile | NDA

AI-приложение Get Art

AI-приложение Get Art

Web

Сайт для туркластера «Арктический»

Сайт для туркластера «Арктический»

Web | NDA

ERP-система лизинга автопарка

ERP-система лизинга автопарка

Mobile

Интернет-магазин Bravo

Интернет-магазин Bravo

Mobile | NDA

Приложение для здоровья CW Clinic

Приложение для здоровья CW Clinic

Web

Маркетплейс горного оборудования

Маркетплейс горного оборудования

Mobile

Приложение-сканер товаров с TV

Приложение-сканер товаров с TV

Web | NDA

Газпром

Газпром

Mobile

Приложение с интеграцией ML

Приложение  с интеграцией ML

Mobile | NDA

Интернет-магазин NL Store

Интернет-магазин  NL Store

Mobile

Образовательный проект Easy. Приложение в VK

Образовательный проект Easy. Приложение в VK

Web | NDA

Мегафон

Мегафон

Web

Корпоративный портал ЕМС Team

Корпоративный портал ЕМС Team

Mobile | NDA

Shell

Shell

Web

Образовательный портал Школа гениев

Образовательный портал Школа гениев

Mobile

Мобильное приложение CMstore

Мобильное приложение CMstore

Mobile

Программа лояльности Vaillant Group

Программа лояльности Vaillant Group

Web

Имиджевый сайт «Шахтинская плитка»

Имиджевый сайт «Шахтинская плитка»

Web | NDA

Aviasales

Aviasales

Web

HR-сайт для SOKOLOV

HR-сайт для SOKOLOV

Web | NDA

Samsung

Samsung

Web

Маркетплейс нефтяных продуктов Proleum

Маркетплейс нефтяных продуктов Proleum

Web

Крупное федеральное СМИ

Крупное федеральное СМИ
Хотите связаться с владельцем
компании напрямую?
Дмитрий Тарасов
Дмитрий Тарасов
СЕО

НАПИСАТЬ