Flutter - Apresentando Layouts - VI
Hoje veremos o conceito de Layouts no Flutter. (baseado na documentação oficial) |
|
O Flutter é um SDK de aplicativo móvel do Google que ajuda a criar aplicativos móveis modernos para iOS e Android usando uma única (quase) base de código.
Se você não conhece o Flutter veja o meu artigo : Flutter - Primeiros contatos e impressões
Continuando a quinta parte do artigo vamos tratar dos detectores de gestos.
Detectores de gestos
Nos exemplos do artigo anterior, vimos como responder à entrada do usuário usando alguns dos widgets comuns disponíveis. Esses widgets fornecem propriedades de retorno de chamada como onPressed e onChanged.
Outros widgets (como o Text ou o Container) não possuem uma maneira integrada de interagir com eles.
O Flutter nos oferece uma maneira fácil de torná-los interativos. Tudo o que você precisa fazer é envolver qualquer widget com um widget GestureDetector.
GestureDetector é um widget que detecta gestos e
tenta reconhecer gestos que correspondem aos seus callbacks não nulos.
Se esse widget tiver um filho, ele será transferido para esse filho por seu
comportamento de dimensionamento. Se não tiver um filho, ele se ajustará ao pai.
Por padrão, um GestureDetector com um filho
invisível ignora os toques; esse comportamento pode ser controlado com
behavior.
O GestureDetector também escuta eventos de acessibilidade e os mapeia para os
callback. Para ignorar eventos de acessibilidade, defina
excludeFromSemantics como true.
Abaixo temos um exemplo de código onde temos um widget Text envolvido com um widget GestureDetector.
GestureDetector(
child: Text('Ola Flutter'),
onTap: () {
// fazer alguma coisa
},
);
|
Quando o texto for tocado, o callback onTap() será executado. Simples não ?
Vejamos um exemplo usando um detector de gesto onde vamos alterar a cor de um texto conforme o usuário toca no texto.
Para isso precisamos importar o pacote 'dart:math' no arquivo main.dart para poder usar a função Random() que gera valores int, bool ou double.
import 'dart:math';
Substitua o código da classe _MeuWidgetState pelo código a seguir:
class MeuWidget extends StatefulWidget {
@override
_MeuWidgetState createState() => _MeuWidgetState();
}
class _MeuWidgetState extends State<MeuWidget> {
Color textColor = Colors.black;
@override
Widget build(BuildContext context) {
return GestureDetector(
child: Text(
'Toque aqui para mudar a cor',
style: TextStyle(
fontSize: 25,
color: textColor,
),
),
onTap: () {
_fazerAlgo();
},
);
}
void _fazerAlgo() {
setState(() {
// Para usar Random() tem que importar 'dart:math'
int randomHexColor = Random().nextInt(0xFFFFFF);
int opaqueColor = 0xFF000000 + randomHexColor;
textColor = Color(opaqueColor);
});
}
}
|
Não estamos limitado a detectar apenas um toque.
Existem muitos outros gestos que são fáceis de detectar.
Abaixo temos uma lista de gestos que podemos detectar:
|
|
Podemos substituir cada um desses gestos e ver o seu comportamento.
Navigation
Para concluir o assunto sobre como tratar a iteração do usuário vamos falar sobre navegação ou Navigation.
Como podemos navegar para uma outra tela no Flutter ?
E como voltar para a tela anterior ?
Bem, como você poderia esperar, uma nova tela no Flutter é apenas um novo widget.
A maneira de obter esses widgets é chamada de rota, e o Flutter usa uma classe chamada Navigator para gerenciar as rotas.
Para mostrar
uma nova tela, você usa o Navigator para empurrar(push)
uma rota para uma pilha. Para descartar uma tela e voltar para a tela anterior,
você tira a rota(pop) do topo da pilha.
Abaixo temos um trecho de código que mostra como você navegaria para um novo
widget chamado SegundaTela:
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SeguntaTela(),
));
|
Veja na figura a seguir o resultado para a exibição do nosso layout:
A seguite temos o código que permite retornar para a tela anterior:
|
A seguir vamos criar um exempo de navegação entre duas telas.
Substitua todo o código de main.dart pelo código abaixo:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Flutter',
home: PrimeiraTela(),
));
}
class PrimeiraTela extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('## Primeira Tela ##')),
body: Center(
child: RaisedButton(
child: Text(
'Ir para a Segunda Tela',
style: TextStyle(fontSize: 24),
),
onPressed: () {
_navegarParaSegundaTela(context);
},
)
),
);
}
void _navegarParaSegundaTela(BuildContext context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SegundaTela(),
));
}
}
class SegundaTela extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('## Segunda Tela ##')),
body: Center(
child: RaisedButton(
child: Text(
'Retornar para a Primeira Tela',
style: TextStyle(fontSize: 24),
),
onPressed: () {
_voltarParaPrimeiraTela(context);
},
),
),
);
}
void _voltarParaPrimeiraTela(BuildContext context) {
Navigator.pop(context);
}
}
|
Passando dados para a próxima tela
Às vezes, você precisa enviar dados para a nova tela que está exibindo.
Isso é fácil de fazer, basta passar os dados como um parâmetro no construtor do widget SegundaTela.
class SegundaTela extends StatelessWidget {
final String text;
SegundaTela({Key key, @required this.text}) : super(key: key);
...
}
|
Essa sintaxe do construtor Dart pode parecer um pouco estranha para você.
Vamos entender..
Agora que o construtor está configurado, você pode passar dados quando ele for chamado partir de PrimeiraTela.
No exemplo a seguir estamos enviados dados digitados pelo usuário em um TextField na primeira tela e recebidos e exibidos na segunda tela.
Substitua todo o código de main.dart pelo código abaixo:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Flutter',
home: PrimeiraTela(),
));
}
class PrimeiraTela extends StatefulWidget {
@override
_PrimeiraTelaState createState() {
return _PrimeiraTelaState();
}
}
class _PrimeiraTelaState extends State<PrimeiraTela> {
// permite acessar o texto do TextField
TextEditingController textFieldController = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Primeira Tela')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(32.0),
child: TextField(
controller: textFieldController,
style: TextStyle(
fontSize: 24,
color: Colors.black,
),
),
),
RaisedButton(
child: Text(
'Ir para Segunda Tela',
style: TextStyle(fontSize: 24),
),
onPressed: () {
_enviaDadosParaSegundaTela(context);
},
)
],
),
);
}
// pega o texto no TextField e inicia a segunda tela
void _enviaDadosParaSegundaTela(BuildContext context) {
String textoAEnviar = textFieldController.text;
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SegundaTela(text: textoAEnviar,),
));
}
}
class SegundaTela extends StatelessWidget {
final String text;
// recebe os dados da PrimeiraTela como um parâmetro
SegundaTela({Key key, @required this.text}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Segunda Tela')),
body: Center(
child: Text(
text,
style: TextStyle(fontSize: 24),
),
),
);
}
}
|
Muito bem...
Como passar dados de volta ?
Em outras
ocasiões, você vai precisa enviar dados de volta para a tela anterior. O Flutter
faz isso de uma maneira interessante.
A primeira tela inicia a segunda tela e, em seguida, aguarda um resultado, que
pode ser usado após a conclusão da segunda tela.
Você vai notar o uso das palavras chaves async e await. (Se você conhece C# esta em casa)
O Dart facilita a execução de tarefas que você precisa esperar (como solicitações da Web ou tarefas de execução demoradas) usando await:
void iniciaSegundaTela(BuildContext context) async {
// inicia SegundaTela e aguarda (await) ate o resultado terminar
final resultado = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SegundaTela(),
));
// após o resultado da SegundaTela voltar
// atualiza o widget Text com ele
setState(() {
text = resultado;
});
}
|
Na segunda tela, você passa os dados de volta, fornecendo-os como um parâmetro no método pop.
Navigator.pop(context, 'Como vai você ?');
|
A seguir temos um exemplo onde mostramos como enviar dados de volta para a primeira tela.
Substitua todo o código de main.dart pelo código abaixo:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(
title: 'Flutter',
home: PrimeiraTela(),
));
}
class PrimeiraTela extends StatefulWidget {
@override
_PrimeiraTelaState createState() {
return _PrimeiraTelaState();
}
}
class _PrimeiraTelaState extends State<PrimeiraTela> {
String text = 'Texto primeira tela...';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Primeira Tela')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(32.0),
child: Text(
text,
style: TextStyle(fontSize: 24),
),
),
RaisedButton(
child: Text(
'Ir para Segunda Tela',
style: TextStyle(fontSize: 24),
),
onPressed: () {
_awaitRetornaValorDaSegundaTela(context);
},
)
],
),
),
);
}
void _awaitRetornaValorDaSegundaTela(BuildContext context)
async {
// inicia a SegundaTela e aguarda(await)
// ate o resultado terminar
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SegundaTela(),
));
// Depois do resultado da segunda tela retornar
// atualiza o widget Text
setState(() {
text = result;
});
}
}
class SegundaTela extends StatefulWidget {
@override
_SegundaTelaState createState() {
return _SegundaTelaState();
}
}
class _SegundaTelaState extends State<SegundaTela> {
// permite acessar o texto em TextField
TextEditingController textFieldController=TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Segunda Tela')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(32.0),
child: TextField(
controller: textFieldController,
style: TextStyle(
fontSize: 24,
color: Colors.black,
),
),
),
RaisedButton(
child: Text(
'Envia texto de volta',
style: TextStyle(fontSize: 24),
),
onPressed: () {
_enviaDadosDeVolta(context);
},
)
],
),
);
}
// pega o texto em TextField e envia de volta para a PrimeiraTela
void _enviaDadosDeVolta(BuildContext context) {
String textoParaEnviarDeVolta = textFieldController.text;
Navigator.pop(context, textoParaEnviarDeVolta);
}
}
|
Assim nesta
série de artigos, abordamos layouts estáticos e layouts dinâmicos com widgets
que respondem à entrada do usuário. Criar widgets responsivos
significa que precisamos lidar com as coisas que mudam, seja texto, cor, tamanho
ou qualquer outra coisa que afete a interface do usuário. O valor dessas
variáveis é conhecido como state e os widgets que possuem estado são
conhecidos como StatefulWidgets.
Gerenciar adequadamente o estado em Flutter é um grande tópico. Vimos duas
maneiras de fazer. Uma delas usava uma variável de método na
classe State e estava disponível para todos os widgets da classe.
À medida que a
complexidade aumenta, não é prático incluir todo o layout em um único método
build(), nem é uma boa prática permitir variáveis globais.
Outra maneira de gerenciar o estado foi passar dados como um parâmetro para
outro widget. Isso funciona muito bem quando um widget está chamando diretamente
outro, mas pode ser complicado quando você precisa do estado de outro widget em
algum lugar distante na árvore de widgets.
Aguarde em breve mais artigos sobre Flutter.
"Louvai ao Senhor dos
senhores; porque a sua benignidade dura para sempre."
Salmos 136:3
Veja os
Destaques e novidades do SUPER DVD Visual Basic
(sempre atualizado) : clique e confira !
Quer migrar para o VB .NET ?
Quer aprender C# ??
Quer aprender os conceitos da Programação Orientada a objetos ? Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ? |
Referências:
Super DVD Vídeo Aulas - Vídeo Aula sobre VB .NET, ASP .NET e C#
Super DVD C# - Recursos de aprendizagens e vídeo aulas para C#
Curso Fundamentos da Programação Orientada a Objetos com VB .NET
https://docs.flutter.io/flutter/widgets/GestureDetector-class.html