As views personalizadas são a base da interface única de qualquer aplicativo móvel. As views personalizadas em aplicativos móveis permitem criar gráficos, animações e elementos próprios não disponíveis em componentes padrão. De acordo com Apple Developer, 2025, o drawRect continua sendo o método principal de desenho personalizado no desenvolvimento móvel iOS. No Android, o onDraw com Canvas desempenha o mesmo papel, e no Flutter — o CustomPainter.
Principais pontos
Views personalizadas são componentes de interface definidos pelo usuário que sobrescrevem os métodos padrão de desenho e medição para criar gráficos únicos. Gráficos, animações, diagramas, elementos de chat personalizados e máscaras AR — são cenários típicos de uso de views personalizadas em aplicativos móveis. Os componentes de UI padrão (Button, TextView, Label) cobrem 80% das tarefas, mas os 20% restantes exigem controle total sobre o desenho no desenvolvimento móvel. O desenho no desenvolvimento móvel é uma habilidade que diferencia um desenvolvedor júnior de um sênior.
Views personalizadas em aplicativos móveis são necessárias para gráficos 2D únicos, animações complexas, gráficos estatísticos e visualização de dados. Se o UIKit padrão não fornece o elemento necessário — cria-se uma view personalizada. Em aplicativos móveis, views personalizadas são usadas para barras de progresso, botões personalizados, preenchimentos gradientes, ícones animados e telas para desenho com o dedo. É importante não abusar — para estilos simples, basta configurar os componentes padrão através da API de aparência.
No iOS, views personalizadas são criadas herdando de UIView e sobrescrevendo o método drawRect. drawRect é chamado na primeira renderização e após cada chamada setNeedsDisplay(). Dentro do drawRect, o desenvolvedor obtém um CGContext via UIGraphicsGetCurrentContext() — uma tela para linhas, formas, gradientes e texto. Core Graphics (Quartz 2D) é um motor de renderização de baixo nível na CPU que dá controle total sobre cada pixel. UIBezierPath é um wrapper conveniente para criar caminhos com sintaxe concisa.
class CircleView: UIView {
private var fillColor: UIColor = .systemBlue
override func drawRect(_ rect: CGRect) {
guard let ctx = UIGraphicsGetCurrentContext() else { return }
ctx.setFillColor(fillColor.cgColor)
ctx.addEllipse(in: bounds.insetBy(dx: 4, dy: 4))
ctx.drawPath(using: .fill)
}
func updateColor(_ newColor: UIColor) {
fillColor = newColor
setNeedsDisplay()
}
}
Core Graphics fornece CGContext — uma tela para todas as operações de desenho. CGGradient cria gradientes lineares e radiais com suporte a CGColorSpace (sRGB, Display P3). As transformações são realizadas via CGAffineTransform — uma matriz 3x3 para rotação, escala e translação. CGContextTranslateCTM, CGContextRotateCTM e CGContextScaleCTM modificam o sistema de coordenadas. Core Graphics funciona na CPU, então gráficos complexos podem sobrecarregar o processador — use Metal para aceleração GPU.
UIBezierPath é um wrapper orientado a objetos sobre CGPath. O método addArc(withCenter:radius:startAngle:endAngle:clockwise:) cria arcos e círculos, addCurve cria curvas Bézier. Os caminhos suportam operações de união, interseção e subtração via addClip. UIBezierPath é conveniente para máscaras personalizadas (maskView), caminhos de recorte e formas complexas — estrelas, corações, ícones personalizados.
No Android, uma view personalizada estende View com onDraw(Canvas) sobrescrito. Canvas contém todos os métodos de desenho no desenvolvimento móvel: drawCircle, drawRect, drawLine, drawText, drawBitmap, drawPath e drawRoundRect. Paint configura os parâmetros — cor, espessura da linha, estilo (FILL, STROKE, FILL_AND_STROKE), suavização (isAntiAlias) e tamanho do texto. onDraw é chamado a cada invalidate() e não deve conter operações pesadas — todos os cálculos são armazenados em cache.
class CustomCircleView(context: Context, attrs: AttributeSet?)
: View(context, attrs) {
private val paint = Paint().apply {
style = Paint.Style.FILL
color = Color.BLUE
isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val cx = width / 2f
val cy = height / 2f
val radius = minOf(cx, cy) - 4f
canvas.drawCircle(cx, cy, radius, paint)
}
}
Path no Android é o equivalente ao UIBezierPath: addCircle, addRect, addOval, lineTo, cubicTo e quadTo. PathMeasure calcula o comprimento do caminho e as coordenadas em qualquer ponto — útil para animar o movimento de um objeto ao longo de um caminho. Para animações, use ValueAnimator com invalidate() no callback. O animador pode mudar cor, raio, posição — qualquer propriedade que afete o desenho. Views personalizadas no Android também suportam StateListDrawable e animated-vector para animação via XML.
Gradientes no Android são definidos via Shader — LinearGradient, RadialGradient e SweepGradient. Shader é atribuído ao Paint via paint.setShader(shader). LinearGradient gera um gradiente linear entre dois pontos com posições de cor opcionais. RadialGradient — radial do centro para as bordas. SweepGradient — cônico em torno de um ponto central. Shader.TileMode (CLAMP, REPEAT, MIRROR) define o comportamento além dos limites da área.
Flutter usa CustomPainter — uma classe com os métodos paint(Canvas canvas, Size size) e shouldRepaint. Canvas no Flutter fornece os mesmos métodos que no Android: drawCircle, drawRect, drawLine, drawPath, drawParagraph e drawImage. Paint no Flutter configura cor, espessura, estilo e suavização. Flutter não usa drawRect ou onDraw — todo o desenho é feito via CustomPainter, incorporado no widget CustomPaint.
class CirclePainter extends CustomPainter {
final Color color;
CirclePainter({required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color
..style = PaintingStyle.fill
..isAntiAlias = true;
final radius = size.shortestSide / 2 - 4;
canvas.drawCircle(size.center(Offset.zero), radius, paint);
}
@override
bool shouldRepaint(CirclePainter oldDelegate) =>
oldDelegate.color != color;
}
shouldRepaint determina se o CustomPainter precisa redesenhar. Retorne true apenas quando os dados que afetam a renderização mudarem — cor, tamanho, dados do gráfico. RepaintBoundary isola o redesenho em uma camada separada — use para widgets que raramente mudam. Flutter não tem setNeedsDisplay ou invalidate — o redesenho é acionado via setState ou ValueNotifier com escuta de alterações.
Flutter suporta LinearGradient, RadialGradient e SweepGradient — os mesmos tipos que no Android. Gradient cria um Shader para Paint via createShader(Rect bounds). As transformações são realizadas via canvas.rotate, canvas.scale, canvas.translate e canvas.skew. Flutter usa uma matriz de transformação 4x4 (Matrix4) para o widget Transform. Animações de desenho no Flutter são feitas com AnimationController junto com setState para redesenhar o CustomPainter.
A medição correta de views personalizadas é a base do comportamento previsível da interface. No iOS, o tamanho da View é determinado via sizeThatFits e intrinsicContentSize. sizeThatFits retorna o tamanho ideal para as restrições dadas. intrinsicContentSize — o tamanho natural do conteúdo sem restrições externas. No Android, a medição é implementada no onMeasure com parâmetros MeasureSpec (UNSPECIFIED, EXACTLY, AT_MOST). No Flutter, o tamanho é passado para paint(Canvas, Size) e pode ser calculado via computeDryLayout ou LayoutBuilder.
intrinsicContentSize retorna um CGSize — o tamanho natural da View baseado em seu conteúdo. setContentHuggingPriority e setContentCompressionResistancePriority controlam o comportamento durante alongamento e compressão. sizeThatFits é chamado pelo sistema para calcular o tamanho no Auto Layout. Para views personalizadas, sobrescreva intrinsicContentSize se seu componente tiver um tamanho natural — por exemplo, um círculo deve ser sempre 100x100.
onMeasure aceita widthMeasureSpec e heightMeasureSpec. MeasureSpec contém modo e tamanho: UNSPECIFIED (sem restrições, a View escolhe seu tamanho), EXACTLY (tamanho exato do pai), AT_MOST (tamanho máximo). setMeasuredDimension(width, height) salva o resultado da medição. Ao alterar dados, chame requestLayout() para remensurar. ViewGroups aninhados exigem sobrescrita de onLayout para organizar os elementos filhos.
| Plataforma | Método de medição | Chamada na alteração | Retorno |
|---|---|---|---|
| iOS | sizeThatFits / intrinsicContentSize | setNeedsLayout / invalidateIntrinsicContentSize | CGSize |
| Android | onMeasure / setMeasuredDimension | requestLayout / invalidate | void (salva tamanho) |
| Flutter | computeDryLayout | setState / LayoutBuilder | Size |
computeDryLayout é um método RenderObject para calcular o tamanho sem um ciclo completo de layout. LayoutBuilder.getSize — uma alternativa para obter o espaço disponível no método build. No Flutter, o tamanho da tela é passado para paint como Size e não requer medição separada — CustomPainter desenha dentro dos limites passados. No entanto, para RenderObjects personalizados, é necessário sobrescrever performLayout e computeDryLayout.
Perguntas frequentes
drawRect é um método UIView para desenho personalizado via Core Graphics. É chamado na primeira renderização da View e após cada chamada setNeedsDisplay(). Use CGContext dentro do drawRect para linhas, formas e texto.
Crie uma classe estendendo View, sobrescreva onDraw(Canvas canvas) e use a API Canvas: drawCircle, drawRect, drawPath. Paint configura cor, espessura e estilo. Para atualizar, chame invalidate().
CustomPainter é uma classe Flutter para desenho personalizado no Canvas. Implemente os métodos paint(Canvas, Size) e shouldRepaint. É colocado através do widget CustomPaint. Flutter não tem drawRect — todo desenho é feito via CustomPainter.
No iOS use sizeThatFits e intrinsicContentSize. No Android — onMeasure com MeasureSpec. No Flutter, o tamanho é passado para paint(Canvas, Size) ou calculado via computeDryLayout. Cada plataforma usa seu próprio método de medição.
Views personalizadas são necessárias para gráficos únicos, animações, gráficos e elementos que não podem ser implementados com UIKit ou View padrão. Para estilos simples, basta configurar os componentes padrão — não complique a arquitetura desnecessariamente.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.