O que é o React Native

  • Primeiros passos no React Native - Aula 1 | Curso React Native (aprendiz) | Onebitcode

    Docs

    image

O que é o expo?

É uma ferramenta de app em react.

Docs expo

Criando um app

Instalando o expo

npm install --global expo-cli

Crinado o app

expo init "nome do projeto" ou yarn create react-native-app "nome do arquivo"

- cd my-project
- yarn start # you can open iOS, Android, or web from here, or run them directly with the commands below.
- yarn android
- yarn ios # requires an iOS device or macOS for access to an iOS simulator
- yarn web
- expo start --tunnel # executa 

Criando o APK

Passo 1: Se não tiver instalado ainda instale

  npx expo install expo-updates

Passo 2: rode o seguinte comando

  npx pod-install

Passo 3: Execute o comando para buildar o projeto

  expo build:android -t apk
  expo build:ios -t apk
  
  ou
  
  npm install -g eas-cli
  eas build -p android

Installing expo-updates

Configurando o app.json

Passo 1: Gerando o icone e o icone de splashscreen

Entre no seguinte site Ape Tools para gerar ambos.

Tutotial

Passo 2: app.json

  {
  "expo": {
    "name": "Campo Minado",
    "slug": "Campo Minado",
    "icon": "./icone/calculadora.png",
    "version": "1.0.0",
    "platforms": [
      "ios",
      "android"
    ],
    "splash": {
      "resizeMode": "contain"
    },
    "android": {
      "package": "com.jeovane.campo_minado",
      "versionCode": 1
    }
  }
}

Bloqueio de orientação da tela

Várias orientações de tela devem funcionar bem por padrão, a menos que você esteja usando a DimensionsAPI e não manipule alterações de orientação. Se você não quiser oferecer suporte a várias orientações de tela, poderá bloquear a orientação da tela para retrato ou paisagem.

No iOS, na guia Geral e na seção Informações de implantação do Xcode, habilite a Orientação do dispositivo que você deseja oferecer suporte (certifique-se de ter selecionado o iPhone no menu Dispositivos ao fazer as alterações). Para Android, abra o arquivo AndroidManifest.xml e, dentro do elemento activity, adicione

  'android:screenOrientation="portrait"'

para travar em retrato ou

  'android:screenOrientation="landscape"'     

para travar em paisagem.

Uma outra alternativa é seguir a documentação do expo.

Passo 1

No termina do seu projeto execute.

    npx expo install expo-screen-orientation

Passo 2

Agora no index.js ou index.tsx importe.

  import * as ScreenOrientation from 'expo-screen-orientation';

Adicione a seguinte função.

  async function changeScreenOrientation() {
      await ScreenOrientation.lockAsync(ScreenOrientation.OrientationLock.PORTRAIT);
  }

E chame ela antes do regiterRootComponent(App).

  changeScreenOrientation()

Docs - expo ScreenOrientation

Expo - Adicionando uma tela inicial e ícones

Adicionando uma splash screen no React Native

Create a Splash Screen

Usando o TypeScript

Configurações

Criar o tsconfig.json e coloque essas linhas nele:

    {
        "extends": "expo/tsconfig.base",
        "compilerOptions": {
        "experimentalDecorators": true,
        "forceConsistentCasingInFileNames": true,
        "isolatedModules": true,
        "lib": ["ESNext"],
        "noEmitHelpers": true,
        "noFallthroughCasesInSwitch": true,
        "noImplicitReturns": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "strict": true
        }
    }

Instalando dependencias

    yarn add @types/react -D
    yarn add  @types/react-native -D
    yarn add ts-node -D
    yarn add typescript -D
   
    ou

    yarn add @types/react @types/react-native ts-node typescript -D

    
    yarn add expo-updates
    yarn add @expo/webpack-config

    ou 

    yarn add expo-updates @expo/webpack-config

Usando o styled-components

    yarn add styled-components
    yarn add @types/styled-components-react-native

OBS:. packege.json

No packege.json do projeto altere o main de "main":"index.js" para "main": "./src/index.tsx"

Executar

    expo start --tunnel # executa

Instalando o @fortawesome

    yarn add react-native-svg @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons @fortawesome/react-native-fontawesome

Add more styles or Pro icons

    yarn add @fortawesome/free-brands-svg-icons

    yarn add @fortawesome/free-regular-svg-icons

@fortawesome/react-native-fontawesome

Instalndo no expo

  npx expo install react-native-svg

Obs: caso aconteça o seguinte erro:

    Erro while updating property fill of a view managed by: RNSVGPath

Instale o seguinte pacote:

    yarn add react-native-svg@9.13.3

Motivo: A partir de 17 de fevereiro de 2020, a instalação da versão 9.13.3 do react-native-svg resolverá seu problema.

Aparentemente, usar a CLI de instalação do expo instalará a versão mais recente deste pacote, mas isso causará um erro ao iniciar o projeto (você verá que especificou qual versão do pacote é suportada pelo expo).

Fonte - stackoverflow

Obs: Caso aconteça o seguinte erro:

Invariant Violation: requireNativeComponent: "RNSVGSvgViewAndroid" was not found in the UIManager.

Instale a seguinte verção do react-native-svg

"react-native-svg": "13.4.0"

Fonte

Caso Swipeable não funcione

Nesse link tem varias alternativas link

Para mim o que deeu certo foi envolver o Swipeable em um GestureHandlerRootView.

AsyncStorage

Pecistencia dos dados.

Instale o seguinte modulo, pois a versão do react-native está depreciada e do Obs: react-native-community/async-storage está descomtinuado.

        yarn add @react-native-async-storage/async-storage
        npx expo install @react-native-async-storage/async-storage - só se estiver usando o expo

Mais informações

react-native-pape

Ocultar cabeçalho no navegador de pilha Navegação React

Para todas as telas

<Stack.Navigator
  screenOptions={{
    headerShown: false
  }}
>
  <Stack.Screen name="route-name" component={ScreenComponent} />
 
</Stack.Navigator>

Se você deseja apenas ocultar o cabeçalho em 1 tela, pode fazer isso definindo screenOptions no componente de tela, veja abaixo, por exemplo:

<Stack.Navigator
  screenOptions={{
    headerShown: false
  }}
>
  <Stack.Screen name="route-name" component={ScreenComponent} />
  <Stack.Screen options={{headerShown: false}} name="route-name" component={ScreenComponent} />
</Stack.Navigator>

Mais informações

React Native LightBox


<View style={styles.container}>
        <Lightbox>
          <Image
            style={{ height: 300, width: 300 }}
            source={{
              uri: 'http://knittingisawesome.com/wp-content/uploads/2012/12/cat-wearing-a-reindeer-hat1.jpg',
            }}
          />
        </Lightbox>
      </View>

Obs: Caso aconteça esse erro

Invariant Violation: ViewPropTypes has been removed from React Native. Migrate to ViewPropTypes exported from 'deprecated-react-native-prop-types'.

  • npm install deprecated-react-native-prop-types
  • em node_modules/react-native/index.js substitua da linha 436 até 464
// Deprecated Prop Types
  get ColorPropType(): $FlowFixMe {

    return require('deprecated-react-native-prop-types').ColorPropType
  },
  get EdgeInsetsPropType(): $FlowFixMe {
    return require('deprecated-react-native-prop-types').EdgeInsetsPropType
  },
  get PointPropType(): $FlowFixMe {
    return require('deprecated-react-native-prop-types').PointPropType
  },
  get ViewPropTypes(): $FlowFixMe {
    return require('deprecated-react-native-prop-types').ViewPropTypes
  },

  • apos execute npx patch-package react-native

Mais informações

Como Criar um Hyperlink no React Native

<Text 
    style={styles.hyperlinkStyle} 
    onPress={() => { 
      Linking.openURL('https://reactnative.dev'); 
    }}> 
    Site Oficial do React Native 
  </Text>

Mais informações

Carrosel feito na mão

const { width } = Dimensions.get('window');
const SPACING = 10;
const THUMB_SIZE = 70;
const [indexSelected, setIndexSelected] = useState(0);

    const onSelect = (indexSelected: number) => {
        setIndexSelected(indexSelected);
        
    };
const onScrollEnd = (e: { nativeEvent: { contentOffset: any; layoutMeasurement: any; }; }) => {
        const contentOffset = e.nativeEvent.contentOffset;
        const viewSize = e.nativeEvent.layoutMeasurement;

        // Divide the horizontal offset by the width of the view to see which page is visible
        const pageNum = Math.floor(contentOffset.x / viewSize.width);
        onSelect(pageNum)

    }

    return (
        <>
            <FlatList

                horizontal
                data={images}
                pagingEnabled
                style={{ marginLeft: -10, }}
                showsHorizontalScrollIndicator={false}
                contentContainerStyle={{
                    paddingHorizontal: SPACING
                }}
                keyExtractor={(item) => item.data}
                onMomentumScrollEnd={onScrollEnd}
                // 
                renderItem={({ item}) => (


                    <Image

                        style={{
                            width: width + 8,

                            height: width,

                        }}
                        source={{ uri: item.data }}
                    />

                )
                }

            />
            <View
                style={{
                    marginTop: 25,
                    paddingHorizontal: 32,
                    alignSelf: 'flex-end',
                    position: "absolute",
                    // position: "relative",
                    borderRadius: 10,

                    justifyContent: "center",
                    alignItems: "center"

                }}
            >
                <Text
                    style={{
                        color: '#fff',
                        fontSize: 18,
                        backgroundColor: "#cccccc2b",
                        
                        marginTop: 105,
                        width: 60,
                        textAlign: "center",
                        borderRadius: 10,


                    }}
                >
                    {indexSelected + 1}/{images.length}
                </Text>
            </View>

        </>
        )

Animated

OBS: O ecesso de uso do FlatList

Ao usar o FlatList dentro de uma componente ScrollView pode acarretar o seguinte erro:


 ERROR  VirtualizedLists should never be nested inside plain ScrollViews with the same orientation 
 because it can break windowing and other functionality - use another VirtualizedList-backed container instead.    
 

Caso isso aconteça a melhor aternativa possa ser o uso do map em vez do FlatList. Esse erro ocorre pois o FlatList possui um ScrollViews na sua composição e o React Native não aceita muito bem isso.

Login pelo google

Passos

  • Instale o npx expo install expo-application

  • Depois entre no link das credenciais

  • Existem 4 tipos diferentes de IDs de cliente que você pode fornecer:

    • expoClientId: ID do cliente proxy para uso na Expo Go em iOS e Android.
    • iosClientId: ID do cliente nativo do iOS para uso em fluxo de trabalho autônomo e básico.
    • androidClientId: ID do cliente nativo do Android para uso em fluxo de trabalho simples e autônomo.
    • webClientId: Expo web client ID para uso no navegador.
  • expoClientId: na pagina de credenciais crie um projeto depois celecione o seu perojeto e click em credenciais, assim no tompo selecione criar credenciais e click em OAuth client ID, apos isso você precisa configura a tela de consentimento. Apos isso resolvido, selecione em Application type a opção Web application.

  • iosClientId: faça o mesmo processo do expoClientId só que em Application type selecione IOS em name coloque o nome que form mais conveniente e no campo Bundle ID coloque ex: host.exp.exponent.

  • androidClientId:faça o mesmo processo do expoClientId só que em Application type selecione Android em name coloque o nome que form mais conveniente e no campo Package name coloque o id gerado do iosClientId. Agora no terminal do projeto diite openssl rand -base64 32 | openssl sha1 -c isso ira gera um código, copio e coloque no campo SHA-1 certificate ...

Facebook

Chamada na api | GET

https://graph.facebook.com/me?fields=id,email&access_token=${accessToken}

Resposta da api

{
  "email": "funlano@email.com",
  "id": "id",
}

Sobreamento

Importando imagens do assets

const images = {
  logo: {
    uri: require('your-image-path/logo.png')
  },
  banner: { 
    uri: require('your-image-path/banner.png')
  }
}

export { images }; 


//YourComponent.js
import { images } from 'yourImagesPath';

// for this test, expected to return [ { name: logo }, { name: banner} ]
const imagesFromTheServer = (your fetch);

imagesFromTheServer.map(image => {
  if (!images[image]) {
    return <Text>Image not found</Text>;
  }
  return <Image source={images[image].uri} />; // if image = logo, it will return images[logo] containing the require path as `uri` key
});

ActivityIndicator

import React from 'react';
import {ActivityIndicator, StyleSheet, View} from 'react-native';

const App = () => (
  <View style={[styles.container, styles.horizontal]}>
    <ActivityIndicator />
    <ActivityIndicator size="large" />
    <ActivityIndicator size="small" color="#0000ff" />
    <ActivityIndicator size="large" color="#00ff00" />
  </View>
);

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
  },
  horizontal: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    padding: 10,
  },
});

export default App;

Video no React Native

import React, { useState, useCallback, useRef } from "react";
import { Button, View, Alert } from "react-native";
import YoutubePlayer from "react-native-youtube-iframe";

export default function App() {
  const [playing, setPlaying] = useState(false);

  const onStateChange = useCallback((state) => {
    if (state === "ended") {
      setPlaying(false);
      Alert.alert("video has finished playing!");
    }
  }, []);

  const togglePlaying = useCallback(() => {
    setPlaying((prev) => !prev);
  }, []);

  return (
    <View>
      <YoutubePlayer
        height={300}
        play={playing}
        videoId={"iee2TATGMyI"}
        onChangeState={onStateChange}
      />
      <Button title={playing ? "pause" : "play"} onPress={togglePlaying} />
    </View>
  );
}

Pegar o tamanho da tela

No proprio react native existe o useWindowDimensions

import { useWindowDimensions } from "react-native";

const { width } = useWindowDimensions();

Também do react native existe outra opção


import { Dimensions } from "react-native";

const { width } = Dimensions.get("window");

Trabalhando com arquivos SVG

Passo 1

Crie um pasta assets e coloque seu arquivo nela.E no arquivo metro.config.js cole o seguinte código.

// Learn more https://docs.expo.io/guides/customizing-metro
const { getDefaultConfig } = require("expo/metro-config");

module.exports = (async () => {
  const {
    resolver: { sourceExts, assetExts },
  } = await getDefaultConfig(__dirname);

  return {
    transformer: {
      babelTransformerPath: require.resolve("react-native-svg-transformer"),
      getTransformOptions: async () => ({
        transform: {
          experimentalImportSupport: false,
          inlineRequires: false,
        },
      }),
    },
    resolver: {
      assetExts: assetExts.filter((ext) => ext !== "svg"),
      sourceExts: [...sourceExts, "svg"],
    },
  };
})();

Depois instale o seguinte pagete

npm i react-native-svg-transformer

Passo 2

Na raiz do projeto crie um arquivo .svgrrc e cole o seguinte codigo dendro


    "replaceAttrValues": {
        "#000": "{props.fill}",
        "#fff": "{props.stroke}"
    }
}

Passo 3

Tambem na raiz do projeto crie um arquivo custom.d.ts e cole o seguinte código dentro

declare module "*.svg" {
  const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
  export default content;
}

Depois importe onde deseja a imagem

import Svg1 from "caminho_da_imagem/nome.svg";

<Svg1
          width={25}
          height={25}
          stroke={"red"}
        />

Dropdow

react-native-paper-dropdown

FlexBox

Flexbox

UX-DropDown

How to mask Text and TextInput components in React Native and Expo

react-native-masked-text

Trabalhando com camera

Upload de imagens

React Native Bottom Sheet

bottom-sheet-preview.mp4
  • Passo 1: instale o pacote react-native-gesture-handler e o importe no aruivo App e envolva com o GestureHandlerRootView
...

import { GestureHandlerRootView } from "react-native-gesture-handler";

export default function App() {
...

 <GestureHandlerRootView style={{ flex: 1 }}>
 ....
 </GestureHandlerRootView>
...


}

  • Passo 2: Instale o @gorhom/bottom-sheet e o importe onte você quer usar o modal
...
import {
  BottomSheetBackdrop,
  BottomSheetBackdropProps,
  BottomSheetModal,
  BottomSheetModalProvider,
} from "@gorhom/bottom-sheet";

const Backdrop = (props: BottomSheetBackdropProps) => {
  return <BottomSheetBackdrop {...props} disappearsOnIndex={-1} />;
};

...
const snapPoints = useMemo(() => [550], []);
return return (
    <BottomSheetModalProvider>
      <BottomSheetModal
        onDismiss={()=>{}}
        detached
        handleIndicatorStyle={{estilo}}
        style={{estilo}}
        enableDismissOnClose
        backdropComponent={Backdrop}
        ref={ref}
        index={0}
        keyboardBlurBehavior="restore"
        snapPoints={snapPoints}
      >
      
      
      
       ...
       
       
       
      </BottomSheetModal>
    </BottomSheetModalProvider>

...

Zod validação