Restaurant App

Sellino Food — the kitchen board, the menu and the floor in your pocket
Version: 1.0.0 (Stable)
Tech Stack: Flutter · Dart · REST API
License: Envato Market – Regular / Extended

Developed By: BugBuild Labs

Introduction

The Restaurant App is what a kitchen runs on: accept orders with a prep time, cook, mark ready, 86 a dish that ran out, open and close the outlet, and — with the Restaurant module on — work the floor and the running checks.

One Flutter codebase for Android and iOS, on the same /api/v10 backend as the panel. A restaurant sees only its own data, enforced on the server, not by hiding buttons.

Everything here also exists in the web restaurant panel. The app is for the pass, where nobody has a laptop.

Kitchen boardassets/images/sellino-food-app/rapp_kitchen.jpg
Floor mapassets/images/sellino-food-app/rapp_floor.jpg

Requirements

  • Flutter SDK (stable channel) with Dart — flutter doctor should pass.
  • Android Studio (Android SDK 21+) and/or Xcode for iOS builds (macOS only).
  • A running Sellino Food backend reachable over HTTPS — set up first via Quick Start.
  • An approved staff login on that backend (cashier, branch manager or admin).
  • For push notifications: a Firebase project — see Common Setup.

Package & Source Code

The download's Main_Files/RestaurantAppSourceCode/ folder holds the complete Flutter project (next to WebSourceCode and the other two app folders). Open it in Android Studio or VS Code and run flutter pub get.

  • lib/core/ — environment config, API client, theme, shared widgets and controllers.
  • lib/features/ — one folder per screen area: kitchen (board, accept/ready, menu availability, add-on groups, outlets), restaurant (floor, check, KDS), orders, products, delivery, payouts, wallet, reports, chat, notifications, settings.
  • assets/branding/ — app icon sources used by the launcher-icon generator.

App Configuration

  1. Open the RestaurantAppSourceCode folder in your IDE and run flutter pub get.
  2. Set the API base URL in lib/core/env/prod_env.dart (release builds) or lib/core/env/dev_env.dart (debug builds) — image URLs, websocket scheme and broadcasting auth URL are derived from it automatically.
  3. Paste the App API key (kAppApiKey) from Admin → Settings → API Security in the same file. Wrong or missing key → 401 on every screen.
  4. If you use real-time features, set kReverbAppKey to the backend's effective Reverb key (Settings → Realtime, else REVERB_APP_KEY from .env).
  5. Configure push notification keys if used — see Common Setup.
// lib/core/env/prod_env.dart   → used by RELEASE builds
class ProdEnv implements Env {
  static const String kBaseUrl      = 'https://yourdomain.com/api/v10';  // your API root
  static const String kAppApiKey    = 'PASTE-KEY-FROM-ADMIN-PANEL';      // Settings → API Security
  static const String kReverbAppKey = 'sellino-app-key';                 // backend .env REVERB_APP_KEY
}

// lib/core/env/dev_env.dart    → used by DEBUG / PROFILE builds (your local backend)
// imageBaseUrl, reverbScheme and broadcastingAuthUrl are all derived from kBaseUrl.

Tip: always use HTTPS — plain HTTP is blocked by default on Android/iOS. Built-in guard: a release build refuses to start if the key is still empty or the URL still points at a dev host (localhost, .test…), so a forgotten swap fails on your desk, not at the counter.

Never regenerate the App API key after release unless it leaked — installed apps stop working until users update. See Web → Security → App API Key.

App Name & Icon

  • Name: change the display name in android/app/src/main/AndroidManifest.xml (android:label) and iOS Info.plist (CFBundleDisplayName).
  • Package / bundle id: set your own unique id (Android applicationId in android/app/build.gradle; iOS bundle identifier in Xcode) before publishing.
  • Icon: replace the sources in assets/branding/ and regenerate launcher icons (flutter_launcher_icons).

Build, Signing & Release

Before either platform: confirm the release build points at your production backend — lib/core/env/prod_env.dart. The app's built-in guard throws on first launch if the key is empty or the URL is still a dev host, so a forgotten swap fails on your desk.

Android — release signing

1. Generate a keystore. Run this once and keep the file safe — Google Play ties your app to it forever; lose it and you cannot publish updates under the same listing.

keytool -genkey -v -keystore ~/sellino-food-release.jks \
  -keyalg RSA -keysize 2048 -validity 10000 -alias sellino-food

# keytool ships with the JDK. On Windows it is:
#   "%JAVA_HOME%\bin\keytool" -genkey -v -keystore %USERPROFILE%\sellino-food-release.jks ^
#     -keyalg RSA -keysize 2048 -validity 10000 -alias sellino-food

It asks for a keystore password, a key password and your name/organisation details. Note both passwords down — the next step needs them.

2. Create android/key.properties (same folder as android/app/), with exactly these four keys:

storePassword=your_keystore_password
keyPassword=your_key_password
keyAlias=sellino-food
storeFile=/Users/you/sellino-food-release.jks

# storeFile — absolute path, or a path relative to the android/ folder.
# Windows: use forward slashes, e.g. C:/Users/you/sellino-food-release.jks

Never commit key.properties or the .jks file to source control, and never ship them to anyone. They are the identity of your published app.

3. The Gradle wiring is already done. android/app/build.gradle.kts reads that file and applies it — nothing to edit, shown here so you know what happens:

// Loads key.properties if the buyer created it
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}

signingConfigs {
    create("release") {
        if (keystorePropertiesFile.exists()) {
            keyAlias      = keystoreProperties["keyAlias"] as String?
            keyPassword   = keystoreProperties["keyPassword"] as String?
            storeFile     = (keystoreProperties["storeFile"] as String?)?.let { file(it) }
            storePassword = keystoreProperties["storePassword"] as String?
        }
    }
}

buildTypes {
    release {
        // Your keystore when key.properties exists; debug signing otherwise,
        // so `flutter run --release` still works before you set one up.
        signingConfig = if (keystorePropertiesFile.exists())
            signingConfigs.getByName("release")
        else
            signingConfigs.getByName("debug")
    }
}

Play Store builds must not be debug-signed. If key.properties is missing, a release build now stops with a clear error instead of silently signing with the debug key (which Google Play rejects); create the file and build again. Create the file before your first release.

4. Build.

flutter build appbundle --release --obfuscate --split-debug-info=build/symbols   # .aab → Google Play
flutter build apk --release --obfuscate --split-debug-info=build/symbols        # .apk → direct install / other stores
# --obfuscate hides Dart symbol names in the binary; keep build/symbols safe — it is what
# turns an obfuscated crash log back into readable stack traces (flutter symbolize).

# Verify what signed it (should NOT say "Android Debug"):
keytool -printcert -jarfile build/app/outputs/bundle/release/app-release.aab

Also set your own applicationId in android/app/build.gradle.kts before publishing — the package ships as com.sellino.seller and every listing needs a unique id you own.

iOS — signing & distribution

iOS builds need macOS with Xcode and a paid Apple Developer Program membership (US$99/year — a third-party cost, not included).

  1. Bundle identifier. Open ios/Runner.xcworkspace in Xcode (the workspace, not the project). Select the Runner target → General → set your own reverse-domain Bundle Identifier (e.g. com.yourbrand.pos). It must match the App ID you register with Apple, and should mirror the Android applicationId.
  2. Signing team. Xcode → Settings → Accounts → add your Apple ID. Then on the Runner target → Signing & Capabilities → tick Automatically manage signing and pick your Team. Xcode then creates the development certificate and provisioning profile for you — this is the recommended route.
  3. Register the App ID at Identifiers using the same bundle id. If you use push, enable the Push Notifications capability here and generate the APNs key — see Common Setup.
  4. Create the app record in App Store Connect → My Apps → + → pick the bundle id, set name, primary language and SKU.
  5. Version & build number. Set them in pubspec.yaml (version: 1.0.0+1 — the part after + is the build number). Every upload to App Store Connect needs a build number higher than the last.
  6. Archive and upload.
    flutter build ipa --release --obfuscate --split-debug-info=build/symbols
    # then either open the generated .xcarchive in Xcode → Distribute App,
    # or upload build/ios/ipa/*.ipa with Transporter (free, Mac App Store)
    In Xcode the equivalent is Product → Archive → Distribute App → App Store Connect.
  7. TestFlight → review. The build appears in App Store Connect after processing (a few minutes to an hour). Test it via TestFlight, fill in the store listing, privacy details and screenshots, then submit for review.

Apple's review requires a reachable Privacy Policy URL and an in-app account deletion path. Both ship with the app — the sign-in screen links the policy (edited in Website → Pages) and Settings carries account deletion.

Store accounts (Google Play one-time US$25 / Apple Developer US$99 per year) are third-party services and are not included with this item.

For Developers — API Structure & Data Model

The app is a pure API client: it holds no business logic of its own and writes nothing the web panel cannot read. Everything goes through /api/v10/* on your Laravel backend.

Every request carries these headers

Accept:        application/json
X-App-Key:     <the App API key from Settings → API Security>   // rejected with 401 if wrong
Authorization: Bearer <jwt>                                     // authenticated calls only
X-Branch-Id:   <active branch id>                               // the multi-branch scope
Accept-Language: en | bn | ar | he                              // drives translated responses

X-Branch-Id is the heart of the app. Stock, counters, registers, orders and reports are all scoped to it server-side, so switching branch in the UI changes what every subsequent endpoint returns. It is restored from secure storage before the first request on launch.

Response envelope

Every endpoint answers in the same shape, so the app has one parser and one error path:

{ "status": true,  "message": "Request successfully executed", "data": { … } }
{ "status": false, "message": "Human readable error",          "data": {} }

// Paginated lists put the rows in data.items alongside:
//   current_page · last_page · per_page · total

Endpoint families

Routes live in routes/api/v10/ on the backend, one file per area. The app's back office uses the staff group (routes/api/v10/staff/, ~34 files) — each route behind auth:api, auth.staff and its own hasPermission:* gate:

auth pos cash-register counter orders products inventory warehouses branches categories brands attributes customers suppliers members accounts reports returns replacements coupon campaigns flash-sell users panel-features activity-logs archive

The same hasPermission:* names drive the app's drawer — see Web → Users & Roles. A role missing order_read gets no Orders menu item and a 403 if it calls the endpoint directly.

Core data model

EntityRelationship
BranchThe top-level scope. Owns warehouses, counters and staff assignments; every stock row, register and order belongs to one.
Warehouse → RackStock lives per warehouse; racks locate it inside one. The cart's rack picker reads these.
Counter → Cash RegisterA counter is the physical till; a register is one shift on it (open → sell → close). One open register per counter, one per user.
Product → VariantProducts carry attributes; the sellable unit is the variant (own SKU, barcode, price, stock).
Stock → Cost layersOn-hand quantity per variant per warehouse, backed by FIFO cost layers that purchases create and sales consume.
Order → Items → PaymentsA sale, its lines, and one or more payments (cash / bank / MFS split), tied to the register it was rung on.
Journal → TransactionsEvery money movement posts a balanced journal of ledger lines against Chart of Accounts heads — sales, expenses, pickups, purchases, returns.
User → Role → PermissionsRoles bundle permissions; users may also carry per-user overrides. This is what gates both menu and API.

Auth & token lifecycle

  • Login returns a JWT plus its expiry; the app stores it with flutter_secure_storage (Keychain on iOS, EncryptedSharedPreferences on Android) — never in plain preferences.
  • An interceptor refreshes the token through POST /login/refresh before expiry, and retries once on a 401; concurrent calls share one refresh instead of racing.
  • Auth endpoints are rate limited server-side (throttle middleware) against brute force.

Extending it

  • Add a backend route under routes/api/v10/staff/ with its hasPermission:* gate, then a matching service + controller in the app's feature folder — the folder layout under lib/features/ mirrors these route files closely.
  • lib/core/network/api_endpoints.dart holds every path in one place; dio_client.dart owns headers, refresh and error mapping.
  • Full endpoint-by-endpoint reference: docs/api-v10-endpoints.md in the backend source.

Login & Outlet

  • Sign in with the restaurant account you approved in the panel, or with a staff user that belongs to it.
  • Outlet switcher — a chain picks which kitchen it is standing in; the board, the menu and the reports all follow that choice.
  • Permissions decide the menu — a chef sees the board, a manager sees payouts and reports.
  • Store Open / Closed sits in the app bar: one tap stops new orders when the kitchen is overwhelmed.
Loginassets/images/sellino-food-app/rapp_login.jpg
Outletsassets/images/sellino-food-app/rapp_outlets.jpg

Kitchen Board

The screen the kitchen actually lives on.

  • Tabs — New, Preparing, Ready, Out. Each card shows the items, the add-ons, the customer note and how long it has been waiting.
  • Accept with a prep time, or Reject — which cancels the order and refunds the customer.
  • Start preparing and Ready, one tap each.
  • New-order alert — a push notification with a sound so a busy kitchen does not miss an order.
  • The board polls, so it keeps working on a weak connection instead of going blank.
Kitchen boardassets/images/sellino-food-app/rapp_kitchen.jpg
Accept with prep timeassets/images/sellino-food-app/rapp_accept.jpg

Outlet Profile

  • Open / Closed override — beats the schedule right now.
  • Map pin and delivery radius — how far this kitchen will go.
  • Opening hours per weekday, several windows a day.
  • Average prep time — half of the ETA the customer sees.
Outlet profileassets/images/sellino-food-app/rapp_outlet_edit.jpg
Opening hoursassets/images/sellino-food-app/rapp_hours.jpg

Floor & Running Checks

Present when the Restaurant module is switched on.

  • Floor map — table tiles with their live status; tap to seat guests or resume the check already running.
  • Check screen — items, add-ons, notes and totals, with Send to kitchen, Request bill and Close.
  • Move a check to another table or merge two checks into one bill.
  • Guest requests — "call waiter" and "bill please" from the QR menu arrive here.
Floor mapassets/images/sellino-food-app/rapp_floor.jpg
Running checkassets/images/sellino-food-app/rapp_check.jpg

Kitchen Display in the App

  • The same three-column board as the web KDS, sized for a tablet propped at the pass.
  • Late tickets go red; one bump per card moves it along.
  • Filter by station so the grill screen shows grill tickets only.
Kitchen displayassets/images/sellino-food-app/rapp_kds.jpg

Orders, Payouts & Reports

  • Orders — every order for this restaurant with its status, kitchen state, rider and payment.
  • Riders — assign the restaurant's own rider to a ready order.
  • Payouts & wallet — balance, commission deducted, withdrawal requests and their status.
  • Reports — sales, best sellers and the kitchen numbers, on the phone.
  • Notifications and chat with customers.
Ordersassets/images/sellino-food-app/rapp_orders.jpg
Reportsassets/images/sellino-food-app/rapp_reports.jpg