Arquitectura de Aplicaciones

1

Pasos para aprender Flutter Avanzado

2

¿Qué es una Arquitectura de software?

3

Tipos de Arquitecturas para Flutter

4

Arquitectura BLoC en Flutter

5

BLoC + Clean Architecture en Flutter

6

Estructurando nuestro proyecto utilizando la Arquitectura BLoC

7

Haciendo BLoC Singleton en Flutter

8

Aplicando Providers al Proyecto

Firebase y Flutter

9

Integrando Firebase Authentication, Cloud Firestore y Firebase Storage al proyecto

10

Integrando Firebase a Flutter para Android

11

Integrando Firebase a Flutter para iOS

12

Creación de Pantalla de Login con Flutter

13

Creando botones reutilizables en Flutter

14

Autenticación de Firebase con Google

15

Implementando Firebase Authentication en BLoC Pattern

16

Streams en Flutter

17

Manejando una sesión con Firebase Authentication y Flutter

18

Implementando Google SignOut en BLoC

19

Implementando Google SignOut en View

20

Monitoreando y validando la conexión al Sign con Google

21

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

Cloud Firestore de Firebase en Flutter

22

¿Qué es Cloud Firestore de Firebase?

23

Analizando un modelo de datos no relacional

24

Creando un Modelo de datos en Cloud Firestore

25

Enviando datos a Cloud Firestore

26

Creando un Widget gradiente personalizado

27

Manejo de Desbordamiendo de Texto de Widget Text

28

Botón de Back en un Appbar en Flutter

29

Navegación entre pantallas en Flutter

30

Widget Text Appbar personalizado en Flutter

31

Widget TextField personalizado en Flutter

32

Creando una Safe Area para una interfaz que tiene un AppBar

33

Widget TextField con iconos en Flutter

34

Retocando el CardView

35

Mostrando imágenes en un CardView

36

Creando un botón de Submit en Flutter

37

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

38

Subiendo datos a Firestore de Firebase

39

Formularios en Flutter

Acceso al Hardware con Flutter

40

Acceso a la cámara en Flutter

41

Librerías de acceso a Hardware en Flutter

Firebase Storage en Flutter

42

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

43

Subiendo una imagen a Firebase Storage desde Flutter

Querys avanzados en Cloud Firestore de Firebase en Flutter

44

Manejo de imágenes en Cloud Firestore

45

Cloud Firestore insertando referencias y arrays en la base de datos

46

Descargar imágenes de Firebase Storage y mostrarlas en Flutter

47

Procesando datos con BLoC Pattern

48

Trayendo datos de Cloud Firestore

49

Persistiendo datos de un usuario logueado

50

Aplicando Filtros en Cloud Firestore

51

Construyendo los Places en la pantalla de Home

52

Mostrando los Places en la pantalla de Home

53

Actualizando datos en tiempo real

54

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

55

Insertando y obteniendo referencias en datos de Firestore.

56

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

57

StreamController, sink, add y StreamBuilder

Conclusiones

58

Conclusiones

Aún no tienes acceso a esta clase

Crea una cuenta y continúa viendo este curso

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

21/58
Recursos

Aquí encuentras el repositorio de esta clase.

Aportes 29

Preguntas 7

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta 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!

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

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

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

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.

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!!

**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!

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],
      ),

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!