Flutter -Nosuchmethoderror:方法' map'被称为null

发布于 2025-02-13 17:21:28 字数 10267 浏览 0 评论 0原文

这是一个扑朔迷离的程序,可以在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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

惯饮孤独 2025-02-20 17:21:28

终于找到了这个问题。当传递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 in widget.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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文