从卡片堆中呈现的 YouTube 视频列表中,第一个视频开始在每张卡片中滑动
From a list of YouTube videos rendered in a card stack, the 1st video starts playing in every card on swipe
我正在尝试使用 Youtube 视频创建类似 Tinder 的滑动功能。我将详细说明我要实现的目标。
分步分解:
- 使用 Youtube 数据 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.etag,
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"],
etag: json["etag"],
id: Id.fromJson(json["id"]),
snippet: Snippet.fromJson(json["snippet"]),
);
Map<String, dynamic> toJson() => {
"kind": kind,
"etag": etag,
"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"],
);
Map<String, dynamic> toJson() => {
"kind": kind,
"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"]),
);
Map<String, 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"]),
);
Map<String, 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"],
);
Map<String, 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"],
);
Map<String, 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,
);
},
);
}
}
- 请注意,您需要一个 Youtube API 密钥(使用 Google 控制台创建)来检索视频列表。我正在使用 Youtube 搜索 API。如果您不想发出请求或创建 API 密钥,您可能可以使用下面给出的 JSON:
{ "kind": "youtube#searchListResponse", "etag":
"E2FpjhO0gVzn8gmf9Q1VSJ72Rwk", "nextPageToken": "CAUQAA",
"regionCode": "IN", "pageInfo": {
"totalResults": 1000000,
"resultsPerPage": 5 }, "items": [
{
"kind": "youtube#searchResult",
"etag": "HmJbuO71viHMk8216TydPkfPIAg",
"id": {
"kind": "youtube#video",
"videoId": "xIspFfN3vfs"
},
"snippet": {
"publishedAt": "2020-03-06T22:13:55Z",
"channelId": "UCR03gYk1xLMV4ko8ljxTeIA",
"title": "Nick O'Bea : How to Wing surf",
"description": "Wing Surfing How to: Skills and Drills on the wing with Nick O'Bea 5'1"x26" 90 liters from supsurfmachines.com 6
meter F1 Swing in 10-12 mph 1020 AXIS ...",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/xIspFfN3vfs/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://i.ytimg.com/vi/xIspFfN3vfs/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://i.ytimg.com/vi/xIspFfN3vfs/hqdefault.jpg",
"width": 480,
"height": 360
}
},
"channelTitle": "Rush Mark Rush",
"liveBroadcastContent": "none",
"publishTime": "2020-03-06T22:13:55Z"
}
},
{
"kind": "youtube#searchResult",
"etag": "PbsGbvz61v4Fpq4DwtLexeIB708",
"id": {
"kind": "youtube#video",
"videoId": "0zO26j6vNGg"
},
"snippet": {
"publishedAt": "2016-08-10T08:07:07Z",
"channelId": "UCjYiM-YLuOQN-4S0I7PfgVg",
"title": "Alison Teal: Surfing Hawaii Volcano Eruption",
"description": "Kilauea Volcano is erupting on the Big Island of Hawaii and flowing into the ocean for the first time since 2011.
Alison, a surfer and film maker, travels the world ...",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/0zO26j6vNGg/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://i.ytimg.com/vi/0zO26j6vNGg/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://i.ytimg.com/vi/0zO26j6vNGg/hqdefault.jpg",
"width": 480,
"height": 360
}
},
"channelTitle": "Alison's Adventures",
"liveBroadcastContent": "none",
"publishTime": "2016-08-10T08:07:07Z"
}
},
{
"kind": "youtube#searchResult",
"etag": "Ab0zXs-KAbFMVeH4jBajBEj60yU",
"id": {
"kind": "youtube#video",
"videoId": "pPGaGZTMc_4"
},
"snippet": {
"publishedAt": "2011-12-05T17:08:27Z",
"channelId": "UChug4c-a2tUGgZ-XeEKdxZQ",
"title": "Strongbow Neon Night Surfing Bondi",
"description": "To mark the start of summer, Strongbow joined forces with legendary surfing filmmaker Jack McCoy (Endless Summer
II), Bali Strickland and Eugene Tan ...",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/pPGaGZTMc_4/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://i.ytimg.com/vi/pPGaGZTMc_4/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://i.ytimg.com/vi/pPGaGZTMc_4/hqdefault.jpg",
"width": 480,
"height": 360
}
},
"channelTitle": "Surfers Village TV",
"liveBroadcastContent": "none",
"publishTime": "2011-12-05T17:08:27Z"
}
},
{
"kind": "youtube#searchResult",
"etag": "XjmemrMgMcym-3mlYs53ie_w3t4",
"id": {
"kind": "youtube#video",
"videoId": "4uwtqRBE4Kk"
},
"snippet": {
"publishedAt": "2010-08-13T02:10:28Z",
"channelId": "UCTYHNSWYy4jCSCj1Q1Fq0ew",
"title": "Andy Irons - i surf because short film",
"description": "Andy Irons is one of the world's greatest ever surfers. A 3 times world champion made famous by his epic battles with
Kelly Slater. But outside all the victories ...",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/4uwtqRBE4Kk/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://i.ytimg.com/vi/4uwtqRBE4Kk/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://i.ytimg.com/vi/4uwtqRBE4Kk/hqdefault.jpg",
"width": 480,
"height": 360
}
},
"channelTitle": "Billabong",
"liveBroadcastContent": "none",
"publishTime": "2010-08-13T02:10:28Z"
}
},
{
"kind": "youtube#searchResult",
"etag": "FZQyT7mq6dN1LX4M5RjXzVJxrtQ",
"id": {
"kind": "youtube#video",
"videoId": "kGvs0Nv5zJo"
},
"snippet": {
"publishedAt": "2013-11-15T09:31:05Z",
"channelId": "UCNSfJB-VQeHpv5ThtV1VtBA",
"title": "Wave cinematographer captures surfer's last wave",
"description": "On Wednesday morning, well known Wave Cinematographer Larry Haynes was filming those big sets from the
shore, and was rolling on Kirk Passmore as the ...",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/kGvs0Nv5zJo/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://i.ytimg.com/vi/kGvs0Nv5zJo/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://i.ytimg.com/vi/kGvs0Nv5zJo/hqdefault.jpg",
"width": 480,
"height": 360
}
},
"channelTitle": "KITV",
"liveBroadcastContent": "none",
"publishTime": "2013-11-15T09:31:05Z"
}
} ] }
卡上堆满不同的视频后,我在最上面刷卡。我一滑动,第一张卡片中的视频就会出现在下面的卡片中(第二张卡片)。我希望第二个视频在第二张卡中播放,因为所有视频 ID 都不同。
如果我稍微拖动并按住它,我可以在第二张卡片中看到第二个视频的缩略图。但是,一旦我向右滑动,第二张卡片中的视频(第二个视频)就会被第一张卡片中的视频(第一个视频)替换。
如此重复直到最后一张牌。
任何破解此问题的帮助将不胜感激。感谢期待。
干杯。
为每张 YouTube 卡片添加一个唯一的密钥,您可以使用 YouTube ID 作为密钥
我正在尝试使用 Youtube 视频创建类似 Tinder 的滑动功能。我将详细说明我要实现的目标。
分步分解:
- 使用 Youtube 数据 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.etag,
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"],
etag: json["etag"],
id: Id.fromJson(json["id"]),
snippet: Snippet.fromJson(json["snippet"]),
);
Map<String, dynamic> toJson() => {
"kind": kind,
"etag": etag,
"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"],
);
Map<String, dynamic> toJson() => {
"kind": kind,
"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"]),
);
Map<String, 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"]),
);
Map<String, 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"],
);
Map<String, 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"],
);
Map<String, 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,
);
},
);
}
}
- 请注意,您需要一个 Youtube API 密钥(使用 Google 控制台创建)来检索视频列表。我正在使用 Youtube 搜索 API。如果您不想发出请求或创建 API 密钥,您可能可以使用下面给出的 JSON:
{ "kind": "youtube#searchListResponse", "etag": "E2FpjhO0gVzn8gmf9Q1VSJ72Rwk", "nextPageToken": "CAUQAA",
"regionCode": "IN", "pageInfo": { "totalResults": 1000000, "resultsPerPage": 5 }, "items": [ { "kind": "youtube#searchResult", "etag": "HmJbuO71viHMk8216TydPkfPIAg", "id": { "kind": "youtube#video", "videoId": "xIspFfN3vfs" }, "snippet": { "publishedAt": "2020-03-06T22:13:55Z", "channelId": "UCR03gYk1xLMV4ko8ljxTeIA", "title": "Nick O'Bea : How to Wing surf", "description": "Wing Surfing How to: Skills and Drills on the wing with Nick O'Bea 5'1"x26" 90 liters from supsurfmachines.com 6 meter F1 Swing in 10-12 mph 1020 AXIS ...", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/xIspFfN3vfs/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/xIspFfN3vfs/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/xIspFfN3vfs/hqdefault.jpg", "width": 480, "height": 360 } }, "channelTitle": "Rush Mark Rush", "liveBroadcastContent": "none", "publishTime": "2020-03-06T22:13:55Z" } }, { "kind": "youtube#searchResult", "etag": "PbsGbvz61v4Fpq4DwtLexeIB708", "id": { "kind": "youtube#video", "videoId": "0zO26j6vNGg" }, "snippet": { "publishedAt": "2016-08-10T08:07:07Z", "channelId": "UCjYiM-YLuOQN-4S0I7PfgVg", "title": "Alison Teal: Surfing Hawaii Volcano Eruption", "description": "Kilauea Volcano is erupting on the Big Island of Hawaii and flowing into the ocean for the first time since 2011. Alison, a surfer and film maker, travels the world ...", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/0zO26j6vNGg/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/0zO26j6vNGg/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/0zO26j6vNGg/hqdefault.jpg", "width": 480, "height": 360 } }, "channelTitle": "Alison's Adventures", "liveBroadcastContent": "none", "publishTime": "2016-08-10T08:07:07Z" } }, { "kind": "youtube#searchResult", "etag": "Ab0zXs-KAbFMVeH4jBajBEj60yU", "id": { "kind": "youtube#video", "videoId": "pPGaGZTMc_4" }, "snippet": { "publishedAt": "2011-12-05T17:08:27Z", "channelId": "UChug4c-a2tUGgZ-XeEKdxZQ", "title": "Strongbow Neon Night Surfing Bondi", "description": "To mark the start of summer, Strongbow joined forces with legendary surfing filmmaker Jack McCoy (Endless Summer II), Bali Strickland and Eugene Tan ...", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/pPGaGZTMc_4/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/pPGaGZTMc_4/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/pPGaGZTMc_4/hqdefault.jpg", "width": 480, "height": 360 } }, "channelTitle": "Surfers Village TV", "liveBroadcastContent": "none", "publishTime": "2011-12-05T17:08:27Z" } }, { "kind": "youtube#searchResult", "etag": "XjmemrMgMcym-3mlYs53ie_w3t4", "id": { "kind": "youtube#video", "videoId": "4uwtqRBE4Kk" }, "snippet": { "publishedAt": "2010-08-13T02:10:28Z", "channelId": "UCTYHNSWYy4jCSCj1Q1Fq0ew", "title": "Andy Irons - i surf because short film", "description": "Andy Irons is one of the world's greatest ever surfers. A 3 times world champion made famous by his epic battles with Kelly Slater. But outside all the victories ...", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/4uwtqRBE4Kk/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/4uwtqRBE4Kk/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/4uwtqRBE4Kk/hqdefault.jpg", "width": 480, "height": 360 } }, "channelTitle": "Billabong", "liveBroadcastContent": "none", "publishTime": "2010-08-13T02:10:28Z" } }, { "kind": "youtube#searchResult", "etag": "FZQyT7mq6dN1LX4M5RjXzVJxrtQ", "id": { "kind": "youtube#video", "videoId": "kGvs0Nv5zJo" }, "snippet": { "publishedAt": "2013-11-15T09:31:05Z", "channelId": "UCNSfJB-VQeHpv5ThtV1VtBA", "title": "Wave cinematographer captures surfer's last wave", "description": "On Wednesday morning, well known Wave Cinematographer Larry Haynes was filming those big sets from the shore, and was rolling on Kirk Passmore as the ...", "thumbnails": { "default": { "url": "https://i.ytimg.com/vi/kGvs0Nv5zJo/default.jpg", "width": 120, "height": 90 }, "medium": { "url": "https://i.ytimg.com/vi/kGvs0Nv5zJo/mqdefault.jpg", "width": 320, "height": 180 }, "high": { "url": "https://i.ytimg.com/vi/kGvs0Nv5zJo/hqdefault.jpg", "width": 480, "height": 360 } }, "channelTitle": "KITV", "liveBroadcastContent": "none", "publishTime": "2013-11-15T09:31:05Z" } } ] }
卡上堆满不同的视频后,我在最上面刷卡。我一滑动,第一张卡片中的视频就会出现在下面的卡片中(第二张卡片)。我希望第二个视频在第二张卡中播放,因为所有视频 ID 都不同。
如果我稍微拖动并按住它,我可以在第二张卡片中看到第二个视频的缩略图。但是,一旦我向右滑动,第二张卡片中的视频(第二个视频)就会被第一张卡片中的视频(第一个视频)替换。
如此重复直到最后一张牌。
任何破解此问题的帮助将不胜感激。感谢期待。
干杯。
为每张 YouTube 卡片添加一个唯一的密钥,您可以使用 YouTube ID 作为密钥