Executando verificação de segurança...
3

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/

Carregando publicação patrocinada...
2
1
1