Другое

Полное руководство: получение данных пользователей Firestore в Flutter

Узнайте, как получать данные пользователей из Firestore в Flutter и передавать их виджетам. Исправьте асинхронные ошибки с помощью FutureBuilder и правильных моделей данных. Полное решение с примерами кода.

Как извлекать свойства пользователя из Firestore в Flutter и передавать их другим виджетам?

Я пытаюсь получить данные пользователя (имя, фамилия, никнейм) из коллекции ‘users’ в Firestore с помощью функции getUserData() в моем приложении Flutter. Мне нужно передать эти данные другим виджетам через конструктор, но я столкнулся с ошибкой:

Сообщение об ошибке гласит: “1 позиционный аргумент ожидается для функции ‘getUserData’, но найдено 0.”

Когда я пытаюсь передать параметр имени, например getUserData(name), я получаю “Неопределенное имя ‘name’”.

Вот мой текущий код:

dart
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 для асинхронного извлечения данных из Firestore. Вот как правильно его реализовать:

dart
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, // Передаем все данные, если необходимо
            );
          },
        );
      },
    );
  }
}

Создание модели данных

Создайте правильную модель данных для структурирования информации о пользователе:

dart
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'] ?? '',
    );
  }
}

Передача данных через конструкторы

Измените ваши виджеты так, чтобы они принимали правильно структурированные данные:

dart
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>:

dart
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 с обработкой ошибок

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(
          forUser1: userData.email,
          forUser2: userData.name,
          userData: userData,
        );
      },
    );
  }
}

Лучшие практики

  1. Используйте правильную обработку ошибок: Всегда обрабатывайте потенциальные ошибки при работе с операциями Firestore
  2. Создавайте модели данных: Используйте классы моделей для правильной структуризации ваших данных
  3. Используйте FutureBuilder/StreamBuilder: Эти виджеты специально разработаны для обработки асинхронных операций
  4. Кэшируйте данные: Рассмотрите возможность кэширования часто запрашиваемых данных пользователя
  5. Безопасность от null: Используйте возможности безопасности от null для предотвращения ошибок времени выполнения

Полный пример

Вот полный рабочий пример, который решает все ваши проблемы:

dart
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'));
  }
}

Источники

  1. How to get data from Firestore and show it on FlutterBuilder or StreamBuilder - DEV Community
  2. How retrieve data from firestore in widget? - Stack Overflow
  3. Get data from Cloud firestore and displays them into widget - Stack Overflow
  4. Cloud Firestore | FlutterFire Documentation
  5. Using FutureBuilder to get Future from firestore - Stack Overflow

Заключение

Чтобы успешно извлекать данные пользователей из Firestore и передавать их другим виджетам в Flutter:

  1. Исправьте обработку асинхронности: Используйте FutureBuilder для корректной обработки асинхронных операций
  2. Создавайте правильные модели данных: Структурируйте данные пользователя с помощью классов моделей для лучшей организации
  3. Возвращайте данные из функций: Измените вашу getUserData() так, чтобы она возвращала фактические данные, а не только Future<void>
  4. Передавайте полные объекты: Вместо передачи отдельных полей передавайте весь объект модели пользователя
  5. Обрабатывайте ошибки: Реализуйте правильную обработку ошибок для операций Firestore

Ключевое понимание заключается в том, что операции Firestore являются асинхронными, поэтому вам нужно дождаться извлечения данных, прежде чем использовать их в конструкторах ваших виджетов. Использование FutureBuilder является наиболее эффективным и чистым способом решения этой задачи в Flutter.

Авторы
Проверено модерацией
Модерация