Já criou aplicativos que você mesmo não gostaria de usar?
Quando eu era desenvolvedor júnior de aplicativos, costumava criar interfaces de usuário caóticas.
Às vezes, ao olhar para essas UIs, eu pensava "quem no mundo iria querer usar isso? Parece horrível".
Outras vezes, havia apenas "algo estranho" que eu simplesmente não conseguia apontar.
Embora eu recebesse designs incríveis e sofisticados da equipe de design, meus aplicativos não pareciam nem 20% tão bons.
Eu estava ciente desse problema e, para corrigi-lo, mergulhei em uma pesquisa na qual me deparei com o conceito de ter um sistema de design que transformasse a maneira como construo aplicativos.
É crucial entender o que é um sistema de design para poder entender por que precisamos dele.
O sistema de design é basicamente uma fonte centralizada de verdade para suas decisões de design e de sua equipe. Diz quais cores usar e onde? Quantos tipos de botões o aplicativo terá? As cartas da sua lista terão sombras? Todas as respostas vêm de um sistema de design.
Aqui estão alguns dos benefícios de ter um sistema de design:
UIs consistentes: Sua interface não terá aquelas lacunas estranhas aqui e ali sem motivo. Ele terá uma aparência uniforme em todos os dispositivos.
Decisões rápidas: Os sistemas de design impõem um certo conjunto de restrições para tornar suas decisões mais fáceis, não mais difíceis. Quanto mais opções você tiver, mais paralisia de análise você encontrará.
Aplicativos escaláveis: À medida que o aplicativo cresce, um sistema de design ajuda a reutilizar componentes em vez de construí-los do zero.
Foco no desenvolvimento: Você não precisa mais se preocupar se o botão deve ser verde ou azul. Em vez disso, você se concentrará no que importa.
Embora existam toneladas de bibliotecas React Native UI por aí, eu uso uma abordagem personalizada, pois tive experiências horríveis com a maioria delas em relação ao desempenho e bugs.
A única biblioteca em que confio para minha abordagem é react-native-size-matters.
Agora, antes de você gritar “o tamanho não importa!”, garanto que sim. Especialmente quando se trata de aplicativos móveis.
Você não quer que seus usuários abram seu aplicativo, vejam um logotipo gigante cobrindo tudo e pensem "O que há de feio..." antes de excluí-los sem sequer tentar, porque seu logotipo escondeu o botão.
É aí que o tamanho do react-native-importa ajuda. Ele torna seus aplicativos responsivos, dimensionando seus componentes para se adequarem ao dispositivo. Portanto, não importa qual dispositivo os usuários tenham, seu logotipo permanecerá exatamente onde você o colocou.
Uma das primeiras coisas que defino são meus tokens de design principais. Esses são os blocos de construção do meu sistema de design. Isso inclui paletas de cores, tipografia, espaçamentos e tamanhos de fonte.
Eu faço isso criando um arquivo theme.ts com o seguinte código:
import {moderateScale} from 'react-native-size-matters'; // after installing custom fonts: export const FontFamily = { bold: 'Poppins-Bold', semibold: 'Poppins-SemiBold', medium: 'Poppins-Medium', regular: 'Poppins-Regular', thin: 'Poppins-Thin', }; const colors = { primary100: '#2E2C5F', primary80: '#524DA0', primary60: '#736DDF', primary40: '#A09BFF', primary20: '#DCDAFF', secondary100: '#484A22', secondary80: '#858945', secondary60: '#D9DF6D', secondary40: '#F8FCA1', secondary20: '#FDFFD4', neutral100: '#131218', neutral90: '#1D1C25', neutral80: '#272631', neutral70: '#343341', neutral60: '#3E3D4D', neutral50: '#53526A', neutral40: '#757494', neutral30: '#9C9AC1', neutral20: '#CBC9EF', neutral10: '#E8E7FF', white: '#fff', black: '#222', error: '#E7002A', success: '#3EC55F', warning: '#FECB2F', info: '#157EFB', }; const theme = { colors, fontSizes: { xxl: moderateScale(32), xl: moderateScale(28), lg: moderateScale(24), md: moderateScale(20), body: moderateScale(17), sm: moderateScale(14), xs: moderateScale(12), xxs: moderateScale(10), xxxs: moderateScale(8), }, spacing: { none: 0, xxs: moderateScale(4), xs: moderateScale(8), md: moderateScale(12), lg: moderateScale(16), xl: moderateScale(20), xxl: moderateScale(24), xxxl: moderateScale(28), }, }; export default theme;
Depois que meus tokens de design estão implementados, defino alguns componentes reutilizáveis, como Caixa, Tipografia e Entrada. Esses componentes aderem aos tokens de design, garantindo consistência em todo o aplicativo.
Por exemplo, aqui está como eu crio o componente Box:
import { View, type ViewProps, type FlexAlignType, type ViewStyle, } from 'react-native'; import theme from '../styles/theme/theme'; export interface IBox extends ViewProps { backgroundColor?: keyof typeof theme.colors; p?: keyof typeof theme.spacing; pv?: keyof typeof theme.spacing; ph?: keyof typeof theme.spacing; pt?: keyof typeof theme.spacing; pb?: keyof typeof theme.spacing; pl?: keyof typeof theme.spacing; pr?: keyof typeof theme.spacing; m?: keyof typeof theme.spacing; mv?: keyof typeof theme.spacing; mh?: keyof typeof theme.spacing; mt?: keyof typeof theme.spacing; mb?: keyof typeof theme.spacing; ml?: keyof typeof theme.spacing; mr?: keyof typeof theme.spacing; gap?: number; flex?: number; flexDirection?: 'row' | 'column' | 'row-reverse' | 'column-reverse'; alignItems?: FlexAlignType; justifyContent?: | 'center' | 'flex-start' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly'; rounded?: boolean; } export default function Box({ backgroundColor, p, pv, ph, pt, pb, pr, pl, m, mv, mh, mt, mb, ml, mr, children, style, flex, alignItems, justifyContent, flexDirection = 'column', rounded = false, gap = undefined, ...rest }: IBox) { const getMargin = () => { const obj: any = {}; if (m) { obj.margin = theme.spacing[m]; return obj; } if (mt) obj.marginTop = mt ? theme.spacing[mt] : 0; if (mb) obj.marginBottom = mb ? theme.spacing[mb] : 0; if (ml) obj.marginLeft = ml ? theme.spacing[ml] : 0; if (mr) obj.marginRight = mr ? theme.spacing[mr] : 0; if (mv) obj.marginVertical = theme.spacing[mv]; if (mh) obj.marginHorizontal = theme.spacing[mh]; return obj; }; const getPadding = () => { const obj: any = {}; if (p) { obj.padding = theme.spacing[p]; return obj; } if (pt) obj.paddingTop = pt ? theme.spacing[pt] : 0; if (pb) obj.paddingBottom = pb ? theme.spacing[pb] : 0; if (pl) obj.paddingLeft = pl ? theme.spacing[pl] : 0; if (pr) obj.paddingRight = pr ? theme.spacing[pr] : 0; if (pv) obj.paddingVertical = theme.spacing[pv]; if (ph) obj.paddingHorizontal = theme.spacing[ph]; return obj; }; const boxStyles: ViewStyle[] = [ { backgroundColor: backgroundColor ? theme.colors[backgroundColor] : undefined, flex, justifyContent, alignItems, flexDirection, borderRadius: rounded ? 10 : 0, gap, }, getMargin(), getPadding(), style, ]; return ({children} ); }
Eu uso este componente Box recém-criado como um substituto do View. Isso me permite estilizá-lo rapidamente por meio de adereços (e dar sugestões se você estiver usando texto digitado), assim:
Aqui está um exemplo de como eu crio o componente Typography que uso em vez do componente Text do React Native:
import React from 'react'; import {Text, type TextProps} from 'react-native'; import theme, {FontFamily} from '../styles/theme/theme'; export interface ITypography extends TextProps { size?: keyof typeof theme.fontSizes; color?: keyof typeof theme.colors; textAlign?: 'center' | 'auto' | 'left' | 'right' | 'justify'; variant?: keyof typeof FontFamily; } export default function Typography({ size, color, textAlign, children, style, variant, ...rest }: ITypography) { return ({children} ); }
Aqui está uma prévia da rapidez com que consigo adicionar estilos ao meu componente de tipografia personalizado:
Em vez de importar o tema repetidamente, torno meu código mais legível criando um gancho useTheme personalizado que chamo em qualquer lugar do aplicativo para adicionar estilos que aderem ao meu tema.
Para fazer isso, uso a API Context do React para passar meu tema no aplicativo.
Eu crio um arquivo ThemeProvider.tsx e dentro dele defino ThemeContext e ThemeProvider para agrupar meu componente de aplicativo dentro dele. Aqui está o código:
import React, {type PropsWithChildren, createContext} from 'react'; import theme from './theme'; export const ThemeContext = createContext(theme); export default function ThemeProvider({children}: PropsWithChildren) { return ({children} ); }
Então, dentro do meu componente App:
export default function App() { return (); }
Agora que todo o meu aplicativo tem acesso ao ThemeContext, crio meu gancho useTheme:
import {useContext} from 'react'; import {ThemeContext} from '../styles/theme/ThemeProvider'; export default function useTheme() { const theme = useContext(ThemeContext); return theme; }
Agora posso acessar meu tema em qualquer lugar chamando o gancho useTheme assim:
const theme = useTheme(); // example usage: theme.colors.primary100; theme.spacing.md; theme.fontSizes.lg;
Para implementar o modo escuro, no arquivo theme.ts, adiciono outra paleta de cores contendo as cores do modo escuro.
export const darkTheme = { // define dark mode colors here keeping the keys same as the light mode only changing the values. }
Então, no ThemeProvider, simplesmente verifico as configurações do usuário e mudo o tema assim:
import {useColorScheme} from 'react-native';
export default function ThemeProvider({children}: PropsWithChildren) {
const isDarkMode = useColorScheme() === 'dark';
return (
{children}
);
}
Seguir essa abordagem estruturada clara trouxe clareza, consistência e estética muito necessárias ao meu aplicativo, ao mesmo tempo que acelerou minha velocidade de desenvolvimento em pelo menos 10x, já que não preciso mais me preocupar com decisões de design.
Eu encorajo você a tentar essa abordagem e me dizer o que vocês acham nos comentários. Talvez melhore um pouco, hein?
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3