Flutter中不使用StreamBuilder从Firestore读取数据到ListTile的问题
解决Firestore文档字段单独访问的问题
首先得说清楚你代码里的问题:你定义的snapshot是一个Streamsnapshot['name']肯定会报错——Stream根本没有下标访问的方法呀!而且在StatelessWidget里直接初始化Stream并同步用异步数据,本身就不符合Flutter异步处理的逻辑,因为数据是要等一会儿才能加载到的。
下面给你两种解决方案,看你需求选:
方案1:单次获取数据(不需要实时更新)
如果你的产品数据不需要实时同步Firestore的修改,只是加载一次就行,用FutureBuilder就很合适,不用改成StatefulWidget:
class ProductList extends StatelessWidget { // 封装一个获取单次文档数据的方法 Future<DocumentSnapshot> _getProductDoc() async { return await FirebaseFirestore.instance.collection("listofprods").doc('ac1').get(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(), body: Padding( padding: const EdgeInsets.all(8.0), child: FutureBuilder<DocumentSnapshot>( future: _getProductDoc(), builder: (context, snapshot) { // 数据还在加载中,显示加载指示器 if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } // 加载出错,显示错误信息 if (snapshot.hasError) { return Center(child: Text('加载失败:${snapshot.error}')); } // 数据加载完成,取出文档内容 final productData = snapshot.data!.data() as Map<String, dynamic>?; return Column( mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ Container( decoration: BoxDecoration( border: Border.all(color: Colors.black) ), child: ListTile( // 加个空值判断,防止字段不存在崩溃 title: Text(productData?['name'] ?? '未命名'), ), ) ], ); }, ), ), ), ); } }
方案2:实时监听数据变化
如果需要Firestore文档更新时,你的ListTile也跟着自动刷新,又不想用StreamBuilder包裹整个结构,那可以改成StatefulWidget,手动监听Stream并更新状态:
class ProductList extends StatefulWidget { @override _ProductListState createState() => _ProductListState(); } class _ProductListState extends State<ProductList> { Map<String, dynamic>? _productData; StreamSubscription? _streamSubscription; @override void initState() { super.initState(); // 监听文档的实时变化 _streamSubscription = FirebaseFirestore.instance .collection("listofprods") .doc('ac1') .snapshots() .listen((DocumentSnapshot docSnapshot) { setState(() { _productData = docSnapshot.data() as Map<String, dynamic>?; }); }); } @override void dispose() { // 页面销毁时取消订阅,避免内存泄漏 _streamSubscription?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(), body: Padding( padding: const EdgeInsets.all(8.0), child: Column( mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ // 数据没加载出来时显示加载指示器 if (_productData == null) const Center(child: CircularProgressIndicator()) else Container( decoration: BoxDecoration( border: Border.all(color: Colors.black) ), child: ListTile( title: Text(_productData!['name'] ?? '未命名'), ), ) ], ), ), ), ); } }
两种方案都帮你处理了加载状态、错误状态和空值判断,避免出现崩溃情况,根据你的实际需求选就行~
内容的提问来源于stack exchange,提问作者Lalit Rawat
相关产品推荐
相关产品推荐

