如何将Firebase 'trip'集合数据存储到本地并读取?
实现Firebase Trip数据的本地存储与读取
一、选择本地存储方案
推荐用shared_preferences(适合轻量键值对存储),如果数据结构复杂或需要频繁查询,也可以用hive(轻量NoSQL本地数据库)。这里以shared_preferences为例演示。
二、步骤实现
1. 添加依赖
在项目的pubspec.yaml文件中添加依赖:
dependencies: flutter: sdk: flutter shared_preferences: ^2.2.2 # 版本号以最新为准 intl: ^0.18.1 # 确保已添加,原代码用到了DateFormat
执行flutter pub get完成依赖安装。
2. 创建Trip数据模型
把Firebase返回的文档数据封装成模型,方便序列化和反序列化:
class Trip { final String tripid; final String tripname; final String location; final String startdate; final String enddate; Trip({ required this.tripid, required this.tripname, required this.location, required this.startdate, required this.enddate, }); // 从Firebase文档快照转成Trip对象 factory Trip.fromDocumentSnapshot(DocumentSnapshot doc) { return Trip( tripid: doc['tripid'], tripname: doc['tripname'], location: doc['location'], startdate: doc['startdate'], enddate: doc['enddate'], ); } // 转成JSON格式,方便存储到本地 Map<String, dynamic> toJson() { return { 'tripid': tripid, 'tripname': tripname, 'location': location, 'startdate': startdate, 'enddate': enddate, }; } // 从JSON转成Trip对象 factory Trip.fromJson(Map<String, dynamic> json) { return Trip( tripid: json['tripid'], tripname: json['tripname'], location: json['location'], startdate: json['startdate'], enddate: json['enddate'], ); } }
3. 实现本地存储工具类
创建工具类处理本地存储的读写逻辑:
import 'package:shared_preferences/shared_preferences.dart'; import 'dart:convert'; class LocalStorageService { // 保存所有Trip到本地 static Future<void> saveTrips(List<Trip> trips) async { final prefs = await SharedPreferences.getInstance(); final List<String> tripsJson = trips.map((trip) => jsonEncode(trip.toJson())).toList(); await prefs.setStringList('saved_trips', tripsJson); } // 从本地读取所有Trip static Future<List<Trip>> getSavedTrips() async { final prefs = await SharedPreferences.getInstance(); final List<String>? tripsJson = prefs.getStringList('saved_trips'); if (tripsJson != null) { return tripsJson.map((json) => Trip.fromJson(jsonDecode(json))).toList(); } return []; } }
4. 整合到现有代码中
修改原StreamBuilder,实现"Firebase数据更新时保存到本地+离线优先读取本地数据"的逻辑:
// 页面状态类中添加变量保存本地Trip列表 List<Trip> _localTrips = []; // 初始化时读取本地数据 @override void initState() { super.initState(); _loadLocalTrips(); } // 读取本地Trip数据 Future<void> _loadLocalTrips() async { _localTrips = await LocalStorageService.getSavedTrips(); setState(() {}); } // 修改后的StreamBuilder StreamBuilder( stream: _trip.snapshots(), builder: (context, AsyncSnapshot<QuerySnapshot> streamSnapshot) { // 默认显示本地数据,Firebase加载完成后替换 List<Trip> displayTrips = _localTrips; if (streamSnapshot.hasData) { // 把Firebase文档转成符合条件的Trip列表 List<Trip> firebaseTrips = streamSnapshot.data!.docs .map((doc) => Trip.fromDocumentSnapshot(doc)) .where((trip) { DateTime dt2 = DateFormat('dd-MM-yyyy').parse(trip.enddate); return dt2.isAfter(DateTime.now()); }) .toList(); // 保存到本地 LocalStorageService.saveTrips(firebaseTrips); displayTrips = firebaseTrips; } else if (streamSnapshot.connectionState == ConnectionState.waiting && _localTrips.isEmpty) { // 本地无数据且正在加载Firebase,显示加载指示器 return const Center(child: CircularProgressIndicator()); } // 构建列表 return ListView.builder( itemCount: displayTrips.length, itemBuilder: (context, index) { final Trip trip = displayTrips[index]; return Card( margin: const EdgeInsets.all(10), elevation: 3, child: ListTile( leading: Image.asset("assets/images/baggage.png"), title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text("Trip: ${trip.tripname}", style: TextStyle(fontWeight: FontWeight.bold, fontSize: 16),), SizedBox(height: 7,), Text("Location: ${trip.location} "), SizedBox(height: 10,) ], ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Text( "Starts: ${trip.startdate}", style: TextStyle(fontSize: 15.5),), SizedBox(height: 7,), Text("Ends: ${trip.enddate}", style: TextStyle(fontSize: 15.5),), ], ), trailing: SizedBox( width: 100, height: 100, child: Row( children: [ IconButton( onPressed: () => _update(trip), // 需修改_update方法参数为Trip对象 icon: Icon(Icons.edit,), ), IconButton( onPressed: () { Navigator.of(context).push( MaterialPageRoute(builder: (context) => ShowPlans(tripid: trip.tripid)) ); }, icon: Icon(Icons.arrow_forward_ios,), ), ], ), ), ), ); }, ); }, )
三、注意事项
- 若原
_update方法接收的是DocumentSnapshot,需修改为接收Trip对象,或在方法内通过tripid重新查询Firebase文档。 - 如需单个Trip的增删改操作,可扩展
LocalStorageService,添加saveSingleTrip、deleteTrip等方法。 - 数据量较大时,推荐用
hive或sqflite,支持更高效的本地数据管理。
内容的提问来源于stack exchange,提问作者Farhana Akter
相关产品推荐
相关产品推荐

