بحث باسم الموضوع المطلوب

كيفية عرض مقاطع فيديو YouTube في Flutter

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

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


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

تثبيت البرنامج الإضافي Youtube Player Flutter
يمكننا تثبيت البرنامج الإضافي عن طريق تنفيذ الأمر التالي داخل مشروعنا:
<C>
flutter pub add youtube_player_flutter:^8.1.2
<C>

السبب الذي يجعلنا نحدد إصدارًا محددًا هو أن الإصدار الأحدث https://pub.dev/packages/youtube_player_flutter/versions/9.0.2 ، وقت كتابة هذا التقرير، يحتوي على خطأ https://github.com/sarbagyastha/youtube_player_flutter/issues/955 يتسبب في ظهور شريط تحميل بشكل مستمر في الفيديو.

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

<C>

dependencies:

  youtube_player_flutter: ^8.1.2

<C>


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

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

<C>

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(),

    );

  }

}

<C>

في main.dartالملف، نقوم بإرجاع عنصر واجهة المستخدم الخاص بالتطبيق MaterialApp. داخل MaterialAppعنصر واجهة المستخدم، نستخدم homeالسمة لعرض YoutubePageعنصر واجهة المستخدم.


صفحة اليوتيوب.دارت

<C>
import 'package:flutter/material.dart';
import 'package:youtube_player_flutter/youtube_player_flutter.dart';

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

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

class _YoutubePageState extends State<YoutubePage> {
  final YoutubePlayerController _controller = YoutubePlayerController(
    initialVideoId: 'PAOAjOR6K_Q',
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: YoutubePlayer(
          controller: _controller,
        ),
      ),
    );
  }
}
<C>

في 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 باستخدام قدر صغير فقط من التعليمات البرمجية.


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

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

<C>

import 'package:flutter/material.dart';

import 'package:youtube_player_flutter/youtube_player_flutter.dart';


class YoutubePage extends StatefulWidget {

  const YoutubePage({super.key});


  @override

  State<YoutubePage> createState() => _YoutubePageState();

}


class _YoutubePageState extends State<YoutubePage> {

  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,

        ),

      ),

    );

  }

}

<C>

في مقتطف التعليمات البرمجية أعلاه، نتخلص من الدالة _controllerعن طريق استدعاء دالة disposeداخل disposeالدالة التي تم تجاوزها. عند استخدام disposeالدالة التي تم تجاوزها، تأكد دائمًا من استدعاء الدالة super.disposeالأخيرة.


إضافة YoutubePlayerFlags

بالإضافة إلى initialVideoIdسمة YoutubePlayerController، يمكننا أيضًا استخدام flagsالسمة. تتيح لنا هذه السمة تعيين مثيل للفئة YoutubePlayerFlagsلتخصيص كيفية تصرف اللاعب.

<C>

final YoutubePlayerController _controller = YoutubePlayerController(

  initialVideoId: 'PAOAjOR6K_Q',

  flags: const YoutubePlayerFlags(

    forceHD: true,

    endAt: 20,

    enableCaption: false,

    hideThumbnail: true,

  ),

);

<C>

في مقتطف التعليمات البرمجية هذا، أضفنا flagsالسمة إلى YoutubePlayerControllerمثيل المتغير _controller. على flagsالسمة، قمنا بتعيين مثيل للفئة YoutubePlayerFlagsوأضفنا أربع سمات لفرض الدقة العالية وإنهاء الفيديو بعد 20 ثانية وتعطيل التسميات التوضيحية وإخفاء الصورة المصغرة.

تخصيص الإجراءات السفلية والعلوية

داخل YoutubePlayer، يمكننا أيضًا تخصيص الإجراءات السفلية والعلوية. راجع المثال أدناه:

<C>

import 'package:flutter/material.dart';

import 'package:youtube_player_flutter/youtube_player_flutter.dart';


class YoutubePage extends StatefulWidget {

  const YoutubePage({super.key});


  @override

  State<YoutubePage> createState() => _YoutubePageState();

}


class _YoutubePageState extends State<YoutubePage> {

  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(),

          ],

        ),

      ),

    );

  }

}

<C>

في مقتطف التعليمات البرمجية أعلاه، قمنا بتخصيص كل من الإجراءات السفلية والعلوية لعلامتي YoutubePlayer. بالنسبة للإجراءات السفلية، قمنا بتضمين CurrentPositionالمؤشر وعلامة ProgressBar, والمؤشر RemainingDuration. لقد استخدمنا SizedBoxعناصر واجهة المستخدم لإضافة مسافات بين هذه العناصر وضبطنا ProgressBarألوان عناصر واجهة المستخدم لتتوافق مع مشغل YouTube الأصلي.

تخصيص مشغل اليوتيوب

لمزيد من المرونة، يمكننا استخدام YoutubePlayerBuilderالفئة لتخصيص المشغل. يوفر هذا النهج تحكمًا أكبر في سلوك المشغل، وخاصةً عند دخوله أو خروجه من وضع ملء الشاشة.

<C>

import 'package:flutter/material.dart';

import 'package:youtube_player_flutter/youtube_player_flutter.dart';


class YoutubePage extends StatefulWidget {

  const YoutubePage({super.key});


  @override

  State<YoutubePage> createState() => _YoutubePageState();

}


class _YoutubePageState extends State<YoutubePage> {

  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: () {},

          ),

        ),

      ),

    );

  }

}

<C>

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


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

تغيير الصورة المصغرة

بالاستمرار في استخدام الكود من القسم السابق، ربما لاحظت أن الصورة المصغرة لا يتم عرضها بشكل صحيح عند إيقاف تشغيل الفيديو مؤقتًا، وخاصة في الوضع الأفقي. لحسن الحظ، يمكننا تخصيص الصورة المصغرة لمعالجة هذه المشكلة.

<C>

import 'package:flutter/material.dart';

import 'package:youtube_player_flutter/youtube_player_flutter.dart';


class YoutubePage extends StatefulWidget {

  const YoutubePage({super.key});


  @override

  State<YoutubePage> createState() => _YoutubePageState();

}


class _YoutubePageState extends State<YoutubePage> {

  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(),

                  ),

                ),

              ),

            ),

          ),

        ),

      ),

    );

  }

}

<C>

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


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

إصلاح المشكلات المتعلقة بشريط التنقل في Android بعد الخروج من وضع ملء الشاشة


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

<C>
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<YoutubePage> createState() => _YoutubePageState();
}

class _YoutubePageState extends State<YoutubePage> {
  final YoutubePlayerController _controller = YoutubePlayerController(
    initialVideoId: 'PAOAjOR6K_Q',
    flags: const YoutubePlayerFlags(
      forceHD: true,
    ),
  );

  Future<void> _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,
          ),
        ),
      ),
    );
  }
}
<C>

في مقتطف التعليمات البرمجية أعلاه، أضفنا _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 ، والذي نستخدمه بعد ذلك لتحديد أفضل نسبة عرض إلى ارتفاع بناءً على أبعاد جهاز المستخدم.

<C>
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<YoutubePage> createState() => _YoutubePageState();
}

class _YoutubePageState extends State<YoutubePage> {
  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,
            ),
          ),
        ),
      ),
    );
  }
}

<C>

في مقتطف التعليمات البرمجية أعلاه، قدمنا ​​دالتين خاصتين لحساب نسبة العرض إلى الارتفاع. _calculateGreatestCommonFactorتحدد الدالة العامل المشترك الأعظم، بينما _calculateAspectRatioتستخدم الدالة هذه القيمة لحساب أفضل نسبة عرض إلى ارتفاع ممكنة بناءً على أبعاد الجهاز. _calculateAspectRatioثم يتم تعيين النتيجة من إلى aspectRatioالسمة لضبط نسبة العرض إلى الارتفاع وفقًا للاتجاه الحالي.

أثناء تطوير تطبيقي Your News https://yournews.app/ ، واجهت مشكلات تتعلق بنسبة العرض إلى الارتفاع. إذا واجهت مشكلات مماثلة، فقد تجد أن الشوكة التي أنشأتها مفيدة https://github.com/sarbagyastha/youtube_player_flutter/compare/develop%E2%80%A6TijnvandenEijnde:youtube_player_flutter:develop . وهي تستند إلى الإصدار 8.1.2 من البرنامج الإضافي Youtube Player Flutter.


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