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

如何从Firebase获取Array数据到Flutter?字符串可获取但Array报错

Flutter Firebase 获取字符串数组数据示例

问题场景

开发Flutter Pokédex应用时,可正常获取Firebase中的name、id、size等字符串字段,但读取elements数组字段时出错,需要实现正确的数组读取与展示逻辑。

修正后的完整代码示例

1. 数据模型优化(明确字符串数组类型)

把List<dynamic>改为List<String>,类型更严谨,避免类型转换异常:

class Pokemon {
  String id;
  final String name;
  final String size;
  final List<String> elements;

  Pokemon({
    this.id = '',
    required this.name,
    required this.size,
    required this.elements,
  });

  Map<String, dynamic> toJson() => {
        'id': id,
        'name': name,
        'size': size,
        'elements': elements,
      };

  static Pokemon fromJson(Map<String, dynamic> json) => Pokemon(
        id: json['id'] ?? '',
        name: json['name'] ?? '',
        size: json['size'] ?? '',
        // 将Firebase返回的数组转为字符串列表,空值时返回空列表
        elements: List<String>.from(json['elements'] ?? []),
      );
}

2. 实现Firebase数据读取Stream

补充readPokemon()方法,从Firebase Firestore中读取完整数据:

Stream<List<Pokemon>> readPokemon() {
  return FirebaseFirestore.instance
      .collection('pokemon') // 替换为你的Firebase集合名称
      .snapshots()
      .map((snapshot) => snapshot.docs
          .map((doc) => Pokemon.fromJson(doc.data()..['id'] = doc.id))
          .toList());
}

3. 数组数据展示(完善卡片构建方法)

实现buildPokemon方法,将数组元素(如宝可梦属性)友好展示:

Widget buildPokemon(Pokemon pokemon, BuildContext context) {
  return Card(
    margin: const EdgeInsets.all(8),
    child: Padding(
      padding: const EdgeInsets.all(12),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            pokemon.name,
            style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 8),
          Text('体型: ${pokemon.size}'),
          const SizedBox(height: 8),
          const Text('属性:', style: TextStyle(fontWeight: FontWeight.w500)),
          // 遍历数组生成标签,自动换行避免溢出
          Wrap(
            spacing: 6,
            children: pokemon.elements
                .map((element) => Chip(
                      label: Text(element),
                      backgroundColor: Colors.blue.shade100,
                    ))
                .toList(),
          ),
        ],
      ),
    ),
  );
}

4. 完整页面代码(整合所有部分)

class PokedexHome extends StatefulWidget {
  const PokedexHome({super.key});

  @override
  _PokedexHomeState createState() => _PokedexHomeState();
}

class _PokedexHomeState extends State<PokedexHome> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.grey,
      appBar: AppBar(
        leading: const Icon(Icons.sports_baseball),
        centerTitle: true,
        title: const Text('Pokédex'),
        backgroundColor: Colors.red,
      ),
      body: StreamBuilder<List<Pokemon>>(
        stream: readPokemon(),
        builder: (context, snapshot) {
          if (snapshot.hasError) {
            return const Text('找不到任何宝可梦 :(');
          } else if (snapshot.hasData) {
            final pokemonList = snapshot.data!;
            return GridView.count(
              crossAxisCount: 2,
              childAspectRatio: 1 / 0.5,
              children: pokemonList
                  .map((pokemon) => buildPokemon(pokemon, context))
                  .toList(),
            );
          } else {
            return const Center(child: CircularProgressIndicator());
          }
        },
      ),
    );
  }

  Stream<List<Pokemon>> readPokemon() {
    return FirebaseFirestore.instance
        .collection('pokemon')
        .snapshots()
        .map((snapshot) => snapshot.docs
            .map((doc) => Pokemon.fromJson(doc.data()..['id'] = doc.id))
            .toList());
  }

  Widget buildPokemon(Pokemon pokemon, BuildContext context) {
    return Card(
      margin: const EdgeInsets.all(8),
      child: Padding(
        padding: const EdgeInsets.all(12),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              pokemon.name,
              style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 8),
            Text('体型: ${pokemon.size}'),
            const SizedBox(height: 8),
            const Text('属性:', style: TextStyle(fontWeight: FontWeight.w500)),
            Wrap(
              spacing: 6,
              children: pokemon.elements
                  .map((element) => Chip(
                        label: Text(element),
                        backgroundColor: Colors.blue.shade100,
                      ))
                  .toList(),
            ),
          ],
        ),
      ),
    );
  }
}

class Pokemon {
  String id;
  final String name;
  final String size;
  final List<String> elements;

  Pokemon({
    this.id = '',
    required this.name,
    required this.size,
    required this.elements,
  });

  Map<String, dynamic> toJson() => {
        'id': id,
        'name': name,
        'size': size,
        'elements': elements,
      };

  static Pokemon fromJson(Map<String, dynamic> json) => Pokemon(
        id: json['id'] ?? '',
        name: json['name'] ?? '',
        size: json['size'] ?? '',
        elements: List<String>.from(json['elements'] ?? []),
      );
}

关键注意点

  • 安全类型转换:使用List<String>.from(json['elements'] ?? [])替代强制类型转换,避免数组为空或类型不匹配时崩溃
  • 空值处理:添加?? ''和?? []处理字段为空的情况,增强代码健壮性
  • 数组展示:用Wrap + Chip的组合可自动换行展示多个数组元素,避免布局溢出问题

内容的提问来源于stack exchange,提问作者Mathias Van Langendonck

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 07:39:58