· case-study · 5 min read
Building a Construction Labor Matching Platform with Flutter and Serverless APIs
A full-stack case study on building a mobile-first platform that connects daily construction workers with construction companies using Flutter, Hono, Cloudflare Workers, Supabase, R2, and a Next.js admin console.
In one sentence
We built a mobile-first matching platform that connects daily construction workers with construction companies. The product includes a worker app, a company workflow, an admin console, serverless APIs, file storage, authentication, and privacy-aware operations.
At first glance it looks like job posts and applications. The real difficulty was state modeling. Who has signed up? Who has been approved? Which posting is open? Which application is selected, rejected, cancelled, or completed? If those states are vague, the whole marketplace becomes unreliable.
A state-driven product, not just a job board
The first thing to define was not the technology stack. It was the roles.
- Workers: job discovery, applications, schedules, documents, surveys
- Companies: company profile, job postings, applicant review, selection, rejection, evaluations
- Admins: company approval, document review, notices, banners, ads, reputation, reporting
This role model became the basis for navigation, API authorization, database state, and QA scenarios. User state controls access. Company state controls whether jobs can be posted. Job postings and applications each move through their own lifecycle.
Loose state definitions create inconsistent products. A button may appear in the app while the API rejects the action. An admin may approve a company, while the mobile app still treats it as pending. Early development was therefore focused on aligning the PRD, data model, API responses, and UI states around the same language.
Splitting worker and company experiences in one Flutter app
The mobile app is one Flutter codebase, but it contains two product experiences. Workers need fast job discovery and schedule management. Companies need a business-like workflow for postings and applicants.
The mobile stack is built around Flutter, GoRouter, Riverpod, Dio, and secure storage. GoRouter handles authenticated routes and role-specific tab structures. Riverpod manages session state, profiles, job lists, calendars, and ads. Dio provides a shared API client with JWT injection, refresh handling, 401 retry behavior, and response parsing.
The main structural decision was to create separate shells by role. The worker shell includes Home, Jobs, Calendar, My Page, and Settings. The company shell includes Home, Job Management, Calendar, Reputation, and Settings. API clients, design tokens, and shared components are reused, but the home screens and core workflows are separated.
The lesson was simple: in role-based apps, the hard question is not how much UI to share. It is where to split the workflow.
Hono and Cloudflare Workers on the backend
The backend was organized around Hono on Cloudflare Workers, Supabase Postgres, Cloudflare R2 for files, and KV for lightweight state and caching needs.
In a serverless platform, boundaries matter more than routes. Authentication middleware, admin permissions, company permissions, and worker permissions need to be separated clearly. File uploads, private documents, and personal data require stricter checks than public content or banners.
File handling became its own product concern. Profile images, document images, and ad banners all have different access rules. Public assets must load quickly, while private documents must never be exposed without authorization. Upload APIs therefore needed to understand file type, owner, and access policy, not only object storage keys.
Authentication, personal data, and document uploads
Authentication is not just a signup step in this kind of product. It is part of the trust model. Phone verification, social login, company approval, worker document submission, and admin review all interact.
Document upload before full signup required care. Before a user has a valid session, the app cannot simply upload private files into protected storage. The mobile app first keeps selected files in local state, completes registration, then requests presigned upload information and uploads the files directly.
Personal data also had to be useful without being overexposed. Applicant lists show only the necessary level of information. Admin screens avoid showing sensitive documents or account data more broadly than needed. Security is not only a backend concern. It is also an admin UX concern.
The admin console was not secondary
In a two-sided marketplace, operations are part of the product. If a company signs up but is never approved, postings cannot be created. If a worker submits documents but nobody reviews them, application quality drops.
The Next.js admin console was therefore designed as an operations pipeline rather than a reporting page. It covers company approval, worker document review, notices, banners, ads, reputation inspection, and reporting. The admin app shares the backend with the mobile app, but its interface and permission model are separate.
Admin authentication moved toward httpOnly cookies. That improved token handling, but it also changed the frontend contract. Requests must include credentials, the server must allow credentialed CORS, and SameSite and Secure policies must match the deployed domain.
Production deployment traps
Several bugs appeared only in production. The clearest example was localhost. The admin app is built with Next.js, so public environment variables can be inlined at build time. If a local environment file wins during build, the production admin site can still point to a local API.
The Flutter app had a similar risk. If the release build does not receive the production environment flag, it may default to a development API. Login or social auth failures can look like backend bugs when the real issue is build configuration.
Release validation therefore became a system of gates: backend typecheck, tests and lint, admin build, mobile analysis and release build checks, route audits, schema comparisons, file access validation, and real-device authentication tests. The backend test suite grew beyond 80 tests, with increasing focus on permissions and state transitions.
What we learned
State before CRUD
A matching platform is not just a set of database tables for jobs and applications. Roles and state transitions decide whether the product can be trusted.
Admin is part of the product
Approvals, document review, notices, ads, and reputation management are not back-office extras. They are what keep the marketplace moving.
Deployment is engineering work
Build-time variables, runtime flags, OAuth callbacks, cookie policies, and file permissions all determine whether the code works in the real product.
FAQ
How long does a similar marketplace take to build?
If the product has multiple roles, approvals, file uploads, authentication, ads, or payments, 12-20 weeks is a realistic range. The state model and permission policy should be designed before screens are finalized.
Can one Flutter app support worker and company experiences?
Yes. Shared authentication, API clients, design tokens, and components work well. Navigation, home screens, signup flow, and main actions should usually be separated by role.
Is a serverless backend a good fit?
It can be. The tradeoff is that file permissions, DB access, test setup, deployment variables, and auth callbacks need careful attention.
If you are planning a marketplace for construction, logistics, education, care, or any other role-based service, start a project with your user roles and key state transitions. The first useful conversation is usually about workflow and permissions, not screen count.
Hammergrid Lab builds mobile apps, serverless backends, admin consoles, and data models for real product workflows.
Related services
3D web, 3dweb, 3D configurator, 3D product configurator, 3D product viewer, WebGPU, Three.js