Flutter, MediaQuery, Orientation y Size: Ejemplo práctico

- Andrés Cruz

In english
Flutter, MediaQuery, Orientation y Size: Ejemplo práctico

Adaptar nuestra aplicación a tamaños distintos de pantalla, es una tarea común cuando estamos desarrollando sobre todo para dispositivos Android, que tenemos una inmensa cantidad de tamaños de pantalla y resoluciones distintas, aunque en los últimos años, en iOS también tenemos múltiples tamaños de pantalla, y eso sin introducir las tabletas, que son aún más grande y por lo tanto tenemos más espacios para mostrar nuestro contenido; en esta lección, vamos a ver un mecanismo básico que debemos de conocer al momento de empezar a crear estas adaptaciones de nuestra interfaz gráfica en Flutter.

Los MediaQuery en Flutter son una forma de adaptar la interfaz de usuario a diferentes dispositivos y tamaños de pantalla. Con los MediaQuery, puedes consultar las características del dispositivo en el que se está ejecutando la aplicación, como la orientación, el tamaño de pantalla, la densidad de píxeles, etc., y ajustar la presentación en consecuencia.

Por ejemplo, si quieres mostrar un diseño diferente para dispositivos móviles y tabletas, puedes utilizar MediaQuery para consultar la anchura del dispositivo y, en consecuencia, ajustar la disposición de los elementos. Esto puede ser especialmente útil para asegurarse de que la aplicación se vea bien en una amplia variedad de dispositivos diferentes.

Además, la clase MediaQuery en Flutter también proporciona algunos métodos útiles para convertir valores de píxeles en unidades lógicas, lo que facilita la creación de interfaces de usuario adaptables que se ven bien en todo tipo de dispositivos.

Los MediaQuery como elemento fundamental para el diseño adaptativo

En Flutter,  tenemos una clase que nos permite dar rápidamente información sobre el equipo que está renderizando nuestra aplicación, datos como la orientación y el tamaño total de pantalla y con esto hacer nuestros cálculos.

La clase de MediaQuery tiene un comportamiento o idea similar al que tenemos en CSS con cuentan con el mismo nombre de esta tecnología, adaptar la aplicación en base a reglas que definimos sea por la orientación o el tamaño de pantalla:

@media (max-width: 600px) {
  .facet_sidebar {
    display: none;
  }
}

@media (min-width: 700px) and (orientation: landscape) { ... }

Orientación de pantalla

Para obtener la orientación de pantalla que puedes ser o landscape o portrait, tenemos

final Orientation orientation = MediaQuery.of(context).orientation;

Obtener el tamaño

Para obtener el tamaño de la pantalla, tenemos la propiedad si se que nos da una tulla de dos valores que corresponden al ancho y alto respectivamente:

final Size size = MediaQuery.of(context).size;

Lógica para indicar la cantidad de elementos, vamos a emplear una variable o mejor dicho, propiedad normalita:

int countItem = 2;

Y con esto, podemos indicar algunas. Reglas según sea lo que sea que estemos mostrando en nuestra aplicación; puedes tomar el tamaño del dispositivo para crear tu árbol de condicionales y o la orientación:

    double space = size.width;

    int countItem = 2;

    if (orientation == Orientation.landscape) {
      countItem = 3;
      //space = size.height;
    }

    if (space > 700.0) {
      countItem = 4;
    }

Boceto del GridView

Ahora, para terminar nuestro ejemplo práctico, vamos a mostrar un recurso que forma parte de mi curso de Flutter, en el cual tenemos una grilla un GridView en el cual mostramos nuestros elementos en una fila; la cantidad de elementos que vamos a mostrar depende del la orientación y tamaño de la pantalla, puedes emplear los dos factores o solamente uno, aunque generalmente empleamos el que nos permite definir reglas en base al tamaño de pantalla, esto que mostramos anteriormente, es solo una idea, tu puedes plasmar tu desarrollo como mejor te parezca; asi que construimos nuestra grilla:

GridView.builder(
                          padding: EdgeInsets.all(7),
                          gridDelegate:
                              SliverGridDelegateWithFixedCrossAxisCount(
                                  crossAxisCount: countItem,
                                  crossAxisSpacing: 7,
                                  mainAxisSpacing: 4),
                          itemCount: products.length,
                          itemBuilder: (context, i) {
                            return GestureDetector(
                              onTap: () {
                                Navigator.pushNamed(context, DetailPage.ROUTE,
                                    arguments: products[i]);
                              },
                              child: ClipRRect(
                                borderRadius: BorderRadius.circular(8.0),
                                child: GridTile(
                                  child: Image.network(
                                    products[i].image,
                                    fit: BoxFit.cover,
                                  ),
                                  header: GridTileBar(
                                      title: Row(
                                    mainAxisAlignment:
                                        MainAxisAlignment.spaceAround,
                                    children: [
                                      Icon(
                                          products[i].inCart
                                              ? Icons.shopping_cart
                                              : Icons.shopping_cart_outlined,
                                          color: Theme.of(context).accentColor),
                                      Icon(
                                          products[i].favorite
                                              ? Icons.favorite
                                              : Icons.favorite_border_outlined,
                                          color: Theme.of(context).accentColor),
                                    ],
                                  )),
                                  footer: Container(
                                      padding: EdgeInsets.symmetric(
                                          horizontal: 20.0, vertical: 6.0),
                                      color: Colors.black87,
                                      child: Column(
                                        crossAxisAlignment:
                                            CrossAxisAlignment.start,
                                        children: [
                                          Text(
                                            products[i].name,
                                            style: TextStyle(fontSize: 20.0),
                                          ),
                                          Padding(
                                            padding: EdgeInsets.only(left: 5.0),
                                            child: Text(
                                              "${products[i].price.toString()} \$",
                                              style: TextStyle(
                                                  color: Colors.grey,
                                                  fontSize: 15.0,
                                                  fontWeight: FontWeight.w100),
                                            ),
                                          )
                                        ],
                                      )),
                                ),
                              ),
                            );
                          }),


Como puedes ver, estamos empleando la propiedad countItem con el valor que calculamos anteriormente para mostrar la cantidad de elementos; recuerda que puedes seguir agregando así condiciones o inclusive crear una fórmula en la cual calculan de manera automática la cantidad de elementos en base al tamaño de la pantalla.

Código completo

@override
  Widget build(BuildContext context) {
    final Orientation orientation = MediaQuery.of(context).orientation;
    final Size size = MediaQuery.of(context).size;

    int countItem = 2;
    double space = size.width;

    if (orientation == Orientation.landscape) {
      countItem = 3;
    }

    if (space > 800.0) {
      countItem = 4;
    }

    return StoreConnector<AppState, AppState>(
        converter: (store) => store.state,
        builder: (context, state) {
          return Scaffold(
            appBar: AppBar(
              leading: Icon(Icons.store),
              centerTitle: true,
              title: state.user == NULL
                  ? Text("Products")
                  : Text(state.user.email),
              actions: [
                state.user == NULL
                    ? Text('')
                    : IconButton(
                        icon: Icon(Icons.exit_to_app), onPressed: () {})
              ],
            ),
            body: Container(
              child: StoreConnector<AppState, AppState>(
                converter: (store) => store.state,
                builder: (_, state) {
                  return GridView.builder(
                    padding: EdgeInsets.all(8),
                    itemCount: state.products.length,
                    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                        crossAxisCount: countItem,
                        crossAxisSpacing: 8.0,
                        mainAxisSpacing: 8.0),
                    itemBuilder: (_, i) {
                      return GestureDetector(
                        onTap: () {
                          Navigator.pushNamed(context, DetailPage.ROUTE,arguments: state.products[i]);
                        },
                        child: ClipRRect(
                          borderRadius: BorderRadius.circular(6),
                          child: GridTile(
                            header: GridTileBar(
                              title: Row(
                                children: [
                                  Icon(
                                    Icons.shopping_cart_outlined,
                                    color: Theme.of(context).accentColor,
                                  ),
                                  Icon(
                                    state.products[i].favorite ? Icons.favorite :  Icons.favorite_border_outlined,
                                    color: Theme.of(context).accentColor,
                                  )
                                ],
                                mainAxisAlignment:
                                    MainAxisAlignment.spaceAround,
                              ),
                            ),
                            footer: Container(
                                color: Colors.black87,
                                padding: EdgeInsets.symmetric(
                                    horizontal: 20.0, vertical: 10.0),
                                child: Column(
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  children: [
                                    Text(
                                      state.products[i].name,
                                      style: TextStyle(fontSize: 20.0),
                                    ),
                                    Padding(
                                      padding: const EdgeInsets.only(left: 5.0),
                                      child: Text(
                                        "${state.products[i].price.toString()} \$",
                                        style: TextStyle(
                                            fontSize: 15.0,
                                            fontWeight: FontWeight.w100,
                                            color: Colors.grey),
                                      ),
                                    ),
                                  ],
                                )),
                            child: Image.network(
                              state.products[i].image,
                              fit: BoxFit.cover,
                            ),
                          ),
                        ),
                      );
                    },
                  );
                },
              ),
            ),
          );
        });
  }
App de demostración

Recuerda que esto forma parte del curso completo sobre Flutter.

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz en Udemy

Acepto recibir anuncios de interes sobre este Blog.