Skip to main content

Overview

Trackmart’s ordering system allows buyers to request product deliveries (such as sand and other construction materials) with real-time pricing and driver selection. The order workflow integrates quantity selection, dynamic pricing, payment methods, and driver assignment.

Order Creation Flow

The order creation process follows these key steps:
1

Select a Driver

Users can search for available drivers by name or phone number, or browse through the list of registered merchants.
2

Configure Order Details

Set the quantity, unit type (Truck or Tonne), and payment method (Mobile money or Cash).
3

Review Pricing

Real-time pricing is fetched and displayed, showing the cost per unit and total amount.
4

Confirm and Submit

Review the order details in a confirmation dialog before submitting the delivery request.

Product Selection

Currently, Trackmart focuses on construction materials, primarily sand delivery.
The product type is currently set to ‘Sand’ in the codebase, but the architecture supports expansion to other products.

Available Units

Truck

Order by full truck load

Tonne

Order by weight (metric tonnes)

Quantity Management

Users can adjust order quantity through multiple methods:
void _minus() {
  if (double.parse(_moneyController.text) >= 1) if (mounted)
    setState(() {
      _moneyController.text =
          '${(double.tryParse(_moneyController.text) ?? 0) - 1}';
      _moneyController2.text =
          '${(double.parse(_moneyController.text) * (rate ?? 0)).toStringAsFixed(0)}';
    });
}

void _plus() {
  if (mounted)
    setState(() {
      _moneyController.text =
          '${(double.tryParse(_moneyController.text) ?? 0) + 1}';
      _moneyController2.text =
          '${((double.tryParse(_moneyController.text) ?? 0) * (rate ?? 0)).toStringAsFixed(0)}';
    });
}
The minimum order quantity is 1 unit. The system automatically calculates the total price when quantity changes.

Dynamic Pricing

Pricing is fetched dynamically and updates in real-time:
_moneyController.addListener(() {
  if (_moneyController.text.isEmpty) {
    if (mounted)
      setState(() {
        _moneyController2.text = "";
      });
  } else if (_focus.hasFocus) {
    {
      _moneyController2.text =
          (double.parse(_moneyController.text) * (rate ?? 0))
              .toStringAsFixed(0);
    }
  }
});
The app displays:
  • Rate per unit: 1 [Unit] of [Product] costs UGX [Price]
  • Total amount: Automatically calculated based on quantity × rate
  • Currency: UGX (Uganda Shillings)
Orders cannot be submitted if pricing information is unavailable. Ensure internet connectivity for rate fetching.

Payment Methods

Trackmart supports two payment methods:
The default payment method for digital transactions through mobile money services.

Order Data Structure

Each order contains the following information:
Order makeOrderNow() {
  return Order(
    destlat: currentLat,
    destlong: currentLong,
    driverId: filteredDrivers[_selected].id,
    driverName: filteredDrivers[_selected].name,
    driverPhone: filteredDrivers[_selected].phone,
    userId: currentUserId,
    userName: currentUserName,
    userPhone: currentUserPhone,
    quantity: double.parse(_moneyController.text),
    payment: _paymnt,
    price: (rate ?? 0),
    unit: _unit,
    timestamp: DateTime.now().millisecondsSinceEpoch,
  );
}
  • destlat/destlong: Delivery destination coordinates
  • driverId/driverName/driverPhone: Selected driver information
  • userId/userName/userPhone: Buyer information
  • quantity: Amount ordered
  • payment: Payment method
  • price: Unit price at time of order
  • unit: Measurement unit (Truck/Tonne)
  • timestamp: Order creation time in milliseconds

Submitting Orders

Orders are submitted to Firebase Realtime Database:
_requestDelivery(Order order, BuildContext context) async {
  showModal('Requesting...', context);
  String key = 'O:${DateTime.now().millisecondsSinceEpoch}';
  await databaseReference
      .child('Drivers')
      .child(order.driverId)
      .child('requests')
      .update({
    key: order.toMap(currentUserId),
  });
  await databaseReference
      .child('buyers')
      .child(currentUserId)
      .child('requests')
      .update({
    key: order.toMap(currentUserId),
  });
  Navigator.of(context).pop();
}
Orders are stored under both the driver’s and buyer’s nodes in the database for efficient querying and real-time updates.

Order Validation

Before submission, the system validates:
  1. Driver Selection: A driver must be selected or searched
  2. Quantity: Must be a valid number greater than 0
  3. Pricing: Rate must be successfully fetched
  4. Location: User’s current location must be available
if (_selected >= 0 &&
    rate != null &&
    double.tryParse(_moneyController.text) != null) {
  // Show confirmation dialog
} else {
  // Show validation error
}
The ordering interface is accessible from the main tab navigation:
TabBar(
  controller: _tabController,
  tabs: [
    Tab(text: ('Chats')),
    Tab(text: ('Request')),  // Ordering tab
    Tab(text: ('Orders')),
  ],
)

Next Steps

Track Orders

Monitor delivery progress in real-time

Chat with Drivers

Communicate directly with delivery drivers

Build docs developers (and LLMs) love