Esta página fue traducida automáticamente del portugués y puede contener errores. La versión en portugués es la canónica.
Arquitectura de app
CrossFly no impone arquitectura y no tiene DI ni binding reactivo (ver Estado y flujo de datos). Eso es libertad — y responsabilidad. A continuación, convenciones sugeridas (compatibles, no obligatorias) que escalan. Es el recorte de tarea del Capítulo 32.
Estructura de paquetes sugerida
app/
├─ ui/screens/ → uma tela = uma subclasse de Window
├─ viewmodel/ → estado + lógica de apresentação por tela
├─ data/repository → fachada sobre dev.crossfly.sqlite / rede
└─ service/ → serviços de app (singletons) + service locator
Una pantalla = una Window
public class ClientesScreen extends Window {
private final ClientesVM vm = new ClientesVM(Services.get(ClienteRepository.class));
@Override public void onShow() throws Exception {
super.onShow(); // arma la UI aquí (bounds válidos)
// … conecta UI ↔ vm …
vm.observe(this::render); // re-renderiza cuando el estado cambia
vm.carregar();
}
@Override public void onRemove() { vm.dispose(); super.onRemove(); } // libera recursos
}
ViewModel ligero (sin setState)
Como no hay binding reactivo, el ViewModel guarda estado y expone un observe(Runnable); la View
re-renderiza manualmente:
public class ClientesVM {
private final ClienteRepository repo;
private Runnable onChange;
public List<Cliente> clientes = List.of();
public ClientesVM(ClienteRepository repo) { this.repo = repo; }
public void observe(Runnable r) { this.onChange = r; }
public void carregar() {
new Thread(() -> {
List<Cliente> dados = repo.listar();
Component.onUI(() -> { clientes = dados; if (onChange != null) onChange.run(); });
}).start();
}
public void dispose() { onChange = null; }
}
Repository sobre SQLite
Aísla el acceso a datos en una fachada — la UI nunca habla SQL directo:
public class ClienteRepository {
public List<Cliente> listar() {
try (SqliteDb db = SqliteDb.open("app.db");
SqliteCursor c = db.query("SELECT id, nome FROM cliente")) {
List<Cliente> out = new ArrayList<>();
while (c.next()) out.add(new Cliente(c.getLong(0), c.getString(1)));
return out;
}
}
}
En lugar de DI: un service locator simple
La antigua Registry fue eliminada. Un Map<Class,Object> propio resuelve (o inyección por constructor):
public final class Services {
private static final Map<Class<?>, Object> reg = new HashMap<>();
public static <T> void put(Class<T> k, T v) { reg.put(k, v); }
public static <T> T get(Class<T> k) { return k.cast(reg.get(k)); }
}
// en el bootstrap: Services.put(ClienteRepository.class, new ClienteRepository());
Buenas prácticas
- UI en
onShow(), recursos liberados enonRemove(). - Estado en el ViewModel; las mutaciones vuelven a la paint thread vía
onUI; la View traduce estado→componentes. - El Repository esconde SQLite/red; la pantalla no conoce SQL.
Errores comunes
- Buscar DI/observables de Flutter — no existen; usa service locator +
observe(...)manual. - Poner SQL/HTTP directo en la
Window(acopla y traba la UI). - Guardar estado de app en campos de la
Window(desaparece al navegar).
Ver también
- Concepto: Estado y flujo de datos · Gestión de estado
¿Te resultó útil esta página?¡Gracias por tu opinión!