StoreMineStoreMine
Guide
Admin Panel
Storefront
Mobile App
Deployment
Live Demo
  • FAQ
  • Changelog
  • Credits
Guide
Admin Panel
Storefront
Mobile App
Deployment
Live Demo
  • FAQ
  • Changelog
  • Credits
  • Mobile App

    • Mobile App

Companion Flutter App

Your download includes the full source of a Flutter customer app for Android and iOS, in the Flutter App/ folder (customer-flutter-app/ if you are working from the repository). It is a real native app, not a WebView wrapper: it talks to your store over the /api/v1 REST endpoints that ship with the Laravel backend, so products, categories, hero slides, flash deals, coupons, promotions, shipping zones, payment gateways and static pages are all driven by the same admin panel you already use.

Web and app are independent

The web storefront works on its own, and so does the app — they share one catalogue, one admin panel and one set of orders. Run either, or both. If you only want the website, skip this page.

What the app covers

AreaScreens
HomeHero carousel, category rail, flash deals with countdown, bestsellers, featured, new arrivals, brand strip
ShopPaginated grid, debounced search, sort, category/brand/in-stock/on-sale filters
ProductImage gallery, variation picker, stock badge, reviews with verified-buyer tags, review composer, recommendations, wishlist
CartDevice-local cart, quantity steppers, swipe to remove, free-shipping progress
CheckoutGuest or signed-in, address book, live shipping quotes, coupon preview, Cash on Delivery / bKash / SSLCommerz / PayPal
OrdersHistory with status chips, receipts, shipment tracking. Guest receipts kept on-device
AccountProfile, password, addresses, blog, legal pages, support link, language and theme pickers, in-app account deletion

Stripe is deliberately not offered in-app — it needs the native Stripe SDK. Leave it enabled for the web shop; the app simply hides it.

Languages and dark mode

The app ships 12 languages, the same set your web storefront offers: English, Bangla, Arabic, Spanish, French, German, Hindi, Urdu, Portuguese, Indonesian, Turkish and Chinese. Arabic and Urdu render right-to-left — navigation, lists and forms all mirror, not just the text.

It also ships light and dark themes, both built from your brand colour, so dark mode still looks like your store.

Your shoppers pick both from Account → Preferences. By default the app follows the phone's language and light/dark setting, so most people never have to choose.

Nothing to configure

Both features work out of the box. There is no setting to switch on, and no extra build step.

Adding or editing a language

Message catalogues are plain JSON files (.arb) in Flutter App/lib/l10n/, one per language:

cd "Flutter App"
cp lib/l10n/app_en.arb lib/l10n/app_it.arb   # Italian, for example

Edit the new file — translate the values on the right, never the keys on the left — then register the language in lib/src/core/l10n.dart:

enum AppLocale {
  en('en', 'English'),
  // ... existing entries ...
  it('it', 'Italiano');           // add yours; pass rtl: true for RTL scripts

Then regenerate and run:

flutter gen-l10n
flutter run --dart-define=API_BASE_URL=https://your-store.com

Any key you leave out falls back to English, so a half-finished translation is safe to ship. To reword existing copy, just edit the value in the matching .arb file — no code changes needed.

Placeholders

Text in braces — {amount}, {count}, {name} — is filled in at runtime. Keep them exactly as they appear in the English file; you can move them within the sentence, but renaming or deleting one breaks the build.

Changing the dark palette

The warm-neutral dark canvas is defined in lib/src/core/theme.dart (darkCanvas, darkSurface, darkInk). Your brand colour seeds both themes, so in most cases setting Brand colour in the admin panel is all you need. If your brand colour is very dark, the app automatically lightens it for dark mode so buttons stay visible.

Requirements

  • Flutter 3.44+ with Dart 3.12+ (flutter --version)
  • Android: Android Studio or the command-line SDK, JDK 17
  • iOS: a Mac with Xcode and CocoaPods (brew install cocoapods)
  • Your store running over HTTPS on a public domain

1. Run the backend migrations

The app authenticates with Bearer tokens stored in the apitoken table. That table is part of the standard migration set, so if you installed with the wizard it already exists. If you are upgrading an older install:

php artisan migrate

2. Point the app at your store

The API base URL is a compile-time constant, passed with --dart-define:

cd "Flutter App"
flutter pub get
flutter run --dart-define=API_BASE_URL=https://your-store.com

Against a local backend from an Android emulator, use the emulator's host alias and run php artisan serve on the Laravel side:

flutter run --dart-define=API_BASE_URL=http://10.0.2.2:8000

Plain HTTP is for development only — ship HTTPS.

3. Branding

Branding comes in two layers.

Runtime — no rebuild needed

Admin → Settings → Business → Brand identity drives the app's whole look:

FieldWhere it shows in the app
Store nameApp bar wordmark, splash, account footer
TaglineSplash, account footer
Brand colourThe entire Material ColorScheme — buttons, tabs, chips, accents
LogoApp bar, splash, auth screens, account

The app reads these from GET /api/v1/config and caches the last known brand on-device, so a cold start paints your colours on the first frame instead of flashing a default palette. With no logo uploaded, it draws a monogram tile from your store name and brand colour — never an unbranded placeholder.

Build time — launcher icon and launch screen

These are baked into the binary. Replace the source art in assets/branding/ (icon.png, icon_foreground.png, splash_logo.png — keep the icon opaque, iOS rejects alpha) and run:

BRAND_COLOR=#FF8400 ./tool/brand.sh

That regenerates both platforms' launcher icons, the native splash, and patches the iOS launch storyboard's background colour. Use the script rather than calling the icon/splash generators directly — on its own, flutter_native_splash leaves the iOS storyboard root colour white, which shows as a white flash before the first frame.

4. Release builds

Android signing

Google Play rejects debug-signed uploads, so create your own keystore once and keep it forever — losing it means you can never update the app again.

keytool -genkey -v -keystore ~/storemine-upload.jks \
  -keyalg RSA -keysize 2048 -validity 10000 -alias upload

Copy Flutter App/android/key.properties.example to Flutter App/android/key.properties and fill in the four values. Gradle picks it up automatically — there is nothing to edit in build.gradle.kts.

Keep it private

Never commit key.properties or the .jks file, and never send them to anyone — including us. Both are already excluded from version control.

Without that file, release builds fall back to the debug keystore so flutter run --release still works while you are developing.

Build

# Android
flutter build appbundle --release --dart-define=API_BASE_URL=https://your-store.com

# iOS — open ios/Runner.xcworkspace in Xcode for signing and capabilities
flutter build ipa --release --dart-define=API_BASE_URL=https://your-store.com

Change the application id / bundle identifier to your own before your first upload; both stores tie it permanently to your listing. The Android id lives in android/app/build.gradle.kts (applicationId); the iOS one is PRODUCT_BUNDLE_IDENTIFIER in Xcode's target settings.

Store listing screenshots

Both stores want screenshots of the app running, and taking them by hand — then retaking them every time you change a price or a product — gets old fast. The app ships with a tool that does it for you:

cd "Flutter App"
API_BASE_URL=https://your-store.com tool/screenshots.sh

It boots an iOS simulator, drives the real app through home, shop, product, cart, checkout, account, the language picker, dark mode and Arabic, and saves a device screenshot of each to build/screenshots/. Because it runs against your store, the shots show your catalogue, your prices and your brand colour.

The files come out at native device resolution with a tidy 9:41 status bar — exactly what both stores accept, so you can upload them as they are.

Android too

The captures are iPhone-sized because the simulator is. For Play Store shots, run the app on an Android emulator and use Android Studio's camera button, or resize these — Google accepts 16:9 to 2:1 between 320px and 3840px.

Requires Xcode for the simulator.

Store review checklist

The app ships with the things Apple and Google reject apps for already in place:

  • In-app account deletion (Apple 5.1.1(v), Google account-deletion policy) — Account → Delete account. The API detaches orders, anonymises reviews and removes the customer's personal data.
  • Guest browsing and guest checkout — no forced registration (Apple 5.1.1(ii)).
  • Physical goods paid through external gateways — permitted outside In-App Purchase (Apple 3.1.3(e), Google Play Payments policy).
  • Privacy policy and terms reachable in-app, served from your admin panel's static pages.
  • No sensitive permissions — INTERNET only on Android, so no permission-purpose declarations are required.
  • HTTPS only; tokens live in the platform keychain/keystore.
  • Portrait-locked on phones, so a reviewer never lands on a broken landscape layout. Remove the SystemChrome.setPreferredOrientations call in lib/main.dart (and restore the landscape keys in ios/Runner/Info.plist) if you rework the layouts for landscape.

Still on you before submitting: your own icons and splash art, signing identities, the privacy "nutrition labels" (data collected: name, email, phone, addresses, order history — linked to the account, used for app functionality only), and the same privacy policy hosted at a public URL for the store listings.

Testing

Unit and widget tests run with no device and no backend:

cd "Flutter App"
flutter test

They check that both themes are readable (every status colour clears the WCAG 4.5:1 text threshold), that a very dark brand colour is lifted for dark mode, that all 12 languages have complete catalogues, and that Arabic and Urdu render right-to-left. If you add a language or change the palette, run this first.

An integration test drives the real app against a running backend — home feed → shop grid → filters → product → add to cart → cart → checkout, plus the legal pages:

php artisan serve                     # in the Laravel project
flutter test integration_test -d <device-id> \
  --dart-define=API_BASE_URL=http://127.0.0.1:8000

Architecture

lib/
  main.dart                 # entry
  l10n/                     # one .arb message catalogue per language
  src/
    app.dart                # MaterialApp.router: themes, locale, routes
    core/                   # config, theme, l10n helpers, Dio client, money
    models/                 # plain Dart models (JSON in/out)
    data/repository.dart    # every API call in one typed class
    state/                  # Riverpod: session, cart, wishlist, providers
    widgets/                # shared UI (product tile, price, empty states…)
    features/               # one folder per screen group

State is flutter_riverpod, navigation is go_router with a bottom-tab shell, networking is dio with a Bearer-token interceptor, and the cart lives in shared_preferences — the backend has no cart table by design, because checkout re-validates stock, prices, coupons, promotions and tax server-side.