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

Flutter中基于Bloc与Cloud Firestore实现数据自动更新的方法

解决方案:Bloc 结合 Firestore 实现数据更新与双向实时同步

一、新增用户数据的 Bloc 处理逻辑

扩展现有 Bloc 的事件、状态体系,新增创建用户的处理逻辑,确保数据更新后界面自动同步。

1. 定义扩展的事件与状态

// 事件类
abstract class UserEvent {}
class FetchUsers extends UserEvent {}
class CreateUser extends UserEvent {
  final User user;
  CreateUser(this.user);
}
class UpdateUserText extends UserEvent {
  final String userId;
  final String newText;
  UpdateUserText(this.userId, this.newText);
}

// 状态类
abstract class UserState {}
class UserInitial extends UserState {}
class UserLoading extends UserState {}
class UserLoaded extends UserState {
  final List<User> users;
  UserLoaded(this.users);
}
class UserOperationSuccess extends UserState {}
class UserError extends UserState {
  final String message;
  UserError(this.message);
}

2. User 数据模型

class User {
  final String? id;
  final String name;
  final String displayText;

  User({this.id, required this.name, required this.displayText});

  // 转换为 Firestore 存储的 Map 格式
  Map<String, dynamic> toMap() {
    return {
      'name': name,
      'displayText': displayText,
    };
  }

  // 从 Firestore 文档快照转换为模型
  factory User.fromFirestore(DocumentSnapshot doc) {
    final data = doc.data() as Map<String, dynamic>;
    return User(
      id: doc.id,
      name: data['name'],
      displayText: data['displayText'],
    );
  }
}

3. 扩展 Bloc 核心逻辑

在主 Bloc 中添加创建用户、文本更新的处理,并通过 Firestore 快照监听实现实时数据同步:

class UserBloc extends Bloc<UserEvent, UserState> {
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;
  StreamSubscription? _usersSubscription;

  UserBloc() : super(UserInitial()) {
    // 绑定事件处理器
    on<FetchUsers>(_onFetchUsers);
    on<CreateUser>(_onCreateUser);
    on<UpdateUserText>(_onUpdateUserText);

    // 监听 Firestore 集合实时变化,自动同步数据到界面
    _usersSubscription = _firestore.collection('users').snapshots().listen((snapshot) {
      final users = snapshot.docs.map((doc) => User.fromFirestore(doc)).toList();
      emit(UserLoaded(users));
    });
  }

  // 初始拉取用户数据
  void _onFetchUsers(FetchUsers event, Emitter<UserState> emit) async {
    emit(UserLoading());
    try {
      final snapshot = await _firestore.collection('users').get();
      final users = snapshot.docs.map((doc) => User.fromFirestore(doc)).toList();
      emit(UserLoaded(users));
    } catch (e) {
      emit(UserError(e.toString()));
    }
  }

  // 创建新用户
  void _onCreateUser(CreateUser event, Emitter<UserState> emit) async {
    try {
      await _firestore.collection('users').add(event.user.toMap());
      emit(UserOperationSuccess());
      // 无需手动拉取新数据,Firestore 快照监听会自动推送更新
    } catch (e) {
      emit(UserError(e.toString()));
    }
  }

  // 更新用户文本
  void _onUpdateUserText(UpdateUserText event, Emitter<UserState> emit) async {
    try {
      await _firestore.collection('users').doc(event.userId).update({
        'displayText': event.newText,
      });
      emit(UserOperationSuccess());
    } catch (e) {
      emit(UserError(e.toString()));
    }
  }

  // 取消订阅避免内存泄漏
  @override
  Future<void> close() {
    _usersSubscription?.cancel();
    return super.close();
  }
}

二、双向实时同步实现

核心逻辑是Firestore 快照监听 + 本地操作触发 Firestore 更新,形成闭环:

  1. 从 Firestore 到界面:Bloc 通过 snapshots() 监听集合变化,一旦 Firestore 中文档新增/修改,自动触发 UserLoaded 状态,界面通过 BlocBuilder 刷新展示最新数据。
  2. 从界面到 Firestore:用户在界面修改文本后,提交时触发 UpdateUserText 事件,Bloc 调用 Firestore 的 update 方法修改对应文档;随后快照监听到变化,再次推送更新后的状态到界面,完成双向同步。

三、主界面实现示例

class HomeScreen extends StatelessWidget {
  final TextEditingController _nameController = TextEditingController();
  final TextEditingController _textController = TextEditingController();

  HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('用户列表')),
      body: BlocBuilder<UserBloc, UserState>(
        builder: (context, state) {
          if (state is UserLoading) {
            return const Center(child: CircularProgressIndicator());
          } else if (state is UserLoaded) {
            return ListView.builder(
              itemCount: state.users.length,
              itemBuilder: (context, index) {
                final user = state.users[index];
                return ListTile(
                  title: Text(user.name),
                  subtitle: TextField(
                    controller: TextEditingController(text: user.displayText),
                    onSubmitted: (newText) {
                      // 提交修改触发更新事件
                      context.read<UserBloc>().add(UpdateUserText(user.id!, newText));
                    },
                    decoration: const InputDecoration(hintText: '修改展示文本'),
                  ),
                );
              },
            );
          } else if (state is UserError) {
            return Center(child: Text(state.message));
          } else {
            return const Center(child: Text('点击下方按钮加载用户'));
          }
        },
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () {
              showDialog(
                context: context,
                builder: (context) => AlertDialog(
                  title: const Text('创建新用户'),
                  content: Column(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      TextField(
                        controller: _nameController,
                        decoration: const InputDecoration(hintText: '输入用户名'),
                      ),
                      TextField(
                        controller: _textController,
                        decoration: const InputDecoration(hintText: '输入展示文本'),
                      ),
                    ],
                  ),
                  actions: [
                    TextButton(
                      onPressed: () => Navigator.pop(context),
                      child: const Text('取消'),
                    ),
                    TextButton(
                      onPressed: () {
                        final user = User(
                          name: _nameController.text,
                          displayText: _textController.text,
                        );
                        // 触发创建用户事件
                        context.read<UserBloc>().add(CreateUser(user));
                        _nameController.clear();
                        _textController.clear();
                        Navigator.pop(context);
                      },
                      child: const Text('创建'),
                    ),
                  ],
                ),
              );
            },
            child: const Icon(Icons.add),
          ),
          const SizedBox(height: 10),
          FloatingActionButton(
            onPressed: () => context.read<UserBloc>().add(FetchUsers()),
            child: const Icon(Icons.refresh),
          ),
        ],
      ),
    );
  }
}

关键注意事项

  • 务必在 Bloc 的 close 方法中取消 StreamSubscription,避免内存泄漏。
  • 确保 Firestore 规则允许对应的读写操作,否则会出现权限错误。
  • 界面中使用 BlocBuilder 来监听状态变化,确保数据更新时界面自动重建。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 13:34:57