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

Flutter中如何对比Firebase Realtime DB键值获取当前登录用户数据

实现方案

你现在的写法有两个核心问题:一是直接拉取了insertdata节点下的全量用户数据,既浪费流量、查询慢,还存在数据安全风险;二是获取用户邮箱的方法是异步执行的,initState里调用不会等待赋值完成就触发build,很容易出现邮箱为空、匹配不到数据的情况。
根据你是否愿意调整现有数据库结构,有两种可行方案:

方案1:用UID作为用户数据的存储键(长期使用强烈推荐)

这是Firebase开发的标准实践,完全不需要做字段匹配,查询效率最高。
你之前写入用户数据的时候应该是用了push()生成随机子节点ID,改成直接用Firebase Auth返回的用户uid作为子节点键即可,写入示例:

final currentUser = FirebaseAuth.instance.currentUser;
if (currentUser != null) {
  // 直接用uid当key存用户数据
  FirebaseDatabase.instance.ref("insertdata").child(currentUser.uid).set({
    "Email": currentUser.email,
    "First Name": "输入的名",
    "Last Name": "输入的姓",
    // 其他用户字段
  });
}

这种结构下,要获取当前登录用户的快照根本不需要匹配任何字段,直接定位路径查询即可:

// 先判断登录状态
final currentUser = FirebaseAuth.instance.currentUser;
if (currentUser == null) {
  // 处理未登录逻辑,比如跳转登录页
  return;
}
// 直接拿当前uid对应节点的快照
final userSnapshot = await FirebaseDatabase.instance
    .ref("insertdata")
    .child(currentUser.uid)
    .get();

if (userSnapshot.exists) {
  final userData = userSnapshot.value as Map<dynamic, dynamic>;
  // 直接使用userData里的字段即可
}

如果需要实时监听用户数据变化,把.get()替换成.onValue流,配合StreamBuilder使用就行。

方案2:兼容现有数据库结构(无需修改已有数据)

如果你已经存了大量历史数据,不想调整结构,可以用Realtime Database的查询语法直接在服务端过滤出邮箱匹配的节点,不需要拉全量数据到本地遍历。
注意:用这个方案需要给Email字段加数据库索引,否则数据量上来后查询性能会很差,索引配置方法后面会说。
直接替换你原来Scaffold里的FutureBuilder部分即可:

body: FutureBuilder<DatabaseEvent>(
  future: () async {
    final currentUser = FirebaseAuth.instance.currentUser;
    if (currentUser == null) throw "用户未登录";
    // 服务端直接过滤Email等于当前用户邮箱的节点,只取1条结果
    final userQuery = dbRef.orderByChild("Email").equalTo(currentUser.email).limitToFirst(1);
    return userQuery.once();
  }(),
  builder: (context, snapshot) {
    if (snapshot.hasError) {
      return Center(child: Text("数据获取失败:${snapshot.error}"));
    }
    if (!snapshot.hasData) {
      return const Center(child: CircularProgressIndicator());
    }
    final rawData = snapshot.data!.snapshot.value;
    if (rawData == null) {
      return const Center(child: Text("暂无对应用户数据"));
    }
    // 查询返回的是匹配到的节点集合,取第一个就是当前用户的数据
    final matchedMap = rawData as Map<dynamic, dynamic>;
    final userData = matchedMap.values.first as Map<dynamic, dynamic>;

    return Card(
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text("邮箱:${userData["Email"]}"),
          Text("名:${userData["First Name"]}"),
          Text("姓:${userData["Last Name"]}"),
        ],
      ),
    );
  },
)

如果用这个方案,记得去Firebase控制台的Realtime Database规则页面,给insertdata节点加Email字段的索引,规则参考:

{
  "rules": {
    "insertdata": {
      ".indexOn": ["Email"],
      ".read": "auth != null",
      ".write": "auth != null"
    }
  }
}

原有代码的其他修正点
  • 你现在的字段映射写反了:把Email显示成Name,First Name显示成Age,Last Name显示成Type,实际使用的时候记得对应上。
  • 异步获取的变量不要在initState里直接调用不等待,很容易出现空指针,把获取登录用户的逻辑直接放到异步查询闭包里,能从根源上避免时序问题。
  • 任何时候都不要拉全量用户数据到本地处理,不管是从流量消耗、查询速度还是数据安全角度,这种写法都不合理,能在服务端完成的过滤/定位就不要放到端上做。

[数据库结构参考截图]
Database

附你提供的原有实现代码:

import 'package:firebase_database/firebase_database.dart';
import 'package:firebase_database/ui/firebase_animated_list.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:helloworld/tictactoe.dart';
import 'package:helloworld/numbergame.dart';
import 'package:helloworld/profile.dart';

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      // Remove the debug banner
      debugShowCheckedModeBanner: false,
      title: 'Search',
      home: Search(),
    );
  }
}

class Search extends StatefulWidget {
  const Search({Key? key}) : super(key: key);
  static const String id = 'search';
  @override
  _SearchState createState() => _SearchState();
}

class _SearchState extends State<Search> with AutomaticKeepAliveClientMixin {
  final dbRef = FirebaseDatabase.instance.ref().child("insertdata");
  List<Map<dynamic, dynamic>> lists = [];

  final _auth = FirebaseAuth.instance;
  late String? userEmail;
  void getCurrentUserEmail() async {
    final user = await _auth.currentUser;
    userEmail = user!.email;
  }
  void initState() {
    super.initState();
    getCurrentUserEmail();
  }
  @override
  Widget build(BuildContext context) {
    super.build(context);
    //btmnavbar

    return Scaffold(
      resizeToAvoidBottomInset: false,
      body: FutureBuilder<DatabaseEvent>(
          future: dbRef.once(),
          builder: (context, AsyncSnapshot<DatabaseEvent> snapshot) {
            if (snapshot.hasData) {
              lists.clear();
              Map<dynamic, dynamic> values = snapshot.data!.snapshot.value as Map<dynamic, dynamic>;
              values.forEach((key, values) {
                lists.add(values);
              });
              return new ListView.builder(
                  shrinkWrap: true,
                  itemCount: lists.length,
                  itemBuilder: (BuildContext context, int index) {
                    return Card(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: <Widget>[
                          Text("Name: " + lists[index]["Email"]),
                          Text("Age: " + lists[index]["First Name"]),
                          Text("Type: " + lists[index]["Last Name"]),
                        ],
                      ),
                    );
                  });
            }
            return CircularProgressIndicator();
          }),
    );
  }

  @override
  // TODO: implement wantKeepAlive
  bool get wantKeepAlive => true;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 06:27:13