Criando um Kanbam do zero com React Native
Desenvolvendo um Kanban com React-native
A ideia principal desse conteúdo é ser informativo, e ajudar muitas pessoas que tiveram a mesma dificuldade que eu tive, de encontrar um conteúdo BR falando a respeito. Quando comecei a desenvolver o quadro pensei em algo estilo trello, e fui mergulhando nas possibilidades, acreditei que seria facil de achar algo em react native, pois eu precisava dessa solução rodando em ios e android.
Encontrei algumas bibliotecas promissoras no caminho, mas com uma documentação complicadinha de entender, bom enfim vamos para a solução.
Requisitos:
- Nodejs / Eu estou usando a versão 18.17.0, a mais recente até o momento desse artigo.
- Uma IDE de sua preferência, no meu caso VS Code
- IDE para execução de código Nativo através de emuladores. / Com isso me refiro ao xcode para quem estiver desenvolvendo em um ios, ou o Android studio caso esteja fazendo isso em Android, no meu caso estou com as duas possibilidades mas como não há uma diferença de instalação nas plataformas sigo com o IOS e deixo você a vontade de escolher a sua.
- Execute npm install react-native-gesture-handler no seu terminal
- Execute npm install react-native-reanimated
Repetindo mais uma vez nesse tutorial não usarei nenhuma biblioteca pronta permitindo assim que criemos algo genuino e novo, quem sabe possa virar uma boa discussão aqui.
Primeiramente vamos criar nossa tela:
import React, {useState} from 'react';
const App = () => {
return (
<View style={styles.container}>
<View style={styles.coluna}>
<Text>Coluna 1</Text>
</View>
<View style={styles.coluna}>
<Text>Coluna 2</Text>
</View>
</View>
);
};
export default App;
Agora vamos definir o estado das nossas colunas, no momento estou trabalhando com duas para que possa ficar o mais claro possível:
const [coluna1, setColuna1] = useState([{ id: 1, nome: 'Item 1' }, { id: 2, nome: 'Item 2' }]);
const [coluna2, setColuna2] = useState([]);
Você deve ter algo assim:
import React, {useState} from 'react';
const App = () => {
const [coluna1, setColuna1] = useState([{ id: 1, nome: 'Item 1' }, { id: 2, nome: 'Item 2' }]);
const [coluna2, setColuna2] = useState([]);
return (
<View style={styles.container}>
<View style={styles.coluna}>
<Text>Coluna 1</Text>
</View>
<View style={styles.coluna}>
<Text>Coluna 2</Text>
</View>
</View>
);
};
export default App;
Agora iremos criar o nosso card, que pode ser arrastado usando a biblioteca, react-native-reanimated.
import {
PanGestureHandler,
} from 'react-native-gesture-handler';
import Animated, {
runOnJS,
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withSpring,
} from 'react-native-reanimated';
const DraggableItem = ({ item, onDrop }: any) => {
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const onGestureEvent = useAnimatedGestureHandler({
onStart: (_, ctx) => {
ctx.startX = translateX.value;
ctx.startY = translateY.value;
},
onActive: (event, ctx) => {
translateX.value = ctx.startX + event.translationX;
translateY.value = ctx.startY + event.translationY;
},
onEnd: () => {
translateX.value = withSpring(0);
translateY.value = withSpring(0);
runOnJS(onDrop)();
},
});
const style = useAnimatedStyle(() => {
return {
transform: [
{ translateX: translateX.value },
{ translateY: translateY.value },
],
};
});
return (
<PanGestureHandler onGestureEvent={onGestureEvent}>
<Animated.View style={[styles.item, style]}>
<Text>{item.nome}</Text>
</Animated.View>
</PanGestureHandler>
);
};
e inserir ele no nosso App:
import React, { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import {
PanGestureHandler,
} from 'react-native-gesture-handler';
import Animated, {
runOnJS,
useAnimatedGestureHandler,
useAnimatedStyle,
useSharedValue,
withSpring,
} from 'react-native-reanimated';
const App = () => {
const [coluna1, setColuna1] = useState([{ id: 1, nome: 'Item 1' }, { id: 2, nome: 'Item 2' }]);
const [coluna2, setColuna2] = useState([]);
const moverItem = (item: any, de: any, para: any) => {
de((prev: any) => prev.filter((i: any) => i.id !== item.id));
para((prev: any) => [...prev, item]);
};
return (
<View style={styles.container}>
<View style={styles.coluna}>
<Text>Coluna 1</Text>
{coluna1.map((item) => (
<DraggableItem key={item.id} item={item} onDrop={() => moverItem(item, setColuna1, setColuna2)} />
))}
</View>
<View style={styles.coluna}>
<Text>Coluna 2</Text>
{coluna2.map((item: any) => (
<DraggableItem key={item.id} item={item} onDrop={() => moverItem(item, setColuna2, setColuna1)} />
))}
</View>
</View>
);
};
const DraggableItem = ({ item, onDrop }: any) => {
const translateX = useSharedValue(0);
const translateY = useSharedValue(0);
const onGestureEvent = useAnimatedGestureHandler({
onStart: (_, ctx) => {
ctx.startX = translateX.value;
ctx.startY = translateY.value;
},
onActive: (event, ctx) => {
translateX.value = ctx.startX + event.translationX;
translateY.value = ctx.startY + event.translationY;
},
onEnd: () => {
translateX.value = withSpring(0);
translateY.value = withSpring(0);
runOnJS(onDrop)();
},
});
const style = useAnimatedStyle(() => {
return {
transform: [
{ translateX: translateX.value },
{ translateY: translateY.value },
],
};
});
return (
<PanGestureHandler onGestureEvent={onGestureEvent}>
<Animated.View style={[styles.item, style]}>
<Text>{item.nome}</Text>
</Animated.View>
</PanGestureHandler>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
coluna: {
flex: 1,
alignItems: 'center',
},
item: {
width: 100,
height: 50,
backgroundColor: 'lightgray',
marginBottom: 10,
justifyContent: 'center',
alignItems: 'center',
},
});
export default App;
Nesse momento você ja deve conseguir arrastar de uma coluna para a outra, agora é só desenvolver toda a aparencia do nosso quadro, assim que estiver pronto irei atualizar esse artigo, mas no momento veja, utilizamos animated e gesture-handler, embora existam soluções prontas para copiar e colar no seu sistema, eu vejo que dessa forma é melhor por que você tem mais liberdade para manipular os estados do seu componente.
gesture-handler é uma biblioteca que trabalha justamente com eventos de toque e manipulação de itens com toque e etc... e tem uma biblioteca completa para te auxiliar:
https://docs.swmansion.com/react-native-gesture-handler/