🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

YouTube API

YouTube APIはYouTubeのビデオコンテンツと機能性をウェブサイト、アプリケーション、デバイスに統合することを可能にします。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

Q&A

解決済

2回答

1540閲覧

NoSuchMethodError: The method 'map' was called on null. youtube data apiのvideosからデータを取りたい

limerhyme

総合スコア0

Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

YouTube API

YouTube APIはYouTubeのビデオコンテンツと機能性をウェブサイト、アプリケーション、デバイスに統合することを可能にします。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

0グッド

0クリップ

投稿2021/02/24 11:19

編集2021/02/24 11:38

前提・実現したいこと

youtube data apiのvideosからデータを取得してfirestoreにデータを入れるということがしたいです。
エラーの内容をググると
home_screen.dart

Future<VideoItem> _loadVideos() async { VideosList _videosList = await Services.getVideosList( videosId: _videosId, ); setState(() {}); return _videosList.videoItems[0]; }

ここのreturnの部分でvideoItems[0]がnullになってしまっているのかなと予想します。間違っているかもしれませんが。
もしそうならvideos_list.dartの中のどこかが間違っているんだろうなとは思っているんですが、分かりません。
videoItemsの値を取得するにはどうすればいいですか?
flutter勉強し始めて2週間で基礎がわかっていない部分が多くありますがご容赦ください。
足りない情報あれば追記します。

発生している問題・エラーメッセージ

[VERBOSE-2:ui_dart_state.cc(186)] Unhandled Exception: NoSuchMethodError: The method 'map' was called on null. Receiver: null Tried calling: map(Closure: (dynamic) => VideoItem) #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5) #1 new VideosList.fromJson (package:nanashi_schedule/models/videos_list.dart:27:48) #2 videosListFromJson (package:nanashi_schedule/models/videos_list.dart:7:57) #3 Services.getVideosList (package:nanashi_schedule/utils/services.dart:49:29) <asynchronous suspension> #4 _HomeScreenState._loadVideos (package:nanashi_schedule/screens/home_screen.dart:179:30) <asynchronous suspension> #5 _HomeScreenState._getSearchInfo (package:nanashi_schedule/screens/home_screen.dart:109:31) <asynchronous suspension>

該当のソースコード

home_screen.dart

import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:nanashi_schedule/models/search_info.dart'; import 'package:nanashi_schedule/models/videos_list.dart'; import 'package:nanashi_schedule/utils/services.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:url_launcher/url_launcher.dart'; class HomeScreen extends StatefulWidget { @override _HomeScreenState createState() => _HomeScreenState(); } class _HomeScreenState extends State<HomeScreen> { // final List list = [ ["https://yt3.ggpht.com/ytc/AAUvwngvgRbvQSxZHcosptXrl6PO3djyKHY7ZLIGbQHo=s176-c-k-c0x00ffffff-no-rj", "UC0Owc36U9lOyi9Gx9Ic-4qg"], ["https://yt3.ggpht.com/ytc/AAUvwnj2XS4F6MS5aaKGDaQ4mcrPlW44lEN-p9oXqj9x=s176-c-k-c0x00ffffff-no-rj", "UC2kyQhzGOB-JPgcQX9OMgEw"], ["https://yt3.ggpht.com/ytc/AAUvwnit824wjmPA7AE7LDtG0EVjmutNl4-sICtStfTh=s176-c-k-c0x00ffffff-no-rj", "UCRvpMpzAXBRKJQuk-8-Sdvg"], ["https://yt3.ggpht.com/ytc/AAUvwngUM0DtcqRxbIHvfd8t3D-YEFLudJbN29dpdy44=s176-c-k-c0x00ffffff-no-rj", "UCXp7sNC0F_qkjickvlYkg-Q"], ["https://yt3.ggpht.com/ytc/AAUvwnhkL2lOLKPB4cqpPgqjHDV9AVypXtkNt10Eqm0=s176-c-k-c0x00ffffff-no-rj", "UCW8WKciBixmaqaGqrlTITRQ"], ]; // SearchInfo _searchInfo; VideosList _videosList; Item _item; bool _loading; String _videosId; @override void initState() { super.initState(); _loading = true; _videosList = VideosList(); _videosList.videoItems = []; } Future<void> _getSearchInfo(List<dynamic> list) async { for(int i = 0; i < list.length; i++) { _searchInfo = await Services.searchInfo(channelId: list[i][1]); print(_searchInfo.items.length); for(int itemIndex = 0; itemIndex < _searchInfo.items.length; i++) { print('do'); _item = _searchInfo.items[itemIndex]; _videosId = _item.id.videoId; print('${list[i][0]} =======_videosId: $_videosId'); print('get a video'); VideoItem videoItem = await _loadVideos(); print('success get video info'); var documentID = _videosId; Firestore.instance.collection('videos') .document(documentID) .snapshots() .listen((snapshot) { if (!snapshot.exists) { //登録されてない新しいドキュメント debugPrint('iconUrl: '+ list[i][0]); debugPrint('channelTitle: '+ videoItem.videosnippet.channelTitle); String tes = '公開時間:${videoItem.liveStreamingDetails.scheduledStartTime.hour}:${videoItem.liveStreamingDetails.scheduledStartTime.minute}'; debugPrint(tes); debugPrint('thumbnailUrl: '+ videoItem.videosnippet.thumbnails .thumbnailsDefault.url); debugPrint('title: '+ videoItem.videosnippet.title); debugPrint('videoUrl: '+ _videosId); debugPrint('channelUrl: '+list[i][1]); Firestore.instance.collection('videos') .document(documentID) .setData( { 'iconUrl': list[i][0], 'channelTitle': videoItem.videosnippet.channelTitle, 'scheduledStartTime': videoItem.liveStreamingDetails .scheduledStartTime, 'thumbnailUrl': videoItem.videosnippet.thumbnails .thumbnailsDefault.url, 'title': videoItem.videosnippet.title, 'videoUrl': 'https://www.youtube.com/watch?v=' + _videosId, 'channelUrl': 'https://www.youtube.com/channel/' + list[i][1], } ); } else { Firestore.instance.collection('videos') .document(documentID) .updateData( { 'iconUrl': list[i][0], 'channelTitle': videoItem.videosnippet.channelTitle, 'scheduledStartTime': videoItem.liveStreamingDetails .scheduledStartTime, 'thumbnailUrl': videoItem.videosnippet.thumbnails .thumbnailsDefault.url, 'title': videoItem.videosnippet.title, 'videoUrl': 'https://www.youtube.com/watch?v=' + _videosId, 'channelUrl': 'https://www.youtube.com/channel/' + list[i][1], } ); } }); } } print('setstate'); setState(() { _loading = true; }); } Future<VideoItem> _loadVideos() async { VideosList _videosList = await Services.getVideosList( videosId: _videosId, ); setState(() {}); return _videosList.videoItems[0]; } @override Widget build(BuildContext context) { 以下UI~~~

videos_list.dart

import 'dart:convert'; VideosList videosListFromJson(String str) => VideosList.fromJson(json.decode(str)); String videosListToJson(VideosList data) => json.encode(data.toJson()); class VideosList { VideosList({ this.kind, this.etag, this.videoItems, this.pageInfo, }); String kind; String etag; List<VideoItem> videoItems; PageInfo pageInfo; factory VideosList.fromJson(Map<String, dynamic> json) => VideosList( kind: json["kind"], etag: json["etag"], videoItems: List<VideoItem>.from(json["items"].map((x) => VideoItem.fromJson(x))), //new VideosList.fromJson (package:nanashi_schedule/models/videos_list.dart:27:48) pageInfo: PageInfo.fromJson(json["pageInfo"]), ); Map<String, dynamic> toJson() => { "kind": kind, "etag": etag, "items": List<dynamic>.from(videoItems.map((x) => x.toJson())), "pageInfo": pageInfo.toJson(), }; } class VideoItem { VideoItem({ this.kind, this.etag, this.id, this.videosnippet, this.liveStreamingDetails, }); String kind; String etag; String id; VideoSnippet videosnippet; LiveStreamingDetails liveStreamingDetails; factory VideoItem.fromJson(Map<String, dynamic> json) => VideoItem( kind: json["kind"], etag: json["etag"], id: json["id"], videosnippet: VideoSnippet.fromJson(json["snippet"]), liveStreamingDetails: LiveStreamingDetails.fromJson(json["liveStreamingDetails"]), ); Map<String, dynamic> toJson() => { "kind": kind, "etag": etag, "id": id, "snippet": videosnippet.toJson(), "liveStreamingDetails": liveStreamingDetails.toJson(), }; }

試したこと

ここに問題に対して試したことを記載してください。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

多分ですが

videoItems: List<VideoItem>.from(json["items"].map((x) => VideoItem.fromJson(x))),

の行で、json["items"]が空とか取得できていない気がします。

IDEでブレークポイントをvideosListFromJson(String str)factory VideosList.fromJson(Map<String, dynamic> json)のラインに置いて、デバッグしてみてください。

または、以下のようにprintデバッグして見ても良いでしょう。

VideosList videosListFromJson(String str) { print(str); return VideosList.fromJson(json.decode(str)); } class VideosList { VideosList({ this.kind, this.etag, this.videoItems, this.pageInfo, }); factory VideosList.fromJson(Map<String, dynamic> json) { print(json); return VideosList( kind: json["kind"], etag: json["etag"], videoItems: List<VideoItem>.from(json["items"].map((x) => VideoItem.fromJson( x))), //new VideosList.fromJson (package:nanashi_schedule/models/videos_list.dart:27:48) pageInfo: PageInfo.fromJson(json["pageInfo"]), ); } String kind; String etag; List<VideoItem> videoItems; PageInfo pageInfo; }

また、printの行があることでブレークポイントも置きやすいので、一旦はPrintデバッグの形にして確認してみることをお勧めします。

function(arg) => value

の形式は短くて書きやすいですが、ブレークポイントを置いたり細かい確認をするのに不便ですので、ある程度動くことが確認されるまでは、ワンステップずつ確認しながら実行できるように

function(arg) { return value; }

の書き方で進めて行った方がかえって楽な気がします。特に慣れるまでは、同じエラー行に命令がたくさん書き込まれているとどこでダメになったのか把握しづらいので、不恰好でも以下のようにprintなどで適度に変数の中身を吐き出しながら進めて行くと良いと思います。

function(arg) { print(arg); return value; }

投稿2021/02/24 13:11

aya-eiya

総合スコア97

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

自己解決

services.dartに問題がありました。

static Future<VideosList> getVideosList({String videosId}) async { Map<String, String> parameters = { 'part': 'snippet,liveStreamingDetails', 'id': videosId, 'key': Constants.API_KEY, }; Map<String, String> headers = { HttpHeaders.contentTypeHeader: 'application/json', }; Uri uri = Uri.https( _baseUrl, '/youtube/v3/videos', parameters, ); var response = await http.get(uri, headers: headers); print(response.body); VideosList videosList = videosListFromJson(response.body); return videosList; }

part:の部分を('snippet' 'liveStreamingDetails')と書いていたのが原因でした。
カンマを描いていない理由はカンマを消すとエラーメッセージが消えていたからです。
エラーが出ている時のprint(response.body);の中身は

{ "error": { "code": 400, "message": "'snippetliveStreamingDetails'", "errors": [ { "message": "'snippetliveStreamingDetails'", "domain": "youtube.part", "reason": "unknownPart", "location": "part", "locationType": "parameter" } ] } }

こんな感じでmessageのところが'snippetliveStreamingDetails'と繋がっていました。
そもそもこの部分に気づかなかったのはこのコードはこのコードを参考にしていたのでprintをコメントアウトしていました。aya-eiyaさん本当に感謝です。

投稿2021/02/24 14:39

limerhyme

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問