Arquitectura de Aplicaciones

1

¡Renovaremos este curso! Te quedan unos días para concluirlo.

2

Pasos para aprender Flutter Avanzado

3

¿Qué es una Arquitectura de software?

4

Tipos de Arquitecturas para Flutter

5

Arquitectura BLoC en Flutter

6

¡Renovaremos este curso! Te quedan unos días para concluirlo.

7

BLoC + Clean Architecture en Flutter

8

Estructurando nuestro proyecto utilizando la Arquitectura BLoC

9

Haciendo BLoC Singleton en Flutter

10

Aplicando Providers al Proyecto

Firebase y Flutter

11

¡Renovaremos este curso! Te quedan unos días para concluirlo.

12

Integrando Firebase Authentication, Cloud Firestore y Firebase Storage al proyecto

13

Integrando Firebase a Flutter para iOS

14

Integrando Firebase a Flutter para Android

15

Creación de Pantalla de Login con Flutter

16

Creando botones reutilizables en Flutter

17

Autenticación de Firebase con Google

18

¡Renovaremos este curso! Te quedan unos días para concluirlo.

19

Implementando Firebase Authentication en BLoC Pattern

20

Streams en Flutter

21

Manejando una sesión con Firebase Authentication y Flutter

22

Implementando Google SignOut en BLoC

23

Implementando Google SignOut en View

24

Monitoreando y validando la conexión al Sign con Google

25

Mostrando los datos de usuario de Google en la interfaz en Flutter

Cloud Firestore de Firebase en Flutter

26

¡Renovaremos este curso! Te quedan unos días para concluirlo.

27

¿Qué es Cloud Firestore de Firebase?

28

Analizando un modelo de datos no relacional

29

Creando un Modelo de datos en Cloud Firestore

30

Enviando datos a Cloud Firestore

31

Creando un Widget gradiente personalizado

32

Manejo de Desbordamiendo de Texto de Widget Text

33

Botón de Back en un Appbar en Flutter

34

Navegación entre pantallas en Flutter

35

Widget Text Appbar personalizado en Flutter

36

¡Renovaremos este curso! Te quedan unos días para concluirlo.

37

Widget TextField personalizado en Flutter

38

Creando una Safe Area para una interfaz que tiene un AppBar

39

Widget TextField con iconos en Flutter

40

Retocando el CardView

41

Mostrando imágenes en un CardView

42

Creando un botón de Submit en Flutter

43

Envío de datos de un fórmulario en Flutter

44

Subiendo datos a Firestore de Firebase

45

Formularios en Flutter

Acceso al Hardware con Flutter

46

¡Renovaremos este curso! Te quedan unos días para concluirlo.

47

Acceso a la cámara en Flutter

48

Librerías de acceso a Hardware en Flutter

Firebase Storage en Flutter

49

¡Renovaremos este curso! Te quedan unos días para concluirlo.

50

Qué es y cómo funciona Firebase Storage en Flutter

51

Subiendo una imagen a Firebase Storage desde Flutter

Querys avanzados en Cloud Firestore de Firebase en Flutter

52

¡Renovaremos este curso! Te quedan unos días para concluirlo.

53

Manejo de imágenes en Cloud Firestore

54

Cloud Firestore insertando referencias y arrays en la base de datos

55

Descargar imágenes de Firebase Storage y mostrarlas en Flutter

56

Procesando datos con BLoC Pattern

57

Trayendo datos de Cloud Firestore

58

Persistiendo datos de un usuario logueado

59

Aplicando Filtros en Cloud Firestore

60

¡Renovaremos este curso! Te quedan unos días para concluirlo.

61

Construyendo los Places en la pantalla de Home

62

Mostrando los Places en la pantalla de Home

63

Actualizando datos en tiempo real

64

Manejando la lógica de likes, como botón toggle.

65

Insertando y obteniendo referencias en datos de Firestore.

66

Usando el caché para cargar imágenes más rápido

67

StreamController, sink, add y StreamBuilder

Conclusiones

68

¡Renovaremos este curso! Te quedan unos días para concluirlo.

69

Conclusiones

No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Curso Avanzado de Flutter

Curso Avanzado de Flutter

Anahí Salgado Díaz de la Vega

Anahí Salgado Díaz de la Vega

Mostrando los datos de usuario de Google en la interfaz en Flutter

25/69
Recursos

Aquí encuentras el repositorio de esta clase.

Aportes 32

Preguntas 7

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

En profile_header.dart pueden simplificar el codigo del switch de la siguiente manera:

    switch (snapshot.connectionState) {
      case ConnectionState.active:
      case ConnectionState.done:
        return showProfileData(snapshot);
      default:
        return new CircularProgressIndicator();
    }

Hola para los que NO hayan utilizado el bottonNavigationBar de cupertino y tengan problemas a la hora de mostrar los datos, deben agregar el userBloc a la clase PlatziTrips. yo lo solucioné de la siguiente manera:

 int indexTap = 0;
  UserBloc userBloc;

  final List<Widget> widgetsChildren = [
    HomePage(),
    SearchTrips(),
    BlocProvider<UserBloc>(bloc: UserBloc(), child: ProfileTrips())
  ];

Sólo quería decir que hasta ahora el curso ha cumplido 100% con mis expectativas, Anahí es una buenísima profesora y tenemos la suerte de tenerla en Platzi; gracias por todo!

Hola a todos, vengo a advertir una cosa, cuando en el video se realiza lo siguiente…

 final List<Widget> widgetsChildren = [
    HomePage(),
    SearchTrips(),
    BlocProvider<UserBloc>(bloc: UserBloc(), child: ProfileTrips())
  ];

Esto debo decir que ESTA MAL. Porque me atrevo a decir esto? Debido a que BLoC debería ser Single Instance (una sola instancia, debe haber un solo llamado a UserBloc() ). En este caso, tenemos 2 instancias de BLoC (en el main.dart y platzi_trips_cupertino.dart), entonces tenemos información del FirebaseUser en 2 instancias diferentes, y es POR ESO QUE NO CIERRA LA SESIÓN cuando da signOut y debe forzarla, porque son 2 instancìas diferentes y solo cierra sesión en una, mientras que en la otra persiste la información.

Pueden comprobarlo sacando el BlocProvider mencionado anteriormente, si lo sacan van a ver que el inicio de sesión no deben forzar a cerrar el anterior, aunque van a tener otro problema, ConnectionState siempre queda como ConnectionState.waiting, debido a que no hay nuevos elementos en el stream y por defecto no devuelve el ultimo que habia, es decir, queda esperando por siempre un nuevo elemento, esto aun no lo soluciono, cuando lo arregle actualizo el comentario.

Recuerden que el patrón BLoC manejar la lógica de negocio, por lo que no podemos tener varias instancias debido a que si en ese BLoC yo tengo una variable _user, en la otra instancia esa variable va a ser diferente. Acá no se percibe tanto aún, pero en el futuro esto será un problema debido a falta de consistencia en los datos.

Espero se entienda 😃 Saludos

Genial el curso!, solo que es logueado no logeado.
Se tenía que decir y se dijo! 😃

A las personas que están usando el navigationBar que NO es el Cupertino, probablemente le estará saliendo el siguiente error (ConnectionState.waiting de manera indefinida) al entrar al perfil del usuario (sin hacer hot reload), se verá el CircularProgressIndicator() infinito. Daré la solución y luego la explicación.

Solución:

 final List<Widget> widgetsChildren = [
    HomePage(),
    SearchTrips(),
    BlocProvider(
	child: ProfileTrips(),
	bloc: UserBloc())
  ];

Ya varias persona la han comentado, sin embargo, no era clara, no se entiende porque hay que hacer esto o como dice @Lean Gutierrez en otro comentario en esta misma clase, según el patrón Singleton, esto no es lo correcto.

Explicación:

Recuerden: Los streams son datos los cuales el servidor manda por medio de “un tubo” (como nos lo enseñó Anahi clases atras) . Ese flujo e datos corre solo una vez y fluye por el árbol de la siguiente manera.
Representación grafica del árbol que tenemos.

Se puede apreciar en el gráfico que el flujo de datos no llega a los Widgets SearchTrip y ProfileTrips (que en este es donde necesitamos acceso a ese provider para que funcione correctamente nuestra app). ¿Por qué no llegan los datos allí? Porque recuerda que el stream llega en un flujo de datos único cada vez que el servidor nos lo entrega como respuesta, así que, en el momento en que el servidor nos entrega esa respuesta, nuestra pantalla está en HomeTrips ya que tenemos inicializado el índice 0 como predeterminado en el widget PlatziTrips, por eso a este widget hijo si le llega el flujo de datos en tiempo real, a los otros dos hermanos(SearchTrips y ProfileTrips) no. Cuando hacemos el cambio de tab o de pantalla, ya el stream o flujo de datos ha pasado, por esto es que no le llega a estos widgets como se ve en el grafico.
Con la solución que expuse más arriba, lo que estas haciendo es dandole ese contexto y ese acceso al flujo en tiempo real específicamente al hijo que lo necesita (ProfileTrips).
¿Esto está mal? podríamos responder que sí y no. SI porque al hacer esto no estaríamos respetando el patrón de diseño Singleton (como tambien lo “irrespeta” el widget de Cupertino, por eso Cupertino funcionaba y nuestro widget ProfileHeader no). Y NO porque los patrones de diseño en el mundo del desarrollo profesional son una guía, no una camisa de fuerza, en la medida de lo posible hay que respetarlos, pero en el mundo laboral lo que casi siempre prima es hacer las cosas de la mejor manera posible pero que sea de manera ágil y que funcionen. Debes entender que JAMAS tu código (ni el de nadie) va a ser perfecto, siempre se va a poder mejorar o refactorizar, al final, somos humanos quienes lo escribimos. ¿Qué otra solución existiría si queremos respetar a cabalidad el patrón Singleton? Refactorizar la lógica del cambio de tab situado en el widget PlatziTrips para asegurarnos que a todos los hijos si se les pase en tiempo real el snapshot o stream. ¿Como? No se xd, creo que seguiré el curso y dejaré que alguien mas experimente esa parte porque ya llevo bastante tiempo intentando entender porque ocurría lo que ocurría. No soy 100tifiko ni 1000litar, Salu2.

como ya sabemos que tipo de dato viene en nuestro stream
podemos hacer:

AsyncSnapshot<FirebaseUser> snap

de esa manera mi variable snap tiene acceso a las propiedades y/o métodos del tipo FirebaseUser

por ejemplo podemos hacer… sin adivinar el nombre del método
snap.data.displayName

Para los que están tomando últimamente el curso, y tienen problemas con el circular button que se queda cargando. Agregar el siguiente código en el platzi_trips para proveer de la información del user a los hijos del widget.

file: platzi_trips.dart

body: BlocProvider<UserBloc>(
        bloc: UserBloc(),
        child: widgetsChildren[indexTap],
      ),

Pesima explicacion y codigo muy antiguo, tiene que ser actualizado este curso 😦 y muchos if-ELSE y Case sin optimizar 😕

amamos a anahi

el curso va genial, pero en este paso siempre que intento cargar la info de mi usuario logueado siempre esta en modo waiting y solo se ve el CircularProgressIndicator() … alguien sabe que puede estar pasando ? AYUDAAA!!

He tenido que ver como tres o cuatro veces los vídeos del 9 al 21, pero feliz les digo que he entendido todo. 😄

para los que tengan problema con la imagen.
no usen

<  photo: snapshot.data.photoUrl > 

mejor usen

<  photo: snapshot.data.photoURL > 

**A mi nunca me cambia el estado de waiting, ya le he movido mucho y nose que pueda ser.I/flutter (13545): AsyncSnapshot<User>(ConnectionState.waiting, null, null)

import 'package:flutter/material.dart';
import 'package:generic_bloc_provider/generic_bloc_provider.dart';
import 'package:platzi_trips_app/User/bloc/bloc_user.dart';
import 'package:platzi_trips_app/User/model/user.dart';
import 'package:platzi_trips_app/User/ui/screens/profile_header.dart';
import 'package:platzi_trips_app/User/ui/widgets/profile_places_list.dart';
import 'package:platzi_trips_app/User/ui/widgets/profile_background.dart';
import 'package:platzi_trips_app/Widgets/button_green.dart';
import 'package:generic_bloc_provider/generic_bloc_provider.dart';

class ProfileTrips extends StatefulWidget {

  @override
  State<StatefulWidget> createState() {
   return _ProfileTrips();
  }
}

class _ProfileTrips extends State<ProfileTrips> {
  UserBloc userBloc;

  @override
  Widget build(BuildContext context) {

    userBloc = BlocProvider.of<UserBloc>(context);

   /*
   return BlocProvider(child: Stack(
      children: <Widget>[
        ProfileBackground(),
        ListView(
          children: <Widget>[
            ProfileHeader(),
            ProfilePlacesList()

          ],
        ),
      ],
    ));
    */

    return StreamBuilder(
      stream: userBloc.authStatus,
      builder: (BuildContext context, AsyncSnapshot snapshot){
        print('Estado de los datos usuario: ${snapshot}');
        print("${userBloc.authStatus}");
        print(snapshot);
        switch (snapshot.connectionState){
          case ConnectionState.waiting:
            return Center(child: CircularProgressIndicator(),);
          case ConnectionState.done:
            return Center(child: CircularProgressIndicator(),);

          case ConnectionState.active:
            return showProfileData(snapshot);
          case ConnectionState.none:
            return showProfileData(snapshot);
          default:
        }
      },
    );
  }

  Widget showProfileData(AsyncSnapshot snapshot){
    if(!snapshot.hasData || snapshot.hasError){
      print("No logeado");

      return Stack(
        children: <Widget>[

          ListView(
            children: <Widget>[
              Container(
                alignment: Alignment.center,
                child: Text("Usuario no logeado. Haz Login.", textAlign: TextAlign.center,),
              )
            ],
          ),
        ],
      );
    } else{
      print("Usuario logeado");
      var user = User(
          name: snapshot.data.displayName,
          email: snapshot.data.email,
          photoURL: snapshot.data.photoUrl
      );
      return Stack(
        children: <Widget>[

          Container(
            margin: EdgeInsets.only(top: 30.0, left: 20.0),
            child: Text("Perfil"),
          ),
          Container(
            margin: EdgeInsets.only(top: 80.0),
            child: ListView(
              children: <Widget>[
                ProfileHeader(), //User datos
                ProfilePlacesList() //User uid
              ],
            ),
          ),
        ],
      );
    }
  }



}```

Me causa cierto nivel de ansiedad, el hecho de que Anahi, programe tanto codigo sin ni siquiera ir viendo nada en el emulador, yo tnego que ir viendo que va pasando con cada cambio jajaja

El signOut() al inicio del método del botón de “Login With Google” no me funciono.
Así que lo puse al inicio del método signIn() de nuestra clase FirebaseAuthApi del archivo firebase_auth_api.dart

Future<FirebaseUser> signIn() async {
    await signOut();
    GoogleSignInAccount gsa = await googleSignIn.signIn();
    .
    .
    .

Me encantó este modulo!

A mi me pasa que se queda en waiting, pero al darle al hot reaload, en la consola se ve que pasa por waiting y enseguida a active. Alguien que le haya pasado lo mismo?

A alguien más le pasa que viene con el estado: waiting y se queda con el “CircularProgressIndicator” de manera indefinida?
He buscado pero no encuentro la solución, dada mi casi nula inexperiencia en Flutter y en Dart no he logrado avanzar con esto.
Agradecería mucho su ayuda.

Hola compañeros muy buenas noches desde Perú, les contaré mi caso cuando corrí la app me salia pantalla roja en perfil, estaba viendo mi código y me faltaba el signo de admiración en la condición de if de la clase showProfileData y ademas el photoUrl lo había dejado en mayúscula, pero lo corregí y ya me funciono la app, hasta ahora todo bien.

Genial! parece que vamos bien 😃

Excelente explicaciòn

2020-02-18 09:35:11.759 3992-30143/? E/FA: Missing google_app_id. Firebase Analytics disabled. See

Tengo este error al compilar hasta esta clase por que sera?

Para los que no le muestre la imagen deben cambiar la fuente de esta a lo siguiente

image: DecorationImage(
              fit: BoxFit.cover,
              image: new NetworkImage(user.photoURL)
          )

Cuando doy fuera del cuadro de la app cuando me pide seleccionar mi gmail me aparece este error

Exception has occurred.
PlatformException (PlatformException(sign_in_canceled, com.google.android.gms.common.api.ApiException: 12501: , null))

alguna ayuda?? gracias

Hasta ahora con cada practica voy profundizando más y más.

amigos ya pude hacer que la etiqueta CircularProgressIndicator(); no estuviera en loop.
les dejo mi código espero que les sirva. me base en la clase main. que igual la hice un poquitín diferente.

Tambien use ayuda de este link:
https://www.youtube.com/watch?v=cHFV6JPp-6A
#NuncaParenDeAprender 😉.


class WelcomePage extends StatelessWidget {
  BlocUser blocUser;

  @override
  Widget build(BuildContext context) {
    blocUser = BlocProvider.of(context);
    return BlocProvider(
        child: MaterialApp(
          theme: ThemeData(
            primarySwatch: Colors.green,
          ),
          home: Prueba(),
        ),
        bloc: BlocUser());
  }
}

class Prueba extends StatelessWidget {
  BlocUser blocUser;

  @override
  Widget build(BuildContext context) {
    blocUser = BlocProvider.of(context);
    return funt();
  }

  Widget funt() {
    return StreamBuilder(
      stream: blocUser.authStatus,
      builder: (BuildContext context, AsyncSnapshot asyncsnashot) {
        if (asyncsnashot.connectionState == ConnectionState.waiting) {
          return CircularProgressIndicator();
        } else {
          return showNameDataProbe(asyncsnashot);
        }
      },
    );
  }

  Widget showNameDataProbe(AsyncSnapshot asyncSnapshot) {
    if (!asyncSnapshot.hasData || asyncSnapshot.hasError) {
      print("NOT COOL :(");
      return Container(
        margin: EdgeInsets.only(
          bottom: 50.0,
          top: 50.0,
          left: 20.0,
          right: 10.0,
        ),
        child: Text("NO se Puede Cargar la Info =S.!",
            style: TextStyle(color: Colors.white)),
      );
    } else {
      print("cool =D");
      return Scaffold(
        appBar: AppBar(
          title: Text("Hello ${asyncSnapshot.data.displayName}"),
        ),
        body: Center(
          child: Column(
            children: <Widget>[
              Container(
                child: Text("email ${asyncSnapshot.data.email}"),
              ),
              Container(
                width: 90.0,
                height: 90.0,
                decoration: BoxDecoration(
                  image: DecorationImage(
                    image: NetworkImage(asyncSnapshot.data.photoUrl),
                  ),
                ),
              ),
              Container(
                child: ButtonGreen(
                  t: "Logout with Gmail",
                  funt: () {
                    blocUser.SingOut().then((FirebaseUser u) {
                      print("adios");
                    });
                  },
                ),
              ),
            ],
          ),
        ),
      );
    }
  }
}

Les paso una librearía bastante útil:
Es para el manejo de la foto de perfil, por si no hay conexión a Internet:

En user_info.dart:

    final userpPhoto =  CachedNetworkImage(
      imageUrl: user.photoURL,
      width: 90.0,
      height: 90.0,
      imageBuilder: (context, imageProvider) => Container(
        width: 90.0,
        height: 90.0,
        margin: EdgeInsets.only(right: 20.0),
        decoration: BoxDecoration(
          border: Border.all(
              color: Colors.white,
              width: 2.0,
              style: BorderStyle.solid
          ),
          shape: BoxShape.circle,
          image: DecorationImage(
              image: imageProvider,
              fit: BoxFit.cover
          ),
        ),
      ),
      placeholder: (context, url) => CircularProgressIndicator(),
      errorWidget: (context, url, error) => Icon(Icons.error),
    );

En el pubspec.yaml agregar la siguiente dependencia:

dependencies:
  cached_network_image: ^2.1.0+1

[https://flutter.dev/docs/cookbook/images/cached-images](Por más información, click aquí)

cuando uso …Trips() no me carga los datos de la cuenta de google
pero cuando uso …TripsCuppertino() si me cargan los datos
¿CÓMO PUEDO hacer posible que esto funcione usando el modelo que no usa Cuppertino?
final List<Widget> widgetsChildren = [
HomeTrips(),
SearchTrips(),
ProfileTrips(),
];

aqui les dejo el proyecto terminado sin errores https://github.com/josephciriaco/GoldenBoys.git

me imprime flutter: FirebaseUser(Instance of ‘PlatformUser’) saben por que?

Wooo! Genial!