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

Flutter Cubit始终停留在初始状态,请求排查问题

问题:Cubit调用Firebase获取数据后状态未更新,始终停留在初始状态

我尝试使用Cubit从Firebase Firestore中获取列表数据,但调用context.read<LocationHistoryCubit>().getLocationHistoryInitial后,状态并未更新,始终停留在初始状态。已经在main.dart中声明BlocProvider,也尝试用BlocProvider包裹组件树,但问题仍未解决。

Cubit类

class LocationHistoryCubit extends Cubit<LocationHistoryState> {
  LocationHistoryCubit() : super(LocationHistoryInitial());
  final db = FirebaseFirestore.instance;

  Future<String?> getNamaPerusahaan() async {
    String uid = FirebaseAuth.instance.currentUser!.uid;
    log("Current uid : $uid");
    final docRef = db.collection("DataUser").doc(uid).withConverter(
      fromFirestore: UserModel.fromFirestore,
      toFirestore: (UserModel userModel, _) => userModel.toFirestore(),
    );
    final docSnap = await docRef.get();
    final user = docSnap.data();
    log("user company name : ${user?.companyId}");
    return user?.companyId;
  }

  void getLocationHistoryInitial(String idKendaraan) async {
    //TODO : convert custom date time
    emit(LocationHistoryLoading());
    String? companyName = await getNamaPerusahaan();
    List<LatLng> latLngList = [];
    DateTime dateInit = DateTime.now();

    TimeOfDay timeEndRaw = const TimeOfDay(hour: 23, minute: 59);
    TimeOfDay timeStartRaw = const TimeOfDay(hour: 00, minute: 01);

    DateTime dateStart = DateTime(
        dateInit.year, dateInit.month, dateInit.day, timeStartRaw.hour,
        timeStartRaw.minute);
    DateTime dateEnd = DateTime(
        dateInit.year, dateInit.month, dateInit.day, timeEndRaw.hour,
        timeEndRaw.minute);

    String timeStartFormat = DateFormat('yyyy-MM-dd HH:mm').format(dateStart);
    String timeEndFormat = DateFormat('yyyy-MM-dd HH:mm').format(dateEnd);


    final docRef = db.collection("DataPerusahaan").doc(companyName).collection(
        'DaftarKendaraan')
        .doc(idKendaraan).collection("LocationHistory")
        .where(
        'TIS', isGreaterThan: timeStartFormat, isLessThan: timeEndFormat)
        .orderBy(
        'TIS', descending: false);

    var docSnap = await docRef.get();
    final latlong = docSnap.docs;

    for (var coord in latlong) {
      GeoPoint location = coord.get('GeoPoint');
      latLngList.add(
          LatLng(location.latitude, location.longitude)
      );
    }
    log("latLong List : ${latLngList.length}");

    if (latLngList.isEmpty) {
      emit(LocationHistoryEmpty());
    } else {
      emit(LocationHistoryComplete(latLngList));
    }
  }
}

main.dart相关代码

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      initialRoute: '/home',
      routes: {
        '/history' : (context) => MultiBlocProvider(
            providers: [
              BlocProvider(
                  create: (context)=>AuthCubit()
              ),
              BlocProvider(
                  create: (context)=>LocationHistoryCubit()
              ),
            ],
            child: const NewLocationHistory()
        ),

状态类

@immutable
abstract class LocationHistoryState {}

class LocationHistoryInitial extends LocationHistoryState {}

class LocationHistoryLoading extends LocationHistoryState{}

class LocationHistoryComplete extends LocationHistoryState{
  List<LatLng> latLongList;
  LocationHistoryComplete(this.latLongList);
}

class LocationHistoryError extends LocationHistoryState{}

class LocationHistoryEmpty extends LocationHistoryState{}

组件树代码

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocBuilder<LocationHistoryCubit, LocationHistoryState>(
          builder: (context, state){
            log("Location History : $state");
            if(state is LocationHistoryEmpty){
              log("Location History : $state");
              log("Location History Error");
            }else if(state is LocationHistoryComplete){
              return FlutterMap(
                options: MapOptions(
                    center: LatLng(-6.9022141, 107.622682), zoom: 14),
                children: [
                  TileLayer(
                    urlTemplate:
                    'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
                  ),
              
              PolylineLayer(
                polylines: [
                  Polyline(
                      color: Colors.red,
                      strokeWidth: 2,
                      points: state.latLongList)
                ],
              )
                ],
              );
            }else if(state is LocationHistoryError){
              log("Location History : $state");
              log("Location History Error");
            }
            return Container();
          }
      ),
    );

调用代码(在另一个页面的IconButton中)

IconButton(
                                  onPressed: () {
                                    //log("Markers id : ${markers[1].key}");
                                    context.read<LocationHistoryCubit>().getLocationHistoryInitial(document.id);
                                    Navigator.pushNamed(context, '/history',
                                        arguments: document.id);
                                  },
                                  icon: jenisKendaraan == "Motor"
                                      ? const Icon(Icons.motorcycle)
                                      : const Icon(Icons.directions_car))

问题分析与修复方案

核心错误点

你当前的逻辑存在两个关键问题:

  1. Cubit实例不共享:跳转前调用的context.read<LocationHistoryCubit>()是当前页面的Cubit实例,而/history页面的Cubit是在路由内重新创建的新实例,两者完全独立。
  2. 操作顺序错误:先触发旧实例的状态更新,再跳转到监听新实例的页面,导致新页面永远只能看到初始状态。

修复方案

方案1:在新页面初始化时触发数据加载(推荐)

把数据加载逻辑移到NewLocationHistory页面内部,确保调用的是当前页面的Cubit实例:

修改NewLocationHistory页面

class NewLocationHistory extends StatefulWidget {
  const NewLocationHistory({Key? key}) : super(key: key);

  @override
  State<NewLocationHistory> createState() => _NewLocationHistoryState();
}

class _NewLocationHistoryState extends State<NewLocationHistory> {
  @override
  void initState() {
    super.initState();
    // 获取路由参数里的车辆ID
    final idKendaraan = ModalRoute.of(context)?.settings.arguments as String;
    // 调用当前页面的Cubit加载数据
    context.read<LocationHistoryCubit>().getLocationHistoryInitial(idKendaraan);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocBuilder<LocationHistoryCubit, LocationHistoryState>(
          builder: (context, state){
            log("Location History : $state");
            if(state is LocationHistoryLoading){
              return const Center(child: CircularProgressIndicator());
            }else if(state is LocationHistoryEmpty){
              log("Location History : $state");
              return const Center(child: Text("暂无位置历史数据"));
            }else if(state is LocationHistoryComplete){
              return FlutterMap(
                options: MapOptions(
                    center: LatLng(-6.9022141, 107.622682), zoom: 14),
                children: [
                  TileLayer(
                    urlTemplate:
                    'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
                  ),
                  PolylineLayer(
                    polylines: [
                      Polyline(
                          color: Colors.red,
                          strokeWidth: 2,
                          points: state.latLongList)
                    ],
                  )
                ],
              );
            }else if(state is LocationHistoryError){
              log("Location History : $state");
              return const Center(child: Text("加载数据失败"));
            }
            return Container();
          }
      ),
    );
  }
}

修改调用页面的IconButton

IconButton(
  onPressed: () {
    Navigator.pushNamed(context, '/history', arguments: document.id);
  },
  icon: jenisKendaraan == "Motor"
      ? const Icon(Icons.motorcycle)
      : const Icon(Icons.directions_car)
)

方案2:使用全局共享Cubit实例(适合简单场景)

如果需要跨页面共享同一个Cubit状态,可将BlocProvider移到MaterialApp外层:

修改main.dart

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider(create: (context)=>AuthCubit()),
        BlocProvider(create: (context)=>LocationHistoryCubit()),
      ],
      child: MaterialApp(
        initialRoute: '/home',
        routes: {
          '/history' : (context) => const NewLocationHistory(),
          // 其他路由...
        },
      ),
    );
  }
}

这种方式下,跳转前后页面使用同一个Cubit实例,原有的调用顺序可正常生效,但需注意避免不同页面的状态污染问题。

额外优化建议

  1. 添加异常捕获:在Cubit方法中捕获Firebase请求异常,防止崩溃:
void getLocationHistoryInitial(String idKendaraan) async {
  try {
    emit(LocationHistoryLoading());
    // 原有逻辑代码...
    if (latLngList.isEmpty) {
      emit(LocationHistoryEmpty());
    } else {
      emit(LocationHistoryComplete(latLngList));
    }
  } catch (e) {
    log("加载位置历史失败: $e");
    emit(LocationHistoryError());
  }
}
  1. 完善状态UI:补充LocationHistoryLoading状态的加载UI,提升用户体验。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 09:54:52