Ter uma ótima IU e UX está entre os fatores mais importantes para o sucesso de qualquer aplicativo, especialmente quando há tantas alternativas disponíveis para quase todos os aplicativos. Fazer com que seu aplicativo tenha a aparência e o comportamento que você deseja requer tempo, paciência e prática.

No entanto, se você não tiver a ajuda de designers e desenvolvedores de experiência do usuário ao criar seu aplicativo, provavelmente vai passar a maior parte do tempo fazendo sua ideia funcionar. Tornar a interface do usuário bonita é provavelmente a menor das suas preocupações-especialmente se você for um desenvolvedor solo.

É aqui que entram ferramentas como Magnus UI. Com um punhado de componentes e adereços de utilitários intuitivos, ele permite que você crie Componentes de IU para seus aplicativos React Native que parecem ótimos.

Bibliotecas de estilo que priorizam o utilitário são as novidades do setor-bem, elas já existem há algum tempo, mas estão ganhando força rapidamente recentemente. Magnus UI está seguindo essa tendência. Se você quer ver do que se trata todo esse hype, este é o post para você.

Nesta postagem do blog, vou orientá-lo na construção de duas telas completas de um aplicativo usando Magnus UI. Não sou nada perto de um designer, então vou recorrer a escolher algo do Dribbble . É assim que parece:

Dribble Design Mobile Template

Estou escolhendo este design porque ele contém alguns dos blocos de IU mais comuns que quase todos os aplicativos têm:

  1. Uma lista de coisas com imagens para cada um e algum conteúdo de texto. Neste caso, é uma lista de categorias de cursos
  2. Uma navegação inferior que permite navegar entre várias páginas
  3. Um padrão de detalhe mestre em que cada categoria tem sua própria tela mostrando os cursos e um botão de ação para a tela
  4. Alguns detalhes menores, como perfil do usuário, barra de pesquisa, exibição de estatísticas, etc.

Claro, os modelos do Dribbble sempre contêm alguns bits extras que não agregam valor ao usuário final e servem apenas para fazer o design se destacar. Então, vamos reduzir um pouco isso para manter o conteúdo desta postagem relevante e no ponto.

Tudo bem, agora vamos ao que interessa! Ah, e antes de fazermos isso, se você quiser apenas dar uma olhada em como é o resultado final antes de se comprometer a ler tudo, aqui está uma prévia rápida para você , e aqui está a base de código inteira no GitHub .

Nosso kit de ferramentas

Em primeiro lugar, adoro o TypeScript e, depois que comecei a usá-lo, voltar para o JavaScript simplesmente não parece certo. Portanto, usarei o TypeScript para este projeto, mas fique à vontade para usar a versão JS se preferir.

Começar e gerenciar projetos React Native pode ser um pouco entediante, por isso sempre prefiro Expo , embora tenha suas limitações. Felizmente, para demonstrar a construção de um aplicativo de duas telas, não vamos atingir nenhuma dessas limitações.

Para navegação de detalhes mestres entre a categoria do curso e sua página de detalhes, eu sou escolha do pacote react-navigation . Isso também nos ajudará a organizar rapidamente um monte de coisas complicadas, como barra de navegação inferior, área de cabeçalho superior, etc.

E, claro, a estrela principal do show, Magnus UI , precisa ser incluída nessa lista. No design, existem algumas ilustrações que não estão disponíveis separadamente, por isso irei substituí-las por ilustrações de unDraw , que é absolutamente estelar site para encontrar rapidamente ilustrações lindas para o seu projeto.

Estou listando tudo isso para dar a você, leitor, uma chance de se familiarizar com essas ferramentas antes de mergulhar. No entanto, não é obrigatório, pois tentarei o meu melhor para documentar o uso de todas elas como necessário.

Construindo nosso aplicativo React Native

Se você não está familiarizado com o React Native ou o Expo CLI, ambos têm alguns boilerplates muito úteis prontos para iniciar qualquer projeto. Como estamos usando o Expo, o que melhor atende às nossas necessidades é o modelo com guias.

Para obter o boilerplate, execute o comando expo init elarn em seu terminal e escolha o modelo no prompt. Uma vez feito isso, vá para a pasta recém-criada usando cd elarn .

Boilerplate Command Expo Init Elarn

O padrão já tem um pouco de código, mas antes de nos aprofundarmos nele, vamos instalar Magnus UI a partir do npm. Você pode seguir o guia de instalação aqui , mas é o que você precisa para executar:

 yarn add react-native-magnus color react-native-modal react-native-animatable-S 

OK, agora vamos ver como o aplicativo fica com o código padrão em vigor. Execute yarn start para disparar o serviço Expo. Assim que o serviço estiver em execução, você pode usar o cliente Expo para executar o aplicativo no dispositivo físico de sua escolha (iOS/Android) ou executá-lo em um emulador/simulador. Vou usar o Simulador para iOS. Siga a documentação da Expo se tiver problemas com qualquer um deles. Quando o aplicativo estiver em execução, você verá uma tela como a abaixo:

Saída de execução do aplicativo no iOS

Como você pode ver, temos muito trabalho a fazer para que isso se pareça com o incrível design do Dribbble que escolhemos, então vamos lá!

Estilizando nossas guias inferiores

O modelo de guias Expo vem integrado com a navegação reativa implementada, incluindo duas telas de guia. No entanto, as guias seguem o estilo padrão nativo com base no sistema operacional do dispositivo. Para torná-lo mais parecido com o design, temos que configurá-lo um pouco.

Uma vez que a navegação reativa e nosso boilerplate Expo já construíram a maior parte dele, tentaremos não reconectar muito com a IU do Magnus. Em vez disso, usaremos apenas algumas configurações e estilos básicos do React Native para fazer com que se pareça com o design.

No clichê, temos apenas duas guias, mas o design tem quatro. Então, vamos adicionar mais algumas guias e ícones para que as guias se pareçam mais com o design. Temos uma grande variedade de ícones disponíveis por meio do pacote @ expo/vector-icons e, para os ícones de guia, estamos usando o conjunto de ícones Ionicons.

Primeiro, abra o arquivo navigation/BottomTabNavigator.tsx e substitua o componente BottomNavigator pelo seguinte:

   , }} />  , }} />  , }} />  , }} />  

As adições aqui são:

  1. campos style e showLabel na propriedade tabBarOptions no componente BottomTab.Navigator . Isso adiciona um estilo personalizado para torná-lo parecido com o design
  2. TabThree e TabFour guias que estão apenas apontando para telas existentes, essencialmente duplicando TabTwo , mas usando ícones diferentes

Agora, esses ícones parecem um pouco grandes em comparação com o design, então, no mesmo arquivo, procure o componente TabBarIcon e altere a propriedade size para 25. Para ajustar o espaçamento com o novo tamanho, altere marginBottom para-5:

 return ; 

Devido às alterações acima, o TypeScript reclamará um pouco porque adicionamos duas novas guias sem definir seus tipos adequadamente. Abra o arquivo types.tsx e, na definição BottomTabParamList , adicione TabThree e TabFour , como abaixo:

 exportar tipo BottomTabParamList={ TabOne: indefinido; TabTwo: indefinido; TabThree: indefinido; TabFour: indefinido;
}; 

Se você está observando o estado atual do aplicativo, provavelmente percebeu que as cores parecem diferentes na tela em comparação com o design, então vamos corrigi-las. Altere a cor da tonalidade das guias em constants/Colors.ts definindo const tintColorLight='# D84343'; e, em seguida, altere o fundo da barra da guia definindo fundo:'# EFE3E3'.

Este padrão vem com suporte para o modo escuro e claro, então vamos ajustar nosso design um pouco para o modo escuro também. Adicione uma nova propriedade ao esquema claro e escuro: tabBarBackground:'#fff', porque usamos essa propriedade na propriedade de estilo da propriedade tabBarOptions .

Para sua informação, extraí esses valores hexadecimais diretamente do design do Dribbble, então, se eles não corresponderem à sua expectativa ou preferência, sinta-se à vontade para ajustá-los como achar necessário.

Assim que as alterações acima forem feitas, sua tela deve ficar assim:

Ajuste padrão para modo escuro

Configurando a IU Magnus para construir nossa IU Nativa React

Finalmente, vamos nos concentrar na construção do conteúdo da página com Magnus UI. Para usar todos os adereços do utilitário Magnus UI para estilização, precisamos configurá-lo com alguns detalhes básicos. Primeiro, vamos importar o Magnus ThemeProvider no arquivo App.tsx e definir um tema personalizado com nossa cor primária:

 import {ThemeProvider} de"react-native-magnus"; const ElarnTheme={ cores: { pink900:'# D84343', }
} 

Essas definições de cores ajudam a construir um subconjunto de variações de cores que você pode usar em todo o seu aplicativo com nomes mais memoráveis ​​e facilmente identificáveis. Tudo o que temos que fazer agora é envolver todo o nosso aplicativo com o ThemeProvider e passar o objeto theme para o provedor:

     

Agora, vamos construir nossa tela inicial do topo. A primeira coisa que estou notando é o cabeçalho que diz Tab One Title aparecendo como um polegar dolorido. Para nos livrarmos disso, precisamos voltar para BottomTabNavigator.tsx e passar um novo objeto para a definição de TabOneScreen :

  

Obviamente, headerShown: false é o que está escondendo o cabeçalho.

Etapa 1: Construindo a IU da tela inicial

Como você deve ter notado, os nomes dos arquivos, o código dentro deles, etc. são escritos de forma genérica, com TabOne , TabTwo , etc. Sinta-se à vontade para renomear se quiser, mas vou mantê-los como estão. Vamos começar com screens/TabOne.tsx , remover todas as definições de estilo dele e, em seguida, colocar o seguinte código:

 import * as React from'react';
importar constantes de"expo-constantes";
import {Avatar, Div, Icon, Input, Text} de"react-native-magnus"; importar {categorias, CategoryCard} de'../components/CategoryCard';
importar {ScrollView} de"react-native"; função padrão de exportação TabOneScreen () { Retorna (  
Olá Sheila
Vamos atualizar suas habilidades
} />
Popular Ver tudo
{categorias.map ((categoria)=> ( ))}
); }

Vamos analisar isso. Estamos envolvendo nossa página inteira em um componente ScrollView para que a lista de categorias de cursos pode ser rolada. Então, há outro wrapper chamado Div com um prop px={25} .

Div é uma alternativa Magnus UI a um componente View básico do React Native. Ele pode receber qualquer acessório de utilitário para estilização, e px é um desses acessórios; ele define um preenchimento horizontal ao redor do contêiner.

Como removemos a área do cabeçalho, o conteúdo da tela da guia começará a se sobrepor à área da barra de status do seu dispositivo. Para evitar isso, estamos envolvendo o conteúdo da área superior em um div e adicionando uma margem superior que é igual à barra de status do dispositivo, que pode ser extraída usando Constants.statusBarHeight da biblioteca expo-constantes.

Graças a row justifyContent={"space-between"} , os filhos imediatos deste componente serão posicionados um após o outro na mesma linha e serão empurrados para as bordas horizontais do contêiner e alinhado no centro vertical do contêiner.

Dentro dele, temos uma Div que envolve todo o conteúdo do texto para saudar o usuário. Para estilizar o texto, usamos os adereços fontSize e fontWeight . O valor de fontSize é definido em sm , lg , xl , etc. em vez de tamanhos reais para garantir a consistência em todo o aplicativo.

No lado direito, para exibir uma foto de perfil, estamos usando o componente Avatar da IU do Magnus e passando para ele um URL de um serviço de avatar . Observe como esse componente posiciona bem a imagem em um círculo e como é fácil adicionar sombra a ela simplesmente passando um objeto shadow={1} prop.

Em seguida, temos o campo de entrada de pesquisa, que é outro componente da IU do Magnus. Podemos estilizá-lo facilmente com acessórios úteis como rounded={30} , o que dá à borda do campo de entrada um raio de 30px. color="gray900" é outro adereço interessante aqui, que extrai a cor gray900 da definição de tema padrão.

Você pode substituir essas cores simplesmente definindo-as no tema personalizado que foi passado para o ThemeProvider . Os outros adereços são semelhantes aos que vimos até agora.

Agora precisamos de alguns dados para a lista de categorias de cursos. Idealmente, em um aplicativo da vida real, eles viriam de um servidor em algum lugar. Por enquanto, vamos apenas codificá-los.

Para manter nosso código limpo, moveremos a exibição da categoria para seu próprio componente denominado CategoryCard e passaremos os dados da categoria para ele enquanto mapeamos todas as categorias. Ambos estão sendo importados de components/CategoryCard.tsx , que ainda não existe. Então, vamos criar esse arquivo e colocar o seguinte código dentro:

 import * as React from"react";
importar {Div, Imagem, Texto} de"react-native-magnus";
importar {ImageSourcePropType} de"react-native"; tipo Categoria={ número de contagem; categoria: string; imagem: ImageSourcePropType;
} exportar categorias const=[ {category:'Marketing', count: 12, picture: require ('../assets/images/illustration_one.png')}, {category:'Investing', count: 8, picture: require ('../assets/images/illustration_two.png')}, {category:'Drawing', count: 22, picture: require ('../assets/images/illustration_three.png')}, {category:'Marketing', count: 12, picture: require ('../assets/images/illustration_one.png')}, {category:'Investing', count: 8, picture: require ('../assets/images/illustration_two.png')}, {category:'Drawing', count: 22, picture: require ('../assets/images/illustration_three.png')}, {category:'Drawing', count: 22, picture: require ('../assets/images/illustration_three.png')},
]; export const CategoryCard=({category, count, picture}: Category)=> ( 
{category} {count} Cursos
);

O componente em si é bastante simples. Temos uma Div com bordas arredondadas e um fundo branco. Cada cartão ocupa 48% da largura disponível, de modo que cada linha cabe apenas dois cartões com algum espaçamento no meio. A imagem é mostrada na parte superior e parte da categoria e do curso contam na parte inferior do cartão.

Também estamos definindo alguns dados de categoria falsos em uma matriz para preencher a tela. O objeto imagem em cada categoria aponta para um arquivo local no diretório ativos/imagens , onde coloquei três ilustrações baixadas do unDraw. Neste ponto, você deve ver uma tela como esta na tela inicial:

Tela inicial com imagens carregadas

Etapa 2: Criação da IU da página de detalhes

No padrão mestre-detalhe, você normalmente teria uma lista de itens e clicar em um dos itens o levará a uma tela diferente com mais detalhes sobre o item. Para criar nossa tela de detalhes para cada categoria, vamos criar um novo arquivo chamado telas/CourseDetail.tsx e colocar o seguinte código dentro:

 import * as React from'react';
importar {ScrollView} de"react-native";
import {useHeaderHeight} de'@ react-navigation/stack';
import {Div, Icon, Image, Text} de"react-native-magnus";
importar {StackScreenProps} de"@ react-navigation/stack";
importar {ListHeader} de"../components/ListHeader";
importar {CourseVideo} de"../components/CourseVideo";
importar {Category, TabOneParamList} de"../types"; exportar a função padrão CourseDetailScreen ({route}: StackScreenProps ) { const {categoria}: {categoria: categoria}=rota.params; const headerHeight=useHeaderHeight (); Retorna (  
{category.category}
por {category.author}
{category.subscriberCount}
{category.rating}
{category.duration}
{category.videos.map (video=> ( ))}
); }

Esta tela terá conteúdo dinâmico baseado em qualquer categoria que o usuário selecionar, por isso ele precisa aceitar a categoria como um parâmetro de rota. Os parâmetros de rota são usados ​​para passar dados de uma tela para outra ao navegar entre as telas.

No nosso caso, queremos passar os dados da categoria selecionada para esta tela. Para facilitar a passagem desses dados, precisamos digitar este componente corretamente via TabOneParamList , então abra o arquivo types.tsx e atualize-o conforme abaixo:

 tipo de exportação TabOneParamList={ TabOneScreen: indefinido; CourseDetailScreen: {categoria: Categoria};
}; 

Agora, enquanto estamos neste arquivo, observe que a página de detalhes de uma categoria tem muito mais dados do que eram exibidos na tela inicial para cada categoria-classificação, inscritos, lista de vídeos do curso, etc.

Portanto, precisamos atualizar nossos dados de categoria e, como são dados brutos, provavelmente não deveríamos ter isso dentro do componente do cartão. Então, vamos removê-lo de components/CategoryCard.tsx e adicionar o código abaixo no arquivo types.tsx :

 exportar categorias const=[ { categoria:'Marketing', contagem: 12, imagem: require ('./assets/images/illustration_one.png'), autor:'Jack Mi', subscriberCount:'11K', classificação: 4,8, duração:'2 horas e 30 minutos', bg:'pink500', vídeos: [ {título:'01. Introdução', duração:'03: 53'}, {título:'02. O que está investindo', duração:'08: 13'}, {título:'03. Fundamentos', duração:'15: 23'}, {título:'04. Aulas', duração:'20: 33'}, ] }, { categoria:'Investimento', contagem: 8, imagem: require ('./assets/images/illustration_two.png'), autor:'Jack Mi', subscriberCount:'11K', classificação: 4,8, duração:'2 horas e 30 minutos', bg:'purple500', vídeos: [ {título:'01. Introdução', duração:'03: 53'}, {título:'02. O que está investindo', duração:'08: 13'}, {título:'03. Fundamentos', duração:'15: 23'}, {título:'04. Aulas', duração:'20: 33'}, ] }, { categoria:'Desenho', contagem: 22, imagem: require ('./assets/images/illustration_three.png'), autor:'Jack Mi', subscriberCount:'11K', classificação: 4,8, duração:'2 horas e 30 minutos', bg:'red500', vídeos: [ {título:'01. Introdução', duração:'03: 53'}, {título:'02. O que está investindo', duração:'08: 13'}, {título:'03. Fundamentos', duração:'15: 23'}, {título:'04. Aulas', duração:'20: 33'}, ] }, { categoria:'Marketing', contagem: 12, imagem: require ('./assets/images/illustration_one.png'), autor:'Jack Mi', subscriberCount:'11K', classificação: 4,8, duração:'2 horas e 30 minutos', bg:'blue500', vídeos: [ {título:'01. Introdução', duração:'03: 53'}, {título:'02. O que está investindo', duração:'08: 13'}, {título:'03. Fundamentos', duração:'15: 23'}, {título:'04. Aulas', duração:'20: 33'}, ] }, { categoria:'Investimento', contagem: 8, imagem: require ('./assets/images/illustration_two.png'), autor:'Jack Mi', subscriberCount:'11K', classificação: 4,8, duração:'2 horas e 30 minutos', bg:'pink500', vídeos: [ {título:'01. Introdução', duração:'03: 53'}, {título:'02. O que está investindo', duração:'08: 13'}, {título:'03. Fundamentos', duração:'15: 23'}, {título:'04. Aulas', duração:'20: 33'}, ] }, { categoria:'Desenho', contagem: 22, imagem: require ('./assets/images/illustration_three.png'), autor:'Jack Mi', subscriberCount:'11K', classificação: 4,8, duração:'2 horas e 30 minutos', bg:'pink500', vídeos: [ {título:'01. Introdução', duração:'03: 53'}, {título:'02. Whats investing', duration:'08:13'}, {title:'03. Fundamentals', duration:'15:23'}, {title:'04. Lessons', duration:'20:33'}, ] }, { category:'Drawing', count: 22, picture: require('./assets/images/illustration_three.png'), author:'Jack Mi', subscriberCount:'11K', rating: 4.8, duration:'2 hours 30 minutes', bg:'pink500', videos: [ {title:'01. Introduction', duration:'03:53'}, {title:'02. Whats investing', duration:'08:13'}, {title:'03. Fundamentals', duration:'15:23'}, {title:'04. Lessons', duration:'20:33'}, ] },
];

As you can see, we have a few new properties in each category, including an array of videos. That means we need to adjust the type definition for Category. Let’s remove its type definition from components/CategoryCard.tsx and place this code in the types.tsx file:

import {ImageSourcePropType} from"react-native";
export type Video={ title: string; duration: string;
}; export type Category={ bg: string; count: number; author: string; rating: number; category: string; duration: string; videos: Video[]; subscriberCount: string; picture: ImageSourcePropType;
}

OK, now let’s get back to the screen again. Similar to our homepage, we have a top area with some details on the left side. The image of the category is on the right side, so we use a flex wrapper to contain this.

Notice that the subscriberCount icon uses color="pink500". To match it with the design, we need to overwrite its default value via the theme modifier in App.tsx:

const ElarnTheme={ colors: { pink900:'#D84343', pink500:'#D59999' }
}

Then, underneath, we are mapping through all the videos for the course and rendering a CourseVideo component. Let’s create a new file called components/CourseVideo.tsx and put the following code in it:

import {Button, Div, Icon, Text} from"react-native-magnus";
import React from"react"; export const CourseVideo=({title, duration}: {title: string, duration: string})=> ( 
);

The last thing I want you to note on this screen is that we are using a ListHeader component to show the video list header. Since this header is exactly the same as the list header we had on the home screen, it makes sense to abstract it into its own component.

Let’s create a new file, components/ListHeader.tsx, and put the following code in the file:

import * as React from"react";
import {Div, Text} from"react-native-magnus"; export const ListHeader=({title}: {title: string})=> ( 
{title} See all
);

I simply copied the header content from the home screen and made the title dynamic through prop. So now we can replace the header in the home screen with this: .

Now to allow the user to select a category and open the detail screen, we have to first turn every category card into a button and attach an onPress event handler. Open up the components/CategoryCard.tsx file and replace the wrapper Div with this:

export const CategoryCard=({category, count, picture, bg, onPress}: CategoryCardProp)=> ( 

onPress is expected as a prop, so we need to go back to the home screen and pass this onPress function to every category. Every screen in react-navigation receives a navigation prop, which can be used to navigate between screens. Open up the screens/TabOneScreen.tsx and adjust the component definition like so:

export default function TabOneScreen({ navigation }: StackScreenProps) {

Then pass the onPress prop to the CategoryCard component like below:

  navigation.navigate('TabOne',{ screen:'CourseDetailScreen', params: {category} })} />

So, when any of the categories are pressed, we are navigating to the CourseDetailScreen within the TabOne navigator and passing the category info to the screen as param. For this navigation to work, we need to register our newly created screen in the navigator by adding the below tab definition in navigation/BottomTabNavigator.tsx:

   }} /> 

For this tab, we keep the header but replace its content with an icon to match the design. The icon itself is rendered using the Button component from Magnus UI and contains only a back arrow icon.

If you followed every step so far, you should be able to click on any category item on the home screen, and it should open up the detail page.

However, notice that the detail page has the same bottom tab as the home screen, whereas in the design, the detail page only has one action button in the same area occupied by the bottom tab on the home screen. This is a tricky bit and requires some rewiring of the navigators.

Final step: Replacing the bottom tab on the detail screen

Due to the way react-navigation works, you can’t hide bottom tabs in a nested screen in a tab navigator. So to hide the bottom tab from our detail screen, we need to move it out of the tab navigator and put it in the main stack navigator. Open the navigation/index.tsx and add the new screen like below:

    }} />

Then, remove our previously added CourseDetail screen in the bottom tab navigator from the navigation/BottomTabNavigator.tsx file.

Since the detail screen is now part of the stack, we need to change how we navigate to it. Go back into TabOneScreen.tsx and replace the onPress function to look like this:

navigation.navigate(‘CourseDetailScreen’, { category })}/>

Notice how we’re passing the param directly with the field name category instead of under a params object? This means it will be passed differently to our screen component. Let’s adjust our screens/CourseDetail.tsx definition to:

export default function CourseDetailScreen({route}: StackScreenProps) {

So, instead of the TabOneParamList, we are now using the RootStackParamList. Before we look into the RootStackParamList definition, let’s add the action button on this screen since we’re already in the file. Add the following button component under the ScrollView component and wrap both of them in a Fragment (<>)

 <>  //...previous code   

Finally, open up the types.tsx file and move the detail screen param definition like below:

Detail Screen Param Definition

 

At this point, your detail screen should no longer have the bottom tab and instead show an action button like below:

Detail Screen with Action Button

Wrapping up

I hope that throughout the post, it’s been apparent how easy UI building becomes with Magnus UI. We did a lot of configuration steps since it’s an app from scratch, but once you get these out of the way, you get to continue building components with Magnus UI, and the props become second nature.

The post Magnus UI tutorial: Building out React Native UI components appeared first on LogRocket Blog.

Source link