ClassiMarket documentation
Run it, point it at your own accounts, ship it under your own brand. Written against version 1.1.0 of the download you get on CodeCanyon.
- Version v1.1.0
- Updated 2026
- Platform Flutter
- Stack Flutter · Next.js
On this page
Introduction
Welcome to ClassiMarket — a production-ready classified marketplace starter kit.
ClassiMarket gives you everything you need to launch a Craigslist- or OLX-style marketplace. The package includes three interconnected projects that work together out of the box:
Requirements
Install these tools before you begin.
flutter doctor in your terminal to check for missing dependencies before proceeding.Project structure
After extracting the ZIP, you will find this top-level structure.
classi-flutter/
├── shared/ ← API contract & shared types (source of truth)
│ ├── api-contract.yaml ← OpenAPI endpoint definitions
│ ├── types/
│ │ ├── api.ts ← TypeScript interfaces (used by Next.js)
│ │ └── api.dart ← Dart models (used by Flutter)
│ └── constants/
│ ├── endpoints.ts
│ └── endpoints.dart
│
├── web/ ← Next.js 16 — API + Web + Admin
│ ├── app/api/ ← 50+ REST API endpoints
│ ├── app/(public)/ ← Public marketplace pages
│ ├── app/dashboard/ ← Seller dashboard
│ ├── app/admin/ ← Admin panel (11 sections)
│ ├── prisma/
│ │ ├── schema.prisma ← Database schema
│ │ └── seed.ts ← Seeds admin user + demo data
│ └── .env.local ← You must create this file
│
├── flutter_app/ ← Flutter mobile app (Android + iOS)
│ ├── lib/core/ ← API client, config, services
│ ├── lib/data/ ← Models & repositories
│ ├── lib/presentation/ ← Screens, widgets, Riverpod providers
│ ├── android/ ← Android native config
│ └── ios/ ← iOS native config
│
└── docs/ ← This documentationWeb and API setup
Set up the Next.js backend and run database migrations.
- 01
Install dependencies
terminal# Navigate to the web directory cd web npm install - 02
Create environment file
Copy the example file and fill in your values (see Environment variables for all variables).
terminalcp .env.example .env.localWarningRequired: setDATABASE_URL. At minimum you must setDATABASE_URLbefore running migrations. Example:postgresql://user:password@localhost:5432/classimarket - 03
Start a PostgreSQL database
You can use a local Docker container or a hosted service like Railway, Supabase, or Neon.
Option A — Docker (local)
terminaldocker run -d \ --name classimarket-db \ -e POSTGRES_DB=classimarket \ -e POSTGRES_USER=user \ -e POSTGRES_PASSWORD=password \ -p 5432:5432 \ postgres:16Option B — Railway / Supabase / Neon
Create a free PostgreSQL database and copy the connection string into
DATABASE_URL. - 04
Run migrations & seed
terminal# Create all database tables npx prisma migrate dev --name init # You must supply admin credentials — there are no defaults. export SEED_ADMIN_EMAIL="you@yourdomain.com" export SEED_ADMIN_PASSWORD="<a strong password you choose>" # Seed admin user and sample categories npx prisma db seedWarningAdmin credentials are not bundled. The seed script requiresSEED_ADMIN_EMAILandSEED_ADMIN_PASSWORDto be set in the environment and will exit with an error if either is missing. The password you choose is hashed with bcrypt before storage and is never logged or printed. Pick a strong password and store it in your password manager — to reset it later you’ll need to update the admin row’spasswordcolumn directly (it’s a bcrypt hash, not plaintext).WarningThe seed script is for development / demo use only.
web/prisma/seed.tsalso creates five demo seller accounts (alice / bob / carol / david / emma @example.com) and a full set of demo listings, reviews, and conversations to make the marketplace feel populated during local development. Never run this script against a production database.If you need to run it in any non-throwaway environment, do one of the following first:
- Delete the demo-seller blocks (sellers 1–5) and every section that depends on them (listings, reviews, conversations) from
seed.ts, or - Leave
SEED_DEMO_PASSWORDunset — the script will generate a random, unrecoverable password for each demo account so the rows exist but nobody can log in as them.
For a true production deployment, the recommendation is to migrate (
prisma migrate deploy), then seed only the admin row by running a trimmed seed script. - Delete the demo-seller blocks (sellers 1–5) and every section that depends on them (listings, reviews, conversations) from
- 05
Start the development server
terminalnpm run dev # Server starts at http://localhost:3000 # Admin panel at http://localhost:3000/admin
Flutter app setup
Configure and run the mobile app on Android or iOS.
- 01
Set the API base URL
The base URL is read from the
API_BASE_URLDart define — you don’t need to editapp_config.dart. The default (http://10.0.2.2:3000/api) targets a local Next.js server from the Android emulator, soflutter runworks out of the box for local dev.terminal# Real device on the same Wi-Fi flutter run --dart-define=API_BASE_URL=http://192.168.1.42:3000/api # Production build pointing at your deployed API flutter build apk --release --dart-define=API_BASE_URL=https://yoursite.com/apiSee Build-time configuration (Dart defines) below for the full list of defines (environment, ad units, Maps key, Stripe key).
- 02
Replace the bundled Firebase project with your own
The source ships
flutter_app/lib/firebase_options.dart,google-services.json, andGoogleService-Info.plistwith placeholder values only (e.g.YOUR_PROJECT_ID). You must connect your own Firebase project before building — runningflutterfire configureoverwrites all three files with your project’s real values so push notifications, FCM tokens, and other Firebase services flow into your project.WarningRequired before any release build. This is mandatory for publishing. The bundled config is for local compilation only.① Install the FlutterFire CLI (one-time)
terminaldart pub global activate flutterfire_cli② Create / sign in to your Firebase project
Go to console.firebase.google.com, create a new project, and enable Cloud Messaging.
③ Wire your Firebase project into the app
terminalcd flutter_app flutterfire configure \ --project=<your-firebase-project-id> \ --platforms=android,ios \ --android-package-name=<your.android.package> \ --ios-bundle-id=<your.ios.bundle.id> \ --out=lib/firebase_options.dart \ --yesThis overwrites
lib/firebase_options.dartwith values from your Firebase project and downloads the matchinggoogle-services.json(Android) andGoogleService-Info.plist(iOS) into the right directories.④ Verify the replacement
terminalgrep YOUR_PROJECT_ID lib/firebase_options.dart # should return nothing after you reconfigure grep projectId lib/firebase_options.dart # should show YOUR project id - 03
Add Google Maps API key
Android
flutter_app/android/local.propertiesGOOGLE_MAPS_API_KEY=your_key_hereiOS
flutter_app/ios/Runner/Info.plist<key>GOOGLE_MAPS_API_KEY</key> <string>your_key_here</string> - 04
Install Flutter dependencies
terminalcd flutter_app flutter pub get - 05
Generate serialization code
terminaldart run build_runner build --delete-conflicting-outputs - 06
Run the app
terminalflutter run # default connected device flutter run -d emulator-5554 # specific Android emulator flutter run -d iPhone # iOS simulator
Release signing
Android requires every release build to be signed with your own keystore before you can publish to the Play Store. Debug builds use a pre-generated debug key and do not require this step.
- 01
Generate an upload keystore
terminalkeytool -genkey -v -keystore ~/upload-keystore.jks \ -keyalg RSA -keysize 2048 -validity 10000 \ -alias upload - 02
Create
flutter_app/android/key.propertiesflutter_app/android/key.propertiesstorePassword=your-keystore-password keyPassword=your-key-password keyAlias=upload storeFile=/Users/you/upload-keystore.jks - 03
That’s it — the Gradle config is already wired up
No Gradle edits are required.
flutter_app/android/app/build.gradle.ktsalready loadskey.propertiesand applies a releasesigningConfig(withminifyEnabled,shrinkResources, and ProGuard rules enabled). For reference, this is the relevant Kotlin DSL already in the file:flutter_app/android/app/build.gradle.ktsval keystoreProperties = Properties() val keystorePropertiesFile = rootProject.file("key.properties") val hasReleaseKeystore = keystorePropertiesFile.exists() if (hasReleaseKeystore) { keystoreProperties.load(FileInputStream(keystorePropertiesFile)) } // inside android { } : signingConfigs { if (hasReleaseKeystore) { create("release") { 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 { signingConfig = if (hasReleaseKeystore) signingConfigs.getByName("release") else signingConfigs.getByName("debug") isMinifyEnabled = true isShrinkResources = true proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } }When
key.propertiesis absent the release build falls back to debug signing so the demo still runs — but you must add your own keystore before publishing.
key.properties or the .jks file to Git — add both to .gitignore. Keep offline backups in at least two secure locations. Losing your upload keystore means you can never update your app on the Play Store again (unless you’ve enrolled in Play App Signing, which lets Google reset your upload key).flutter_app/ios/Runner.xcworkspace, select the Runner target → Signing & Capabilities tab, and pick your Apple Developer team. Xcode auto-manages provisioning profiles for Debug and Release. For TestFlight / App Store distribution you need a paid Apple Developer Program account.Building for release. Android: flutter build apk --release or flutter build appbundle --release (requires the signing setup above)
iOS: flutter build ipa --release (requires Apple Developer account)
Build-time configuration (Dart defines)
The Flutter app is configured entirely through --dart-define flags so you never need to edit source code to switch environments. Defaults are dev-safe (test ads, dev environment, local API). For a production release, set them all explicitly:
flutter build apk --release \
--dart-define=APP_ENV=production \
--dart-define=API_BASE_URL=https://your-domain.com/api \
--dart-define=USE_TEST_ADS=false \
--dart-define=ADMOB_BANNER_ANDROID=ca-app-pub-XXX/YYY \
--dart-define=ADMOB_BANNER_IOS=ca-app-pub-XXX/YYY \
--dart-define=ADMOB_INTERSTITIAL_ANDROID=ca-app-pub-XXX/YYY \
--dart-define=ADMOB_INTERSTITIAL_IOS=ca-app-pub-XXX/YYY \
--dart-define=GOOGLE_MAPS_API_KEY=AIza... \
--dart-define=STRIPE_PUBLISHABLE_KEY=pk_live_...AppEnvironment.current. Accepted: development, staging, production.true, AdMob serves Google’s public test ads regardless of the IDs below. Set false for production.USE_TEST_ADS=false but a value is empty, the app falls back to test IDs (never bills the wrong account).Environment variables
Create web/.env.local with the following variables.
Database & auth
postgresql://user:pass@host/dbhttps://yoursite.comNEXT_PUBLIC_BASE_URL; if both are unset, all cross-origin WebSocket connections are rejected.https://yoursite.com,https://admin.yoursite.comprisma db seed. The seed script exits with an error if this is missing — there is no default.you@yourdomain.comFile storage (AWS S3 or Cloudflare R2)
ap-southeast-1)Firebase (push notifications)
Payments
sk_live_…)Google OAuth & Maps
Email (SMTP)
.env.local to git. The .env.local file is already in .gitignore. Never expose your secret keys in a public repository.Third-party services
Set up external services required by the app.
Firebase (push notifications & auth)
- 01Go to console.firebase.google.com and create a new project.
- 02Enable Cloud Messaging in the Firebase console.
- 03Register your Android app with package name
com.devsnack.classiflutter(or your custom ID). Downloadgoogle-services.json. - 04Register your iOS app with your Bundle ID. Download
GoogleService-Info.plist. - 05Go to Project Settings → Service Accounts, generate a new private key, and use the values for
FIREBASE_PROJECT_ID,FIREBASE_CLIENT_EMAIL, andFIREBASE_PRIVATE_KEY.
Google Maps
- 01Open console.cloud.google.com and create or select a project.
- 02Enable these APIs: Maps SDK for Android, Maps SDK for iOS, Geocoding API.
- 03Create an API key under Credentials and restrict it to your app’s package/bundle ID.
- 04Add the key to
android/local.propertiesandios/Runner/Info.plist.
Stripe
- 01Create an account at stripe.com.
- 02From the dashboard, copy your Secret Key (
sk_live_…) and Publishable Key (pk_live_…). - 03In Webhooks → Add endpoint, set your URL to
https://yoursite.com/api/payments/webhookand subscribe tocheckout.session.completedandcustomer.subscription.*events. Copy the signing secret. - 04Add the publishable key to the Flutter app via
--dart-define=STRIPE_PUBLISHABLE_KEY=pk_live_…in your build command.
AWS S3 / Cloudflare R2 (image storage)
- 01Create an S3 bucket (or Cloudflare R2 bucket). Enable public read access or use presigned URLs.
- 02Create an IAM user with
s3:PutObject,s3:DeleteObject, ands3:GetObjectpermissions on your bucket. - 03Copy the Access Key ID and Secret to your
.env.local. - 04For Cloudflare R2, set
AWS_ENDPOINTto your R2 endpoint URL.
Google AdMob (in-app ads)
- 01Create an AdMob account at admob.google.com.
- 02Add your app and note the App ID.
- 03Create ad units: one Banner and one Interstitial per platform (Android + iOS).
- 04Add the App ID to
android/app/src/main/AndroidManifest.xmlascom.google.android.gms.ads.APPLICATION_IDmeta-data, and toios/Runner/Info.plistunderGADApplicationIdentifier. - 05Supply the four ad unit IDs at build time via
--dart-define(ADMOB_BANNER_ANDROID,ADMOB_BANNER_IOS,ADMOB_INTERSTITIAL_ANDROID,ADMOB_INTERSTITIAL_IOS) and setUSE_TEST_ADS=false. See Build-time configuration for the full command.
App configuration
Rebrand the app with your own name, colors, and package ID.
Change the app name
name: fieldandroid:label="…"CFBundleDisplayNameChange the package / bundle ID
The default Android package is com.devsnack.classiflutter and the default iOS bundle ID is com.devsnack.classiflutter. You must change both before publishing to the Play Store or App Store — the stores will reject duplicate identifiers.
Android — change the package name
- 01Edit
flutter_app/android/app/build.gradle.kts→defaultConfig { applicationId = "com.yourcompany.yourapp" }and the same value innamespace = "...". - 02Rename the folder
flutter_app/android/app/src/main/kotlin/com/devsnack/classiflutterto match your new package path (e.g.com/yourcompany/yourapp). - 03Update the
packagedeclaration at the top ofMainActivity.ktto match. - 04Update
flutter_app/android/app/src/main/AndroidManifest.xmlif it contains apackage="…"attribute. - 05Re-run
flutterfire configurewith the new--android-package-namesogoogle-services.jsonmatches.
iOS — change the bundle identifier
- 01Open
flutter_app/ios/Runner.xcworkspacein Xcode. - 02Select the Runner project → Runner target → General → Bundle Identifier. Set it to
com.yourcompany.yourapp. - 03Switch to the Signing & Capabilities tab, pick your Team, and let Xcode regenerate the provisioning profile. (For TestFlight / App Store distribution you need a paid Apple Developer Program account; for personal device testing a free Apple ID works.)
- 04If you have widgets / share extensions, update their bundle IDs too (must share the prefix of the main bundle ID).
- 05Re-run
flutterfire configurewith the new--ios-bundle-idsoGoogleService-Info.plistmatches.
flutter clean && flutter pub get, then re-generate Firebase config (flutterfire configure) and AdMob registrations so all third-party services know about the new identifier.Change primary color
The brand color is defined in one place — update it and all screens adapt automatically.
class AppColors {
static const Color seed = Color(0xFF004357); // ← change this
static const Color primary = Color(0xFF004357); // ← and this
}Change the app icon
- 01Replace
flutter_app/assets/icon/app_icon.pngwith your 1024×1024 icon. - 02Replace
flutter_app/assets/icon/app_icon_fg.pngwith the foreground layer (transparent background) for Android adaptive icons. - 03Run:
dart run flutter_launcher_icons
Change API base URL
defaultValue: 'https://yoursite.com/api'Admin panel
Manage your marketplace from the built-in admin dashboard.
First admin login. URL: http://localhost:3000/admin
The admin email and temporary password are printed by the Prisma seed script (Web and API setup → step 4). Copy them from the terminal output and change the password immediately in Admin → Settings after signing in.
Panel sections
Users
- View, search, filter users
- Ban / unban accounts
- Change user roles
- View user listings & reviews
Listings
- Approve / reject pending listings
- Feature / unfeature listings
- Delete listings
- View listing reports
Categories
- Create / edit / delete categories
- Create, edit, and reorder categories
- Set category icons and order
Subscriptions
- Create & manage plans
- Set listing & featured quotas
- View subscriber list
Reports
- Review user/listing reports
- Mark as resolved or dismissed
- Take action on reported content
Payments
- View all transactions
- Subscription payments
- Featured listing payments
Notifications
- Send broadcast push to all users
- Send to specific user groups
CMS & blog
- Edit Terms, Privacy, About pages
- Create & publish blog posts
- Manage static pages
Deployment
Deploy the Next.js web app to production.
Railway (recommended)
Railway can host both the app and the database with zero configuration.
- 01Push your code to a GitHub repository.
- 02Go to railway.app, create a new project, and link your GitHub repo.
- 03Add a PostgreSQL plugin — Railway will auto-set
DATABASE_URL. - 04Add all other environment variables in the Railway dashboard under Variables.
- 05The
railway.tomlin the project root handles build & start commands automatically.
npx prisma migrate deploy && node server.jsVercel
# Install Vercel CLI
npm i -g vercel
# Deploy from the web/ directory
cd web && vercel --prodDocker
# Build the image
docker build -t classimarket .
# Run with environment file
docker run -p 3000:3000 --env-file web/.env.local classimarketVPS / self-hosted
cd web
npm run build
npm start # starts on port 3000
# Use Nginx as reverse proxy for SSL/domainSecurity headers and production signing
HTTP security headers
A baseline set of security headers is configured in web/next.config.ts and applied to every response out of the box:
Content-Security-Policy entry in web/next.config.ts to remove 'unsafe-inline' / 'unsafe-eval' (you may need to migrate inline scripts to nonce-based ones) and replace the broad https: in connect-src / img-src with the specific hostnames you actually use.Production app signing (Flutter mobile app)
Before uploading your Flutter app to the Play Store or App Store, complete the signing setup covered in Release signing. Key points for production:
- Android: generate a dedicated upload keystore with
keytooland reference it fromflutter_app/android/key.properties(copy fromkey.properties.example). ThesigningConfigs.releaseblock inbuild.gradle.ktsis already wired up — no Gradle edits needed. Build withflutter build appbundle --releaseand upload the resulting.aab. - Back up the keystore to at least two offline locations. Losing it locks you out of future Play Store updates unless you enrolled in Play App Signing (recommended — Google stores the signing key and lets you reset the upload key).
- iOS: use Xcode’s automatic signing tied to your Apple Developer team. Archive with Product → Archive, then distribute via Organizer to TestFlight or the App Store.
- Never commit
key.properties,.jksfiles, or.p12/.p8certificates to Git.
Push notifications
Enable Firebase Cloud Messaging (FCM) for push notifications on Android and iOS.
- 01
Create a Firebase project
Go to console.firebase.google.com → New Project. Enable Google Analytics if desired. - 02
Register Android app
Add Android app with package namecom.devsnack.classiflutter. Downloadgoogle-services.jsonand place it atflutter_app/android/app/google-services.json. - 03
Register iOS app
Add iOS app with your Bundle ID. DownloadGoogleService-Info.plistand place it atflutter_app/ios/Runner/GoogleService-Info.plist. In Xcode, also enable the Push Notifications capability. - 04
Add server credentials to
.env.localIn Firebase Console → Project Settings → Service Accounts → Generate new private key. Copy the JSON values:
web/.env.localFIREBASE_PROJECT_ID=your-project-id FIREBASE_CLIENT_EMAIL=firebase-adminsdk-xxx@your-project.iam.gserviceaccount.com FIREBASE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n…\n-----END PRIVATE KEY-----\n" - 05
Test notifications
Log in to the Flutter app, then go to Admin → Notifications → Broadcast and send a test push. The device should receive it within seconds.
Features overview
Everything included in ClassiMarket.
Marketplace
- Infinite scroll listing feed
- Multi-level category hierarchy
- Per-category custom fields
- Full-text search with filters
- Price types: Fixed, Negotiable, Free, Contact
- Item condition tracking
- Featured / promoted listings
- Nearby listings map (Google Maps)
- Listing view counter
- Listing expiry & sold status
- Up to 10 images per listing
- Image compression on upload
- Report listing
- Share listing
Users & auth
- Email / password registration
- Google Sign-In
- Sign in with Apple (iOS)
- Email verification
- Password reset via email
- JWT bearer (mobile) + httpOnly cookie (web)
- Role-based access (User / Seller / Admin)
- Edit profile (name, avatar, bio, location)
- Public seller profile page
- Account deletion
Chat
- Real-time messaging (Socket.io)
- Conversation list with last message
- Image sharing in chat
- Read receipts
- Unread message badge
- Block user
- Push notification on new message
Offers
- Offers on Negotiable listings only
- Accept / decline / counter / withdraw
- Unlimited counter-offer rounds
- One live offer per conversation
- Interactive offer cards inline in chat
- Live status updates via Socket.io
- Optional per-offer expiry
- Push + in-app notification per event
- Seller offer management in the dashboard
Monetization
- Subscription plans (Free / Basic / Premium)
- Per-plan listing & featured quotas
- Feature listing boost (one-time)
- Stripe payments
- Google AdMob (banner + interstitial)
- Ads only for free-plan users
Notifications
- Firebase Cloud Messaging (FCM)
- In-app notification center
- Mark all as read
- Swipe to delete
- Transactional emails (welcome, reset)
- Listing approval / rejection emails
- Admin broadcast push
Reviews & bookmarks
- 1–5 star seller ratings
- Written reviews with moderation
- Average rating on seller profile
- Save / bookmark listings
- Bookmarks with infinite scroll
Admin panel
- Dashboard with metrics
- User management & banning
- Listing approval workflow
- Category management
- Subscription plan management
- Report moderation queue
- Payment & transaction history
- Broadcast notifications
- CMS (Terms, Privacy, Blog)
- Platform settings
Web platform
- SEO-optimized listing pages (SSR)
- OpenGraph meta tags
- Sitemap & robots.txt
- Seller dashboard
- Category pages (SSG)
- Light & dark mode
- Responsive design
Customization
Make ClassiMarket your own.
App icon
- 01Create a 1024×1024 PNG icon with no rounded corners — the OS applies the mask.
- 02Replace
flutter_app/assets/icon/app_icon.png(flat icon with background). - 03Replace
flutter_app/assets/icon/app_icon_fg.png(foreground layer, transparent background, for Android adaptive icon). - 04
Run from the
flutter_app/directory:terminaldart run flutter_launcher_iconsThis generates all required sizes for Android and iOS automatically.
Splash screen
Edit flutter_app/pubspec.yaml under the flutter_native_splash section, then run dart run flutter_native_splash:create.
Colors & theme
All colors are centralized. Change the seed color in flutter_app/lib/core/constants/app_colors.dart and the Material 3 system generates the full palette automatically:
static const Color seed = Color(0xFF004357); // ← your brand color
static const Color primary = Color(0xFF004357); // ← same valueFonts
Add a Google Font to pubspec.yaml under fonts:, then update flutter_app/lib/app/app_theme.dart to use it in the ThemeData.
Custom fields per category
Custom fields are defined in the database seed at web/prisma/seed.ts. Each field has a type (Text, Number, Select, Multi-select, Boolean, or Date) and is attached to a category; once seeded, fields appear automatically on the listing create/edit forms in both the web app and the Flutter app. To add or change fields, edit seed.ts and re-run npx prisma db seed, or insert rows into the CustomField table directly.
Subscription plans
Configure plans (name, price, listing quota, featured quota) from Admin → Subscriptions. Update corresponding Stripe products to match pricing.
Web branding (Next.js)
Update web/app/layout.tsx for site name and meta, web/globals.css for Tailwind color variables, and web/public/ for logos and favicon.
Troubleshooting
Fixes for the most common issues you may hit during setup, development, and deployment.
Flutter — stale build artifacts
If the Flutter app fails to compile after pulling updates, dependency changes, or switching branches, clear the build cache:
cd flutter_app
flutter clean
flutter pub get
dart run build_runner build --delete-conflicting-outputsiOS — CocoaPods install fails
CocoaPods issues (“Unable to find a specification”, podspec version conflicts, corrupted pod cache) usually clear after a full reset:
cd flutter_app/ios
pod deintegrate
pod cache clean --all
rm -rf Pods Podfile.lock
pod install --repo-updateIf CocoaPods itself is out of date: sudo gem install cocoapods. On Apple Silicon, run under Rosetta if you see arch errors: arch -x86_64 pod install.
build_runner conflicts
Generated *.g.dart and *.freezed.dart files can go out of sync after model changes. Force a clean regen:
dart run build_runner clean
dart run build_runner build --delete-conflicting-outputsPrisma — client out of sync with schema
If your Next.js app throws “The table does not exist” or “Unknown argument” errors after a schema change:
cd web
npx prisma generate # regenerate the client
npx prisma migrate dev # apply pending migrationsPrisma — migration drift
If you see “Drift detected: Your database schema is not in sync with your migration history”, reset the local database (wipes all local data):
npx prisma migrate reset
npx prisma db seedmigrate reset against production. It drops every table. Use it only on local / development databases.PostgreSQL — connection refused
Common causes and fixes for ECONNREFUSED 127.0.0.1:5432:
- The Postgres container isn’t running — check
docker ps, thendocker start classimarket-db. - Port 5432 is already in use by a local Postgres install — stop it (
brew services stop postgresql) or change the Docker port mapping. - Wrong credentials or DB name in
DATABASE_URL— it must match thePOSTGRES_USER,POSTGRES_PASSWORD, andPOSTGRES_DByou passed to Docker. - Hosted DB (Railway / Supabase / Neon) requires SSL — append
?sslmode=requireto the connection string.
Flutter — can’t reach the API
- Android emulator: use
http://10.0.2.2:3000/api—localhostrefers to the emulator itself, not the host machine. - iOS simulator:
http://localhost:3000/apiworks because the simulator shares the host network. - Physical device: phone and dev machine must be on the same Wi-Fi. Use your machine’s LAN IP (e.g.
http://192.168.1.42:3000/api). On macOS, allow incoming connections to Node in System Settings → Network → Firewall. - HTTPS only on iOS release builds — plain HTTP is blocked by App Transport Security. Deploy behind HTTPS for production builds.
Google Maps — grey / blank map
A grey tile almost always means the Maps SDK is not authorised for your app:
- Enable Maps SDK for Android and Maps SDK for iOS in Google Cloud Console → APIs & Services.
- Enable billing on the Google Cloud project — Maps requires a billing account even for the free tier.
- Remove overly strict key restrictions while debugging, then re-add them once you confirm maps render.
- Ensure the key is set in
flutter_app/android/local.propertiesand inside<dict>offlutter_app/ios/Runner/Info.plist.
Firebase — push notifications not arriving
google-services.jsonandGoogleService-Info.plistmust be placed in the correct folders and the app’s package / bundle ID must exactly match the Firebase app registration.- iOS only: upload an APNs authentication key in Firebase Console → Project Settings → Cloud Messaging.
- Backend
FIREBASE_PRIVATE_KEYenv var must keep the literal\nnewline escapes — copy it from the service-account JSON as-is and wrap it in double quotes. - Test with a real device — the iOS Simulator does not receive remote push notifications.
Android release build is unsigned / rejected by Play Console
Make sure you completed Release signing (sign your Android release build). Also verify:
- The
storeFilepath inkey.propertiesis absolute and readable. - You’re building in release mode:
flutter build appbundle --release. - For Play Store upload, prefer
.aab(App Bundle) over.apk.
Node / npm — mismatched versions
The web app requires Node 18+. If npm install fails with engine warnings or unexpected syntax errors, check node -v and upgrade via nvm: nvm install 20 && nvm use 20.
Changelog
Every version published so far. Updates are free for the life of the item.
Version 1.1.0 · 2026
- Offers & price negotiation (offer / counter / accept / decline / withdraw, live in chat).
- Stack upgraded to Next.js 16, React 19, Prisma 7, Tailwind v4; Flutter dependencies refreshed.
Version 1.0.0 · 2025
- Initial release — full feature set
Support and licensing
We’re here to help you get ClassiMarket running smoothly. We respond to all support requests within 1–2 business days (Monday–Friday).
Enjoying ClassiMarket? A 5-star review on CodeCanyon means the world to us and helps other buyers find the product. Rate on CodeCanyon
What support covers
- Six months of support from purchase, extendable at checkout
- Support covers bugs in the template and questions about how it is put together
- It does not cover custom feature work, third-party API changes or store review outcomes
- The Regular licence covers one free end product. Charging users for the app itself needs the Extended licence