This repository contains the solution for the Mobile Engineer tech test. It is a Flutter application designed to remotely control and monitor a simulated robot device.
The project demonstrates:
- A scalable, modular architecture (Clean Architecture, Monorepo).
- Robust state management (BLoC).
- Graceful handling of intermittent connectivity and offline command queuing.
- Native device integration.
(Watch the full video demonstration: demo_video.mp4)
| Dashboard (Status) | Control (Disconnection) | Control (Queued Commands) |
|---|---|---|
| Beattery dead Offline | Control Screen | Settings Screen |
|---|---|---|
Breakdown of UI interactions:
- Splash & Connect: The initial launch seamlessly transitions into the manual IP pairing screen to configure the simulator URL.
- Dashboard (Telemetry): A live view reading the 1Hz WebSocket stream to display real-time battery voltage alongside polished 3D assets.
- Control: An interactive directional pad routing synchronous
POST /moveandPOST /stopintents. - Offline Reconnection: When the Wi-Fi drops or the backend simulator triggers
--chaosmode crashes, an active "Reconnecting... X seconds" banner appears. Movements hit an offline queue. - Battery Dead Edge-case: If the simulator completely drains to 0%, the UI automatically locks into a mandatory sheet forcing a device disconnect.
The project is structured as a monorepo managed by Melos, divided into specialized packages:
app/: The Flutter presentation layer containing UI, routing, and native platform integrations.robot_service/: The core business logic, adhering to Domain-Driven Design (DDD) and Clean Architecture principles. It contains three layers:- Domain: Interfaces and models (
RobotStatus,RobotCommand). - Application: State management using the BLoC pattern (
RobotConnectionBloc,RobotTelemetryBloc,RobotCommandBloc). - Infrastructure: API clients, WebSocket streams, and offline queueing (SharedPreferences).
- Domain: Interfaces and models (
design_system/: A separate package for UI components, theming, colors, and typography, ensuring consistent design across the app.robot_simulator/: A lightweight local backend written in Dart (using Shelf) that acts as the simulated robot device.
This project uses Melos and Dart 3.5+ workspace features to manage the multi-package setup. This provides several key benefits:
- Shared Dependencies: Using a unified root
pubspec.yaml(workspace resolution), all internal packages share the exact same dependency versions. This prevents version resolution conflicts across packages and drastically reducespub gettimes. - Unified Tooling: Running tests, code generation, and analyzing across all packages can be done with a single command (e.g.,
melos run build:codegen:build:all). - Strict Boundaries: Splitting the codebase into separate packages strictly enforces architectural boundaries, ensuring that core business logic (
robot_service) cannot accidentally import Flutter UI libraries.
We use the BLoC (Business Logic Component) pattern (flutter_bloc) to manage state predictably and scalably:
RobotConnectionBloc: Manages the connection lifecycle (connecting, connected, disconnected, connection failed).RobotTelemetryBloc: Subscribes to the live telemetry WebSocket stream, pushing real-timeliveandstalestates to the UI.RobotCommandBloc: Handles user intents (Move/Stop). If offline, it defers to theOfflineCommandQueueand flushes them upon reconnection.
- API Behavior: The robot communicates over an HTTP REST API for commands (Connect, Disconnect, Move, Stop) and a WebSocket (
/ws/telemetry) for real-time status and battery updates. - Command Lifecycles: A "Stop" command is treated with higher priority than "Move". If the app comes back online, issuing a queued "Stop" will supersede and clear any pending "Move" commands.
- Network: Connectivity drops are common. The app actively monitors WebSocket health and ping failures to detect disconnection, moving into a
stalestate if the telemetry stream drops. - Trust/Security: For this simulated tech test, pairing is done via a manual local IP/URL entry without OAuth or TLS cert pinning.
- Android Foreground Service: To ensure the user can instantly stop the robot even if the app is backgrounded, an Android Foreground Service is implemented. It displays an ongoing notification with a quick-action "Stop" button while the robot is moving. (Note: An iOS Live Activity widget was originally explored but removed to ensure smooth cross-platform portability without requiring paid Apple Developer provisioning profiles).
- API Retries: HTTP requests use a 3x exponential backoff strategy with a 5s per-attempt timeout.
- WebSocket Reconnects: Automatically attempts to reconnect the telemetry stream with an exponential backoff capped at 30 seconds.
- Offline Command Queue: If a user sends a movement command while the WebSocket/network is down, the command is persisted locally. Once the connection is re-established, the queue is flushed to the simulated API in FIFO order.
- UI Feedback: The UI immediately reflects
stalestates (greyed out or warning indicators) if telemetry is lost.
- Flutter SDK (3.24+) & Dart (3.5+)
- An Android Emulator, iOS Simulator, or physical device.
- Melos: Install via
dart pub global activate melos
Run the following from the root of the workspace to install dependencies and generate required code (Freezed, JSON Serializable, AutoRoute, Injectable):
melos bootstrap
melos run build:codegen:build:allThe simulator is a pure-Dart server. Start it in a separate terminal window from the project root:
sh scripts/run_simulator.sh --port 8080 --chaos --latency 50--chaosrandomly drops connections and fails 5% of commands to test resilience.--latency 50adds a 50ms artificial delay.
Open a new terminal and run your desired platform script:
sh scripts/run_dev_android.sh
# OR
sh scripts/run_dev_ios.shAlternatively, you can navigate to the app/ directory and run flutter run manually.
Connection URL:
- If running on an iOS Simulator, enter:
http://127.0.0.1:8080 - If running on an Android Emulator, enter:
http://10.0.2.2:8080 - If using a physical device, enter your computer's local LAN IP (e.g.,
http://192.168.x.x:8080).
The project includes unit and bloc tests, verifying state transitions and the offline queue logic. Run all tests workspace-wide using Melos:
melos run test:unitIf this were moving to a production environment, here are several improvements that should be considered:
- Security: Implement mTLS (Mutual TLS) for robot-device pairing, enforce HTTPS/WSS, and add certificate pinning to prevent MITM attacks.
- Protocol Efficiency: Replace JSON over REST/WebSockets with gRPC/Protobuf for lower latency, smaller payload sizes, and strictly typed multi-language contracts.
- Observability: Integrate Sentry (Crashlytics) and OpenTelemetry/Datadog to monitor real-time command latency and failure rates in the wild.
- E2E Testing: Add Maestro or integration_test suites to run fully automated end-to-end UI tests checking offline-to-online reconnection flows.
- Fleet Management: Extend the connection flow to support discovering and managing multiple robots (e.g., via mDNS or Bluetooth LE).