1. Introdução
Os componentes do Material Design (MDC, na sigla em inglês) ajudam os desenvolvedores a implementar o Material Design. Criados por uma equipe de engenheiros e designers de UX do Google, os MDC apresentam dezenas de componentes de IU bonitos e funcionais e estão disponíveis para Android, iOS, Web e Flutter.material.io/develop |
Agora, você pode usar os MDC para personalizar o estilo distinto dos seus apps mais do que nunca. A recente expansão do Material Design oferece aos designers e desenvolvedores uma maior flexibilidade para expressar a marca do produto.
Nos codelabs MDC-101 e MDC-102, você usou componentes do Material Design (MDC) para criar os elementos básicos do Shrine, um app de e-commerce que vende roupas e artigos domésticos. Esse app contém um fluxo de usuários que começa com uma tela de login e direciona o usuário para uma tela inicial que exibe produtos.
O que você criará
Neste codelab, você personalizará o app Shrine usando:
- Cor
- Tipografia
- Elevação
- Forma
- Layout
Android | iOS |
Componentes e subsistemas do MDC-Flutter neste codelab
- Temas
- Tipografia
- Elevação
- Lista de imagens
Como você classificaria seu nível de experiência em desenvolvimento com o Flutter?
2. Configurar o ambiente de desenvolvimento do Flutter
Você precisa de dois softwares para concluir este laboratório: o SDK do Flutter e um editor.
É possível executar o codelab usando qualquer um destes dispositivos:
- Um dispositivo físico Android ou iOS conectado ao seu computador e configurado para o modo de desenvolvedor.
- O simulador para iOS, que exige a instalação de ferramentas do Xcode.
- O Android Emulator, que requer configuração no Android Studio.
- Um navegador (o Chrome é necessário para depuração).
- Como um aplicativo para computador Windows, Linux ou macOS. Você precisa desenvolver na plataforma em que planeja implantar. Portanto, se quiser desenvolver um app para um computador Windows, você terá que desenvolver no Windows para acessar a cadeia de builds adequada. Há requisitos específicos de cada sistema operacional que são abordados em detalhes em docs.flutter.dev/desktop.
3. Fazer o download do app inicial do codelab
Está continuando do MDC-102?
Se você concluiu o MDC-102, o código para este codelab já está pronto. Pule para a etapa: Mudar as cores.
Está começando do zero?
Faça o download do app inicial do codelab
O app inicial está localizado no diretório material-components-flutter-codelabs-103-starter_and_102-complete/mdc_100_series
.
... ou clone-o do GitHub
Para clonar este codelab do GitHub, execute estes comandos:
git clone https://github.com/material-components/material-components-flutter-codelabs.git cd material-components-flutter-codelabs/mdc_100_series git checkout 103-starter_and_102-complete
Abrir o projeto e executar o app
- Abrir o projeto no seu editor favorito.
- Siga as instruções para "Executar o app" em Get Started: Test drive no editor escolhido.
Pronto. A página de login do Shrine dos codelabs anteriores será exibida no seu dispositivo.
Android | iOS |
Clique em "Next" para ver a página inicial do codelab anterior.
Android | iOS |
4. Mudar as cores
Um esquema de cores que representa a marca do Shrine foi criado, e o designer quer que você implemente esse esquema no app.
Para começar, vamos importar essas cores para o nosso projeto.
Criar colors.dart
Crie um novo arquivo dart em lib
com o nome colors.dart
. Importe os componentes do Material Design e adicione constantes de valores de cor:
import 'package:flutter/material.dart';
const kShrinePink50 = Color(0xFFFEEAE6);
const kShrinePink100 = Color(0xFFFEDBD0);
const kShrinePink300 = Color(0xFFFBB8AC);
const kShrinePink400 = Color(0xFFEAA4A4);
const kShrineBrown900 = Color(0xFF442B2D);
const kShrineErrorRed = Color(0xFFC5032B);
const kShrineSurfaceWhite = Color(0xFFFFFBFA);
const kShrineBackgroundWhite = Colors.white;
Paleta de cores personalizada
Este tema de cor foi criado por um designer usando cores personalizadas, mostradas na imagem abaixo. Ele contém cores que foram selecionadas da marca do Shrine e aplicadas ao Material Theme Editor, que as usou como base para criar uma paleta mais completa. Essas cores não são das Paleta de cores do Material Design de 2014 (link em inglês).
O Material Theme Editor organizou as cores em tonalidades marcadas numericamente, incluindo as etiquetas 50, 100, 200 e assim por diante até chegar em 900 para cada cor. O Shrine usa apenas as tonalidades 50, 100 e 300 da amostra rosa e a 900 da amostra marrom.
Cada parâmetro colorido de um widget é associado a uma cor desses esquemas. Por exemplo, a cor das decorações de um campo de texto que está recebendo ativamente a entrada do usuário precisa ser a cor principal do tema. Se essa cor não for acessível (fácil de ver em relação ao segundo plano), use a classe PrimaryVariant.
Essas variações foram criadas para as diretrizes do Material Design de 2014 e ainda estão disponíveis nas diretrizes atuais (Artigo do sistema de cores, em inglês) e no MDC-Flutter. Para acessá-las no código, basta chamar a cor de base e depois a tonalidade (geralmente, um valor na casa das centenas). Por exemplo, a cor Pink 400 é acessada com o comando: Colors.pink[400]
.
Não há problema em usar essas paletas para seus designs e códigos. Caso já tenha cores específicas para sua marca, você poderá gerar paletas harmoniosas usando a ferramenta de geração de paleta ou o Material Theme Editor.
Agora que já temos as cores que queremos usar, podemos aplicá-las à IU. Faremos isso definindo os valores de um widget ThemeData que será aplicado à instância de MaterialApp na parte superior da nossa hierarquia de widgets.
Personalizar ThemeData.light()
O Flutter inclui alguns temas integrados. O tema claro é um deles. Em vez de criar um widget ThemeData do zero, copiaremos o tema claro e mudaremos os valores para personalizá-lo em nosso app.
Vamos importar colors.dart
em app.dart.
import 'colors.dart';
Em seguida, adicione o código a seguir ao app.dart fora do escopo da classe ShrineApp:
// TODO: Build a Shrine Theme (103)
final ThemeData _kShrineTheme = _buildShrineTheme();
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.light();
return base.copyWith(
colorScheme: base.colorScheme.copyWith(
primary: kShrinePink100,
onPrimary: kShrineBrown900,
secondary: kShrineBrown900,
error: kShrineErrorRed,
),
// TODO: Add the text themes (103)
// TODO: Add the icon themes (103)
// TODO: Decorate the inputs (103)
);
}
Agora, defina o theme:
no final da função build()
do ShrineApp (no widget MaterialApp) para ser nosso novo tema:
// TODO: Add a theme (103)
theme: _kShrineTheme, // New code
Salve o projeto. Sua tela de login ficará assim:
Android | iOS |
E sua tela inicial vai ficar assim:
Android | iOS |
5. Modificar a tipografia e os estilos de etiquetas
Além das mudanças das cores, o designer também forneceu uma tipografia específica para ser usada. O ThemeData do Flutter inclui três temas de texto. Cada um deles é uma coleção de estilos de texto, como "manchete" e "título". Usaremos alguns estilos em nosso app e mudaremos alguns dos valores.
Personalizar o tema do texto
Para importar fontes para o projeto, elas precisam ser adicionadas ao arquivo pubspec.yaml.
No arquivo pubspec.yaml, adicione o código a seguir imediatamente após a tag flutter:
:
# TODO: Insert Fonts (103)
fonts:
- family: Rubik
fonts:
- asset: fonts/Rubik-Regular.ttf
- asset: fonts/Rubik-Medium.ttf
weight: 500
Agora você pode acessar e usar a fonte Rubik.
Como solucionar problemas no arquivo pubspec
Poderá haver erros na execução de pub get se você recortar e colar a declaração acima. Se houver erros, comece removendo o espaço em branco no começo da declaração e substitua-o por espaços usando o recuo de dois espaços. Use dois espaços antes de:
fonts:
Quatro espaços antes de:
family: Rubik
E assim por diante.
Se a mensagem Mapping values are not allowed here for exibida, verifique o recuo da linha que causou o problema e o recuo das linhas acima dela.
Em login.dart
, mude o código a seguir em Column()
:
Column(
children: <Widget>[
Image.asset('assets/diamond.png'),
const SizedBox(height: 16.0),
Text(
'SHRINE',
style: Theme.of(context).textTheme.headline5,
),
],
)
Em app.dart
, adicione o código a seguir após _buildShrineTheme()
:
// TODO: Build a Shrine Text Theme (103)
TextTheme _buildShrineTextTheme(TextTheme base) {
return base.copyWith(
headline5: base.headline5!.copyWith(
fontWeight: FontWeight.w500,
),
headline6: base.headline6!.copyWith(
fontSize: 18.0,
),
caption: base.caption!.copyWith(
fontWeight: FontWeight.w400,
fontSize: 14.0,
),
bodyText1: base.bodyText1!.copyWith(
fontWeight: FontWeight.w500,
fontSize: 16.0,
),
).apply(
fontFamily: 'Rubik',
displayColor: kShrineBrown900,
bodyColor: kShrineBrown900,
);
}
Esse código usa um TextTheme e muda a forma como as manchetes, os títulos e as legendas são exibidos.
Usar a fontFamily
dessa maneira faz que as mudanças sejam aplicadas somente aos valores de escala de tipografia especificados em copyWith()
(manchete, título, legenda).
Para algumas fontes, definimos um fontWeight personalizado em incrementos de 100: w500 (o peso 500) corresponde a médio e w400 a regular.
Usar os novos temas de textos
Adicione os estes temas ao _buildShrineTheme
após o erro:
// TODO: Add the text themes (103)
textTheme: _buildShrineTextTheme(base.textTheme),
textSelectionTheme: const TextSelectionThemeData(
selectionColor: kShrinePink100,
),
Salve o projeto. Desta vez, reinicie também o app (ou faça uma recarga dinâmica) porque modificamos as fontes.
Android | iOS |
O texto na tela inicial e de login parece diferente. Alguns textos usam a fonte Rubik, e outros são renderizados em marrom, em vez de preto ou branco. Os ícones também são renderizados em marrom.
Reduzir o texto
As etiquetas estão muito grandes.
Em home.dart
, mude children:
da coluna mais interna:
// TODO: Change innermost Column (103)
children: <Widget>[
// TODO: Handle overflowing labels (103)
Text(
product.name,
style: theme.textTheme.button,
softWrap: false,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
const SizedBox(height: 4.0),
Text(
formatter.format(product.price),
style: theme.textTheme.caption,
),
// End new code
],
Centralizar e abaixar o texto
Queremos centralizar as etiquetas e alinhar o texto com a parte inferior de cada card, em vez da parte inferior de cada imagem.
Mova as etiquetas para o final (abaixo) do eixo principal e centralize-as:
// TODO: Align labels to the bottom and center (103)
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.center,
Salve o projeto.
Android | iOS |
Ficou muito melhor.
Atribuir um tema aos campos de texto
Também é possível aplicar um tema à decoração dos campos de texto usando um InputDecorationTheme.
Em app.dart
, no método _buildShrineTheme()
, especifique um valor de inputDecorationTheme:
:
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
),
No momento, os campos de texto têm uma decoração filled
. Vamos remover isso. Se você remover filled
e especificar inputDecorationTheme
, os campos de texto usarão o estilo delineado.
Em login.dart
, remova os valores filled: true
:
// Remove filled: true values (103)
TextField(
controller: _usernameController,
decoration: const InputDecoration(
// Removed filled: true
labelText: 'Username',
),
),
const SizedBox(height: 12.0),
TextField(
controller: _passwordController,
decoration: const InputDecoration(
// Removed filled: true
labelText: 'Password',
),
obscureText: true,
),
Execute uma recarga dinâmica. A tela de login ficará como no exemplo a seguir quando o campo "Username" estiver ativo (quando o usuário estiver digitando nele):
Android | iOS |
Digite em um campo de texto: as bordas e os rótulos flutuantes são renderizados na cor primária. Mas eles não são fáceis de ver. O app não é acessível para pessoas com problemas para diferenciar pixels sem um contraste de cor alto o suficiente. Para ver mais informações sobre cores acessíveis, consulte a seção "Accessible colors" no artigo sobre as cores (link em inglês) do Material Design.
Em app.dart
, especifique uma focusedBorder:
no inputDecorationTheme:
:
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
),
Depois, especifique um floatingLabelStyle:
em inputDecorationTheme:
:
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: OutlineInputBorder(),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
floatingLabelStyle: TextStyle(
color: kShrineBrown900,
),
),
Por fim, vamos fazer com que o botão "Cancelar" use a cor secundária em vez da primária para aumentar o contraste.
TextButton(
child: const Text('CANCEL'),
onPressed: () {
_usernameController.clear();
_passwordController.clear();
},
style: TextButton.styleFrom(
primary: Theme.of(context).colorScheme.secondary,
),
),
Salve o projeto.
Android | iOS |
6. Ajustar a elevação
Agora que você definiu o estilo da página com cores e tipografia específicas para combinar com o Shrine, vamos ajustar a elevação.
Mudar a elevação do botão NEXT
A elevação padrão para ElevatedButton
é 2. Vamos aumentá-la.
Em login.dart
, adicione um valor de style:
ao ElevatedButton PRÓXIMA:
ElevatedButton(
child: const Text('NEXT'),
onPressed: () {
Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
elevation: 8.0,
),
),
Salve o projeto.
Ajustar a elevação do card
No momento, os cards aparecem em uma superfície branca ao lado da navegação do site.
Em home.dart
, adicione um valor de elevation:
aos cards:
// TODO: Adjust card heights (103)
elevation: 0.0,
Salve o projeto.
Android | iOS |
Você removeu a sombra embaixo dos cards.
7. Adicionar uma forma
O Shrine tem um estilo geométrico que define elementos usando formas octogonais ou retangulares. Vamos implementar esse estilo de forma nos cards na tela inicial e nos campos de texto e botões da tela de login.
Mudar as formas dos campos de texto na tela de login
Em app.dart
, importe o seguinte arquivo:
import 'supplemental/cut_corners_border.dart';
Ainda em app.dart
, modifique o tema de decoração do campo de texto para usar uma borda de cantos recortados:
// TODO: Decorate the inputs (103)
inputDecorationTheme: const InputDecorationTheme(
border: CutCornersBorder(),
focusedBorder: CutCornersBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrineBrown900,
),
),
floatingLabelStyle: TextStyle(
color: kShrineBrown900,
),
),
Mudar as formas dos botões na tela de login
Em login.dart
, adicione uma borda retangular chanfrada ao botão CANCEL:
TextButton(
child: const Text('CANCEL'),
onPressed: () {
_usernameController.clear();
_passwordController.clear();
},
style: TextButton.styleFrom(
primary: Theme.of(context).colorScheme.secondary,
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0)),
),
),
),
O TextButton não tem uma forma visível. Então, por que estamos adicionando um ícone de borda a ele? Fazemos isso para que a animação de ondulação seja ligada à mesma forma quando for tocada.
Agora, adicione a mesma forma ao botão NEXT:
ElevatedButton(
child: const Text('NEXT'),
onPressed: () {
Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
elevation: 8.0,
shape: const BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0)),
),
),
Para mudar a forma de todos os botões, também podemos usar elevatedButtonTheme
ou textButtonTheme
em app.dart
. Você poderá fazer depois, como um desafio.
Faça uma recarga dinâmica.
Android | iOS |
8. Mudar o layout
Agora, vamos modificar o layout para mostrar os cards em proporções e tamanhos distintos, de modo que cada card seja diferente dos demais.
Substituir uma GridView por uma AsymmetricView
Já criamos os arquivos para um layout assimétrico.
Em home.dart
, adicione a seguinte importação:
import 'supplemental/asymmetric_view.dart';
Exclua _buildGridCards
e substitua body
:
body: AsymmetricView(
products: ProductsRepository.loadProducts(Category.all),
),
Salve o projeto.
Android | iOS |
Agora, os produtos rolam horizontalmente na tela em um padrão inspirado pela costura.
9. Testar outro tema (opcional)
As cores são uma maneira eficiente de expressar sua marca, e uma pequena mudança de cor pode ter um grande efeito na experiência do usuário. Para testar isso, vamos conferir como o Shrine seria se o esquema de cores da marca fosse um pouco diferente.
Modificar cores
Em colors.dart
, adicione a seguinte cor:
const kShrinePurple = Color(0xFF5D1049);
Em app.dart
, mude a função _buildShrineTheme()
para o seguinte:
ThemeData _buildShrineTheme() {
final ThemeData base = ThemeData.light();
return base.copyWith(
colorScheme: base.colorScheme.copyWith(
primary: kShrinePurple,
secondary: kShrinePurple,
error: kShrineErrorRed,
),
scaffoldBackgroundColor: kShrineSurfaceWhite,
textSelectionTheme: const TextSelectionThemeData(
selectionColor: kShrinePurple,
),
inputDecorationTheme: const InputDecorationTheme(
border: CutCornersBorder(),
focusedBorder: CutCornersBorder(
borderSide: BorderSide(
width: 2.0,
color: kShrinePurple,
),
),
floatingLabelStyle: TextStyle(
color: kShrinePurple,
),
),
);
}
Faça uma recarga dinâmica. O novo tema será exibido.
Android | iOS |
Android | iOS |
O resultado é muito diferente. Vamos reverter app.dart's
_buildShrineTheme
ao que era antes desta etapa. Como alternativa, faça o download do código inicial para 104.
10. Parabéns!
Agora, você já criou um app que parece seguir as especificações do seu designer.
Próximas etapas
Até agora, você já usou os seguintes componentes do MDC: tema, tipografia, elevação e forma. Você pode ver mais componentes e subsistemas na biblioteca MDC-Flutter.
Analise os arquivos no diretório supplemental
para ver como fizemos a rolagem horizontal e a grade de layout assimétrica.
E se o design do app planejado tiver elementos que não têm componentes na biblioteca MDC? No codelab MDC-104: componentes avançados do Material Design, mostraremos como criar componentes personalizados usando a biblioteca MDC para criar uma aparência específica para o app.