A classified ads marketplace, not a set of screens

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.

36 app screens 29 API endpoints 11 admin screens 213 tests
The Listaro home feed with featured and nearby ads An ad detail screen with gallery, attributes and the seller
What's inside

Three parts, one product

Most classifieds templates are screens with nothing behind them. You buy 40 beautiful pages, and then you write the server yourself.

Flutter app

  • 36 screens
  • Five-step posting flow
  • 10,326 lines of Dart
  • 111 tests

Laravel API

  • 29 endpoints
  • Sanctum authentication
  • 27 tables with migrations
  • 102 tests

Admin panel

  • 11 screens
  • Category fields
  • Moderation queue
  • No build step

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
The classifieds difference

Fields belong to categories

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.

The posting flow asking for the fields that belong to the chosen category
Negotiation

An offer is a message

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 chat thread with an offer card carrying its amount, accept and decline
Revenue

Four ways for a seller to pay you

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.

The boost packages screen with Bump up, Featured, Urgent and Bundle
Operator side

Eleven screens you actually run it from

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 Listaro admin dashboard with live listings, members, open reports and revenue The category editor, where a category's fields are defined

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

Open the panel  ·  Call the API

The install, unedited

What it takes to put this on your own hosting

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.

Screens

Rendered from the running app

Not drawn, and not mockups — these come straight out of the Flutter app under test.

Honesty

What is not included

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.