You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter调用Moralis API获取NFT时遇空类型转换错误求助

Flutter调用Moralis API获取NFT时遇到Null转String错误的解决方案

错误信息

Unhandled Exception: type 'Null' is not a subtype of type 'String'

报错关联代码段

  1. Metadata解析逻辑:
final meta = jsonDecode(map?['metadata']);

meta_name = meta['name'] ?? '';
meta_image = meta['image'] ?? '';
meta_description = meta['description'] ?? '';
  1. API响应解析逻辑:
if (response.statusCode == 200) {
  nfts = jsonDecode(response.body)['result'].map<Nfts>((result) {
    return Nfts.fromMap(result);
  }).toList();
}

问题根源

  1. Metadata字段可能为空:Moralis返回的部分NFT数据中metadata字段为null,直接传入jsonDecode会导致解码失败,后续访问meta的键值会触发类型错误。
  2. 类字段定义不匹配API响应:API中total、cursor为可空值,但Nfts类中定义为非空String;且类中错误定义了result字段(API里的result是数组,对应NFT列表,无需在单个NFT类中声明)。
  3. 空URL触发NetworkImage错误:若meta_image为空字符串,NetworkImage会因无效URL报错。

解决方案

1. 修正Nfts类的字段与构造方法

匹配API响应调整字段类型,增加Metadata空值与解码异常处理:

import 'dart:convert';

class Nfts {
  String? total;
  late String page;
  late String page_size;
  String? cursor;
  late String token_id;
  late String token_address;
  late String amount;
  late String owner_of;
  late String token_hash;
  late String block_number_minted;
  late String block_number;
  late String contract_type;
  late String name;
  late String symbol;
  String? token_uri;
  String? metadata;
  late String last_token_uri_sync;
  late String last_metadata_sync;
  late String minter_address;
  late String meta_name;
  late String meta_image;
  late String meta_description;

  Nfts({
    this.total,
    required this.page,
    required this.page_size,
    this.cursor,
    required this.token_id,
    required this.token_address,
    required this.amount,
    required this.owner_of,
    required this.token_hash,
    required this.block_number_minted,
    required this.block_number,
    required this.contract_type,
    required this.name,
    required this.symbol,
    this.token_uri,
    this.metadata,
    required this.last_token_uri_sync,
    required this.last_metadata_sync,
    required this.minter_address,
    required this.meta_name,
    required this.meta_image,
    required this.meta_description,
  });

  Nfts.fromMap(Map<String, dynamic> map) {
    total = map['total']?.toString();
    page = map['page']?.toString() ?? '1';
    page_size = map['page_size']?.toString() ?? '100';
    cursor = map['cursor'];
    token_id = map['token_id'] ?? '';
    token_address = map['token_address'] ?? '';
    amount = map['amount'] ?? '';
    owner_of = map['owner_of'] ?? '';
    token_hash = map['token_hash'] ?? '';
    block_number_minted = map['block_number_minted'] ?? '';
    block_number = map['block_number'] ?? '';
    contract_type = map['contract_type'] ?? '';
    name = map['name'] ?? '';
    symbol = map['symbol'] ?? '';
    token_uri = map['token_uri'];
    metadata = map['metadata'];
    last_token_uri_sync = map['last_token_uri_sync'] ?? '';
    last_metadata_sync = map['last_metadata_sync'] ?? '';
    minter_address = map['minter_address'] ?? '';

    // 处理metadata为空或解码失败的情况
    if (metadata != null && metadata!.isNotEmpty) {
      try {
        final meta = jsonDecode(metadata!);
        meta_name = meta['name'] ?? '';
        meta_image = meta['image'] ?? '';
        meta_description = meta['description'] ?? '';
      } catch (_) {
        meta_name = '';
        meta_image = '';
        meta_description = '';
      }
    } else {
      meta_name = '';
      meta_image = '';
      meta_description = '';
    }
  }
}

2. 优化NftsProviders的API调用逻辑

支持动态传入钱包地址,增加响应空值判断:

import 'package:http/http.dart' as http;
import 'dart:convert';

class NftsProviders{
  Future<List<Nfts>> getNfts(String walletAddress) async {
    final Uri uri = Uri.parse('https://deep-index.moralis.io/api/v2/$walletAddress/nft?chain=polygon&format=decimal');
    List<Nfts> nfts = [];

    final response = await http.get(uri, headers: {
      'accept': 'application/json',
      'X-API-Key' : 'o1g9ywaRjZvZaeaByxhZc7mFOBVVvDJEksU0jeZ8b34fNX03ISTc72fltfsAnuYG'
    });

    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      if (data['result'] != null && data['result'] is List) {
        nfts = data['result'].map<Nfts>((result) {
          return Nfts.fromMap(result);
        }).toList();
      }
    } else {
      throw Exception('Failed to load NFT: ${response.statusCode}');
    }

    return nfts;
  }
}

3. 修复UI展示的空值问题

处理NFT图片为空的情况,添加默认占位图:

// NftsScreen的itemBuilder部分修改
itemBuilder: (context, index) {
  final nft = nfts[index];
  return Container(
    padding: EdgeInsets.all(8.0),
    child: Column(
      children: [
        CupertinoButton(
          onPressed: () {},
          child: CircleAvatar(
            radius: 100,
            backgroundImage: nft.meta_image.isNotEmpty 
                ? NetworkImage(nft.meta_image) 
                : const AssetImage('assets/default_nft.png') as ImageProvider,
            backgroundColor: Colors.grey[200],
          ),
        ),
        if (nft.meta_name.isNotEmpty)
          Text(nft.meta_name),
      ],
    ),
  );
},

注:需在项目assets目录添加default_nft.png,并在pubspec.yaml中配置资源路径。

4. 从Firebase获取钱包地址并调用API

在initNfts方法中传入从Firebase获取的用户钱包地址:

Future initNfts() async {
  // 替换为实际从Firebase获取钱包地址的逻辑
  String walletAddress = await FirebaseAuth.instance.currentUser!.displayName!; 
  nfts = await nftsProvider.getNfts(walletAddress);
}

内容的提问来源于stack exchange,提问作者dev cat

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 12:51:18