Service-based business (take saloon) that manages appointments across multiple independent service counters.
OmniBook is a Flutter-based booking application designed for businesses that run multiple service counters at the same time.
The app helps users:
- Select services and calculate total duration and price
- See real-time-like slot availability
- Understand how many counters are free per slot
- Choose a counter before final booking confirmation
- Flutter (Dart)
- State Management: flutter_bloc / bloc
- Equality utilities: equatable
- SVG rendering: flutter_svg
- UI/UX assets: custom SVG + custom font
This project follows a feature-first layered structure with Cubit-based state management.
flowchart TD
UI[Presentation Layer\nScreens + Widgets] --> CUBIT[BookingCubit]
CUBIT --> STATE[BookingState]
CUBIT --> SERVICE[BookingService\nBusiness Rules + Slot Logic]
SERVICE --> MODELS[Models\nBooking / Service / SlotStatus]
lib/features/presentation/: screens, widgets, theme, UI utilitieslib/features/cubit/: booking state and business interaction layerlib/features/services/: slot and counter availability logiclib/features/models/: domain entities and data models
- Multi-service selection with dynamic total duration and price
- Date-aware slot generation
- Counter availability check per slot
- Dynamic disabling (grey out) for invalid slots without continuous gap
- Counter selection before confirmation
- Booking confirmation summary
- Asset-driven UI (SVG icons, splash logo, custom font)
Availability is computed in BookingService with a simple but reliable per-counter check:
- For a candidate start slot and selected total duration, the app first validates business-hour boundaries.
- It then checks each counter independently (1 to 3).
- For each counter, it tests overlap against that counter's existing bookings only.
- Any counter with no overlap for the full requested duration is marked free.
- The slot is enabled when the free-counter list is not empty; otherwise it is greyed out.
List<int> findFreeCounters(DateTime start, int duration) {
if (!isWithinBusinessHours(start, duration)) {
return <int>[];
}
final freeCounters = <int>[];
for (var counterId = 1; counterId <= totalCounters; counterId++) {
if (isCounterFree(counterId, start, duration)) {
freeCounters.add(counterId);
}
}
return freeCounters;
}
SlotStatus checkSlot(DateTime start, int duration) {
final freeCounters = findFreeCounters(start, duration);
return SlotStatus(
isAvailable: freeCounters.isNotEmpty,
freeCounters: freeCounters,
);
}This ensures users only see slots where at least one counter can serve the entire service window continuously, not just the first part of it.
The project currently uses in-memory mock data to simulate a real salon/barber workflow.
Source: lib/features/presentation/data/sample_services.dart
| Service | Duration (mins) | Price |
|---|---|---|
| Quick Trim | 15 | 20 |
| Haircut & Style | 45 | 50 |
| Full Grooming | 90 | 120 |
| Beard Trim | 20 | 15 |
Source: lib/features/services/booking_service.dart
All mock bookings are generated for the current date using DateTime.now().
| Counter | Booked From | Booked To |
|---|---|---|
| Counter 1 | 10:00 | 11:00 |
| Counter 2 | 10:30 | 11:30 |
| Counter 3 | 09:00 | 10:30 |
- Business hours:
09:00to18:00 - Slot interval: every
30 minutes - A slot is enabled only if at least one counter has a continuous free gap equal to selected total service duration.
gantt
title Mock Bookings on Current Date
dateFormat YYYY-MM-DD HH:mm
axisFormat %H:%M
tickInterval 30minute
section Counter 1
Busy : 2026-03-20 10:00, 60m
section Counter 2
Busy : 2026-03-20 10:30, 60m
section Counter 3
Busy : 2026-03-20 09:00, 90m
| Candidate Start Slot | Free Counter(s) | Slot State |
|---|---|---|
| 09:00 | 2 | Enabled |
| 09:30 | None | Greyed out |
| 10:00 | None | Greyed out |
| 10:30 | 3 | Enabled |
This is the same dynamic disabling logic used in the app: if no counter can provide a continuous duration window (for example 90 mins), that slot is disabled.
- Flutter SDK (stable)
- Dart SDK (included with Flutter)
- Clone the repository
- Install dependencies:
flutter pub get- Run the app:
flutter runUsed Figma Community for taking app UI design inspiration.
Claude was used for helping project planning, logic verification, and discussing architecture decisions.
For coding help, GitHub Copilot was used.
- Backend/API integration for live bookings
- Authentication and user profiles
- Push notifications and reminders
- Admin dashboard for counter scheduling
- Payment integration
Thanks for checking out this project. Love to do this.
By Souvik