Flutter -Nosuchmethoderror:方法' map'被称为null
这是一个扑朔迷离的程序,可以在YouTube播放列表中获取视频列表。我面临一个奇怪的问题,只有当我将字符串变量从文字字符串替换为字符串的参数时,才会发生。
我有以下代码:
class ChosenSubject extends StatefulWidget {
final Subject subject;
ChosenSubject({Key? key, required this.subject}) : super(key: key);
@override
State<ChosenSubject> createState() => _ChosenSubjectState();
}
class _ChosenSubjectState extends State<ChosenSubject> {
late Playlist _playlist;
@override
void initState() {
super.initState();
_playlist = Playlist();
_playlist.items = List.empty(growable: true);
_loadPlaylist();
}
_loadPlaylist() async {
//String playlistId = 'PLSBMQBvBnLEOYH-gxMHCcdmA4liLhK-F8';
String playlistId = widget.subject.playlistId; // this returns the string 'PLSBMQBvBnLEOYH-gxMHCcdmA4liLhK-F8'. Verified it.
print(playlistId);
Playlist playlist = await Services.getPlaylist(playlistId: playlistId); // This call errors
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.subject.name),
centerTitle: true,
elevation: 0,
),
);
}
}
services.dart文件是:
class Services {
static const _baseUrl = 'youtube.googleapis.com';
static Future<Playlist> getPlaylist({required String playlistId}) async {
Map<String, String> parameters = {
'part': 'snippet',
'playlistId': playlistId,
'maxResults': '25',
'key': Constants.API_KEY,
};
Map<String, String> headers = {
HttpHeaders.contentTypeHeader: 'application/json',
};
Uri uri = Uri.https(
_baseUrl,
'youtube/v3/playlistItems',
parameters,
);
http.Response response = await http.get(uri, headers: headers);
Playlist playlistItem = playlistFromJson(response.body);
return playlistItem;
}
}
playlist.dart文件
import 'dart:convert';
Playlist playlistFromJson(String str) => Playlist.fromJson(json.decode(str));
String playlistToJson(Playlist data) => json.encode(data.toJson());
class Playlist {
Playlist({
this.kind,
this.etag,
this.items,
this.pageInfo,
});
String? kind;
String? etag;
List<Item>? items;
PageInfo? pageInfo;
factory Playlist.fromJson(Map<String, dynamic> json) => Playlist(
kind: json["kind"],
etag: json["etag"],
items: List<Item>.from(json["items"].map((x) => Item.fromJson(x))),
pageInfo: PageInfo.fromJson(json["pageInfo"]),
);
Map<String, dynamic> toJson() => {
"kind": kind,
"etag": etag,
"items": List<dynamic>.from(items!.map((x) => x.toJson())),
"pageInfo": pageInfo?.toJson(),
};
}
class Item {
Item({
required this.kind,
required this.etag,
required this.id,
required this.snippet,
});
String kind;
String etag;
String id;
Snippet snippet;
factory Item.fromJson(Map<String, dynamic> json) => Item(
kind: json["kind"],
etag: json["etag"],
id: json["id"],
snippet: Snippet.fromJson(json["snippet"]),
);
Map<String, dynamic> toJson() => {
"kind": kind,
"etag": etag,
"id": id,
"snippet": snippet.toJson(),
};
}
class Snippet {
Snippet({
required this.publishedAt,
required this.channelId,
required this.title,
required this.description,
required this.thumbnails,
required this.channelTitle,
required this.playlistId,
required this.position,
required this.resourceId,
required this.videoOwnerChannelTitle,
required this.videoOwnerChannelId,
});
DateTime publishedAt;
String channelId;
String title;
String description;
Thumbnails thumbnails;
String channelTitle;
String playlistId;
int position;
ResourceId resourceId;
String videoOwnerChannelTitle;
String videoOwnerChannelId;
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"],
playlistId: json["playlistId"],
position: json["position"],
resourceId: ResourceId.fromJson(json["resourceId"]),
videoOwnerChannelTitle: json["videoOwnerChannelTitle"],
videoOwnerChannelId: json["videoOwnerChannelId"],
);
Map<String, dynamic> toJson() => {
"publishedAt": publishedAt.toIso8601String(),
"channelId": channelId,
"title": title,
"description": description,
"thumbnails": thumbnails.toJson(),
"channelTitle": channelTitle,
"playlistId": playlistId,
"position": position,
"resourceId": resourceId.toJson(),
"videoOwnerChannelTitle": videoOwnerChannelTitle,
"videoOwnerChannelId": videoOwnerChannelId,
};
}
class ResourceId {
ResourceId({
required this.kind,
required this.videoId,
});
String kind;
String videoId;
factory ResourceId.fromJson(Map<String, dynamic> json) => ResourceId(
kind: json["kind"],
videoId: json["videoId"],
);
Map<String, dynamic> toJson() => {
"kind": kind,
"videoId": videoId,
};
}
class Thumbnails {
Thumbnails({
required this.thumbnailsDefault,
required this.medium,
required this.high,
required this.standard,
});
Default thumbnailsDefault;
Default medium;
Default high;
Default standard;
factory Thumbnails.fromJson(Map<String, dynamic> json) => Thumbnails(
thumbnailsDefault: Default.fromJson(json["default"]),
medium: Default.fromJson(json["medium"]),
high: Default.fromJson(json["high"]),
standard: Default.fromJson(json["standard"]),
);
Map<String, dynamic> toJson() => {
"default": thumbnailsDefault.toJson(),
"medium": medium.toJson(),
"high": high.toJson(),
"standard": standard.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,
};
}
运行程序时
String playlistId = 'PLSBMQBvBnLEOYH-gxMHCcdmA4liLhK-F8';
Playlist playlist = await Services.getPlaylist(playlistId: playlistId);
,以下呼叫正常:但是当我替换playlistid string string string时:
String playlistId = widget.subject.playlistId;
Playlist playlist = await Services.getPlaylist(playlistId: playlistId);
我得到“ nosuchmethoderror呼叫null“错误”。
E/flutter ( 8015): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: NoSuchMethodError: The method 'map' was called on null.
E/flutter ( 8015): Receiver: null
E/flutter ( 8015): Tried calling: map(Closure: (dynamic) => Item)
E/flutter ( 8015): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:38:5)
E/flutter ( 8015): #1 new Playlist.fromJson
E/flutter ( 8015): #2 playlistFromJson
E/flutter ( 8015): #3 Services.getPlaylist
E/flutter ( 8015): <asynchronous suspension>
E/flutter ( 8015): #4 _ChosenSubjectState._loadPlaylist
E/flutter ( 8015): <asynchronous suspension>
E/flutter ( 8015):
Restarted application in 912ms.
Reloaded 1 of 1102 libraries in 279ms.
Reloaded 1 of 1102 libraries in 229ms.
字符串playlistid = widget.subject.playlistid;
返回相同的字符串'plsbmqbvbnleoyh-gxmhccdma4lilhk-f8'。通过在控制台上登录来验证它。
有人可以帮我吗?
编辑: API调用结果:
{
"kind": "youtube#playlistItemListResponse",
"etag": "F9DlUsG8_KHE4LmXUFhEuInW02c",
"items": [
{
"kind": "youtube#playlistItem",
"etag": "jvRF3UDOSJ3jOpT9yF0HK4cJYoM",
"id": "UExTQk1RQnZCbkxFTnJVN3lPOVRNRVFBUzJrQ1k5UEdzWS41NkI0NEY2RDEwNTU3Q0M2",
"snippet": {
"publishedAt": "2022-06-30T09:21:42Z",
"channelId": "UCB3igi7VFgReyXmnvS3EZ4A",
"title": "Mathematics-1",
"description": "",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/vCwOjckCe30/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://i.ytimg.com/vi/vCwOjckCe30/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://i.ytimg.com/vi/vCwOjckCe30/hqdefault.jpg",
"width": 480,
"height": 360
},
"standard": {
"url": "https://i.ytimg.com/vi/vCwOjckCe30/sddefault.jpg",
"width": 640,
"height": 480
},
"maxres": {
"url": "https://i.ytimg.com/vi/vCwOjckCe30/maxresdefault.jpg",
"width": 1280,
"height": 720
}
},
"channelTitle": "SARP CRT",
"playlistId": "PLSBMQBvBnLENrU7yO9TMEQAS2kCY9PGsY",
"position": 0,
"resourceId": {
"kind": "youtube#video",
"videoId": "vCwOjckCe30"
},
"videoOwnerChannelTitle": "SARP CRT",
"videoOwnerChannelId": "UCB3igi7VFgReyXmnvS3EZ4A"
}
}
],
"pageInfo": {
"totalResults": 1,
"resultsPerPage": 5
}
}
This is a flutter program to get the list of videos in a YouTube PlayList. I am facing a weird issue that happens only when I substitute a String variable from literal string to a parameter that is a String.
I have the following code:
class ChosenSubject extends StatefulWidget {
final Subject subject;
ChosenSubject({Key? key, required this.subject}) : super(key: key);
@override
State<ChosenSubject> createState() => _ChosenSubjectState();
}
class _ChosenSubjectState extends State<ChosenSubject> {
late Playlist _playlist;
@override
void initState() {
super.initState();
_playlist = Playlist();
_playlist.items = List.empty(growable: true);
_loadPlaylist();
}
_loadPlaylist() async {
//String playlistId = 'PLSBMQBvBnLEOYH-gxMHCcdmA4liLhK-F8';
String playlistId = widget.subject.playlistId; // this returns the string 'PLSBMQBvBnLEOYH-gxMHCcdmA4liLhK-F8'. Verified it.
print(playlistId);
Playlist playlist = await Services.getPlaylist(playlistId: playlistId); // This call errors
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.subject.name),
centerTitle: true,
elevation: 0,
),
);
}
}
The services.dart file is:
class Services {
static const _baseUrl = 'youtube.googleapis.com';
static Future<Playlist> getPlaylist({required String playlistId}) async {
Map<String, String> parameters = {
'part': 'snippet',
'playlistId': playlistId,
'maxResults': '25',
'key': Constants.API_KEY,
};
Map<String, String> headers = {
HttpHeaders.contentTypeHeader: 'application/json',
};
Uri uri = Uri.https(
_baseUrl,
'youtube/v3/playlistItems',
parameters,
);
http.Response response = await http.get(uri, headers: headers);
Playlist playlistItem = playlistFromJson(response.body);
return playlistItem;
}
}
The playlist.dart file
import 'dart:convert';
Playlist playlistFromJson(String str) => Playlist.fromJson(json.decode(str));
String playlistToJson(Playlist data) => json.encode(data.toJson());
class Playlist {
Playlist({
this.kind,
this.etag,
this.items,
this.pageInfo,
});
String? kind;
String? etag;
List<Item>? items;
PageInfo? pageInfo;
factory Playlist.fromJson(Map<String, dynamic> json) => Playlist(
kind: json["kind"],
etag: json["etag"],
items: List<Item>.from(json["items"].map((x) => Item.fromJson(x))),
pageInfo: PageInfo.fromJson(json["pageInfo"]),
);
Map<String, dynamic> toJson() => {
"kind": kind,
"etag": etag,
"items": List<dynamic>.from(items!.map((x) => x.toJson())),
"pageInfo": pageInfo?.toJson(),
};
}
class Item {
Item({
required this.kind,
required this.etag,
required this.id,
required this.snippet,
});
String kind;
String etag;
String id;
Snippet snippet;
factory Item.fromJson(Map<String, dynamic> json) => Item(
kind: json["kind"],
etag: json["etag"],
id: json["id"],
snippet: Snippet.fromJson(json["snippet"]),
);
Map<String, dynamic> toJson() => {
"kind": kind,
"etag": etag,
"id": id,
"snippet": snippet.toJson(),
};
}
class Snippet {
Snippet({
required this.publishedAt,
required this.channelId,
required this.title,
required this.description,
required this.thumbnails,
required this.channelTitle,
required this.playlistId,
required this.position,
required this.resourceId,
required this.videoOwnerChannelTitle,
required this.videoOwnerChannelId,
});
DateTime publishedAt;
String channelId;
String title;
String description;
Thumbnails thumbnails;
String channelTitle;
String playlistId;
int position;
ResourceId resourceId;
String videoOwnerChannelTitle;
String videoOwnerChannelId;
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"],
playlistId: json["playlistId"],
position: json["position"],
resourceId: ResourceId.fromJson(json["resourceId"]),
videoOwnerChannelTitle: json["videoOwnerChannelTitle"],
videoOwnerChannelId: json["videoOwnerChannelId"],
);
Map<String, dynamic> toJson() => {
"publishedAt": publishedAt.toIso8601String(),
"channelId": channelId,
"title": title,
"description": description,
"thumbnails": thumbnails.toJson(),
"channelTitle": channelTitle,
"playlistId": playlistId,
"position": position,
"resourceId": resourceId.toJson(),
"videoOwnerChannelTitle": videoOwnerChannelTitle,
"videoOwnerChannelId": videoOwnerChannelId,
};
}
class ResourceId {
ResourceId({
required this.kind,
required this.videoId,
});
String kind;
String videoId;
factory ResourceId.fromJson(Map<String, dynamic> json) => ResourceId(
kind: json["kind"],
videoId: json["videoId"],
);
Map<String, dynamic> toJson() => {
"kind": kind,
"videoId": videoId,
};
}
class Thumbnails {
Thumbnails({
required this.thumbnailsDefault,
required this.medium,
required this.high,
required this.standard,
});
Default thumbnailsDefault;
Default medium;
Default high;
Default standard;
factory Thumbnails.fromJson(Map<String, dynamic> json) => Thumbnails(
thumbnailsDefault: Default.fromJson(json["default"]),
medium: Default.fromJson(json["medium"]),
high: Default.fromJson(json["high"]),
standard: Default.fromJson(json["standard"]),
);
Map<String, dynamic> toJson() => {
"default": thumbnailsDefault.toJson(),
"medium": medium.toJson(),
"high": high.toJson(),
"standard": standard.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,
};
}
When I run the program, the following call works fine:
String playlistId = 'PLSBMQBvBnLEOYH-gxMHCcdmA4liLhK-F8';
Playlist playlist = await Services.getPlaylist(playlistId: playlistId);
But when I substitute playListId string with:
String playlistId = widget.subject.playlistId;
Playlist playlist = await Services.getPlaylist(playlistId: playlistId);
I get the "NoSuchMethodError: The method 'map' was called on null" error.
E/flutter ( 8015): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: NoSuchMethodError: The method 'map' was called on null.
E/flutter ( 8015): Receiver: null
E/flutter ( 8015): Tried calling: map(Closure: (dynamic) => Item)
E/flutter ( 8015): #0 Object.noSuchMethod (dart:core-patch/object_patch.dart:38:5)
E/flutter ( 8015): #1 new Playlist.fromJson
E/flutter ( 8015): #2 playlistFromJson
E/flutter ( 8015): #3 Services.getPlaylist
E/flutter ( 8015): <asynchronous suspension>
E/flutter ( 8015): #4 _ChosenSubjectState._loadPlaylist
E/flutter ( 8015): <asynchronous suspension>
E/flutter ( 8015):
Restarted application in 912ms.
Reloaded 1 of 1102 libraries in 279ms.
Reloaded 1 of 1102 libraries in 229ms.
String playlistId = widget.subject.playlistId;
returns the same string 'PLSBMQBvBnLEOYH-gxMHCcdmA4liLhK-F8'. Verified it by logging on the console.
Can someone help me with this?
Edit:
The API call result:
{
"kind": "youtube#playlistItemListResponse",
"etag": "F9DlUsG8_KHE4LmXUFhEuInW02c",
"items": [
{
"kind": "youtube#playlistItem",
"etag": "jvRF3UDOSJ3jOpT9yF0HK4cJYoM",
"id": "UExTQk1RQnZCbkxFTnJVN3lPOVRNRVFBUzJrQ1k5UEdzWS41NkI0NEY2RDEwNTU3Q0M2",
"snippet": {
"publishedAt": "2022-06-30T09:21:42Z",
"channelId": "UCB3igi7VFgReyXmnvS3EZ4A",
"title": "Mathematics-1",
"description": "",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/vCwOjckCe30/default.jpg",
"width": 120,
"height": 90
},
"medium": {
"url": "https://i.ytimg.com/vi/vCwOjckCe30/mqdefault.jpg",
"width": 320,
"height": 180
},
"high": {
"url": "https://i.ytimg.com/vi/vCwOjckCe30/hqdefault.jpg",
"width": 480,
"height": 360
},
"standard": {
"url": "https://i.ytimg.com/vi/vCwOjckCe30/sddefault.jpg",
"width": 640,
"height": 480
},
"maxres": {
"url": "https://i.ytimg.com/vi/vCwOjckCe30/maxresdefault.jpg",
"width": 1280,
"height": 720
}
},
"channelTitle": "SARP CRT",
"playlistId": "PLSBMQBvBnLENrU7yO9TMEQAS2kCY9PGsY",
"position": 0,
"resourceId": {
"kind": "youtube#video",
"videoId": "vCwOjckCe30"
},
"videoOwnerChannelTitle": "SARP CRT",
"videoOwnerChannelId": "UCB3igi7VFgReyXmnvS3EZ4A"
}
}
],
"pageInfo": {
"totalResults": 1,
"resultsPerPage": 5
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
终于找到了这个问题。当传递
widget.subject.playlistid
时,API正在返回错误。构建URI时,它将在widget.subject.playlistid
的字符串中添加一个额外的“+”字符。因此,API返回错误,并且程序崩溃了。解析了“+”,它现在有效。
谢谢@Sagar Acharya,您的建议帮助我找到了解决方案。
Finally found the issue. The API was returning an error when
widget.subject.playlistId
was passed. When Uri was constructed, it was adding an extra '+' character to the string inwidget.subject.playlistId
. So the API returned error and the program crashed.Parsed the '+' out it works now.
Thanks @Sagar Acharya your suggestions helped me in finding a solution.