Usando Keys em Flutter


Neste artigo vamos falar sobre usar o parâmetro key em widgets.

Neste artigo vamos falar sobre usar o parâmetro key em widgets. Se não sabe o que é um widget, recomendo ver este artigo que introduzimos blocos de construção da Flutter.

Keys preservam state quando widgets se movem na sua árvore de widget.

Eles podem ser usados para preservar scrollLocation do usuário, ou manter state quando modificar uma coleção.

Neste artigo, vamos abordar essas três coisas:

  1. Quando precisa de keys;
  2. Onde colocá-las na árvore de widget;
  3. Qual key é adequada para você.

Quando precisa usar keys?

Não é sempre que se usa, mas se adicionar, remover ou reordenar coleções de widgets do mesmo tipo que suportam algum state, é hora da key.

Um exemplo é um app de lista de tarefas: Usando o app, você quer reorganizar os itens com base na sua lista de tarefas por prioridade, e depois removê-los quando terminar.

Para ilustrar a necessidade de keys, escrevi um app bem simples com widgets de duas cores aleatórias que trocam de lugar quando tocar em um botão.

O código do app é esse:

import 'package:flutter/material.dart';
import 'dart:math';

void main() = runApp(new MaterialApp(home: PositionedTiles()));

class PositionedTiles extends StatefulWidget {
@override
State‹StatefulWidget› createState() = PositionedTilesState();
}

class PositionedTilesState extends State‹PositionedTiles› {
List‹Widget› tiles;

@override
void initState(){
super.initState();
tiles = [
StatelessColorfulTile(),
StatelessColorfulTile(),
];
}

@override
Widget build(BuildContext context){
return Scaffold(
body: SafeArea(
child: Row(
children: tiles)),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.sentiment_very_satisfied), onPressed: swapTiles),
);
}

swapTiles() {
setState(() {
tiles.insert(1, tiles.removeAt(0));
});
}
}

Contém um stateful widget chamado PositionTiles, que constrói dois statelessTile e os coloca em uma linha.

Quando toco no botão Floating Action no final, ele troca de posição na lista, como esperado.

Mas o que acontece se trocarmos os tiles coloridos por stateful ao invés de stateless armazená-los no state?

import 'package:flutter/material.dart';
import 'dart:math';

void main() = runApp(new MaterialApp(home: PositionedTiles()));

class PositionedTiles extends StatefulWidget {
@override
State‹StatefulWidget› createState() = PositionedTilesState();
}

class PositionedTilesState extends State‹PositionedTiles› {
List‹Widget› tiles;

@override
void initState(){
super.initState();
tiles = [
StatefulColorfulTile(),
StatefulColorfulTile(),
];
}

@override
Widget build(BuildContext context){
return Scaffold(
body: SafeArea(
child: Row(
children: tiles)),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.sentiment_very_satisfied), onPressed: swapTiles),
);
}

swapTiles() {
setState(() {
tiles.insert(1, tiles.removeAt(0));
});
}
}

Agora, quando aperto o botão, parece que nada mudou.

Mas se adiciono uma key a cada tile widget, os widgets trocam de lugar, como esperado.

...
StatefulColorfulTile(key: UniqueKey()),
StatefulColorfulTile(key: UniqueKey()),
...

Mas lembre que se a árvore inteira de widget na sua coleção é stateless, keys não são necessárias.

Tecnicamente, só precisa saber disso para usar keys em seus apps para situações simples.

Agora que todos saíram, veremos como keys funcionam por trás quando o Flutter renderizam os widgets.

Explicando:

Na versão stateless widget, a linha widget tem um conjunto de lugares pedidos para suas children.

A árvore de elementos é muito simples, só mantém informações sobre o tipo de cada widget e uma referência a elementos children.

Pode pensar em árvore de elementos como um esqueleto do seu app Flutter.

Mostra a estrutura do seu app, mas toda informação adicional pode ser vista por referência ao widget original.

Quando trocamos a ordem dos tile widgets na linha, Flutter anda na árvore de elementos para ver se a estrutura esqueleto é a mesma.

Começa com o elemento linha e passa para children.

A árvore de elementos verifica que o novo widget é do mesmo tipo e key que o antigo. Caso seja, atualiza referências ao novo widget.

Neste caso, os widgets não têm keys, então o Flutter só checa o tipo.

Faz o mesmo para o segundo child. Vamos ver o mesmo caso de novo, mas agora com stateful tile widgets.

Pode ver que tenho os mesmos widgets e elementos que antes, mas agora tem um par de objetos state com eles.

E a informação das cores são guardadas ali, não nos próprios widgets.

Dessa vez, quando troco a ordem de dois widgets, o Flutter anda pela árvore de elementos, checa o tipo de widget linha e atualiza a referência.

Depois, o elemento tile checa que o widget correspondente é do mesmo tipo, um tile widget, e é mesmo.

E faz o mesmo para o segundo child. Flutter usa a árvore de elementos e seu state correspondente para determinar o conteúdo mostrado no seu dispositivo.

Da nossa perspectiva, parece que seus widgets não trocaram direito.

Na segunda versão com stateful tiles, adicionei propriedades key nos widgets. Agora se trocarmos widgets, os widgets de linha combinam como antes, mas a key do elemento tile não combina com a key do tile widget correspondente.

Então o Flutter desativa os elementos, movendo as referências para os elementos tile na árvore, começando com o primeiro que não combina.

Depois, o Flutter procura nos children que não foram combinados por um elemento com a key correspondente.

Acha um par e atualiza a referência no widget correspondente.

Flutter faz a mesma coisa para o segundo child. Agora, o Flutter mostra o que esperamos, trocando widgets de lugar e atualizando suas cores.

Resumindo, keys são úteis para modificar a ordem ou número de staple widgets em uma coleção.

Para ilustrar, armazenei a cor como state neste exemplo. Frequentemente, state é mais sutil. Reproduzindo animações, mostrando dados que o usuário inseriu e scroll location envolvem state.

Onde colocar Keys?

Você tem uma situação em que precisa de key para seu app. Onde coloca? A resposta é especificar uma key no topo da subárvore do widget que precisa preservar.

Como falamos muito sobre state, pode pensar que é o primeiro stateful widget. Mas está errado.

Para mostrar o porquê, enrolei tile widgets coloridos com padding widgets, mas deixei as keys nos tiles.

...
Padding(

padding: const EdgeInsets.all(8.0),
child: StatefulColorfulTile(key: UniqueKey()),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: StatefulColorfulTile(key: UniqueKey()),
),
...

Quando clico no botão, os tiles mudam para cores aleatórias.

O que está acontecendo? Quando trocamos as posições dos children, o algoritmo de combinação de elemento para widget da Flutter olha para um nível da árvore de cada vez.

No primeiro nível, tudo combina corretamente.

No segundo nível, o Flutter percebe que a key do elemento do tile não combina com a do widget.

Então ele desativa aquele elemento tile, cortando as conexões.

As keys usadas são locais. O que significa que quando combina widgets com elementos, Flutter só procura uma key que combina em um nível específico na árvore.

Como não pode encontrar um elemento tile naquele nível com aquele valor de key, cria uma nova e inicia um novo state.

Nesse caso, transformando o widget em laranja.

O mesmo problema acontece no outro tile. Se adicionarmos keys no nível das padding widgets, Flutter percebe o problema e atualiza as conexões, como fez no exemplo anterior.

A ordem é restaurada no universo.

 

Boa criação.

423 Visualizações
Awesome Flutter