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

Flutter访问Cloud Firestore报无documents getter实例错误排查

报错原因

Class '_JsonQuerySnapshot' has no instance getter 'documents' 是cloud_firestore 2.0及以上版本API不兼容导致的:旧版本QuerySnapshot通过documents属性返回文档列表,新版本该属性已被废弃,统一改用docs获取文档数组。

你的代码除了属性名用错之外,还有几处逻辑错误,仅替换属性名无法正常运行:

  • 错误套用HTTP接口请求逻辑处理Firestore返回值:snapshots()返回的是Stream对象,调用.get()得到的是结构化的QuerySnapshot,不存在body字段,不需要用json.decode转码
  • 取值逻辑错误:docs是List<QueryDocumentSnapshot>类型的数组,不能直接当作Map通过['quote']读取字段,需要先获取单个文档实例,再从文档的data()返回的Map中取对应字段
  • 实时流、一次性请求、StreamController混用导致逻辑冗余,你需要的双击切换下一条名言效果,不需要每次操作都重新请求流,首次加载拉取全量名言到本地列表,维护索引切换即可,响应速度更快也能减少读写消耗。
  • 原有代码中_streamController未初始化,运行时会直接抛空指针异常。

修复后可运行代码
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:cloud_firestore/cloud_firestore.dart';

class MyQuoteApp extends StatefulWidget {
  final String title;
  MyQuoteApp({required this.title});

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

class _MyQuoteAppState extends State<MyQuoteApp> {
  String _imageUrl = "https://source.unsplash.com/random/";
  int counter = 0;
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;
  // 本地缓存名言列表
  List<QueryDocumentSnapshot> _quotesList = [];
  // 当前展示的名言索引
  int _currentQuoteIndex = 0;
  bool _isLoading = true;

  // 刷新随机背景图
  void _newImage() {
    setState(() {
      _imageUrl = 'https://source.unsplash.com/random/$counter';
      counter++;
    });
  }

  // 首次加载拉取全量名言数据
  Future<void> _loadQuotes() async {
    setState(() {
      _isLoading = true;
    });
    try {
      QuerySnapshot quoteSnapshot = await _firestore.collection("quotes").get();
      _quotesList = quoteSnapshot.docs;
      _newImage();
    } catch (e) {
      print("名言加载失败: $e");
    } finally {
      setState(() {
        _isLoading = false;
      });
    }
  }

  // 双击切换下一条名言
  void _nextQuote() {
    if (_quotesList.isEmpty) return;
    setState(() {
      // 到列表末尾后自动回到第一条
      _currentQuoteIndex = (_currentQuoteIndex + 1) % _quotesList.length;
    });
    _newImage();
  }

  @override
  void initState() {
    super.initState();
    _loadQuotes();
  }

  @override
  Widget build(BuildContext context) {
    SystemChrome.setEnabledSystemUIOverlays([SystemUiOverlay.bottom]);
    return Scaffold(
      backgroundColor: Colors.black26,
      appBar: AppBar(
        title: Center(child: Text(widget.title)),
      ),
      body: Center(
        child: _isLoading
            ? const Center(child: Text("正在加载名言....."))
            : _quotesList.isEmpty
                ? const Center(child: Text("暂无名言数据"))
                : GestureDetector(
                    onDoubleTap: _nextQuote,
                    child: Stack(
                      children: [
                        ColorFiltered(
                          colorFilter: ColorFilter.mode(
                              Colors.black.withOpacity(0.5), BlendMode.darken),
                          child: FadeInImage.assetNetwork(
                            placeholder: 'assets/loading.gif',
                            image: _imageUrl,
                            fit: BoxFit.fitHeight,
                            width: double.maxFinite,
                            height: double.maxFinite,
                          ),
                        ),
                        Padding(
                          padding: const EdgeInsets.all(4.0),
                          child: Center(
                              child: Text(
                            (_quotesList[_currentQuoteIndex].data() as Map)['quote']
                                .toString()
                                .toUpperCase(),
                            textAlign: TextAlign.center,
                            style: const TextStyle(
                                letterSpacing: 0.8,
                                fontSize: 25.0,
                                color: Colors.white,
                                fontWeight: FontWeight.bold),
                          )),
                        ),
                        Column(
                          mainAxisAlignment: MainAxisAlignment.end,
                          crossAxisAlignment: CrossAxisAlignment.end,
                          children: [
                            Padding(
                              padding: const EdgeInsets.only(bottom: 50.0),
                              child: Center(
                                  child: Text(
                                "- ${(_quotesList[_currentQuoteIndex].data() as Map)['author'].toString().toUpperCase()}",
                                textAlign: TextAlign.right,
                                style: const TextStyle(
                                    letterSpacing: 0.8,
                                    fontSize: 18.0,
                                    color: Colors.white,
                                    fontWeight: FontWeight.bold),
                              )),
                            ),
                          ],
                        ),
                      ],
                    ),
                  ),
      ),
    );
  }
}

注意事项
  • 如果需要实时同步Firestore后台的名言增删改,可以把_loadQuotes里的get()换成snapshots()监听,每次数据变动更新本地_quotesList即可
  • 提前在pubspec.yaml中配置好assets/loading.gif的静态资源路径,否则会出现加载报错
  • 如果Unsplash随机图接口加载缓慢,可以替换为其他可用的随机图源

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 01:15:36