Flutter, MediaQuery, Orientation and Size: Practical Example

- Andrés Cruz

En español
Flutter, MediaQuery, Orientation and Size: Practical Example

Adapting our application to different screen sizes is a common task when we are developing especially for Android devices, which have a huge number of different screen sizes and resolutions, although in recent years, in iOS we also have multiple screen sizes, and that without introducing the tablets, which are even bigger and therefore we have more spaces to display our content; In this lesson, we are going to see a basic mechanism that we must know when starting to create these adaptations of our graphical interface in Flutter.

MediaQuery in Flutter is a way to adapt the user interface to different devices and screen sizes. With MediaQueries, you can query the characteristics of the device the app is running on, such as orientation, screen size, pixel density, etc., and adjust the presentation accordingly.

For example, if you want to display a different layout for mobile and tablet devices, you can use MediaQuery to query the width of the device and adjust the layout of the elements accordingly. This can be especially useful for making sure your app looks good on a wide variety of different devices.

In addition, the MediaQuery class in Flutter also provides some useful methods for converting pixel values to logical units, making it easy to create responsive user interfaces that look good on all types of devices.

MediaQuery as a fundamental element for adaptive design

In Flutter, we have a class that allows us to quickly give information about the computer that is rendering our application, data such as orientation and total screen size, and with this to do our calculations.

The MediaQuery class has a similar behavior or idea to the one we have in CSS with the same name of this technology, adapting the application based on rules that we define either by orientation or screen size:

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

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

Screen orientation

To get the screen orientation that can be either landscape or portrait, we have

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

Get the size

To obtain the size of the screen, we have the if property that gives us a tulla of two values that correspond to the width and height respectively:

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

Logic to indicate the number of elements, we are going to use a variable or rather, normal property:

int countItem = 2;

And with this, we can indicate some. Rules based on whatever we are displaying in our app; you can take the size of the device to create your tree of conditionals and or the orientation:

    double space = size.width;

    int countItem = 2;

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

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

Sketch of the GridView

Now, to finish our practical example, we are going to show a resource that is part of my Flutter course, in which we have a grid, a GridView in which we display our elements in a row; The number of elements that we are going to show depends on the orientation and size of the screen, you can use both factors or only one, although we generally use the one that allows us to define rules based on the screen size, what we showed previously, is only an idea, you can capture your development as you see fit; so we build our grid:

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

As you can see, we're using the countItem property with the value we calculated earlier to display the number of items; remember that you can continue adding conditions like this or even create a formula in which they automatically calculate the number of elements based on the size of the screen.

Full code

@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

Remember that this is part of the complete course on Flutter.

Andrés Cruz

Develop with Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz In Udemy

I agree to receive announcements of interest about this Blog.