---
title: "Guía Base UI UX, Personalidad y Arquitectura Compartida (Angular + Flutter)"
description: "Metodología en 5 pasos para construir apps profesionales con carácter. Define tu ADN visual, UX y una arquitectura compartida sin importar el framework."
canonical: https://arbe.blog/guides/guia-base-ui-ux-personalidad-y-arquitectura-compartida-angular-flutter
published: 2026-06-24
updated: 2026-06-26
language: es
content_type: technical-guide
tags: ["FLUTTER","ANGULAR","PATRONES","DESIGN SYSTEM","CAPAS"]
---

# Guía Base UI UX, Personalidad y Arquitectura Compartida (Angular + Flutter)

> Metodología en 5 pasos para construir apps profesionales con carácter. Define tu ADN visual, UX y una arquitectura compartida sin importar el framework.

> Una metodología en 5 pasos para construir productos que se vean profesionales, tengan carácter propio y compartan la misma arquitectura sin importar el framework.

---

## Cómo leer esta guía

El orden importa. Cada paso depende del anterior, y saltártelos es la razón #1 por la que una app "se ve hecha por defecto". La regla mental es:

**Primero decides *qué* (Paso 1) → luego el *ADN visual* (Paso 2) → cómo *se siente* (Paso 3) → cómo *se comporta* (Paso 4) → y recién al final *cómo lo organizas en código* (Paso 5).**

No abras el editor de código hasta el Paso 5. En serio.

Voy a usar un ejemplo que arrastraré por toda la guía para que sea concreto: **una app para iPad que gestiona facturas de forma rápida.**

---

## Paso 1 — La idea y el alcance (sin diseño ni código)

### 1.1 El propósito en una frase

Escribe **una sola frase** que diga qué hace tu producto y para quién. Si necesitas un "y" para unir dos ideas, tu producto todavía no está enfocado.

> *"Una app para iPad que permite a freelancers crear y enviar facturas en menos de 30 segundos."*

Fíjate que esa frase ya esconde tres cosas valiosas: el **usuario** (freelancers), el **trabajo principal** (crear/enviar factura) y una **promesa** (en 30 segundos). Esa promesa es la que vas a defender en cada decisión de diseño.

### 1.2 Lista de funcionalidades (Features)

Divide la idea en bloques que el usuario necesita para cumplir su trabajo. Cada bloque será una carpeta en tu código. Para cada feature anota su **trabajo** (qué resuelve), no su pantalla.

| Feature | Trabajo del usuario | ¿Imprescindible para v1? |
| --- | --- | --- |
| `auth` | Entrar de forma segura a sus datos | Sí |
| `dashboard` | Ver de un vistazo cuánto le deben | Sí |
| `invoicing` | Crear, ver y enviar facturas | Sí |
| `clients` | Guardar a quién le factura | Quizás (puede ir en v2) |
| `settings` | Configurar su negocio (logo, impuestos) | No (v2) |

**El truco de principiante que cambia todo:** marca qué es v1 y qué no. Una app pequeña pero *terminada* siempre se ve más profesional que una app grande llena de pantallas a medias.

---

## Paso 2 — El sistema de diseño (tus tokens, tu ADN visual)

Los tokens son las variables de tu diseño. Definirlos primero garantiza que, aunque la app sea sencilla, se vea **coherente** — y coherencia es el 80% de lo que el ojo lee como "profesional".

### 2.1 La idea clave que casi nadie te explica: tokens en dos capas

No definas un solo nivel de tokens. Define **dos**:

**Primitivos** — la paleta cruda, sin significado. Solo nombres descriptivos:

```
slate-50:  #F8F9FA
slate-800: #1E293B
white:     #FFFFFF
indigo-600:#4F46E5
```

**Semánticos** — el *rol* que cumple cada color, apuntando a un primitivo:

```
color-background      → slate-50
color-surface         → white
color-primary         → indigo-600
color-text            → slate-800
color-text-muted      → slate-500
color-border          → slate-200
```

¿Por qué molestarte? Porque el día que quieras cambiar de "indigo" a "negro brutalista", tocas **una línea** (`color-primary → black`) y toda la app cambia. Tus botones nunca dicen `#4F46E5`; dicen `color-primary`. Esta separación es lo que hace que un sistema escale.

### 2.2 Los seis grupos de tokens (no solo color)

La mayoría de principiantes define color y tipografía y se detiene. Un sistema profesional define **seis** grupos. Define todos antes de diseñar:

**1. Color (semántico, máximo 4–6 roles)** Background, Surface, Primary, Text, Text-muted, Border. Una regla de oro: **el texto nunca es negro puro** (`#000`) sobre blanco puro — usa un gris muy oscuro (`#1E293B`). El negro puro "vibra" y cansa la vista.

**2. Tipografía (máximo 2 familias)**

- *Display*: con personalidad, para títulos. Úsala con **restricción** (solo títulos grandes).
- *Body*: limpísima y legible, para texto largo y botones (Inter, Roboto, Geist).
- Define también una **escala de tamaños** fija, no inventes tamaños: `xs 12 · sm 14 · base 16 · lg 20 · xl 28 · 2xl 40`.

**3. Espaciado (escala de 4 en 4)** `4 · 8 · 12 · 16 · 24 · 32 · 48 · 64`. Nunca pongas un margen de "23px sobre la marcha". Esta es la regla más fácil de seguir y la que más rápido hace que todo se vea ordenado.

**4. Radios (la decisión de tu *vibe*)**

- `0px` → crudo, brutalista, técnico.
- `8px` → minimalista amigable (lo más seguro para empezar).
- `16px+` → suave, juguetón, consumer. Elige **uno** como base y sé consistente. Mezclar radios al azar es la marca del amateur.

**5. Elevación / sombras** Define 2–3 niveles (`none · sm · md`) en vez de inventar sombras. En estilos brutalistas, en lugar de sombras difusas usa **sombras duras y desplazadas** (`4px 4px 0 #000`), que es parte de su firma visual.

**6. Movimiento (¡sí, las animaciones también son tokens!)** Esto es lo que casi nadie hace y lo que separa una app "viva" de una rígida:

```
duration-fast:  150ms   (hovers, cambios pequeños)
duration-base:  250ms   (transiciones de UI, aparecer/desaparecer)
duration-slow:  400ms   (cambios de pantalla, modales)

easing-standard: cubic-bezier(0.4, 0, 0.2, 1)   (entradas y salidas normales)
easing-spring:   curva con rebote                (para deleite, botones, éxito)
```

Definir esto **antes** evita que cada animación dure un tiempo distinto y se sienta caótica.

### 2.3 La REGLA DE ORO cross-platform

> **Define los tokens UNA sola vez, en un formato neutral (JSON), y genera desde ahí el tema de cada plataforma.** Una única fuente de verdad.

```
📄 tokens.json   ←  la ÚNICA fuente de verdad (color, type, spacing, motion…)
       │
       ├──►  theme.dart   (para Flutter)
       └──►  theme.ts      (para Angular)
```

La herramienta estándar para esto se llama **Style Dictionary** (de Amazon): lee tu `tokens.json` y escupe automáticamente las variables para Dart, para CSS/SCSS, para TypeScript, etc. Así, cuando cambias un color, **ambas apps cambian a la vez** y nunca se desincronizan.

Cuando empiezas, puedes hacerlo a mano (un `theme.dart` y un `theme.ts` que copien los mismos valores), pero piensa siempre en `tokens.json` como el "original" y los demás como "copias generadas". Esa mentalidad es la que hace el diseño verdaderamente compartido.

---

## Paso 3 — Personalidad y micro-animaciones (lo que hace que se sienta vivo)

Aquí está tu objetivo #2. Una UI sin movimiento se siente como una foto; con el movimiento correcto, se siente como un objeto físico que responde. Pero **animar por animar arruina una app** (y delata que fue generada sin criterio). Cada animación debe tener un *trabajo*.

### 3.1 Las cuatro razones legítimas para animar

1. **Feedback** — confirmar que el sistema te oyó. Un botón que se hunde 2px al presionarlo.
2. **Continuidad** — explicar de dónde viene y a dónde va algo. Un modal que crece desde el botón que lo abrió, en vez de aparecer de golpe.
3. **Jerarquía / atención** — guiar el ojo. El total de la factura que cuenta hacia arriba (`$0 → $1,250`) cuando se calcula.
4. **Deleite** — el toque de personalidad. Un *check* que se dibuja con un pequeño rebote al marcar "factura pagada".

Si una animación no cae en ninguna de estas cuatro, bórrala.

### 3.2 La regla del "gasta tu osadía en un solo lugar"

No hagas que *todo* tenga personalidad — eso es ruido. Elige **un elemento firma**: el momento que tu usuario recordará. En la app de facturas podría ser la animación de "factura enviada" (el papel que se dobla y sale volando). Todo lo demás: discreto, rápido, casi invisible.

### 3.3 Catálogo de micro-interacciones para empezar

| Elemento | Qué pasa al interactuar | Token de tiempo |
| --- | --- | --- |
| Botón | Baja de escala a `0.97` + se aclara al presionar | `fast` |
| Card / tarjeta | Sube 2–4px y gana sombra `md` al hacer hover | `fast` |
| Aparición lista | Cada item entra con *fade + slide-up*, escalonado (50ms) | `base` |
| Cambio de pantalla | Desliza/funde con `easing-standard` | `slow` |
| Éxito (enviado) | Check animado con `easing-spring` (rebote) | `base` |
| Error | *Shake* horizontal corto del campo | `fast` |
| Carga | *Skeleton* que pulsa (no un spinner genérico, ver Paso 4) | loop |

### 3.4 Cómo se traduce a cada framework (misma idea, distinta herramienta)

| Concepto | Flutter | Angular |
| --- | --- | --- |
| Animación implícita | `AnimatedContainer`, `AnimatedOpacity` | Transiciones CSS sobre cambio de clase |
| Animación con control | `AnimationController` + `Tween` | Angular Animations (`trigger`, `state`) |
| Rebote / spring | `CurvedAnimation` con `Curves.elasticOut` | `cubic-bezier` con overshoot / `@angular/animations` |
| Entrada escalonada | `flutter_animate` (paquete) | `@stagger()` en Angular Animations |
| Respetar reduce-motion | `MediaQuery.disableAnimations` | `prefers-reduced-motion` media query |

> **Piso de calidad obligatorio:** siempre respeta `prefers-reduced-motion`. Hay personas a las que el movimiento les causa mareo. Si el usuario lo desactivó en su sistema, tus animaciones deben reducirse a un simple fundido o desaparecer.

---

## Paso 4 — UX: cómo se comporta (no solo cómo se ve)

Tu borrador era fuerte en UI; esta capa es la UX que le faltaba. Es lo que hace que la app se sienta *terminada* en vez de rota.

### 4.1 Los cuatro estados de TODA pantalla

El error de principiante #1: diseñar solo el estado "lleno de datos bonitos". Toda pantalla que muestre datos tiene en realidad **cuatro estados**, y debes diseñar los cuatro:

1. **Vacío** — el usuario aún no tiene datos. *No* es una pantalla en blanco: es una invitación a actuar. ("Aún no tienes facturas. Crea la primera →").
2. **Cargando** — usa *skeletons* (siluetas grises del contenido que viene), no un spinner centrado. El skeleton hace que la espera se sienta más corta y predice el layout.
3. **Error** — explica *qué* pasó y *cómo* arreglarlo, en la voz de la app, sin disculpas vagas. ("No pudimos cargar tus facturas. Revisa tu conexión y reintenta.")
4. **Con datos** — el estado feliz.

Diseña siempre vacío → cargando → error → datos. Esta sola disciplina te pone por encima del 90% de las apps de principiante.

### 4.2 El texto es parte del diseño

Las palabras de tu interfaz son material de diseño, no decoración. Tres reglas que rinden mucho:

- **Nombra las cosas por lo que el usuario controla**, no por cómo está hecho el sistema. El usuario "envía una factura", no "ejecuta un POST".
- **El botón dice exactamente qué pasa.** "Enviar factura", no "Aceptar". Y el nombre se mantiene por todo el flujo: si el botón dice "Enviar", el mensaje de éxito dice "Factura enviada".
- **Voz activa, frases cortas, sin relleno.** "Guardar cambios" pesa más que "Proceder a guardar".

### 4.3 El piso de accesibilidad (no negociable)

No es opcional ni "para después". Es barato si lo haces desde el inicio:

- **Contraste:** el texto debe tener contraste suficiente sobre su fondo (mínimo 4.5:1 para texto normal). Hay extensiones que lo miden en segundos.
- **Áreas táctiles:** mínimo `44×44px` en lo que se toca (especialmente en iPad).
- **Foco visible:** quien navega con teclado debe ver dónde está parado.
- **No solo color:** no comuniques "error" únicamente con rojo; añade un ícono o texto (hay personas daltónicas).

---

## Paso 5 — Arquitectura compartida: Feature-Driven (recién aquí tocas código)

La gran idea: en lugar de agrupar por **tipo de archivo** (todos los botones juntos, todas las pantallas juntas), agrupas por **funcionalidad (feature)**. Cuando trabajas en "facturación", todo lo de facturación está en una sola carpeta.

> **Lo que se comparte entre Angular y Flutter NO es el código — es la *estructura* y la *filosofía de capas*.** El mismo plano, materiales distintos.

### 5.1 La estructura de carpetas (idéntica en ambos frameworks)

```
📁 tu_proyecto/
├── 📁 core/                  ← lo global: lo que usa TODA la app
│   ├── 📄 theme              # tus tokens (theme.dart / theme.ts)
│   ├── 📄 api_service        # conexión a la base de datos / backend
│   └── 📁 utils/             # helpers, constantes, formateadores
│
├── 📁 shared/                ← componentes REUTILIZABLES sin lógica de negocio
│   └── 📁 ui/                # AppButton, AppTextField, AppCard… (usan los tokens)
│
└── 📁 features/              ← ¡la magia del Feature-Driven!
    ├── 📁 auth/
    │   ├── 📁 presentation/  # pantallas + widgets SOLO de auth
    │   ├── 📁 domain/        # las reglas: "qué es un usuario válido"
    │   └── 📁 data/          # de dónde vienen los datos (API, repos)
    │
    ├── 📁 dashboard/
    │   ├── 📁 presentation/  # pantalla + tarjetas resumen, gráficos
    │   ├── 📁 domain/
    │   └── 📁 data/
    │
    └── 📁 invoicing/
        ├── 📁 presentation/
        ├── 📁 domain/
        └── 📁 data/
```

### 5.2 Por qué tres capas dentro de cada feature

Esto es lo que sube tu objetivo #3 (la "mejor arquitectura"). Cada feature se parte en tres capas con una regla de dependencia: **presentation → domain → data**, nunca al revés.

- `presentation` — lo que el usuario ve y toca (pantallas, widgets/componentes, estado de UI). No sabe *de dónde* salen los datos; solo los pide.
- `domain` — el corazón limpio: las reglas de negocio y los modelos. "Una factura tiene fecha, cliente y total"; "una factura no puede enviarse sin cliente". Esta capa **no depende de Flutter ni de Angular ni del internet** — es Dart/TypeScript puro. Por eso es la más fácil de portar y la más fácil de testear.
- `data` — el plomero: habla con la API o la base de datos y entrega modelos limpios al domain. Si mañana cambias de backend, solo tocas esta capa.

La ventaja: puedes cambiar el diseño (presentation) sin tocar las reglas (domain), o cambiar el backend (data) sin tocar la pantalla. Cada parte cambia por su cuenta.

### 5.3 Mapa de conceptos Flutter ↔ Angular

Misma arquitectura, distinto vocabulario. Esta tabla es tu traductor:

| Concepto | Flutter | Angular |
| --- | --- | --- |
| Bloque de UI | Widget | Component |
| Pantalla | Screen / Page (un Widget) | Routed Component |
| Estilos globales | `ThemeData` | CSS variables / theme service |
| Estado / lógica de UI | Riverpod, BLoC, Provider | Services + Signals / RxJS |
| Inyección de dependencias | `get_it`, Riverpod | Inyector nativo de Angular |
| Llamadas HTTP | `dio` / `http` | `HttpClient` |
| Modelo de datos | `class Invoice {...}` | `interface Invoice {...}` |
| Navegación | `go_router` | Angular Router |

> Decide tu manejador de estado y mantenlo. En Flutter, **Riverpod** es una opción moderna y recomendable para empezar. En Angular, **Signals** (lo más nuevo) o **Services + RxJS**.

### 5.4 La regla que mantiene todo limpio

> Un componente en `shared/ui` (como `AppButton`) **solo** puede usar tokens del `core/theme`. Nunca un color "a mano". Un widget dentro de `features/invoicing` **solo** vive ahí; si lo necesitas en dos features, *entonces* lo subes a `shared/`. No antes.

Esto evita el caos donde "todo usa todo" y nadie sabe qué rompe qué.

---

## El orden de ataque (tu checklist)

Cuando arranques un proyecto nuevo, sigue exactamente este orden:

 1. **\[ \]** Escribe el propósito en una frase.
 2. **\[ \]** Lista las features y marca cuáles son v1.
 3. **\[ \]** Crea `tokens.json` con los 6 grupos (color, type, spacing, radius, elevation, motion).
 4. **\[ \]** Elige tu *elemento firma* (la animación/momento que te van a recordar).
 5. **\[ \]** Para cada pantalla, diseña los 4 estados (vacío, cargando, error, datos).
 6. **\[ \]** Escribe el texto de botones y errores en voz activa.
 7. **\[ \]** Crea la estructura de carpetas `core / shared / features`.
 8. **\[ \]** Construye primero los componentes `shared/ui` (botón, input, card) usando solo tokens.
 9. **\[ \]** Construye feature por feature, empezando por la más imprescindible (`auth` o la principal).
10. **\[ \]** Revisa accesibilidad y `reduce-motion` antes de dar por terminada cada pantalla.

---

## Errores comunes de principiante (y su antídoto)

- **Inventar márgenes y tamaños sobre la marcha.** → Usa siempre la escala de espaciado y tipografía.
- **Diseñar solo el estado "con datos".** → Diseña los 4 estados, siempre.
- **Negro puro sobre blanco puro.** → Gris muy oscuro sobre blanco hueso.
- **Mezclar radios y sombras al azar.** → Un radio base, 2–3 niveles de sombra, y basta.
- **Animar todo.** → Una firma con personalidad; el resto, discreto y rápido.
- **Agrupar por tipo de archivo.** → Agrupa por feature.
- **Copiar colores** `#hex` **en cada botón.** → Apunta siempre al token semántico.
- **Dejar accesibilidad "para el final".** → Es barata al inicio, carísima al final.

---

### Una última idea

La elegancia no es agregar; es **ejecutar bien una visión simple**. Una app con 4 pantallas, 6 colores, 2 fuentes y una sola animación memorable — toda coherente — se ve más profesional que una con 20 pantallas y todo distinto. Empieza pequeño, termínalo bien, y crece desde ahí.

---
Fuente canónica: https://arbe.blog/guides/guia-base-ui-ux-personalidad-y-arquitectura-compartida-angular-flutter
