Listaro ships all three parts: the Flutter app buyers and sellers use, the Laravel API behind it, and the web admin panel you run the marketplace from. They were built together, and the API the app talks to is the same one in the download.
Most classifieds templates are screens with nothing behind them. You buy 40 beautiful pages, and then you write the server yourself.
It runs the moment you unzip it. The app opens on bundled sample data — real photos, real categories, real conversations — so every screen works before any server exists. One flag on the build command points it at the API instead:
flutter run --dart-define=LISTARO_API=https://your-server.test/api/v1
A marketplace is not a shop. A car needs a year, a mileage and a gearbox; a flat needs rooms, floor area and a floor number; a phone needs storage and condition. A product catalogue cannot express that, and hardcoding it means an app release every time you add a category.
You define those fields in the admin panel. Five types ship — text, number, select, multi-select and boolean. Mark one filterable and it appears in three places at once: the posting form, the ad detail, and the search filters. No app release, no migration, no deploy.
Every classifieds app has a chat. What sellers actually do in that chat is haggle, and a plain message thread makes that unreadable — nobody can tell what was agreed.
In Listaro an offer sits in the thread where it was made. It carries its own amount, it has an accept and a decline that both sides can see, and it expires on its own. The conversation stays a conversation, and the negotiation stays legible.
A marketplace with no way to charge is a hobby. Listaro ships the standard classifieds model, working end to end: Bump up, Featured, Urgent and a Bundle, each with its own price, duration and benefit list.
Behind them is a wallet with a real ledger — every credit and debit is a row, balances are derived, never edited — and money is held in integer minor units everywhere, from the database to the API to the app. No float arithmetic anywhere near a price.
Prices, durations and benefits are rows in a table you edit in the panel. Repricing your marketplace is a form, not a release.
Moderation queue, category fields, users, reports, payments, boost packages and settings. The panel is plain Blade and one hand-written stylesheet — no Vite, no Tailwind build, no npm manifest anywhere in the download, because the person deploying this is on shared hosting where there is no Node and often no shell.
The panel above is live. The demo account is printed on the login page and fills itself with one click.
demo@listaro.test · ListaroAdmin!26
Two minutes forty, no narration, captions on screen. Every frame is the archive you download, installed with the documented commands on a plain Ubuntu host — nginx, PHP 8.3 and MySQL 8, the stack your host already runs. Nothing here is reenacted.
It opens with node -v answering “command not found”,
and no Node is ever installed: the panel has no build step, so there is nothing
to compile on your server. It ends signed in to the panel, with the API serving the
listings you just watched being seeded.
Not drawn, and not mockups — these come straight out of the Flutter app under test.
Said here rather than left to be found after buying.
| No live payment gateway | The wallet, the ledger and buying a boost with wallet credit all work
end to end. What is missing is the gateway that puts money
into the wallet — WalletService::credit() is
where your Stripe or PayPal call goes. |
|---|---|
| No map tiles | Location, radius search and the location picker all work. The map screen plots real ads on a drawn street grid rather than fetched tiles, so the app needs no key and no billing account to run. |
| No push delivery | Notifications are stored, listed and counted. Wiring FCM is your key and your certificate. |
| No SMS | The phone-verification screens are built — code entry, resend timer, the fraud warning — but nothing sends an SMS, and there is no OTP endpoint in the API. |
Each has one documented place to change. And in the app itself, a control that depends on one of these says what is missing rather than doing nothing when you tap it.