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

BLoC + Clean Architecture en Flutter

7

Estructurando nuestro proyecto utilizando la Arquitectura BLoC

8

Haciendo BLoC Singleton en Flutter

9

Aplicando Providers al Proyecto

Firebase y Flutter

10

隆Renovaremos este curso! Te quedan unos d铆as para concluirlo.

11

Integrando Firebase Authentication, Cloud Firestore y Firebase Storage al proyecto

12

Integrando Firebase a Flutter para iOS

13

Integrando Firebase a Flutter para Android

14

Creaci贸n de Pantalla de Login con Flutter

15

Creando botones reutilizables en Flutter

16

Autenticaci贸n de Firebase con Google

17

隆Renovaremos este curso! Te quedan unos d铆as para concluirlo.

18

Implementando Firebase Authentication en BLoC Pattern

19

Streams en Flutter

20

Manejando una sesi贸n con Firebase Authentication y Flutter

21

Implementando Google SignOut en BLoC

22

Implementando Google SignOut en View

23

Monitoreando y validando la conexi贸n al Sign con Google

24

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

Cloud Firestore de Firebase en Flutter

25

隆Renovaremos este curso! Te quedan unos d铆as para concluirlo.

26

驴Qu茅 es Cloud Firestore de Firebase?

27

Analizando un modelo de datos no relacional

28

Creando un Modelo de datos en Cloud Firestore

29

Enviando datos a Cloud Firestore

30

Creando un Widget gradiente personalizado

31

Manejo de Desbordamiendo de Texto de Widget Text

32

Bot贸n de Back en un Appbar en Flutter

33

Navegaci贸n entre pantallas en Flutter

34

Widget Text Appbar personalizado en Flutter

35

隆Renovaremos este curso! Te quedan unos d铆as para concluirlo.

36

Widget TextField personalizado en Flutter

37

Creando una Safe Area para una interfaz que tiene un AppBar

38

Widget TextField con iconos en Flutter

39

Retocando el CardView

40

Mostrando im谩genes en un CardView

41

Creando un bot贸n de Submit en Flutter

42

Env铆o de datos de un f贸rmulario en Flutter

43

Subiendo datos a Firestore de Firebase

44

Formularios en Flutter

Acceso al Hardware con Flutter

45

隆Renovaremos este curso! Te quedan unos d铆as para concluirlo.

46

Acceso a la c谩mara en Flutter

47

Librer铆as de acceso a Hardware en Flutter

Firebase Storage en Flutter

48

隆Renovaremos este curso! Te quedan unos d铆as para concluirlo.

49

Qu茅 es y c贸mo funciona Firebase Storage en Flutter

50

Subiendo una imagen a Firebase Storage desde Flutter

Querys avanzados en Cloud Firestore de Firebase en Flutter

51

隆Renovaremos este curso! Te quedan unos d铆as para concluirlo.

52

Manejo de im谩genes en Cloud Firestore

53

Cloud Firestore insertando referencias y arrays en la base de datos

54

Descargar im谩genes de Firebase Storage y mostrarlas en Flutter

55

Procesando datos con BLoC Pattern

56

Trayendo datos de Cloud Firestore

57

Persistiendo datos de un usuario logueado

58

Aplicando Filtros en Cloud Firestore

59

隆Renovaremos este curso! Te quedan unos d铆as para concluirlo.

60

Construyendo los Places en la pantalla de Home

61

Mostrando los Places en la pantalla de Home

62

Actualizando datos en tiempo real

63

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

64

Insertando y obteniendo referencias en datos de Firestore.

65

Usando el cach茅 para cargar im谩genes m谩s r谩pido

66

StreamController, sink, add y StreamBuilder

Conclusiones

67

隆Renovaremos este curso! Te quedan unos d铆as para concluirlo.

68

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

24/68
Recursos

Aqu铆 encuentras el repositorio de esta clase.

Aportes 34

Preguntas 7

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

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())
  ];

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

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

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 鈥渦n 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 鈥渋rrespeta鈥 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!!

**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
              ],
            ),
          ),
        ],
      );
    }
  }



}```

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 > 

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 鈥淟ogin 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 el 6 de enero 2024 hay que poner una linea asi: user = User(name: snapshot.data.displayName, email: snapshot.data.email, photoURL: snapshot.data.photoURL); de photoUrl paso a photoURL
Estoy llevando este curso el a帽o 2023, y pues creo que las versiones son el problema en este curso. tal vez alguien me puede ayudar con este problema. En el archivo platzi\_trips\_cupertino.dart mi android studio me marca error, justo en el tabBuilder, muestra mensaje de que no puede enviar valor nulo. 聽tabBuilder: (BuildContext context, int index) {聽 聽 聽 聽 聽 switch (index) {聽 聽 聽 聽 聽 聽 case 0:聽 聽 聽 聽 聽 聽 聽 return CupertinoTabView(聽 聽 聽 聽 聽 聽 聽 聽 builder: (BuildContext context) => HomeTrips(),聽 聽 聽 聽 聽 聽 聽 );```js tabBuilder: (BuildContext context, int index) { switch (index) { case 0: return CupertinoTabView( builder: (BuildContext context) => HomeTrips(), ); ```justo marca la llave antes de comenzar con el switch, no se si a ustedes les dio el mismo problema.

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 鈥rips() no me carga los datos de la cuenta de google
pero cuando uso 鈥ripsCuppertino() 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 鈥楶latformUser鈥) saben por que?

Wooo! Genial!