Hoje, é difícil surpreender qualquer pessoa com a capacidade de deslizar itens da lista em aplicativos para dispositivos móveis. Um de nossos aplicativos nativos a reagentes também tinha essa funcionalidade, mas recentemente houve a necessidade de expandi-lo com a capacidade de arrastar e soltar itens da lista. E como o processo de encontrar uma solução me custou uma certa quantidade de células nervosas, decidi arquivar um pequeno artigo para economizar um tempo valioso para as gerações futuras.

Em nosso aplicativo, usamos o pacote react
react-native-swipe-list-view
para criar a lista swipeable. O primeiro pensamento foi pegar um pacote com a funcionalidade drag'n'drop e atravessar o ouriço com uma cobra.
Uma pesquisa na Internet deu três candidatos:
react-native-draggable-flatlist
.
Usando o primeiro pacote, não foi possível executar nem o exemplo em anexo (no entanto, não apenas para mim, o problema correspondente é indicado nos
problemas ).
Eu tive que mexer no segundo pacote, mas consegui criar uma lista arrastável e deslizante. No entanto, o resultado não foi inspirado - o componente estava descaradamente piscando: piscamento do redesenho, queda de elementos muito além da lista ou mesmo seu desaparecimento. Tornou-se claro que, desta forma, não pode ser usado.
A princípio, o último pacote também se comportava como uma dama caprichosa, mas depois aconteceu que eu simplesmente não sabia cozinhar. Depois de pegar a chave do coração dessa "dama", consegui alcançar um resultado aceitável.
Em nosso projeto, havia uma lista deslizante na qual você precisa prender o arraste e solte, mas, na prática, é melhor começar do outro lado: primeiro faça uma lista arrastável e depois adicione a capacidade de deslizar.
Os leitores devem saber como criar um projeto nativo de reação, portanto, vamos nos concentrar na criação da lista de que precisamos. O exemplo discutido abaixo é o código TypeScript.
Fazendo uma lista arrastável
Então, vamos começar instalando o pacote:
yarn add react-native-draggable-flatlist
Importamos os módulos necessários:
import React, { Component } from 'react' import { View } from 'react-native' import styles from './styles' import DraggableFlatList, { RenderItemInfo, OnMoveEndInfo } from 'react-native-draggable-flatlist' import ListItem from './components/ListItem' import fakeData from './fakeData.json'
Aqui
DraggableFlatList
é um componente do pacote instalado que implementa a
ListItem
arrastar e soltar,
ListItem
é nosso componente para exibir um item da lista (o código será apresentado abaixo),
fakeData
é um arquivo json que contém dados falsos - neste caso, uma matriz de objetos do formulário:
{"id": 0, "name": "JavaScript", "favorite": false}
Em um aplicativo real, esses dados provavelmente chegarão ao seu componente a partir de adereços ou serão baixados da rede, mas, no nosso caso, gerenciaremos com pouco sangue.
Como o TypeScript é usado neste exemplo, descrevemos algumas entidades:
type Language = { id: number, name: string, favorite: boolean, } interface AppProps {} interface AppState { data: Array<Language> }
O tipo de
Language
nos diz quais campos os itens da lista terão.
Neste exemplo, não obteremos nada dos adereços, portanto a interface
AppProps
é trivial e, na história, armazenaremos uma matriz de objetos
Language
, indicada na interface
AppState
.
Como o código do componente não é muito grande, darei em sua totalidade:
Código do componente do aplicativo class App extends Component<AppProps, AppState> { constructor(props: AppProps) { super(props) this.state = { data: fakeData, } } onMoveEnd = ({ data }: OnMoveEndInfo<Language>) => { this.setState({ data: data ? [...data] : [] }) } render() { return ( <View style={styles.root}> <DraggableFlatList data={this.state.data} renderItem={this.renderItem} keyExtractor={(item) => item.id.toString()} scrollPercent={5} onMoveEnd={this.onMoveEnd} /> </View> ) } renderItem = ({ item, move, moveEnd, isActive }: RenderItemInfo<Language>) => { return ( <ListItem name={item.name} move={move} moveEnd={moveEnd} isActive={isActive} /> ) } }
O método
onMoveEnd
chamado quando o item é movido. Nesse caso, precisamos colocar a lista com a nova ordem dos elementos no estado, então chamamos o método
this.setState
.
O método
renderItem
um item da lista e aceita um objeto do tipo RenderItemInfo <Idioma>. Este objeto inclui os seguintes campos:
item
- o próximo elemento da matriz passado como dados para a lista,move
e moveEnd
são funções chamadas quando um item da lista é movido, o componente DraggableFlatList
fornece essas funções,isActive
é um campo booleano que determina se o item está atualmente arrastável.
O componente para exibir o item da lista é, de fato,
TouchableOpacity
, que, quando pressionado por um longo tempo, chama de
move
e, quando liberado,
moveEnd
-
moveEnd
.
Código do componente ListItem import React from 'react' import { Text, TouchableOpacity } from 'react-native' import styles from './styles' interface ListItemProps { name: string, move: () => void, moveEnd: () => void, isActive: boolean, } const ListItem = ({ name, move, moveEnd, isActive }: ListItemProps) => { return ( <TouchableOpacity style={[styles.root, isActive && styles.active]} onLongPress={move} onPressOut={moveEnd} > <Text style={styles.text}>{name}</Text> </TouchableOpacity> ) } export default ListItem
Os estilos para todos os componentes são movidos para arquivos separados e não são fornecidos aqui, mas podem ser visualizados no
repositório .
O resultado resultante:

Adicione a capacidade de deslizar
Bem, lidamos com sucesso com a primeira parte, seguimos para a segunda parte do balé Marlezon.
Para adicionar a capacidade de deslizar itens da lista, usamos o pacote
react-native-swipe-list-view
.
Para começar, vamos instalá-lo:
yarn add react-native-swipe-list-view
Este pacote contém o componente
SwipeRow
, que, de acordo com a documentação, deve incluir dois componentes:
<SwipeRow> <View style={hiddenRowStyle} /> <View style={visibleRowStyle} /> </SwipeRow>
Observe que a primeira vista é desenhada sob a segunda.
Vamos mudar o código do componente
ListItem
.
Código do componente ListItem import React from 'react' import { Text, TouchableOpacity, View, Image } from 'react-native' import { SwipeRow } from 'react-native-swipe-list-view' import { Language } from '../../App' import styles from './styles' const heart = require('./icons8-heart-24.png') const filledHeart = require('./icons8-heart-24-filled.png') interface ListItemProps { item: Language, move: () => void, moveEnd: () => void, isActive: boolean, onHeartPress: () => void, } const ListItem = ({ item, move, moveEnd, isActive, onHeartPress }: ListItemProps) => { return ( <SwipeRow rightOpenValue={-180}> <View style={styles.hidden}> <TouchableOpacity onPress={onHeartPress}> <Image source={item.favorite ? filledHeart : heart} /> </TouchableOpacity> </View> <TouchableOpacity activeOpacity={1} style={[styles.root, isActive && styles.active]} onLongPress={move} onPressOut={moveEnd} > <Text style={styles.text}>{item.name}</Text> </TouchableOpacity> </SwipeRow> ) } export default ListItem
Primeiro, adicionamos o componente
SwipeRow
com a propriedade
SwipeRow
, que determina a distância pela qual o elemento pode ser deslizado.
Em segundo lugar, movemos nossa
TouchableOpacity
dentro do
SwipeRow
e adicionamos uma View, que será desenhada sob esse botão.
Dentro dessa visualização, é desenhada uma imagem para determinar se o idioma é um favorito. Quando você clica nele, o valor deve ser revertido e, como os dados estão no componente pai, é necessário acionar um retorno de chamada aqui que executa esta ação.
Faça as alterações necessárias no componente pai:
Código do componente do aplicativo import React, { Component } from 'react' import { View } from 'react-native' import styles from './styles' import DraggableFlatList, { RenderItemInfo, OnMoveEndInfo } from 'react-native-draggable-flatlist' import ListItem from './components/ListItem' import fakeData from './fakeData.json' export type Language = { id: number, name: string, favorite: boolean, } interface AppProps {} interface AppState { data: Array<Language> } class App extends Component<AppProps, AppState> { constructor(props: AppProps) { super(props) this.state = { data: fakeData, } } onMoveEnd = ({ data }: OnMoveEndInfo<Language>) => { this.setState({ data: data ? [...data] : [] }) } toggleFavorite = (value: Language) => { const data = this.state.data.map(item => ( item.id !== value.id ? item : { ...item, favorite: !item.favorite } )) this.setState({ data }) } render() { return ( <View style={styles.root}> <DraggableFlatList data={this.state.data} renderItem={this.renderItem} keyExtractor={(item) => item.id.toString()} scrollPercent={5} onMoveEnd={this.onMoveEnd} /> </View> ) } renderItem = ({ item, move, moveEnd, isActive }: RenderItemInfo<Language>) => { return ( <ListItem item={item} move={move} moveEnd={moveEnd} isActive={isActive} onHeartPress={() => this.toggleFavorite(item)} /> ) } } export default App
Fontes do projeto no
GitHub .
O resultado é apresentado abaixo:
