微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

从卡片堆中呈现的 YouTube 视频列表中,第一个视频开始在每张卡片中播放

如何解决从卡片堆中呈现的 YouTube 视频列表中,第一个视频开始在每张卡片中播放

我正在尝试使用 Youtube 视频创建类似 Tinder 的滑动功能。我将详细说明我要实现的目标。

分步分解:

  1. 使用 Youtube Data API v3 获取 Youtube 视频。

youtube _model.dart

// To parse this JSON data,do
//
//     final youtubeSearchVideos = youtubeSearchVideosFromJson(jsonString);

import 'dart:convert';

YoutubeSearchVideos youtubeSearchVideosFromJson(String str) =>
    YoutubeSearchVideos.fromJson(json.decode(str));

String youtubeSearchVideosToJson(YoutubeSearchVideos data) =>
    json.encode(data.toJson());

class YoutubeSearchVideos {
  YoutubeSearchVideos({
    required this.kind,required this.etag,this.nextPagetoken,this.prevPagetoken,required this.regionCode,required this.pageInfo,required this.items,});

  String kind;
  String etag;
  String? nextPagetoken;
  String? prevPagetoken;
  String regionCode;
  PageInfo pageInfo;
  List<Item> items;

  factory YoutubeSearchVideos.fromJson(Map<String,dynamic> json) =>
      YoutubeSearchVideos(
        kind: json["kind"],etag: json["etag"],nextPagetoken: json["nextPagetoken"],prevPagetoken: json["prevPagetoken"],regionCode: json["regionCode"],pageInfo: PageInfo.fromJson(json["pageInfo"]),items: List<Item>.from(json["items"].map((x) => Item.fromJson(x))),);

  Map<String,dynamic> toJson() => {
        "kind": kind,"etag": etag,"nextPagetoken": nextPagetoken,"prevPagetoken": prevPagetoken,"regionCode": regionCode,"pageInfo": pageInfo.toJson(),"items": List<dynamic>.from(items.map((x) => x.toJson())),};
}

class Item {
  Item({
    required this.kind,required this.id,required this.snippet,});

  String kind;
  String etag;
  Id id;
  Snippet snippet;

  factory Item.fromJson(Map<String,dynamic> json) => Item(
        kind: json["kind"],id: Id.fromJson(json["id"]),snippet: Snippet.fromJson(json["snippet"]),"id": id.toJson(),"snippet": snippet.toJson(),};
}

class Id {
  Id({
    required this.kind,required this.videoId,});

  String kind;
  String videoId;

  factory Id.fromJson(Map<String,dynamic> json) => Id(
        kind: json["kind"],videoId: json["videoId"],"videoId": videoId,};
}

class Snippet {
  Snippet({
    required this.publishedAt,required this.channelId,required this.title,required this.description,required this.thumbnails,required this.channelTitle,required this.livebroadcastContent,required this.publishTime,});

  DateTime publishedAt;
  String channelId;
  String title;
  String description;
  Thumbnails thumbnails;
  String channelTitle;
  String livebroadcastContent;
  DateTime publishTime;

  factory Snippet.fromJson(Map<String,dynamic> json) => Snippet(
        publishedAt: DateTime.parse(json["publishedAt"]),channelId: json["channelId"],title: json["title"],description: json["description"],thumbnails: Thumbnails.fromJson(json["thumbnails"]),channelTitle: json["channelTitle"],livebroadcastContent: json["livebroadcastContent"],publishTime: DateTime.parse(json["publishTime"]),dynamic> toJson() => {
        "publishedAt": publishedAt.toIso8601String(),"channelId": channelId,"title": title,"description": description,"thumbnails": thumbnails.toJson(),"channelTitle": channelTitle,"livebroadcastContent": livebroadcastContent,"publishTime": publishTime.toIso8601String(),};
}

class Thumbnails {
  Thumbnails({
    required this.thumbnailsDefault,required this.medium,required this.high,});

  Default thumbnailsDefault;
  Default medium;
  Default high;

  factory Thumbnails.fromJson(Map<String,dynamic> json) => Thumbnails(
        thumbnailsDefault: Default.fromJson(json["default"]),medium: Default.fromJson(json["medium"]),high: Default.fromJson(json["high"]),dynamic> toJson() => {
        "default": thumbnailsDefault.toJson(),"medium": medium.toJson(),"high": high.toJson(),};
}

class Default {
  Default({
    required this.url,required this.width,required this.height,});

  String url;
  int width;
  int height;

  factory Default.fromJson(Map<String,dynamic> json) => Default(
        url: json["url"],width: json["width"],height: json["height"],dynamic> toJson() => {
        "url": url,"width": width,"height": height,};
}

class PageInfo {
  PageInfo({
    required this.totalResults,required this.resultsPerPage,});

  int totalResults;
  int resultsPerPage;

  factory PageInfo.fromJson(Map<String,dynamic> json) => PageInfo(
        totalResults: json["totalResults"],resultsPerPage: json["resultsPerPage"],dynamic> toJson() => {
        "totalResults": totalResults,"resultsPerPage": resultsPerPage,};
}

youtube_api_service.dart

    import 'package:http/http.dart' as http;
    import 'package:starcast_intros/models/youtube_search.dart';
    import 'package:starcast_intros/private_keys.dart';
    
    class YoutubeApi {
      static const String youtubeAPI =
          'https://youtube.googleapis.com/youtube/v3/search?part=snippet&maxResults=5&q=surfing&type=video&videoDeFinition=standard&videoDimension=2d&videoDuration=short&videoEmbeddable=true&key=$YOUTUBE_DATA_API_KEY';
    
      Future<YoutubeSearchVideos> fetchVideos() async {
        try {
          final response = await http.get(Uri.parse(youtubeAPI));
    
          if (response.statusCode == 200) {
            return youtubeSearchVideosFromJson(response.body);
          }
    
          throw Exception('Failed to fetch videos ${response.body}');
        } catch (e) {
          print(e);
          throw Exception('Failed to fetch videos $e');
        }
      }
    }

2. After retrieving the list of youtube video IDs from the API,render the Youtube videos like Tinder cards which can be swiped left or right.

import 'package:Flutter/material.dart';
import 'package:Flutter_spinkit/Flutter_spinkit.dart';
import 'package:starcast_intros/models/youtube_search.dart';
import 'package:starcast_intros/services/youtube_api.dart';
import 'package:tcard/tcard.dart';
import 'package:youtube_player_Flutter/youtube_player_Flutter.dart';

class Home extends StatefulWidget {
  const Home({Key? key}) : super(key: key);
  static const HOME = 'Home';

  @override
  _HomeState createState() => _HomeState();
}

class _HomeState extends State<Home> {
  late Future<YoutubeSearchVideos> futureVideos;

  @override
  void initState() {
    super.initState();

    final youtubeAPI = YoutubeApi();
    futureVideos = youtubeAPI.fetchVideos();
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<YoutubeSearchVideos>(
      future: futureVideos,builder: (context,snapshot) {
        if (snapshot.hasData) {
          List<Widget> cards = List.generate(
            snapshot.data!.items.length,(int index) {
              YoutubePlayerController _controller = YoutubePlayerController(
                initialVideoId: snapshot.data!.items[index].id.videoId,flags: YoutubePlayerFlags(
                  autoplay: false,mute: true,isLive: false,disableDragSeek: true,loop: false,forceHD: false,),);

              return Container(
                decoration: Boxdecoration(
                  color: Colors.white,borderRadius: BorderRadius.circular(16.0),BoxShadow: [
                    BoxShadow(
                      offset: Offset(0,17),blurRadius: 23.0,spreadRadius: -13.0,color: Colors.black54,)
                  ],child: ClipRRect(
                  borderRadius: BorderRadius.circular(16.0),child: YoutubePlayer(
                    controller: _controller,);
            },);

          return TCard(
            size: Size(
              MediaQuery.of(context).size.width,MediaQuery.of(context).size.height,cards: cards,);
        } else if (snapshot.hasError) {
          return Text('${snapshot.error}');
        }

        // By default,show a loading spinner.
        return SpinKitDoubleBounce(
          color: Theme.of(context).accentColor,size: 75.0,);
      },);
  }
}
  1. 请注意,您需要一个 Youtube API 密钥(使用 Google 控制台创建)来检索视频列表。我正在使用 Youtube 搜索 API。如果您不想发出请求或创建 API 密钥,可能可以使用下面给出的 JSON:

{ "kind": "youtube#searchListResponse","etag": "E2FpjhO0gVzn8gmf9Q1VSJ72Rwk","nextPagetoken": "CAQAA",
“区域代码”:“IN”,“页面信息”:{ “总结果”:1000000, "resultsPerPage": 5 },"items": [ { "kind": "youtube#searchResult","etag": "HmJbuO71viHMk8216TydPkfPIAg",“ID”: { "kind": "youtube#video","videoId": "xispffN3vfs" },“片段”:{ "publishedAt": "2020-03-06T22:13:55Z","channelId": "UCR03gYk1xLMV4ko8ljxTeIA","title": "Nick O'Bea : How to Wing surf","description": "机翼冲浪操作方法:机翼上的技巧和训练,来自 supsurfmachines.com 6 仪表 F1 以 10-12 英里/小时的速度摆动 1020 轴 ...",“缩略图”:{ “认”: { "url": "https://i.ytimg.com/vi/xispffN3vfs/default.jpg",“宽度”:120, “高度”:90 },“中等的”: { "url": "https://i.ytimg.com/vi/xispffN3vfs/mqdefault.jpg",“宽度”:320, “高度”:180 },“高的”: { "url": "https://i.ytimg.com/vi/xispffN3vfs/hqdefault.jpg",“宽度”:480, “高度”:360 } },"channelTitle": "rush Mark rush","livebroadcastContent": "无","publishTime": "2020-03-06T22:13:55Z" } },{ "kind": "youtube#searchResult","etag": "PbsGbvz61v4Fpq4DwtLexeIB708","videoId": "0zO26j6vNGg" },“片段”:{ "publishedAt": "2016-08-10T08:07:07Z","channelId": "UCjYiM-YLuOQN-4S0I7PfgVg","title": "Alison teal: Surfing Hawaii Volcano Eruption",“描述”:“夏威夷大岛上的基拉韦厄火山自 2011 年以来首次喷发并流入海洋。 艾莉森,冲浪者和电影制作人,周游世界......",“缩略图”:{ “认”: { "url": "https://i.ytimg.com/vi/0zO26j6vNGg/default.jpg",“中等的”: { "url": "https://i.ytimg.com/vi/0zO26j6vNGg/mqdefault.jpg",“高的”: { "url": "https://i.ytimg.com/vi/0zO26j6vNGg/hqdefault.jpg","channelTitle": "艾莉森历险记","publishTime": "2016-08-10T08:07:07Z" } },"etag": "Ab0zXs-KabFMVeH4jBajBEj60yU","videoId": "pPGaGZTMc_4" },“片段”:{ "publishedAt": "2011-12-05T17:08:27Z","channelId": "UChug4c-a2tUGgZ-XeEKdxZQ","title": "Strongbow Neon Night Surfing Bondi","description": "为了纪念夏天的开始,Strongbow 与传奇冲浪电影制作人 Jack McCoy (Endless Summer II)、Bali Strickland 和 Eugene Tan ......",“缩略图”:{ “认”: { "url": "https://i.ytimg.com/vi/pPGaGZTMc_4/default.jpg",“中等的”: { "url": "https://i.ytimg.com/vi/pPGaGZTMc_4/mqdefault.jpg",“高的”: { "url": "https://i.ytimg.com/vi/pPGaGZTMc_4/hqdefault.jpg","channelTitle": "冲浪者村电视台","publishTime": "2011-12-05T17:08:27Z" } },"etag": "XjmemrMgMcym-3mlYs53ie_w3t4","videoId": "4uwtqRBE4Kk" },“片段”:{ "publishedAt": "2010-08-13T02:10:28Z","channelId": "UCTYHNSWYy4jCSCj1Q1Fq0ew","title": "安迪·艾恩斯 - 我冲浪是因为短片",“描述”:“安迪·艾恩斯是世界上有史以来最伟大的冲浪者之一。3 次世界冠军,因与他的史诗般的战斗而闻名 凯利·斯莱特。但在所有的胜利之外......",“缩略图”:{ “认”: { "url": "https://i.ytimg.com/vi/4uwtqRBE4Kk/default.jpg",“中等的”: { "url": "https://i.ytimg.com/vi/4uwtqRBE4Kk/mqdefault.jpg",“高的”: { "url": "https://i.ytimg.com/vi/4uwtqRBE4Kk/hqdefault.jpg","channelTitle": "Billabong","publishTime": "2010-08-13T02:10:28Z" } },"etag": "FZQyT7mq6dN1LX4M5RjXzVJxrtQ","videoId": "kGvs0Nv5zJo" },“片段”:{ "publishedAt": "2013-11-15T09:31:05Z","channelId": "UCNSfJB-VQeHpv5ThtV1VtBA","title": "Wave 摄影师捕捉冲浪者的最后一波",“描述”:“星期三早上,著名的 Wave 摄影师拉里·海恩斯(Larry Haynes)正在拍摄来自 岸边,在柯克·帕斯莫尔 (Kirk Passmore) 上滚动,因为……",“缩略图”:{ “认”: { "url": "https://i.ytimg.com/vi/kGvs0Nv5zJo/default.jpg",“中等的”: { "url": "https://i.ytimg.com/vi/kGvs0Nv5zJo/mqdefault.jpg",“高的”: { "url": "https://i.ytimg.com/vi/kGvs0Nv5zJo/hqdefault.jpg","channelTitle": "KITV","publishTime": "2013-11-15T09:31:05Z" } } ] }

一旦卡片上堆满了不同的视频,我就会在卡片顶部刷卡。我一刷卡,第一张卡片中的视频就会出现在下面的卡片中(第二张卡片)。由于所有视频 ID 都不同,我预计第二个视频会在第二个卡中播放。

如果我只是拖动一点并按住它,我可以在第二张卡片中看到第二个视频的缩略图。但是,一旦我向右滑动,第二张卡(第二个视频)中的视频就会被第一张卡(第一个视频)中的视频替换。

一直重复到最后一张牌。

对破解此问题的任何帮助将不胜感激。感谢期待。

干杯。

解决方法

为每张 YouTube 卡片添加一个唯一的密钥,您可以使用 YouTube ID 作为密钥

When to Use Keys

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?