Curso de Flutter

Curso de Flutter

Anahí Salgado Díaz de la Vega

Anahí Salgado Díaz de la Vega

Personalizando nuestro BottomNavigation Bar a Cupertino iOS BottomBar

35/38

Lectura

Una posibilidad que tenemos con Flutter es general Widgets idénticos a los de las plataformas nativas, en este caso para iOS con Cupertino.

En Flutter contamos con una sección especial para iOS, donde podemos generar botones, diálogos, pickers y hasta TabBar’s como es el caso de nuestro requerimiento.

Necesitamos que nuestro BottomBar luzca con una transparencia nativa de iOS

Captura de pantalla 2018-12-28 a la(s) 13.16.21.png

Para eso comenzaremos creando un nuevo archivo llamado platzi_trips_cupertino.dart
Crearemos una clase que herede de StatelessWidget de esta forma:

class PlatziTripsCupertino extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   // TODO: implement build
   return null;
}

Ahora para disponer de la paquetería de Widgets Cupertino es necesario importar lo siguiente en el encabezado del documento debajo del paquete material.

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';

La estructura será la misma utilizando Scaffold en la propiedad bottomNavigationBar colocaremos el Widget CupertinoTabScaffold y dentro en la propiedad tabBar pondremos los tabs con el Widget CupertinoTabBar, como se ve a continuación:

class PlatziTripsCupertino extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
   // TODO: implement build
   return Scaffold(
      bottomNavigationBar: CupertinoTabScaffold(
        tabBar: CupertinoTabBar(
            items: [
              BottomNavigationBarItem(
                  icon: Icon(Icons.home, color: Colors.indigo),
                  title: Text("")
              ),
              BottomNavigationBarItem(
                  icon: Icon(Icons.search, color: Colors.indigo),
                  title: Text("")
              ),
              BottomNavigationBarItem(
                  icon: Icon(Icons.person, color: Colors.indigo),
                  title: Text("")
              ),
            ]
        ),
	);
}

De esta forma ya estamos generando un TabBar con el estilo transparente que tiene iOS.
Ahora nos faltará manejar la navegación, eso lo haremos ahí mismo con la propiedad tabBuilder de la siguiente forma:

tabBuilder: (BuildContext context, int index) {
          switch (index) {
            case 0:
              return CupertinoTabView(
                builder: (BuildContext context) => HomeTrips(),
              );
              break;
            case 1:
              return CupertinoTabView(
                builder: (BuildContext context) => SearchTrips(),
              );
              break;
            case 2:
              return CupertinoTabView(
                builder: (BuildContext context) => ProfileTrips(),
              );
              break;

          }
},

Mira el código unificado aquí: https://github.com/anncode1/Curso-de-Flutter-en-Platzi/tree/16.BottomNavigationBarCupertino/lib

Este será el resultado:

Captura de pantalla 2018-12-28 a la(s) 13.19.52.png
Captura de pantalla 2018-12-28 a la(s) 13.20.24.png

Ahora ya puedes generar Widgets que luzcan tan nativos como tu diseño te lo exija.

Aportes 48

Preguntas 3

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad? Crea una cuenta o inicia sesión.

Si quieren poner que los botones con cupertino se vean morados activados y grises cuando no, en el widget CupertinoTabBar hay una propiedad llamada "activeColor" y ahi declaran el color "Colors.indigo" o "Colors.purple" en vez de en Icon()

Si te muestra el siguiente error:

The body might complete normally, causing ‘null’ to be returned, but the return type is a potentially non-nullable type. Try adding either a return or a throw statement at the end.

Necesitas agregarle al final un default y retornar tu vista por ejemplo Home:

De esta manera quedó mi switch

 switch (index) {
            case 0:
              return CupertinoTabView(
                builder: (BuildContext context) => Home(),
              );
            case 1:
              return CupertinoTabView(
                builder: (BuildContext context) => Search(),
              );
            case 2:
              return CupertinoTabView(
                builder: (BuildContext context) => Profile(),
              );
            default:
              return CupertinoTabView(
                builder: (BuildContext context) => Home(),
              );
          }

Código completo aquí, presentará algunas variaciones

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'home.dart';
import 'search.dart';
import 'profile.dart';

class NavifationBarCupertinoIos extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // ignore: todo
    // TODO: implement build
    return Scaffold(
      bottomNavigationBar: CupertinoTabScaffold(
        tabBar: CupertinoTabBar(items: [
          BottomNavigationBarItem(
              icon: Icon(Icons.home, color: Colors.indigo), label: ""),
          BottomNavigationBarItem(
              icon: Icon(Icons.search, color: Colors.indigo), label: ""),
          BottomNavigationBarItem(
              icon: Icon(Icons.person, color: Colors.indigo), label: ""),
        ]),
        tabBuilder: (BuildContext context, int index) {
          switch (index) {
            case 0:
              return CupertinoTabView(
                builder: (BuildContext context) => Home(),
              );
            case 1:
              return CupertinoTabView(
                builder: (BuildContext context) => Search(),
              );
            case 2:
              return CupertinoTabView(
                builder: (BuildContext context) => Profile(),
              );
            default:
              return CupertinoTabView(
                builder: (BuildContext context) => Home(),
              );
          }
        },
      ),
    );
  }
}

Aqui les dejo el codigo listo para correr, ya corregi el atributo “title” que se encuentra obsoleta en las versiones mas mas nuevas, solo la cambie por “label”.

class PlatziTripsCupertino extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      bottomNavigationBar: CupertinoTabScaffold(
        tabBar: CupertinoTabBar(
            backgroundColor: Color(0x33FFFFFF),
            items: [
              BottomNavigationBarItem(
                  icon: Icon(Icons.home, color: Colors.indigo),
                  label: ""
              ),
              BottomNavigationBarItem(
                  icon: Icon(Icons.search, color: Colors.indigo),
                  label: ""
              ),
              BottomNavigationBarItem(
                  icon: Icon(Icons.person, color: Colors.indigo),
                  label: ""
              ),
            ]
        ),
        tabBuilder: (BuildContext context, int index) {
          switch (index) {
            case 0:
              return CupertinoTabView(
                builder: (BuildContext context) => HomeTrips(),
              );
              break;
            case 1:
              return CupertinoTabView(
                builder: (BuildContext context) => SearchTrips(),
              );
              break;
            case 2:
              return CupertinoTabView(
                builder: (BuildContext context) => ProfileTrips(),
              );
              break;
          }
        },
      ),
    );
  }

}

Yo recomendaría quitar la propiedad Text() si no vamos a colocar nada ahí porque se los iconos se verán pegados al borde del CupertinoTabBar, pero si le ponemos texto se ve muy bonito 😄

Tengo una duda, si desarrollo para Android y para iOS, como consigo usar las particularidades de uno, al crear la app en ambas versiones.

Asi me quedo 😄, gracias a los compañeros por sus aportes, ayudan bastante

 //cambia el color del menuItem seleccionado
 activeIcon: Icon(Icons.home, color: Color(0xCFF584CD1),)
CupertinoTabBar(
          backgroundColor: Colors.white.withAlpha(50),
          items: [
          BottomNavigationBarItem(
            icon: Icon(Icons.home, color: Colors.white54),
            activeIcon: Icon(Icons.home, color: Color(0xCFF584CD1),)

          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.search, color: Colors.white54),
            activeIcon: Icon(Icons.search, color: Color(0xCFF584CD1),)

          ),
          BottomNavigationBarItem(
            icon: Icon(Icons.person, color: Colors.white54),
            activeIcon: Icon(Icons.person, color: Color(0xCFF584CD1),)

          )
        ])

.


.

Por que no funciona el SnackBar con la versión de cupertino?

Estimada AnnCode, recibe un cordial saludo, tengo una duda al implementar Cupertino BottomNavigation no funciona el snackBar, es un issue o estaré omitiendo algo del código?

SnackBar no funciona con Cupertino?

Si se recicla la lista del código anterior

static final List<Widget> navigationWidget = [
    HomeView(),
    SearchView(),
    ProfileView()
  ];

Se puede optimizar el tabBuilder

tabBuilder: (BuildContext context, int index){
            return CupertinoTabView(
              builder: (BuildContext context) => navigationWidget[index]
            );
          }

Listo!. Aunque a mi parecer noto un poco menos el efecto de transparencia en Android. De ahi en fuera lo unico que me dio unos problemas fue la clase donde se implemento Cuppertino ya que por alguna razón el editor te da una advertencia de que no se devuelve un valor, no impide el funcionamiento de la aplicación pero se soluciona agregando el comentario ignore: missing_return .

platzi_trips.dart

class PlatziTrips extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return _PlatziTrips();
  }


}

class _PlatziTrips extends State<PlatziTrips>{

 static int indexTap =0;


  final List<Widget> widgetsChildren = [
    HomeTrips(),
    SearchTrips(),
    ProfileTrips()
  ];

  void onTapTapped (int index){
    //Controlando el estado del widget
    setState(() {
      indexTap = index; //Recibe un indice y se lo asigna a indexTap
    });
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      body: PlatziTripsCupertino(),

    );
  }

}

platzi_trips_cupertino.dart

 @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      bottomNavigationBar: CupertinoTabScaffold(
        tabBar: CupertinoTabBar(
            items: [
              BottomNavigationBarItem(
                  icon: Icon(Icons.home, color: Colors.indigo),
                  title: Text("")
              ),
              BottomNavigationBarItem(
                  icon: Icon(Icons.search, color: Colors.indigo),
                  title: Text("")
              ),
              BottomNavigationBarItem(
                  icon: Icon(Icons.person, color: Colors.indigo),
                  title: Text("")
              ),
            ]
        ),
        //ignore: missing_return
        tabBuilder: (BuildContext context, int index) {
          switch (index) {
            case 0:
              return CupertinoTabView(
                builder: (BuildContext context) => HomeTrips(),
              );
              break;
            case 1:
              return CupertinoTabView(
                builder: (BuildContext context) => SearchTrips(),
              );
              break;
            case 2:
              return CupertinoTabView(
                builder: (BuildContext context) => ProfileTrips(),
              );
              break;

          }

        },
      ),
    );
  }

Resultado:

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'home_trips.dart';
import 'search_trips.dart';
import 'map_trips.dart';
import 'notification_trips.dart';
import 'profile_trips.dart';

class PlatziTripsCupertino extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      bottomNavigationBar: CupertinoTabScaffold(
        tabBar: CupertinoTabBar(
            backgroundColor: Colors.white.withAlpha(50),//Color desvanecido en CupertinoTabScaffold
            items: [
              BottomNavigationBarItem(
                  icon: Icon(Icons.home),
                  title: Text("")
              ),
              BottomNavigationBarItem(
                  icon: Icon(Icons.search),
                  title: Text("")
              ),
              BottomNavigationBarItem(
                  icon: Icon(Icons.place),
                  title: Text("")
              ),
              BottomNavigationBarItem(
                  icon: Icon(Icons.notifications),
                  title: Text("")
              ),
              BottomNavigationBarItem(
                  icon: Icon(Icons.person),
                  title: Text("")
              ),
            ]
        ),

        // ignore: missing_return
        tabBuilder: (BuildContext context, int index) {
          switch (index) {
            case 0:
              return CupertinoTabView(
                builder: (BuildContext context) => HomeTrips(),
              );
              break;
            // ignore: missing_return
            case 1:
              return CupertinoTabView(
                builder: (BuildContext context) => SearchTrips(),
              );
              break;
            case 2:
              return CupertinoTabView(
                builder: (BuildContext context) => MapTrips(),
              );
              break;
            case 3:
              return CupertinoTabView(
                builder: (BuildContext context) => NotificationTrips(),
              );
              break;
            case 4:
              return CupertinoTabView(
                builder: (BuildContext context) => ProfileTrips(),
              );
              break;

          }

        },
      ),
    );
  }

}

He seguido todos los paso para usar el tema de Cupertino pero noto que no toma la transparencia, pero si los estilo y colores de la barra inferior.

Hay forma de que manipulando la propiedad canvasColor se agregue ese mismo color entre blanco y transparente, sin necesidad de importar los packages de cupertino?

¿Eliminaron el repositorio?

import ‘package:flutter/material.dart’;
import ‘package:flutter/cupertino.dart’;
import ‘home_trips.dart’;
import ‘search_trips.dart’;
import ‘profile_trips.dart’;

class TripsCupertino extends StatelessWidget{
@override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
bottomNavigationBar: CupertinoTabScaffold(
tabBar: CupertinoTabBar(
backgroundColor: (Colors.white30),

      items: [
        BottomNavigationBarItem(
          icon: Icon(Icons.home,color: Colors.greenAccent),
          title: Text("")
        ),

        BottomNavigationBarItem(
            icon: Icon(Icons.search,color: Colors.greenAccent),
            title: Text("")
        ),

        BottomNavigationBarItem(
            icon: Icon(Icons.person,color: Colors.greenAccent),
            title: Text("")
        ),
      ],
    ),

// ignore: missing_return
tabBuilder: (BuildContext context, int index) {
  // ignore: missing_return
  switch (index) {
    case 0:
      return CupertinoTabView(
        builder: (BuildContext context) => HomeTrips(),
      );
      break;
    case 1:
      return CupertinoTabView(
        builder: (BuildContext context) => SearchTrips(),
      );
      break;
    case 2:
      return CupertinoTabView(
        builder: (BuildContext context) => ProfileTrips(),
      );
      break;
  }
},


  ),

);

}

}

Tengo una duda, el BottomNavigationBar de Cupertino siempre debe ser un Stateless Widget, es que encontré una diferencia en el comportamiento de ambos, en el bar de Cupertino( statless) cuando hago un cambio en la pantalla si cambio de Tab se mantiene el cambio, en cambio en el caso de el widget de Material(Stateful) si cambio de Tab los cambios que hice en la pantalla se reinician

Si no les aparece un blur en su nav (que por defencto es blanco), agreguen el atributo

//Con blur de en color
backgroundColor: Colors.white30
//Con opacidad en tono
backgroundColor: Colors.white.withAlpha(50)

Para intensificar (o difuminar) su efecto

NO LO ENTIENDO. TAMPOCO SE EXPLICA BIEN.

Se supone que cada vez que cree la app tendré que mirar si está o no seleccionado el modo IOS para cupertino?? Me explico mejor… Si lanzo la app para Android, pues como hasta ahora y punto. Pero si quiero que la app esté para IOS, tendré que comentar el home del main y poner el cupertino??? O los dos a la vez??? Por qué no se explica este punto tan importante… Se supone que Flutter es para dos sistemas, IOS y Android… Se debería mostrar cómo le hacemos para que esté bien en los dos… No de uno en uno por separado. Y si ahora yo quisiera subir esta app a IOS y a Android, qué debería hacer???

Por cierto. los snackBar para IOS, qué??? Cómo se soluciona esto?

Para los que tengan problema con la transparencia de la barra, simplemente se soluciona con esto 😄 :

return Scaffold(
      bottomNavigationBar: CupertinoTabScaffold(
        tabBar: CupertinoTabBar(
          backgroundColor: Colors.white.withAlpha(70),
          items: [
            BottomNavigationBarItem(
                icon:Icon(Icons.home, color: Colors.indigo,),
                label: "",
            ),
            BottomNavigationBarItem(
              icon:Icon(Icons.search, color: Colors.indigo,),
              label: "",
            ),
            BottomNavigationBarItem(
              icon:Icon(Icons.person, color: Colors.indigo,),
              label: "",
            ),
          ],```

Quedo Excelente!

Bueno en principio me dio algo de error, pero viendo la estructura lo pude identificar y reparar:

import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';

import 'package:platzi_trips_app/home_trips.dart';
import 'package:platzi_trips_app/profile_trips.dart';
import 'package:platzi_trips_app/search_trips.dart';

class PlatziTripsCupertino extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
        bottomNavigationBar: CupertinoTabScaffold(
      tabBar: CupertinoTabBar(items: [
        BottomNavigationBarItem(
            icon: Icon(Icons.home, color: Colors.indigo), title: Text("")),
        BottomNavigationBarItem(
            icon: Icon(Icons.search, color: Colors.indigo), title: Text("")),
        BottomNavigationBarItem(
            icon: Icon(Icons.person, color: Colors.indigo), title: Text("")),
      ]),
      tabBuilder: (BuildContext context, int index) {
        switch (index) {
          case 0:
            return CupertinoTabView(
              builder: (BuildContext context) => HomeTrips(),
            );
            break;
          case 1:
            return CupertinoTabView(
              builder: (BuildContext context) => SearchTrips(),
            );
            break;
          case 2:
            return CupertinoTabView(
              builder: (BuildContext context) => ProfileTrips(),
            );
            break;
        }
      },
    ));
  }
}

mi resultado:

no tengo iphone asi que te creo 😛

switch tambien se puede en android

Hola,
a mi me queda practicamente blanco, alguien podría ayudarme en decirme que podría estar mal.
Gracias

Está caido el link, deben estar pendientes, para algo se paga

Hola que tal, intente ingresar al recurso pero ya no se encuentra disponible 😦

https://github.com/anncode1/Curso-de-Flutter-en-Platzi/tree/16.BottomNavigationBarCupertino/platzi_trips_app/lib

¡Contento con el resultado! 😉

Estoy contento con el resultado. Lo único: la descripción se pone un poco más arriba con Cupertino, pero se soluciona dando más espacio en el top (me dio pereza hacerlo 😃)

Recomendaría cambiar el switch:

tabBuilder: (BuildContext context, int index) {
          switch (index) {
            case 0:
              return CupertinoTabView(
                builder: (BuildContext context) => HomeTrips(),
              );
              break;
            case 1:
              return CupertinoTabView(
                builder: (BuildContext context) => SearchTrips(),
              );
              break;
            case 2:
              return CupertinoTabView(
                builder: (BuildContext context) => ProfileTrips(),
              );
              break;
          }

por solamente:

 tabBuilder: (BuildContext context, int index) {
           return  CupertinoTabView(
              builder: (BuildContext context) => 	 
               screenChildren[index],
            );
    },

Funcionan exactamente igual y son muchas menos líneas de codigo

presenta un error en la llave del tapBuilder…
The body might complete normally, causing ‘null’ to be returned, but the return type is a potentially non-nullable type. Try adding either a return or a throw statement at the end.

tabBuilder: (BuildContext context, int index) {

Me encantó como se ve así, creo que usaré esta barra para próximos desarrollos

gracias!

@anncode El código no esta actualizado en github

Genial, me gusta mucho esta posibilidad que ofrece flutter.

Link caido por favor solucionen y actualicen… Gracias

Cuando agregó el CupertinoTabScaffold el margin top del description_place se reduce y el texto inicia bajo la primera imagen del slider. ¿Hay forma de solo activar este Widget si es cargado en iOS?

Sucede que cuando cambio entre vistas rápidamente el emulador me saca de la app.
¿Alguien más con este problema?

Sí me funcionó

en el ejemplo anterio

tabBuilder: (BuildContext context, int index) {
          switch (index) {
            case 0:
              return CupertinoTabView(
                builder: (BuildContext context) => HomeTrips(),
              );
              break;
            case 1:
              return CupertinoTabView(
                builder: (BuildContext context) => SearchTrips(),
              );
              break;
            case 2:
              return CupertinoTabView(
                builder: (BuildContext context) => ProfileTrips(),
              );
              break;

          }
},

a

return CupertinoTabView(
              builder: (BuildContext context) => widgetChlidren[index],
            );
final List<Widget> widgetChlidren = [
    Home(),
    SearchTrips(),
    ProfileTrips(),
  ];

Wow. Todo el tiempo y codigo que ahorra Flutter.

No me toma la transparencia y no se porque me cambia el margin top del titulo de la pagina, esta raro 🤔
Menu normal:

con cupertino:

Alguien sabe porque??

    return Scaffold(
      bottomNavigationBar: CupertinoTabScaffold(
        tabBar: CupertinoTabBar(
          backgroundColor: Colors.black26,
          inactiveColor: Colors.white,
          activeColor: Colors.lime,
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.home_filled),
              label: null
            ),

            BottomNavigationBarItem(
              icon: Icon(Icons.search_off),
              label: null,
            ),

            BottomNavigationBarItem(
              icon: Icon(Icons.person_pin_rounded),
              label: null,
            ),
          ],
        ),

        tabBuilder: (BuildContext context, int index) {

          switch(index) {

            case 0: return CupertinoTabView(
              builder: (BuildContext context) => HomeTrips(),
            ); break;

            case 1: return CupertinoTabView(
              builder: (BuildContext context) => SearchTrips(),
            ); break;

            case 2: return CupertinoTabView(
              builder: (BuildContext context) => PorfileTrips(),
            ); break;
          }

          return null;
        }
      ),
    );