Texto de: Lucas Galdino
Introdução
Hoje irei abordar um tema bem importante para se pensar na hora de fazer nossos aplicativos: os tamanhos relativos. Esses tipos de tamanhos permitem fazer com que os elementos na sua tela ocupem um espaço adequado em diferentes tamanhos de dispositivos, o que melhora bastante a experiência do usuário.
Criando projeto
Para iniciar vamos criar um projeto para demonstração com o comando abaixo:
flutter create tamanhos
Após criar o projeto, vamos abrir ele no VSCode ou em qualquer editor de sua preferência. Editei um pouco o código inicial para ilustrar melhor os exemplos, então basta abrir o arquivo "main.dart" e deixar ele da seguinte maneira:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Tamanhos Relativos',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.dark,
),
useMaterial3: true,
),
home: const MyHomePage(title: 'Tamanhos Relativos'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: 50,
width: 50,
decoration: const BoxDecoration(
color: Colors.red,
),
),
],
),
),
);
}
}
O resultado vai ser uma simples tela limpa com um "Container" de 50 x 50 pixels na cor vermelha, dessa forma:
Tamanho absoluto
Quando falamos de tamanho absoluto estamos falando de colocar os números exatos como tamanho de um widget. No "Container" criado inicialmente utilizei o tamanho absoluto de 50 pixels de altura por 50 pixels de largura, ou seja, esse é tamanho exato do widget na tela.
É bem difícil que esse tamanho acabe gerando erro em algum aparelho, afinal, hoje em dia os celulares têm uma resolução bem alta e 50 x 50 é um tamanho pequeno. Porém, quando utilizamos tamanhos absolutos podemos nos deparar com um problema. O que acontece caso o tamanho utilizado ultrapasse o tamanho da tela? Vou editar o código para aumentar o "Container" de 50 x 50 para 5000 x 5000.
Container(
height: 7000,
width: 4000,
decoration: BoxDecoration(
color: Colors.blue,
),
),
O resultado na tela do aplicativo fica assim:
Recebemos também um aviso lá no log do VSCode. Basicamente é um simples aviso de que algum widget está ultrapassando os limites de pixels da tela do dispositivo sendo testado. Esse é um problema que pode acontecer sem percebermos, afinal, podemos estar fazendo testes em um dispositivo com uma tela de resolução alta e nunca ter um problema desse, mas considerando que a aplicação pode rodar em diferentes dispositivos, em algum momento, um aparelho com uma tela menor pode sofrer por conta disso.
Tamanhos relativos
A forma que temos para lidar com essa situação é utilizar tamanhos relativos. Para isso, precisamos apenas de dois dados simples, a altura e largura da tela em questão, ou seja, as dimensões do aparelho que está executando o aplicativo. O Flutter tem uma forma de obter os dados de tamanhos da tela onde o código está rodando, ou seja, o mesmo código vai trazer resultados diferentes dependendo do dispositivo que está executando o aplicativo no momento. Assim temos os tamanhos relativos para cada dispositivo. O código em questão é esse:
MediaQuery.of(context).size.height //para altura
MediaQuery.of(context).size.width //para largura
O resultado desses códigos é o tamanho exato que o dispositivo possui. Podemos utilizar tais códigos diretamente no widget, dessa forma:
Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
color: Colors.red,
),
),
Apesar disso, o resultado vai parecer igual ao anterior, com mesmo erro e a mesma tela no emulador. Isso acontece por que utilizar o tamanho total da tela também vai ser um "erro". Todo celular por padrão vai acabar ocupando alguma parte da tela com elementos da interface do próprio sistema, como, por exemplo, a barra onde ficam os símbolos de wi-fi, a bateria e relógio e por isso não devemos utilizar todo o espaço disponível.
A forma correta de utilizar esse recurso vai requerer um pouco de matemática. Pensando na lógica por trás de tudo, temos que cada um desses valores contém 100% do tamanho da tela, ou seja, podemos pegar esse valor e dividir ele para usar tamanhos menores da tela. Para dividir precisamos apenas utilizar um método para fracionar esse valor, sendo que uma das formas, talvez a forma mais utilizada, é multiplicar o valor por algum valor entre 0 e 1. Ou seja, entre 0% e 100% do tamanho da tela.
No caso do exemplo abaixo, vou multiplicar tanto a altura quanto a largura por 0,3 que é referente a 30% dos tamanhos da altura e largura da tela do dispositivo. Se multiplicar por 1 é equivalente a 100% do tamanho, multiplicar por 0,3 é equivalente a 30% do tamanho. Aqui o código:
Container(
height: MediaQuery.of(context).size.height * 0.3,
width: MediaQuery.of(context).size.width * 0.3,
decoration: BoxDecoration(
color: Colors.red,
),
),
E o resultado desse código é o seguinte:
Podemos também obter esses dados e armazenar os valores de tamanho da tela em variáveis mais acima no código para poder utilizar tais valores em qualquer ponto da aplicação, o que deixa o código mais limpo. Dessa forma aqui:
final altura = MediaQuery.of(context).size.height;
final largura = MediaQuery.of(context).size.width;
Como o contexto da aplicação é utilizado na definição das variáveis precisamos inserir esse código dentro do widget responsável pela build da página, dessa forma aqui:
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
final altura = MediaQuery.of(context).size.height;
final largura = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar
Com tais variáveis disponíveis no código podemos utilizar elas da seguinte forma:
Container(
height: altura * 0.5,
width: largura * 0.8,
decoration: BoxDecoration(
color: Colors.red,
),
),
E o resultado é o seguinte:
Aqui está o código final do arquivo "main.dart" após todas as mudanças:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Tamanhos Relativos',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.deepPurple,
brightness: Brightness.dark,
),
useMaterial3: true,
),
home: const MyHomePage(title: 'Tamanhos Relativos'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
final altura = MediaQuery.of(context).size.height;
final largura = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
height: altura * 0.5,
width: largura * 0.8,
decoration: const BoxDecoration(
color: Colors.red,
),
),
],
),
),
);
}
}
Conclusão
Então é isso galera. Fiquem de olho nessa questão em seus projetos, pois é algo super importante para manter seu aplicativo funcionando em qualquer tipo de dispositivo e também algo importante se fazer com qualquer widget para evitar problemas no seu projeto no geral. Bons estudos!