Полное руководство: получение данных пользователей Firestore в Flutter
Узнайте, как получать данные пользователей из Firestore в Flutter и передавать их виджетам. Исправьте асинхронные ошибки с помощью FutureBuilder и правильных моделей данных. Полное решение с примерами кода.
Как извлекать свойства пользователя из Firestore в Flutter и передавать их другим виджетам?
Я пытаюсь получить данные пользователя (имя, фамилия, никнейм) из коллекции ‘users’ в Firestore с помощью функции getUserData() в моем приложении Flutter. Мне нужно передать эти данные другим виджетам через конструктор, но я столкнулся с ошибкой:
Сообщение об ошибке гласит: “1 позиционный аргумент ожидается для функции ‘getUserData’, но найдено 0.”
Когда я пытаюсь передать параметр имени, например getUserData(name), я получаю “Неопределенное имя ‘name’”.
Вот мой текущий код:
class AuthWrapper extends StatelessWidget {
const AuthWrapper({super.key});
@override
Widget build(BuildContext context) {
return StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasData && snapshot.data != null) {
String eMail = snapshot.data!.email.toString();
String Name = getUserData().toString(); // 👈 ПРОБЛЕМА ЗДЕСЬ
return AnApp(forUser1: eMail, forUser2: Name);
} else {
return const LogIn();
}
},
);
}
}
Future<void> getUserData(String name) async {
User? user = FirebaseAuth.instance.currentUser;
if (user != null) {
DocumentSnapshot<Map<String, dynamic>> snapShot = await FirebaseFirestore.instance.collection('users').doc(user.uid).get();
if (snapShot.exists) {
var datA = snapShot.data();
String name = datA?['name'];
String surname = datA?['surname'];
String nickname = datA?['nickname'];
}
}
}
Что я упускаю в своей реализации для правильного извлечения и передачи данных пользователя из Firestore другим виджетам?
Чтобы правильно извлекать данные пользователей из Firestore и передавать их другим виджетам в Flutter, необходимо корректно обрабатывать асинхронные операции и правильно структурировать данные. Основная проблема в вашей текущей реализации заключается в том, что getUserData() возвращает Future<void>, но вы пытаетесь использовать его синхронно, а сигнатура вашей функции не соответствует тому, как вы её вызываете.
Содержание
- Основной подход с использованием FutureBuilder
- Создание модели данных
- Передача данных через конструкторы
- Несколько решений для вашего текущего кода
- Лучшие практики
- Полный пример
Основной подход с использованием FutureBuilder
Самый распространенный и надежный подход - использование FutureBuilder для асинхронного извлечения данных из Firestore. Вот как правильно его реализовать:
class AuthWrapper extends StatelessWidget {
const AuthWrapper({super.key});
@override
Widget build(BuildContext context) {
return FutureBuilder<User?>(
future: FirebaseAuth.instance.currentUser(),
builder: (context, userSnapshot) {
if (userSnapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (!userSnapshot.hasData || userSnapshot.data == null) {
return const LogIn();
}
// Теперь получаем данные пользователя из Firestore
return FutureBuilder<DocumentSnapshot>(
future: FirebaseFirestore.instance
.collection('users')
.doc(userSnapshot.data!.uid)
.get(),
builder: (context, docSnapshot) {
if (docSnapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (!docSnapshot.hasData || !docSnapshot.data!.exists) {
return const LogIn();
}
var userData = docSnapshot.data!.data() as Map<String, dynamic>;
String name = userData['name'];
String surname = userData['surname'];
String nickname = userData['nickname'];
return AnApp(
forUser1: userSnapshot.data!.email!,
forUser2: name,
userData: userData, // Передаем все данные, если необходимо
);
},
);
},
);
}
}
Создание модели данных
Создайте правильную модель данных для структурирования информации о пользователе:
class UserModel {
final String uid;
final String name;
final String surname;
final String nickname;
final String email;
UserModel({
required this.uid,
required this.name,
required this.surname,
required this.nickname,
required this.email,
});
// Метод фабрики для создания из документа Firestore
factory UserModel.fromFirestore(DocumentSnapshot doc) {
Map<String, dynamic> data = doc.data() as Map<String, dynamic>;
return UserModel(
uid: doc.id,
name: data['name'] ?? '',
surname: data['surname'] ?? '',
nickname: data['nickname'] ?? '',
email: data['email'] ?? '',
);
}
}
Передача данных через конструкторы
Измените ваши виджеты так, чтобы они принимали правильно структурированные данные:
class AnApp extends StatelessWidget {
final String forUser1;
final String forUser2;
final UserModel? userData;
const AnApp({
super.key,
required this.forUser1,
required this.forUser2,
this.userData,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Welcome ${userData?.name ?? forUser2}'),
),
body: Column(
children: [
Text('Email: $forUser1'),
Text('Name: ${userData?.name ?? forUser2}'),
Text('Surname: ${userData?.surname ?? ""}'),
Text('Nickname: ${userData?.nickname ?? ""}'),
],
),
);
}
}
Несколько решений для вашего текущего кода
Решение 1: Исправление вашего текущего подхода
Ваша функция getUserData() должна возвращать данные, а не Future<void>:
Future<Map<String, dynamic>?> getUserData() async {
User? user = FirebaseAuth.instance.currentUser;
if (user != null) {
DocumentSnapshot<Map<String, dynamic>> snapshot = await FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.get();
if (snapshot.exists) {
return snapshot.data();
}
}
return null;
}
// Использование с FutureBuilder
FutureBuilder<Map<String, dynamic>?>(
future: getUserData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (!snapshot.hasData || snapshot.data == null) {
return const LogIn();
}
var userData = snapshot.data!;
return AnApp(
forUser1: FirebaseAuth.instance.currentUser!.email!,
forUser2: userData['name'],
);
},
)
Решение 2: Использование Async/Await с обработкой ошибок
class AuthWrapper extends StatelessWidget {
const AuthWrapper({super.key});
Future<UserModel?> getUserData() async {
try {
User? user = FirebaseAuth.instance.currentUser;
if (user == null) return null;
DocumentSnapshot doc = await FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.get();
if (doc.exists) {
return UserModel.fromFirestore(doc);
}
return null;
} catch (e) {
print('Error getting user data: $e');
return null;
}
}
@override
Widget build(BuildContext context) {
return FutureBuilder<UserModel?>(
future: getUserData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (!snapshot.hasData || snapshot.data == null) {
return const LogIn();
}
UserModel userData = snapshot.data!;
return AnApp(
forUser1: userData.email,
forUser2: userData.name,
userData: userData,
);
},
);
}
}
Лучшие практики
- Используйте правильную обработку ошибок: Всегда обрабатывайте потенциальные ошибки при работе с операциями Firestore
- Создавайте модели данных: Используйте классы моделей для правильной структуризации ваших данных
- Используйте FutureBuilder/StreamBuilder: Эти виджеты специально разработаны для обработки асинхронных операций
- Кэшируйте данные: Рассмотрите возможность кэширования часто запрашиваемых данных пользователя
- Безопасность от null: Используйте возможности безопасности от null для предотвращения ошибок времени выполнения
Полный пример
Вот полный рабочий пример, который решает все ваши проблемы:
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class AuthWrapper extends StatelessWidget {
const AuthWrapper({super.key});
Future<UserModel?> getUserData() async {
try {
User? user = FirebaseAuth.instance.currentUser;
if (user == null) return null;
DocumentSnapshot doc = await FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.get();
if (doc.exists) {
return UserModel.fromFirestore(doc);
}
return null;
} catch (e) {
print('Error getting user data: $e');
return null;
}
}
@override
Widget build(BuildContext context) {
return FutureBuilder<UserModel?>(
future: getUserData(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (!snapshot.hasData || snapshot.data == null) {
return const LogIn();
}
UserModel userData = snapshot.data!;
return AnApp(userData: userData);
},
);
}
}
class UserModel {
final String uid;
final String name;
final String surname;
final String nickname;
final String email;
UserModel({
required this.uid,
required this.name,
required this.surname,
required this.nickname,
required this.email,
});
factory UserModel.fromFirestore(DocumentSnapshot doc) {
Map<String, dynamic> data = doc.data() as Map<String, dynamic>;
return UserModel(
uid: doc.id,
name: data['name'] ?? '',
surname: data['surname'] ?? '',
nickname: data['nickname'] ?? '',
email: data['email'] ?? '',
);
}
}
class AnApp extends StatelessWidget {
final UserModel userData;
const AnApp({super.key, required this.userData});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Welcome ${userData.name}'),
),
body: Column(
children: [
Text('Email: ${userData.email}'),
Text('Name: ${userData.name}'),
Text('Surname: ${userData.surname}'),
Text('Nickname: ${userData.nickname}'),
],
),
);
}
}
class LogIn extends StatelessWidget {
const LogIn({super.key});
@override
Widget build(BuildContext context) {
return const Center(child: Text('Please log in'));
}
}
Источники
- How to get data from Firestore and show it on FlutterBuilder or StreamBuilder - DEV Community
- How retrieve data from firestore in widget? - Stack Overflow
- Get data from Cloud firestore and displays them into widget - Stack Overflow
- Cloud Firestore | FlutterFire Documentation
- Using FutureBuilder to get Future from firestore - Stack Overflow
Заключение
Чтобы успешно извлекать данные пользователей из Firestore и передавать их другим виджетам в Flutter:
- Исправьте обработку асинхронности: Используйте FutureBuilder для корректной обработки асинхронных операций
- Создавайте правильные модели данных: Структурируйте данные пользователя с помощью классов моделей для лучшей организации
- Возвращайте данные из функций: Измените вашу
getUserData()так, чтобы она возвращала фактические данные, а не толькоFuture<void> - Передавайте полные объекты: Вместо передачи отдельных полей передавайте весь объект модели пользователя
- Обрабатывайте ошибки: Реализуйте правильную обработку ошибок для операций Firestore
Ключевое понимание заключается в том, что операции Firestore являются асинхронными, поэтому вам нужно дождаться извлечения данных, прежде чем использовать их в конструкторах ваших виджетов. Использование FutureBuilder является наиболее эффективным и чистым способом решения этой задачи в Flutter.