How to Display YouTube Videos in Flutter

إضافة Flutter لمشغل YouTube

لبدء عرض مقاطع فيديو YouTube في Flutter، نحتاج أولاً إلى تثبيت مكون https://pub.dev/packages/youtube_player_flutter Youtube Player Flutter في مشروعنا. يتيح هذا المكون الإضافي البث المباشر لمقاطع فيديو YouTube ويستخدم واجهة برمجة تطبيقات https://developers.google.com/youtube/iframe_api_reference مشغل IFrame https://developers.google.com/youtube/iframe_api_reference الرسمية الخاصة بـ YouTube . وهو متوافق مع كل من منصتي Android وiOS.

تثبيت البرنامج الإضافي Youtube Player Flutter

يمكننا تثبيت البرنامج الإضافي عن طريق تنفيذ الأمر التالي داخل مشروعنا:

flutter pub add youtube_player_flutter:^8.1.2

السبب الذي يجعلنا نحدد إصدارًا محددًا هو أن الإصدار الأحدث

https://pub.dev/packages/youtube_player_flutter/versions/9.0.2
، وقت كتابة هذا التقرير، يحتوي على خطأ

https://github.com/sarbagyastha/youtube_player_flutter/issues/955
يتسبب في ظهور شريط تحميل بشكل مستمر في الفيديو.

بمجرد تنفيذ الأمر، تأكد من فحص ملفك pubspec.yamlبحثًا عن التبعيات المضافة. يجب أن ترى مكون Youtube Player Flutter مضمنًا في قسم التبعيات، مثل هذا:

dependencies:

youtube_player_flutter: ^8.1.2

تنفيذ البرنامج الإضافي لمشغل اليوتيوب

لتنفيذ مكون Youtube Player Flutter، سنقوم بإنشاء YoutubePageعنصر واجهة مستخدم في ملف Dart منفصل باسم . سيتم بعد ذلك عرض youtube_page.dartهذا العنصر باستخدام سمة العنصر في الملف.YoutubePagehomeMaterialAppmain.dart

الرئيسية.دارت

import ‘package:flutter/material.dart’;
import ‘package:youtube_videos_demo/youtube_page.dart’;

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return const MaterialApp(
home: YoutubePage(),
);
}
}
في main.dartالملف، نقوم بإرجاع عنصر واجهة المستخدم الخاص بالتطبيق MaterialApp. داخل MaterialAppعنصر واجهة المستخدم، نستخدم homeالسمة لعرض YoutubePageعنصر واجهة المستخدم.

صفحة اليوتيوب.دارت
import ‘package:flutter/material.dart’;
import ‘package:youtube_player_flutter/youtube_player_flutter.dart’;

class YoutubePage extends StatefulWidget {
const YoutubePage({super.key});

@override
State createState() => _YoutubePageState();
}

class _YoutubePageState extends State {
final YoutubePlayerController _controller = YoutubePlayerController(
initialVideoId: ‘PAOAjOR6K_Q’,
);

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: YoutubePlayer(
controller: _controller,
),
),
);
}
}
في youtube_page.dartالملف، نبدأ بإنشاء عنصر واجهة مستخدم بحالة. داخل حالة العنصر، نقوم بتعريف _controllerمتغير خاص يحمل مثيلًا لـ YoutubePlayerController. في هذه الحالة، نقوم بتعيين initialVideoIdالسمة إلى معرف الفيديو الذي نريد عرضه. أخيرًا، نقوم بإرجاع YoutubePlayerعنصر واجهة مستخدم، مع تعيين _controllerالمتغير إلى controllerسماته.
يمكنك بسهولة العثور على معرف الفيديو في عنوان URL لمقطع فيديو YouTube الذي تريد عرضه. يقع المعرف بعد watch?v=عنوان URL. على سبيل المثال، في عنوان URL https://www.youtube.com/watch?v=PAOAjOR6K_Q، يكون معرف الفيديو هو PAOAjOR6K_Q.

كما هو موضح في لقطة الشاشة أعلاه، يمكننا عرض مقاطع فيديو YouTube في Flutter باستخدام قدر صغير فقط من التعليمات البرمجية.

تجنب تسربات الذاكرة

ولمنع تسرب الذاكرة، من الضروري التخلص من وحدة التحكم بشكل صحيح عندما لا تكون هناك حاجة إليها بعد الآن.

import ‘package:flutter/material.dart’;
import ‘package:youtube_player_flutter/youtube_player_flutter.dart’;

class YoutubePage extends StatefulWidget {
const YoutubePage({super.key});

@override
State createState() => _YoutubePageState();
}

class _YoutubePageState extends State {
final YoutubePlayerController _controller = YoutubePlayerController(
initialVideoId: ‘PAOAjOR6K_Q’,
);

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: YoutubePlayer(
controller: _controller,
),
),
);
}
}
في مقتطف التعليمات البرمجية أعلاه، نتخلص من الدالة _controllerعن طريق استدعاء disposeدالة داخل disposeالدالة التي تم تجاوزها. عند استخدام disposeالدالة التي تم تجاوزها، تأكد دائمًا من استدعاء الدالة super.disposeالأخيرة.

إضافة YoutubePlayerFlags
بالإضافة إلى initialVideoIdسمة YoutubePlayerController، يمكننا أيضًا استخدام flagsالسمة. تتيح لنا هذه السمة تعيين مثيل للفئة YoutubePlayerFlagsلتخصيص كيفية تصرف اللاعب.
final YoutubePlayerController _controller = YoutubePlayerController(
initialVideoId: ‘PAOAjOR6K_Q’,
flags: const YoutubePlayerFlags(
forceHD: true,
endAt: 20,
enableCaption: false,
hideThumbnail: true,
),
);
في مقتطف التعليمات البرمجية هذا، أضفنا flagsالسمة إلى YoutubePlayerControllerمثيل المتغير _controller. على flagsالسمة، قمنا بتعيين مثيل للفئة YoutubePlayerFlagsوأضفنا أربع سمات لفرض الدقة العالية وإنهاء الفيديو بعد 20 ثانية وتعطيل التسميات التوضيحية وإخفاء الصورة المصغرة.

تخصيص الإجراءات السفلية والعلوية
داخل YoutubePlayer، يمكننا أيضًا تخصيص الإجراءات السفلية والعلوية. راجع المثال أدناه:
import ‘package:flutter/material.dart’;
import ‘package:youtube_player_flutter/youtube_player_flutter.dart’;

class YoutubePage extends StatefulWidget {
const YoutubePage({super.key});

@override
State createState() => _YoutubePageState();
}

class _YoutubePageState extends State {
final YoutubePlayerController _controller = YoutubePlayerController(
initialVideoId: ‘PAOAjOR6K_Q’,
);

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: YoutubePlayer(
bottomActions: [
const SizedBox(width: 14),
CurrentPosition(),
const SizedBox(width: 8),
ProgressBar(
isExpanded: true,
colors: const ProgressBarColors(
playedColor: Color(0xFFFF0000),
handleColor: Color(0xFFFF0000),
backgroundColor: Colors.grey,
),
),
RemainingDuration(),
],
controller: _controller,
topActions: const [
PlaybackSpeedButton(),
],
),
),
);
}
}
في مقتطف التعليمات البرمجية أعلاه، قمنا بتخصيص كل من الإجراءات السفلية والعلوية لعلامتي YoutubePlayer. بالنسبة للإجراءات السفلية، قمنا بتضمين CurrentPositionالمؤشر وعلامة ProgressBar, والمؤشر RemainingDuration. لقد استخدمنا SizedBoxعناصر واجهة المستخدم لإضافة مسافات بين هذه العناصر وضبطنا ProgressBarألوان عناصر واجهة المستخدم لتتوافق مع مشغل YouTube الأصلي.

بالنسبة للإجراءات العليا، أضفنا PlaybackSpeedButton.

تخصيص مشغل اليوتيوب
لمزيد من المرونة، يمكننا استخدام YoutubePlayerBuilderالفئة لتخصيص المشغل. يوفر هذا النهج تحكمًا أكبر في سلوك المشغل، وخاصةً عند دخوله أو خروجه من وضع ملء الشاشة.
import ‘package:flutter/material.dart’;
import ‘package:youtube_player_flutter/youtube_player_flutter.dart’;

class YoutubePage extends StatefulWidget {
const YoutubePage({super.key});

@override
State createState() => _YoutubePageState();
}

class _YoutubePageState extends State {
final YoutubePlayerController _controller = YoutubePlayerController(
initialVideoId: ‘PAOAjOR6K_Q’,
);

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
final width = size.width;
final height = size.height;

return Scaffold(
  body: Center(
    child: OrientationBuilder(
      builder: (BuildContext context, Orientation orientation) =>
          YoutubePlayerBuilder(
        builder: (BuildContext context, Widget player) => SizedBox(
          width: orientation == Orientation.landscape ? height : width,
          height: orientation == Orientation.landscape ? width : height,
          child: player,
        ),
        player: YoutubePlayer(
          controller: _controller,
        ),
        onExitFullScreen: () {},
        onEnterFullScreen: () {},
      ),
    ),
  ),
);

}
}
في مقتطف التعليمات البرمجية أعلاه، نستخدم OrientationBuilderالأداة لتحديد اتجاه الشاشة الحالي. داخل منشئها، نعيد YoutubePlayerBuilder، والتي تعيد بدورها SizedBoxأداة. يتم تعديل أبعاد الأداة SizedBoxبناءً على الاتجاه.

في YoutubePlayerBuilder، نقوم بتعريف player، والتي تأخذ مثيلًا من YoutubePlayerالفئة. لدينا أيضًا إمكانية الوصول إلى السمات onEnterFullScreenو onExitFullScreen، والتي يمكن استخدامها لتحريك الإجراءات عندما يدخل اللاعب إلى وضع ملء الشاشة أو يخرج منه. في القسم القادم ، سنناقش onExitFullScreenالسمة بمزيد من التفصيل، وخاصةً لمعالجة مشكلة في شريط التنقل في Android.

تغيير الصورة المصغرة
بالاستمرار في استخدام الكود من القسم السابق، ربما لاحظت أن الصورة المصغرة لا يتم عرضها بشكل صحيح عند إيقاف تشغيل الفيديو مؤقتًا، وخاصة في الوضع الأفقي. لحسن الحظ، يمكننا تخصيص الصورة المصغرة لمعالجة هذه المشكلة.
import ‘package:flutter/material.dart’;
import ‘package:youtube_player_flutter/youtube_player_flutter.dart’;

class YoutubePage extends StatefulWidget {
const YoutubePage({super.key});

@override
State createState() => _YoutubePageState();
}

class _YoutubePageState extends State {
final YoutubePlayerController _controller = YoutubePlayerController(
initialVideoId: ‘PAOAjOR6K_Q’,
);

@override
void dispose() {
_controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
final width = size.width;
final height = size.height;

return Scaffold(
  body: Center(
    child: OrientationBuilder(
      builder: (BuildContext context, Orientation orientation) =>
          YoutubePlayerBuilder(
        builder: (BuildContext context, Widget player) => SizedBox(
          width: orientation == Orientation.landscape ? height : width,
          height: orientation == Orientation.landscape ? width : height,
          child: player,
        ),
        player: YoutubePlayer(
          controller: _controller,
          thumbnail: ColoredBox(
            color: Colors.black,
            child: Image.network(
              YoutubePlayer.getThumbnail(
                videoId: _controller.metadata.videoId.isEmpty
                    ? _controller.initialVideoId
                    : _controller.metadata.videoId,
              ),
              fit: BoxFit.contain,
              loadingBuilder: (_, child, progress) =>
                  progress == null ? child : Container(color: Colors.black),
              errorBuilder: (context, _, __) => Image.network(
                YoutubePlayer.getThumbnail(
                  videoId: _controller.metadata.videoId.isEmpty
                      ? _controller.initialVideoId
                      : _controller.metadata.videoId,
                  webp: false,
                ),
                fit: BoxFit.contain,
                loadingBuilder: (_, child, progress) => progress == null
                    ? child
                    : Container(color: Colors.black),
                errorBuilder: (context, _, __) => Container(),
              ),
            ),
          ),
        ),
      ),
    ),
  ),
);

}
}
في مقتطف التعليمات البرمجية أعلاه، أضفنا thumbnailالسمة إلى YoutubePlayerالمثيل. thumbnailتقبل السمة عنصر واجهة مستخدم، وفي هذه الحالة، استخدمنا ColoredBoxعنصر واجهة مستخدم بخلفية سوداء. داخل ColoredBoxعنصر واجهة المستخدم، قمنا بتضمين Image.networkعنصر واجهة مستخدم لعرض الصورة المصغرة.

نقوم باسترجاع الصورة المصغرة باستخدام getThumbnailوظيفة الفئة YoutubePlayer. بالإضافة إلى ذلك، أضفنا بديلاً للحالات التي لا يمكن فيها تحميل الصورة المصغرة. في هذه المواقف، يتم عرض حاوية سوداء أثناء جلب الصورة المصغرة.

إصلاح المشكلات المتعلقة بشريط التنقل في Android بعد الخروج من وضع ملء الشاشة
في قسم تخصيص مشغل YouTube، ناقشنا هذه onExitFullScreenالخاصية بإيجاز. في هذا القسم، ستتعلم كيفية استخدام هذه الخاصية لحل مشكلة محتملة عندما تريد أن يعرض تطبيقك شريط التنقل الخاص بنظام Android.
import ‘package:flutter/material.dart’;
import ‘package:flutter/services.dart’;
import ‘package:youtube_player_flutter/youtube_player_flutter.dart’;

class YoutubePage extends StatefulWidget {
const YoutubePage({super.key});

@override
State createState() => _YoutubePageState();
}

class _YoutubePageState extends State {
final YoutubePlayerController _controller = YoutubePlayerController(
initialVideoId: ‘PAOAjOR6K_Q’,
flags: const YoutubePlayerFlags(
forceHD: true,
),
);

Future _onExitFullScreen() async {
await SystemChrome.setPreferredOrientations([]);
await SystemChrome.setEnabledSystemUIMode(
SystemUiMode.manual,
overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom],
);
}

@override
void dispose() {
_controller.dispose();
_onExitFullScreen();
super.dispose();
}

@override
Widget build(BuildContext context) {
final size = MediaQuery.of(context).size;
final width = size.width;
final height = size.height;

return Scaffold(
  body: Center(
    child: OrientationBuilder(
      builder: (BuildContext context, Orientation orientation) =>
          YoutubePlayerBuilder(
        builder: (BuildContext context, Widget player) => SizedBox(
          width: orientation == Orientation.landscape ? width : height,
          height: orientation == Orientation.landscape ? width : height,
          child: player,
        ),
        player: YoutubePlayer(
          controller: _controller,
        ),
        onExitFullScreen: _onExitFullScreen,
      ),
    ),
  ),
);

}
}
في مقتطف التعليمات البرمجية أعلاه، أضفنا _onExitFullScreenوظيفة خاصة. يتم تشغيل هذه الوظيفة عندما يخرج المشغل من وضع ملء الشاشة. بشكل افتراضي، يخفي المكون الإضافي شريط التنقل في Android، ولكن يمكننا تجاوز هذا السلوك. نستخدم وظيفة setEnabledSystemUIModeالفصل SystemChromeلاستعادة شريط التنقل في Android. يمكنك ضبط المعلمات لتناسب احتياجات مشروعك.

حساب نسبة العرض إلى الارتفاع
تتضمن الفئة YoutubePlayerأيضًا aspectRatioسمة، والتي تكون افتراضيًا 16:9. ومع ذلك، لدينا خيار تجاوز هذه القيمة. في الكود التالي، نحسب نسبة العرض إلى الارتفاع المثلى باستخدام خوارزمية تقليدية https://sites.math.rutgers.edu/~greenfie/gs2004/euclid.html . تساعدنا هذه الخوارزمية في العثور على القاسم المشترك الأعظم
https://insaneimpact.com/aspect-ratio-calculator/#:~:text=To%20calculate%20an%20aspect%20ratio,height%20divided%20by%20the%20GCF
، والذي نستخدمه بعد ذلك لتحديد أفضل نسبة عرض إلى ارتفاع بناءً على أبعاد جهاز المستخدم.
import ‘dart:math’;

import ‘package:flutter/material.dart’;
import ‘package:youtube_player_flutter/youtube_player_flutter.dart’;

class YoutubePage extends StatefulWidget {
const YoutubePage({super.key});

@override
State createState() => _YoutubePageState();
}

class _YoutubePageState extends State {
final YoutubePlayerController _controller = YoutubePlayerController(
initialVideoId: ‘PAOAjOR6K_Q’,
);

double _calculateAspectRatio(double width, double height) {
final greatestCommonFactor = _calculateGreatestCommonFactor(width, height);

if (greatestCommonFactor == 0.0) {
  return 16 / 9;
}

return (width / greatestCommonFactor) / (height / greatestCommonFactor);

}

double _calculateGreatestCommonFactor(double width, double height) {
final size = [width, height];
final lowest = size.reduce(min);
final highest = size.reduce(max);

var remainder = highest % lowest;
var greatestCommonFactor = 0.0;

while (remainder != 0) {
  if ((lowest % remainder) == 0) {
    greatestCommonFactor = remainder;
    break;
  }

  remainder = lowest % remainder;
}

return greatestCommonFactor.abs();

}

@override
Widget build(BuildContext context) {
final size = MediaQuery.sizeOf(context);
final width = size.width;
final height = size.height;

return Scaffold(
  body: Center(
    child: OrientationBuilder(
      builder: (BuildContext context, Orientation orientation) =>
          YoutubePlayer(
        controller: _controller,
        aspectRatio: _calculateAspectRatio(
          orientation == Orientation.landscape ? height : width,
          orientation == Orientation.landscape ? width : height,
        ),
      ),
    ),
  ),
);

}
}

في مقتطف التعليمات البرمجية أعلاه، قدمنا ​​دالتين خاصتين لحساب نسبة العرض إلى الارتفاع. _calculateGreatestCommonFactorتحدد الدالة العامل المشترك الأعظم، بينما _calculateAspectRatioتستخدم الدالة هذه القيمة لحساب أفضل نسبة عرض إلى ارتفاع ممكنة بناءً على أبعاد الجهاز. _calculateAspectRatioثم يتم تعيين النتيجة من إلى aspectRatioالسمة لضبط نسبة العرض إلى الارتفاع وفقًا للاتجاه الحالي.
أثناء تطوير تطبيقي https://yournews.app/ Your News ، واجهت مشكلات تتعلق بنسبة العرض إلى الارتفاع. إذا واجهت مشكلات مماثلة، فقد تجد أن الشوكة
https://github.com/sarbagyastha/youtube_player_flutter/compare/develop%E2%80%A6TijnvandenEijnde:youtube_player_flutter:develop

التي أنشأتها مفيدة. وهي تستند إلى الإصدار 8.1.2 من البرنامج الإضافي Youtube Player Flutter.
الاستنتاجات

في هذه المقالة، تعلمت كيفية عرض مقاطع فيديو YouTube في Flutter. بدأنا بالتنفيذ الأساسي وناقشنا خيارات التخصيص المختلفة. أخيرًا، تناولنا بعض المشكلات الشائعة مع المكون الإضافي التي اكتشفتها أثناء استخدامه. آمل أن تجد هذه المقالة مفيدة. إذا كانت لديك أي أسئلة أو واجهتك مشكلات، فلا تتردد في ترك تعليق.

Leave a Comment

Your email address will not be published.