obd2 0.10.2 copy "obd2: ^0.10.2" to clipboard
obd2: ^0.10.2 copied to clipboard

PlatformAndroid

A production-ready SAE J1979 OBD-II SDK for Flutter with BLE support for ELM327-compatible adapters. Provides real-time telemetry, DTC parsing, and type-safe PID handling.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';

import 'core/bluetooth_helper.dart';
import 'core/functions.dart';
import 'core/telemetry_provider.dart';

final GlobalKey<ScaffoldMessengerState> snackBarKey = GlobalKey<ScaffoldMessengerState>();
const Color background = Color(0xFF131313);

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]);

  runApp(
    ChangeNotifierProvider(
      create: (context) => TelemetryProvider()..initializeProvider(),
      child: const SampleApp(),
    ),
  );
}

class SampleApp extends StatelessWidget {
  const SampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        fontFamily: "American Captain Patrius",
      ),
      home: const DashboardPage(),
    );
  }
}

enum ValueType { percent, temperature }

class DashboardPage extends StatelessWidget {
  const DashboardPage({super.key});

  // Fixed widths for the columns
  static const double columnWidth = 220.0;
  static const double spacingHeight = 40.0;

  /// Formats an integer with thousands separators (commas).
  ///
  /// Converts a numeric value like:
  /// ```
  /// 13444  ->  13,444
  /// 100000 ->  100,000
  /// 999    ->  999
  /// ```
  ///
  /// This method does **not** rely on external packages (e.g., `intl`)
  /// and uses a regular expression to insert commas every three digits,
  /// starting from the right side of the number.
  ///
  /// Intended for UI display purposes such as formatting RPM values.
  ///
  /// Example:
  /// ```dart
  /// final formatted = _formatWithCommas(13444);
  /// print(formatted); // 13,444
  /// ```
  ///
  /// Returns the formatted string representation of [value].
  String _formatWithCommas(int value) {
    final stringValue = value.toString();
    return stringValue.replaceAllMapped(
      RegExp(r'\B(?=(\d{3})+(?!\d))'),
          (match) => ',',
    );
  }

  @override
  Widget build(BuildContext context) {
    final provider = context.watch<TelemetryProvider>();

    Widget telemetryItem(String label, double? value, { ValueType? type }) {
      final displayValue = _formatWithCommas((value ?? 0).round());
      String finalType = "";

      if (type != null) {
        switch (type) {
          case ValueType.temperature:
            finalType = "°C";
            break;
          case ValueType.percent:
            finalType = "%";
            break;
        }
      }

      return Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                displayValue,
                style: const TextStyle(
                  fontSize: 55,
                  color: Colors.white,
                  fontWeight: FontWeight.bold,
                  height: 1,
                  letterSpacing: 2,
                  fontFeatures: [FontFeature.tabularFigures()],
                ),
              ),
              const SizedBox(width: 3),
              Text(
                finalType,
                style: TextStyle(fontSize: 15, color: Colors.white70),
              ),
            ],
          ),
          Text(
            label.toUpperCase(),
            style: const TextStyle(fontSize: 12, color: Colors.grey),
          ),
        ],
      );
    }

    final bool isBluetoothConnected = provider.scanner?.isConnected == true;

    String statusMessage;

    if (provider.isStreaming) {
      statusMessage = "Stream Started";
    } else if (isBluetoothConnected) {
      statusMessage = "Bluetooth Connected";
    } else {
      statusMessage = "Bluetooth Disconnected";
    }

    /// Toggles the telemetry data stream on or off.
    ///
    /// Handles error logging and user notification via snackbars if the
    /// connection or stream fails.
    ///
    /// ### Usage:
    /// ```dart
    /// toggleStream();
    /// ```
    void toggleStream() {
      if (provider.isStreaming) {
        try {
          provider.stopTelemetryStream();
          snackBar(context, "Live Stream Stopped!");
        } catch (error, stack) {
          logError(error, stack, message: "Error when stopping stream");
          snackBar(context, "Stream break error");
        }
        return;
      }

      try {
        if (provider.scanner == null) {
          snackBar(context, "Scanner is missing. Connect to one");
          return;
        }
        provider.startTelemetryStream();
        snackBar(context, "Live stream started!");
      } catch (error, stack) {
        logError(error, stack, message: 'Failed to start live data streaming');
        snackBar(context, "Live stream failed! Something went wrong");
        provider.stopTelemetryStream();
      }
    }

    return Scaffold(
      backgroundColor: background,
      body: Column(
        children: [
          const Spacer(flex: 1),
          Expanded(
            flex: 7,
            child: Center(
              child: Table(
                // This defines the strict width of your grid
                defaultColumnWidth: const FixedColumnWidth(columnWidth),
                children: [
                  TableRow(
                    children: [
                      telemetryItem("Speed (KPH)", provider.vehicleSpeed),
                      telemetryItem("RPM", provider.engineRpm),
                      telemetryItem("Coolant", provider.coolantTemperature, type: ValueType.temperature),
                    ],
                  ),
                  // Spacer Row
                  const TableRow(
                    children: [
                      SizedBox(height: spacingHeight),
                      SizedBox(height: spacingHeight),
                      SizedBox(height: spacingHeight),
                    ],
                  ),
                  TableRow(
                    children: [
                      telemetryItem("Throttle", provider.throttlePosition, type: ValueType.percent),
                      telemetryItem("Load", provider.engineLoad, type: ValueType.percent),
                      telemetryItem("Timing", provider.timingAdvance, type: ValueType.percent),
                    ],
                  ),
                ],
              ),
            ),
          ),
          // Bottom Bar
          Expanded(
            flex: 2,
            child: Container(
              padding: const EdgeInsets.symmetric(horizontal: 30),
              color: Colors.white.withValues(alpha: 0.03),
              child: Row(
                children: [
                  Icon(
                    Icons.circle,
                    size: 12,
                    color: isBluetoothConnected ? Colors.green : Colors.red,
                  ),
                  const SizedBox(width: 10),
                  Text(
                    statusMessage,
                    style: const TextStyle(color: Colors.white, fontSize: 16, letterSpacing: 1),
                  ),
                  const Spacer(),
                  _ControlButton(
                    icon: Icons.bluetooth,
                    color: isBluetoothConnected ? Colors.blue : Colors.grey[800]!,
                    onPressed: () => BluetoothHelper.handleShowDevices(context, provider),
                  ),
                  const SizedBox(width: 15),
                  _ControlButton(
                    icon: provider.isStreaming ? Icons.stop : Icons.play_arrow,
                    color: provider.isStreaming ? Colors.redAccent : Colors.green,
                    onPressed: () => toggleStream(),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

class _ControlButton extends StatelessWidget {
  final IconData icon;
  final Color color;
  final VoidCallback onPressed;

  const _ControlButton({required this.icon, required this.color, required this.onPressed});

  @override
  Widget build(BuildContext context) {
    return Material(
      color: color,
      borderRadius: BorderRadius.circular(12),
      child: InkWell(
        onTap: onPressed,
        borderRadius: BorderRadius.circular(12),
        child: Padding(
          padding: const EdgeInsets.all(12.0),
          child: Icon(icon, color: Colors.white, size: 28),
        ),
      ),
    );
  }
}
1
likes
160
points
180
downloads
screenshot

Publisher

unverified uploader

Weekly Downloads

A production-ready SAE J1979 OBD-II SDK for Flutter with BLE support for ELM327-compatible adapters. Provides real-time telemetry, DTC parsing, and type-safe PID handling.

Repository (GitHub)
View/report issues

Topics

#obd2 #automotive #telemetry #bluetooth #elm327

Documentation

API reference

License

MPL-2.0 (license)

Dependencies

flutter, flutter_blue_plus, math_expressions

More

Packages that depend on obd2