The Flutter app for the service 7M Cubans get online through

Cuba's national telecom needed one mobile surface for everything a subscriber touches — account, internet quota, captive portal auth. Built the Flutter app end-to-end against the legacy Java portals exactly as they were, with no backend rewrite to lean on.

Client
Etecsa (Cuban national telecom)
Role
Sole Mobile Engineer · Backend evaluation
Year
2024
Timeline
Multi-quarter mobile build + a scoped backend evaluation
Flutter Dart Java Spring Boot Jakarta EE PostgreSQL Docker

Problem

Most internet access in Cuba happens through Etecsa’s Nauta service: you authenticate against a captive portal to come online, and you manage your account, plan and balance somewhere else entirely. For years that meant desktop browser flows that didn’t share state — juggling tabs, copy-pasting credentials, and squinting at a portal page to work out how much connection time you had left.

The two systems behind those flows are nothing alike. One is a modern JSON API. The other is a legacy Java servlet portal that answers in HTML and was only ever designed for a browser to talk to. The app had to land on both exactly as they were, with no backend change to lean on.

The mobile surface

I built the Nauta mobile app end-to-end in Flutter, as the sole mobile developer. It ships as two apps inside one binary, chosen at an entry splash, because they are genuinely different products talking to genuinely different systems:

The Portal side is deliberately a mirror of Etecsa’s public web portal at nauta.cu — same API, same operations, same account model, rebuilt as a native mobile product. I didn’t build that web portal; a teammate owned it and we worked the same backend contract from opposite ends. My job was parity: everything a subscriber could do in a browser had to work on a phone, without the browser.

Decisions worth calling out:

Loading diagram…
Two apps in one binary. The Portal side speaks JSON; the Navegación side reimplements a browser session against a legacy servlet portal — with the countdown running outside the UI entirely.

Underneath, each feature is split domain / data / presentation with its own use cases and repositories, Riverpod for state, and typed failures via Either instead of exceptions thrown across layers. It builds for Android, Windows and Linux from the same codebase.

A note on the backend

Etecsa’s internal operational modules run on a legacy Javax stack. Separately from the app, I was assigned one of those modules to answer a scoped question: would Spring Boot actually be better? I ported that single module and drove both implementations head-to-head with SoapUI under comparable load — the Spring Boot port came out roughly 3× faster, and I wrote up the numbers as evidence for the platform decision. My scope ended there. It’s a measurement I own, not a migration I shipped.

Outcome

The app collapsed two browser flows into one product: get online, see exactly how much time is left, and manage the account without ever opening a browser. It shipped against both backends exactly as they were — and that’s the constraint that shaped every decision in it. When you can’t change the system underneath, the engineering is absorbing its shape so the user never has to.

Technologies used

Flutter Dart Java Spring Boot Jakarta EE PostgreSQL Docker
Visuals

Screens

Keep exploring

More work

Linkeum Wallet: a multi-currency fintech wallet in 30 days
Fintech · 30 days · 2 engineers

Linkeum Wallet: a multi-currency fintech wallet in 30 days

Shipped a production fintech wallet in 30 days with a 2-engineer team — multi-currency, KYC, 2FA, real-time notifications. Built on Nest.js + Next.js Server Actions over a Turborepo monorepo.

Nest.js Next.js Turborepo PostgreSQL
Read the case study
A fuel and cooking-gas marketplace for Cuba, with QR pickup
Fuel & Gas · Web + Android · External integration

A fuel and cooking-gas marketplace for Cuba, with QR pickup

Muévete y Cocina turns the daily scramble for fuel and cooking gas in Cuba into an order-ahead flow: browse nearby service centers and live prices, reserve litres or a gas cylinder, pay, and pick up against a QR. Since the team white-labelled it, the same codebase reaches customers under two selling brands, Mercaful and Gasolero. I proposed and built its offline-first Flutter courier app end to end, shipped heavily on the customer web app alongside the company's frontend lead and a designer, and over time took my work into the backend too: delivery evidence, access control, and per-brand legal documents.

Flutter React Router Vite TypeScript
Read the case study
AI News Intelligence — n8n agent over FastAPI + Redis + SvelteKit
Side project · n8n · Agent · RAG

AI News Intelligence — n8n agent over FastAPI + Redis + SvelteKit

Personal side project: an AI agent that orchestrates news data via n8n, serves it through a FastAPI + Redis cache layer, and talks to users through a SvelteKit chat UI. Whole stack containerized with Docker Compose for one-command deploy.

FastAPI Python Redis Docker
Read the case study