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

Flutter解析JSON显示餐厅列表时String?类型不匹配错误如何解决

问题修复方案

一、模型类构造函数相关错误修复

错误原因

  1. Dart空安全规则限制:声明为非空的final字段必须在构造函数中明确赋值,要么添加required修饰参数,要么将字段标记为可空(类型后加?)
  2. Map泛型写法错误:代码中Map<"String", dynamic>的String多了引号,正确应为Map<String, dynamic>
  3. JSON解析未做类型兼容处理:比如接口返回的rating可能是int类型,直接赋值给double类型会报错,同时未做字段空判断

修正后模型代码

import 'dart:convert';

class RestaurantDetail {
  final Restaurant? restaurants;

  RestaurantDetail({this.restaurants});

  factory RestaurantDetail.fromJson(Map<String, dynamic> json) {
    return RestaurantDetail(
      restaurants: json['restaurants'] != null ? Restaurant.fromJson(json['restaurants']) : null,
    );
  }
}

class Restaurant {
  final String? id;
  final String? name;
  final String? description;
  final String? pictureId;
  final String? city;
  final double? rating;
  final Menus? menus;

  Restaurant({
    this.id,
    this.name,
    this.description,
    this.pictureId,
    this.city,
    this.rating,
    this.menus,
  });

  factory Restaurant.fromJson(Map<String, dynamic> json) {
    return Restaurant(
      id: json['id']?.toString(),
      name: json['name']?.toString(),
      description: json['description']?.toString(),
      pictureId: json['pictureId']?.toString(),
      city: json['city']?.toString(),
      rating: json['rating'] is int ? (json['rating'] as int).toDouble() : json['rating']?.toDouble(),
      menus: json['menus'] != null ? Menus.fromJson(json['menus']) : null,
    );
  }
}

class Menus {
  final Foods? foods;
  final Drinks? drinks;

  Menus({
    this.foods,
    this.drinks,
  });

  factory Menus.fromJson(Map<String, dynamic> json) {
    return Menus(
        foods: json['foods'] != null ? Foods.fromJson(json['foods']) : null,
        drinks: json['drinks'] != null ? Drinks.fromJson(json['drinks']) : null);
  }
}

class Foods {
  final String? name;

  Foods({this.name});

  factory Foods.fromJson(Map<String, dynamic> json) {
    return Foods(
      name: json['name']?.toString(),
    );
  }
}

class Drinks {
  final String? name;

  Drinks({this.name});

  factory Drinks.fromJson(Map<String, dynamic> json) {
    return Drinks(
      name: json['name']?.toString(),
    );
  }
}

List<RestaurantDetail> parseRestaurant(String? json) {
  if (json == null) {
    return [];
  }
  try {
    final List parsed = jsonDecode(json);
    return parsed.map((json) => RestaurantDetail.fromJson(json)).toList();
  } catch (e) {
    return [];
  }
}

二、首页snapshot.data类型不匹配错误修复

错误原因

FutureBuilder的异步请求返回结果是可空类型String?,而原parseRestaurant函数要求传入不可空的String类型,同时没有处理加载中、加载失败的状态直接调用数据会导致异常。

修正后首页代码

import 'package:flutter/material.dart';
import 'package:restaurant/data/restaurant.dart';
import 'package:restaurant/style/theme.dart';

class HomePage extends StatelessWidget {
  static const routeName = '/article_list';
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: kBackgroundColor,
      body: FutureBuilder<String>(
        future: DefaultAssetBundle.of(context)
            .loadString('assets/local_restaurant.json'),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          }
          if (snapshot.hasError) {
            return Center(child: Text('加载失败:${snapshot.error}'));
          }
          final List<RestaurantDetail> restaurants = parseRestaurant(snapshot.data);
          return ListView.builder(
            itemCount: restaurants.length,
            itemBuilder: (context, index) {
              return _buildRestaurantItem(context, restaurants[index]);
            },
          );
        },
      ),
    );
  }

  Widget _buildRestaurantItem(
      BuildContext context, RestaurantDetail restaurantDetail) {
    final restaurant = restaurantDetail.restaurants;
    if (restaurant == null) return const SizedBox.shrink();
    return ListTile(
      contentPadding:
          const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
      leading: Image.network(
        restaurant.pictureId ?? '',
        width: 100,
        errorBuilder: (context, error, stackTrace) => const Icon(Icons.broken_image, size: 100),
      ),
      title: Text(restaurant.name ?? '未知餐厅'),
      subtitle: Text(restaurant.city ?? '未知城市'),
      onTap: () {},
    );
  }
}

内容的提问来源于stack exchange,提问作者muhammad alam nur haris

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 16:18:00