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

如何将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 04:25:35