Dashly

Food, grocery and pharmacy delivery for your town: a customer app and a rider app (Flutter, iOS and Android), and one Next.js server that is the website, the vendor panel, the admin panel and the API. You add your own keys; everything else is ready.

1. Welcome

Thank you for buying Dashly. This guide is written for beginners: follow the chapters in order and you will have the server, the website, the vendor panel and both apps running under your own name and keys.

What is in the download

FolderWhat it is
dashly_customer_flutter/The customer app. People browse stores near them, order, pay, track the rider and chat. Also runs as a single-restaurant app with dine-in and reservations (chapter 11).
dashly_rider_flutter/The rider app. Riders apply, go online, accept delivery offers, pick up, hand over with a code or a photo, and see their earnings.
dashly_flutter_core/Code both apps share: the API client, sign-in, theme, models, widgets and common screens.
dashly_web_nextjs/One Next.js app that is the website, the vendor panel (/vendor), the admin panel (/admin), the API both apps use (/api/v1) and the background worker (pnpm worker).
deploy/The Docker stack: website/API + worker + PostgreSQL database + MinIO file storage, started with one command.
tools/rename.mjsRenames both apps and their package ids in one command.
docs/This documentation (HTML and PDF).

How the parts fit

Both apps and the website talk to the same server. All data (stores, menus, orders, wallets, riders) lives in your PostgreSQL database. Photos, rider documents and delivery proofs live in S3-compatible storage (MinIO in the Docker stack, or Amazon S3 / Cloudflare R2). Firebase is used only for sign-in and push notifications.

The flow of an order: a customer orders → the store accepts on the vendor panel → the worker offers the delivery to the nearest online riders in that zone → the first rider to accept picks it up → the customer follows the rider on the map and gives the 4-digit code at the door.

Nothing is blocked on a key. Without Stripe keys the checkout shows Card (demo), which marks orders paid so you can test; cash and wallet always work. Without SMTP, e-mails are printed to the server log. Without a Google key, address search uses OpenStreetMap.

2. Requirements

To run the server

To build the mobile apps

Accounts you will create (all have free tiers)

3. Quick start (Docker)

This gets the whole server running on your VPS in about 15 minutes. You need the Firebase keys from chapter 4 for sign-in; you can do this chapter first and add them after.

  1. Copy the kit to the server, for example with scp dashly-1.0.0.zip root@YOUR_SERVER_IP:, then on the server:
    apt install -y unzip
    unzip dashly-1.0.0.zip
    cd dashly/deploy
    cp .env.example .env
    nano .env
  2. In .env, fill at least these values:
    KeyWhat to put
    POSTGRES_PASSWORDA long random password, letters and digits only (run openssl rand -hex 24 to make one).
    S3_SECRET_ACCESS_KEYAnother random value for the built-in MinIO storage (at least 8 characters).
    APP_URLYour website address, e.g. https://your-domain.com (or http://YOUR_SERVER_IP:3000 for a first test).
    NEXT_PUBLIC_FIREBASE_*, FIREBASE_*From chapter 4.
  3. Start everything:
    docker compose up -d --build
    The first build takes 5–10 minutes. On every start the database tables are created or updated and the base data (Food, Grocery and Pharmacy modules with their categories) is loaded.
  4. Open APP_URL in your browser. The install wizard opens: create your admin account, name the business, pick the currency and load the sample stores if you want them (chapter 7).
  5. Check it: APP_URL/api/v1/health shows {"ok":true,…}. Then put it on your domain with HTTPS (chapter 5).
Changed .env? Run docker compose up -d again. The Firebase web values are read when the page loads, so no rebuild is needed for them.

4. Firebase setup

Firebase handles sign-in (Google, Apple, phone, guest, and e-mail for staff) and push notifications. Your data stays in your own database.

  1. Go to the Firebase console → Add project. Give it your app name.
  2. Build → Authentication → Get started → Sign-in method. Turn on: Anonymous (guest mode), Google, Phone, Email/Password (vendors and admins) and Apple (see chapter 16).
  3. Authentication → Settings → Authorized domains: add your-domain.com.
  4. Project settings (gear) → General → Your apps → Add app → Web (name it "Website"). Copy the values from the firebaseConfig shown into deploy/.env:
    NEXT_PUBLIC_FIREBASE_API_KEY="…apiKey…"
    NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN="your-project.firebaseapp.com"
    NEXT_PUBLIC_FIREBASE_PROJECT_ID="your-project"
    NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET="your-project.firebasestorage.app"
    NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID="…"
    NEXT_PUBLIC_FIREBASE_APP_ID="1:…:web:…"
  5. Project settings → Service accounts → Generate new private key. A JSON file downloads. Copy three values from it into deploy/.env:
    FIREBASE_PROJECT_ID="your-project"
    FIREBASE_CLIENT_EMAIL="firebase-adminsdk-xxxx@your-project.iam.gserviceaccount.com"
    FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIE…\n-----END PRIVATE KEY-----\n"
    Keep the \n as they are in the file. Keep this file secret.
  6. Register the two mobile apps: Project settings → Your apps → Add app → Android twice (package names, e.g. com.yourcompany.dashly and com.yourcompany.dashly.rider) and iOS twice with the same ids. Rename the apps first if you want your own ids (chapter 17). Or use the FlutterFire CLI, which registers them for you:
    dart pub global activate flutterfire_cli
    cd dashly_customer_flutter
    flutterfire configure --project your-project --platforms android,ios \
      --android-package-name com.yourcompany.dashly --ios-bundle-id com.yourcompany.dashly
  7. Copy each app's values into its .env (dashly_customer_flutter/.env and dashly_rider_flutter/.env): FIREBASE_PROJECT_ID, FIREBASE_MESSAGING_SENDER_ID, FIREBASE_STORAGE_BUCKET, FIREBASE_ANDROID_API_KEY, FIREBASE_ANDROID_APP_ID, FIREBASE_IOS_API_KEY, FIREBASE_IOS_APP_ID, FIREBASE_IOS_CLIENT_ID, FIREBASE_IOS_BUNDLE_ID. You find them in each app's google-services.json / GoogleService-Info.plist (download them from Project settings), or on the app's card in Project settings. The apps read these values from .env, so you do not need to put the downloaded files in the project.
  8. Google sign-in on Android needs GOOGLE_SERVER_CLIENT_ID in each app's .env: Authentication → Sign-in method → Google → Web SDK configuration → Web client ID.
  9. iOS sign-in callbacks: in each app, copy ios/Flutter/Firebase.xcconfig.example to ios/Flutter/Firebase.xcconfig and set GOOGLE_REVERSED_CLIENT_ID (the REVERSED_CLIENT_ID in GoogleService-Info.plist) and FIREBASE_ENCODED_APP_ID (your iOS app id with : replaced by - and app- in front).
  10. Android Google and phone sign-in need your signing key fingerprints: run cd android && ./gradlew signingReport in each app and add the SHA-1 and SHA-256 under Project settings → Your apps → Android → Add fingerprint. Add the fingerprints of your upload key and of Google Play's app signing key too when you publish.
Testing phone sign-in without SMS costs: Authentication → Sign-in method → Phone → Phone numbers for testing, add a number and a code.

5. Put it online (VPS, HTTPS)

The quickest way: the stack has a built-in Caddy web server that gets a free HTTPS certificate for your domain.

  1. Point your domain's DNS A record at the server and wait until it resolves.
  2. In deploy/.env set DOMAIN=your-domain.com and APP_URL=https://your-domain.com.
  3. Open ports 80 and 443 in the firewall (ufw allow 80,443/tcp) and start the stack with the https profile:
    docker compose --profile https up -d
  4. Add your-domain.com to Firebase → Authentication → Settings → Authorized domains, if you have not yet.

Already run nginx, Traefik or another proxy? Skip the profile and forward your domain to 127.0.0.1:3000 (the WEB_PORT).

Backups

Back up the database every day, for example with a cron job:

docker compose exec -T postgres pg_dump -U dashly dashly | gzip > /root/backups/dashly-$(date +%F).sql.gz

Also back up the minio-data volume (photos and documents), or use a cloud bucket (next chapter).

6. Other hosting

Website on Vercel, database on a managed PostgreSQL

  1. Create a PostgreSQL database (Neon, Supabase, Railway…) and copy its connection string.
  2. Import dashly_web_nextjs/ into Vercel. Add every key from dashly_web_nextjs/.env.example as an environment variable, with DATABASE_URL set to your database and the five S3_* keys set to a cloud bucket (Vercel has no disk for uploads).
  3. On your computer, create the tables once: cd dashly_web_nextjs && pnpm install && pnpm prisma:migrate:deploy && pnpm prisma:seed -- --base (with DATABASE_URL in .env).
  4. The background worker does not run on Vercel. Run pnpm worker on any small server or service that keeps a process running (Railway, Render, a VPS) with the same environment variables. Without the worker, rider offers are not re-sent and late orders are not auto-rejected.

Cloud storage instead of MinIO

Create a private bucket on Amazon S3 or Cloudflare R2 and set S3_ENDPOINT (empty for AWS, your R2 endpoint for R2), S3_REGION, S3_BUCKET, S3_ACCESS_KEY_ID and S3_SECRET_ACCESS_KEY. Files are served through the API, so the bucket stays private.

Without Docker

Install Node.js 22 and pnpm 11, then in dashly_web_nextjs/: cp .env.example .env, fill it in, and run pnpm install, pnpm prisma:migrate:deploy, pnpm prisma:seed -- --base, pnpm build, then keep pnpm start and pnpm worker running (for example with pm2).

7. Install wizard and sample data

The first time you open the website, the install wizard (/install) asks for:

  1. Your admin account (name, e-mail and password). This is the super admin; you add more staff later in Admin → Staff.
  2. The business: app name, currency, distance unit, time zone, and what you run — a multi-store marketplace or a single restaurant (chapter 11).
  3. The look and the sample data: the brand colour, and Load the sample stores and menus for 16 sample stores in three zones (around Austin, Texas — move or redraw them in Admin → Zones) with menus, riders, customers, reviews and three weeks of orders, so every screen has content. Leave it off for a clean start.

The wizard closes for good once a super admin exists. Sign in later at /admin.

The sample stores and people are fictional. Delete them in Admin → Stores / Customers / Riders when you add real ones.

8. Setup check

Two ways to see which keys are missing and whether each service answers:

9. Run the mobile apps

  1. In each app folder, copy the settings file and fill it in:
    cd dashly_customer_flutter
    cp .env.example .env
    Set API_BASE_URL to your server (https://your-domain.com; for a server on your computer use http://10.0.2.2:3000 in the Android emulator) and the Firebase values from chapter 4.
  2. Get the packages and run:
    flutter pub get
    flutter run
  3. Do the same in dashly_rider_flutter/.

Until .env has the server and Firebase values, the apps open a "Finish the setup" screen that names what is missing.

Build for release

flutter build appbundle      # Android, for Google Play
flutter build ipa            # iOS, for the App Store (on a Mac)

10. Run your business

TaskWhere
Draw delivery zones and set the base feeAdmin → Zones. Customers outside every zone see "We don't deliver here yet".
Add storesAdmin → Stores, or let owners apply at /become-a-vendor and approve them in Admin → Stores.
Menus, stock, opening hours, promotionsThe store owner signs in at /vendor.
Accept and prepare ordersVendor → Orders (a live board that refreshes every few seconds).
Approve ridersRiders apply in the rider app; review documents and approve in Admin → Riders.
DispatchAutomatic: when a store accepts, the nearest online riders in the zone get an offer (offer time and riders per offer: Admin → Settings → Delivery → Riders). To do it by hand, open the order in Admin → Orders and assign a rider.
Commission and feesAdmin → Commissions (per module or per store) and Admin → Settings → Delivery (customer fees and rider pay).
Pay stores and ridersAdmin → Payouts lists what each store and rider has earned. Pay them by bank transfer, then mark the payout paid. Cash a rider hands in is recorded in Admin → Riders → the rider → Record a cash deposit.
Coupons, banners, push campaignsAdmin → Coupons, Banners, Campaigns.
SupportCustomers chat from the app's Help screen; answer in Admin → Tickets.

11. Single-restaurant mode

Sell the same apps to one restaurant brand: Admin → Settings → General → Mode → Single restaurant, and set Single-restaurant store slug to that store's slug (shown in Admin → Stores). The customer app then opens straight on that restaurant's menu, with a Dine-in tab and Reservations.

12. Payments (Stripe)

Card payments use Stripe Checkout (cards, Apple Pay, Google Pay) for orders, wallet top-ups and dine-in bills, in the apps and on the website. Cash on delivery and wallet always work.

  1. In the Stripe dashboard copy the Secret key into STRIPE_SECRET_KEY.
  2. Developers → Webhooks → Add endpoint: URL https://your-domain.com/api/v1/webhooks/stripe, events checkout.session.completed and checkout.session.async_payment_succeeded. Copy the signing secret into STRIPE_WEBHOOK_SECRET.
  3. Restart: docker compose up -d. The checkout now shows Card instead of Card (demo).
Charging customers or vendors through your product requires the Envato Extended Licence.

13. Push notifications

Order updates, rider offers, chat messages and admin campaigns are sent with Firebase Cloud Messaging through your FIREBASE_* service account — nothing else to set for Android. For iOS, upload an APNs key: Apple Developer → Keys → + (Apple Push Notifications service), then Firebase → Project settings → Cloud Messaging → Apple app configuration → Upload. In Xcode, add the Push Notifications and Background Modes → Remote notifications capabilities to both apps.

Every user also has an in-app notification inbox, and can switch message and promotion notifications off in the app's settings.

14. Maps and addresses

15. Email

Set SMTP_HOST, SMTP_PORT, SMTP_USER and SMTP_PASS (any provider: Amazon SES, Brevo, Mailgun, Postmark, your host's mail server). Use port 587, or 465 with SMTP_SECURE=true. The sender address and name are in Admin → Settings → Email. Until SMTP is set, e-mails are printed to the web container's log (docker compose logs web).

16. Sign-in methods

WhoMethods
Customer appGoogle, Apple (iOS), phone number (SMS code), and guest (browse and order; link a phone later).
Rider appPhone number and Google.
WebsiteGoogle, phone number, e-mail and password, and guest.
Vendor panel and adminE-mail and password.

Apple sign-in (required by Apple when an iOS app offers Google sign-in): in the Apple Developer account, enable Sign in with Apple for the customer app's id, add the capability in Xcode, and turn on Apple in Firebase Authentication.

17. Rename the app and package id

From the folder that holds the apps (requires Node.js):

node tools/rename.mjs --id com.yourcompany.foodly --name "Foodly"

This sets the app names (under the icon on Android and iOS) and the Android package / iOS bundle ids of both apps (the rider app gets com.yourcompany.foodly.rider and "Foodly Rider"; change them with --rider-id and --rider-name). It also moves Android's MainActivity to the new package. Then register the new ids in Firebase (chapter 4).

The name customers see inside the apps and on the website comes from Admin → Settings → General → App name, so you can change it any time without a new build.

19. Basic edits

I want to change…Where
Terms and privacy policyAdmin → Settings → General → Legal pages (shown in both apps and on the website).
Currency, taxes, service and small-order feesAdmin → Settings → General, Taxes and Delivery; per module in Admin → Modules.
Home banners and featured storesAdmin → Banners; Admin → Stores → a store → Featured.
Which modules show (Food / Grocery / Pharmacy)Admin → Modules.
Help centre questionsAdmin → Settings → General → Help centre FAQ.
Support phone and e-mailAdmin → Settings → General.
Force an app update, maintenance messageAdmin → Settings → General → Apps (minimum app version, update prompt, maintenance mode).
Texts inside the appsThe screens are in lib/pages/ of each app; search for the text and edit it.
App download links on the websiteAdmin → Settings → General → Apps (App Store, Google Play and APK links).

20. Every key, explained

Each folder has a .env.example that lists its keys with comments. This table lists all of them: web = dashly_web_nextjs/.env, Docker = deploy/.env, customer / rider = each app's .env.

KeyRequiredWhereWhat it does
Database
DATABASE_URLYeswebPostgreSQL connection string
POSTGRES_DBYesDockerDatabase the Docker stack creates
POSTGRES_USERYesDockerDatabase user for the Docker stack
POSTGRES_PASSWORDYesDockerDatabase password for the Docker stack
App
APP_URLYesweb, DockerPublic URL of the website/API, e.g. https://dashly.example.com
NEXT_PUBLIC_APP_NAME—web, DockerName shown before the admin sets one
WEB_PORT—DockerHost port the web container listens on
DOMAIN—DockerYour domain for automatic HTTPS (docker compose --profile https)
CORS_ORIGINS—web, DockerBrowser apps allowed to call /api/v1 (Flutter web builds)
DEMO_MODE—web, Docker"true" only for a public demo: one-click demo sign-in
Firebase sign-in
NEXT_PUBLIC_FIREBASE_API_KEYYesweb, DockerFirebase web app config
NEXT_PUBLIC_FIREBASE_AUTH_DOMAINYesweb, DockerFirebase web app config
NEXT_PUBLIC_FIREBASE_PROJECT_IDYesweb, DockerFirebase web app config
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET—web, DockerFirebase web app config
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID—web, DockerFirebase web app config
NEXT_PUBLIC_FIREBASE_APP_IDYesweb, DockerFirebase web app config
Firebase Admin + push
FIREBASE_PROJECT_IDYesweb, DockerFirebase project id
FIREBASE_CLIENT_EMAILYesweb, DockerService account e-mail
FIREBASE_PRIVATE_KEYYesweb, DockerService account private key
Payments
STRIPE_SECRET_KEY—web, DockerStripe secret key — card payments (empty = demo card)
STRIPE_WEBHOOK_SECRET—web, DockerStripe webhook signing secret (/api/v1/webhooks/stripe)
DEMO_PAYMENTS—web, Docker"false" hides the demo card when Stripe is not set
Maps
GOOGLE_MAPS_API_KEY—web, DockerGoogle Geocoding for address search (empty = OpenStreetMap)
MAP_TILE_URL—web, DockerMap tiles URL template (MapTiler, Stadia, Mapbox…)
MAP_ATTRIBUTION—web, DockerCredit line your tile provider requires
Storage
S3_ENDPOINT—web, DockerS3-compatible endpoint (MinIO, R2); empty for AWS
S3_REGION—web, DockerBucket region
S3_BUCKET—web, DockerBucket for photos and documents (empty = local uploads folder)
S3_ACCESS_KEY_ID—web, DockerStorage access key
S3_SECRET_ACCESS_KEY—web, DockerStorage secret key
Email
SMTP_HOST—web, DockerSMTP server; empty = emails are printed to the log
SMTP_PORT—web, Docker587 (STARTTLS) or 465 (TLS)
SMTP_USER—web, DockerSMTP user
SMTP_PASS—web, DockerSMTP password
SMTP_SECURE—web, Docker"true" for port 465 (TLS); empty for 587
Storage
UPLOAD_DIR—webFolder for uploads when no bucket is set (default ./uploads)
Mobile apps
API_BASE_URLYescustomer, riderYour server; the app calls <url>/api/v1
APP_NAME—customer, riderApp name in the UI
FIREBASE_PROJECT_IDYescustomer, riderFirebase project id
FIREBASE_MESSAGING_SENDER_IDYescustomer, riderFirebase config
FIREBASE_STORAGE_BUCKET—customer, riderFirebase config
FIREBASE_ANDROID_API_KEYYescustomer, riderFirebase Android config
FIREBASE_ANDROID_APP_IDYescustomer, riderFirebase Android config
FIREBASE_IOS_API_KEYYescustomer, riderFirebase iOS config
FIREBASE_IOS_APP_IDYescustomer, riderFirebase iOS config
FIREBASE_IOS_CLIENT_ID—customer, riderGoogle sign-in on iOS
FIREBASE_IOS_BUNDLE_ID—customer, rideriOS bundle id
GOOGLE_SERVER_CLIENT_ID—customer, riderWeb OAuth client id — Google sign-in on Android
SUPPORT_EMAIL—customer, riderFallback support e-mail until the server config loads
DEMO_SIGN_IN—customer, rider"true" shows one-tap demo sign-in (your public demo only)

21. File structure

Mobile apps

dashly_customer_flutter/lib/
  main.dart            starts Firebase and the app
  app.dart             theme, push handling
  router.dart          every screen's route and the start-up gates (setup → onboarding → sign-in)
  pages/               one folder per area: launch, home, search, store, cart, orders, wallet, profile,
                       address, single (dine-in, reservations)
  providers/           app state with Riverpod (bag, place, data loaded from the API)
  widgets/, utils/     app-only widgets and helpers

dashly_rider_flutter/lib/
  pages/               launch (sign-in, apply), home (online, offers), job (pick-up, hand-over),
                       deliveries, earnings, profile
  providers/           rider state (profile, current job, earnings)

dashly_flutter_core/lib/src/
  api/                 the HTTP client for /api/v1
  auth/                Firebase sign-in (Google, Apple, phone, guest)
  config/              .env reading and Firebase options
  models/              Store, MenuItem, Order, Config…
  theme/               colour tokens, text styles, light and dark themes
  widgets/             buttons, cards, map, sheets, states, chat
  pages/               screens both apps share (splash, setup needed, offline, phone sign-in, legal)

Long lists use lazy .builder lists; state flows through Riverpod providers.

Server (dashly_web_nextjs/src)

app/(site)/          the public website (home, stores, store page, checkout, tracking, account)
app/vendor/          the vendor panel
app/admin/           the admin panel
app/install/         the first-run wizard
app/api/v1/          one route that hands every API call to lib/server/router.ts
lib/server/          business logic: handlers/ (the API routes), orders, pricing, dispatch, wallet,
                     payments (Stripe), notify (push), email, storage, geo, settings, setup-check
lib/client/          browser code: Firebase sign-in, API calls
components/          site/, panel/ (vendor and admin), ui/ (shared building blocks)
database/            Prisma schema, migrations, seed (sample data)
worker/              background jobs (pnpm worker)
scripts/doctor.ts    pnpm run doctor
tests/               API tests (pnpm test)

Outside src, e2e/ holds browser smoke tests for a running server: E2E_BASE_URL=https://your-domain.com pnpm e2e (run pnpm exec playwright install chromium once first).

22. Publish to the stores

You publish both apps under your own developer accounts. Rename them first (chapter 17).

Google Play

  1. Create an upload key: keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload.
  2. Create android/key.properties with storePassword, keyPassword, keyAlias=upload and storeFile, and add a signingConfigs.release block to android/app/build.gradle.kts as in Flutter's guide.
  3. Set the version in pubspec.yaml (version: 1.0.0+1), run flutter build appbundle, and upload build/app/outputs/bundle/release/app-release.aab in the Play Console.
  4. Fill the store listing, the data safety form (the apps collect name, phone, e-mail, location while ordering or delivering, and photos the user uploads) and the content rating. The rider app shares location only while online and in the foreground — say so in the listing.
  5. Add the Play app signing key's SHA-1 and SHA-256 to Firebase (chapter 4).

App Store

  1. In Apple Developer, create the app ids with Push Notifications (and Sign in with Apple for the customer app).
  2. Open ios/Runner.xcworkspace in Xcode, choose your team under Signing & Capabilities, add the capabilities from chapters 13 and 16.
  3. flutter build ipa, then upload with Xcode's Organizer or Transporter, and submit in App Store Connect. Give the reviewer a test phone number and code (chapter 4 tip) and a sample store.

23. Updating

When a new version comes out, read its changelog, back up your database, and copy the new files over your copy (keep your .env files and your edits — a tool like git makes merging easy). Then docker compose up -d --build; migrations run on start. For the apps, run flutter pub get and build again.

24. FAQ and troubleshooting

The app shows "Finish the setup"

Its .env is missing API_BASE_URL or Firebase values. Fill them in and restart the app (a hot reload does not re-read .env).

The app shows "Can't reach the server"

Open API_BASE_URL/api/v1/health in the phone's browser. On the Android emulator, localhost is the emulator itself — use http://10.0.2.2:3000. Android blocks plain http:// to other hosts; use HTTPS.

Sign-in fails on the website

Add your domain under Firebase → Authentication → Settings → Authorized domains, and check the NEXT_PUBLIC_FIREBASE_* values in Admin → Setup.

Google sign-in fails on Android

Add the SHA-1 and SHA-256 of the key that signed the build (debug, upload and Play signing) to the Android app in Firebase, and set GOOGLE_SERVER_CLIENT_ID.

"We don't deliver here yet" for every address

The address is outside every active zone. Draw a zone around your town in Admin → Zones.

A store shows "Closed"

Its opening hours (Vendor → Settings) don't include the current time. Customers can still schedule an order for later.

Riders get no offers

The rider must be approved, online, inside the order's zone and under the cash limit, and the worker must be running (docker compose ps shows worker up).

Payments say "demo"

STRIPE_SECRET_KEY is empty. See chapter 12. Set DEMO_PAYMENTS=false to hide the demo card.

Photos do not upload

Run the setup check: the storage test shows the problem (wrong S3_* keys, or the bucket does not exist).

I changed .env and nothing happened

Server: docker compose up -d recreates the containers with the new values. Apps: stop and start the app again.

Where are the logs?

docker compose logs -f web and docker compose logs -f worker.

25. Credits

Dashly is built on these open-source projects, each under its own licence:

Mobile apps

Flutter, flutter_riverpod, go_router, firebase_core, firebase_auth, firebase_messaging, google_sign_in, sign_in_with_apple, flutter_map, geolocator, mobile_scanner, image_picker, http, flutter_dotenv, shared_preferences, share_plus, url_launcher, package_info_plus, intl, lucide_icons_flutter.

Website, panels and API

Next.js, React, Prisma, PostgreSQL, Tailwind CSS, shadcn/ui, Base UI, Lucide, Leaflet and React Leaflet, Firebase JS SDK and Firebase Admin, Stripe Node, AWS SDK for JavaScript, Nodemailer, Zod, Sonner, node-qrcode, MinIO, Caddy.

Fonts, maps and content

Nunito (SIL Open Font License 1.1, licence file in dashly_flutter_core/assets/fonts/OFL.txt). Map data © OpenStreetMap contributors. The sample stores, menus, people and texts are original and fictional.

26. Changelog

1.0.0 — 2026-10-06

First release. The full list is in CHANGELOG.md.

27. Support

Use the Support tab on the item's CodeCanyon page. Please include your purchase code, what you did, what you expected and what happened, and the output of pnpm run doctor or a screenshot of Admin → Setup.

Support covers questions about the item's features and fixing bugs in the item. Custom changes and installation on your server are not part of item support.