flutter_aclas_scale 1.0.0 copy "flutter_aclas_scale: ^1.0.0" to clipboard
flutter_aclas_scale: ^1.0.0 copied to clipboard

Aclas Scale Flutter Plugin.

example/lib/main.dart

import 'dart:async';
import 'dart:io' show Platform;
import 'package:flutter/material.dart';
import 'package:flutter_aclas_scale/flutter_aclas_scale.dart';
import 'package:permission_handler/permission_handler.dart';

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: ScaleTestScreen(),
    );
  }
}

class ScaleTestScreen extends StatefulWidget {
  const ScaleTestScreen({super.key});

  @override
  State<ScaleTestScreen> createState() => _ScaleTestScreenState();
}

class _ScaleTestScreenState extends State<ScaleTestScreen> {
  final AclasScale _scale = AclasScale();
  StreamSubscription? _eventSub;

  String _status = 'Disconnected';
  double _weight = 0.0;
  String _type = 'usb';
  bool _scanning = false;
  List<String> _devices = const [];

  Map _deviceData = {};
  Map _weightInfo = {};

  bool _isConnected = false;

  void _snack(String msg) {
    if (!mounted) return;
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(msg),
        duration: const Duration(milliseconds: 1500),
      ),
    );
  }

  @override
  void initState() {
    super.initState();
    _eventSub = _scale.events.listen((event) {
      final evt = event['event'];
      if (evt == 'connected') {
        _isConnected = true;
        _deviceData = event;
        setState(() => _status = 'Connected');
        _snack('Connected');
      } else if (evt == 'disconnected') {
        _isConnected = false;
        setState(() {
          _status = 'Disconnected';
          _weight = 0.0;
        });
        _snack('Disconnected');
      } else if (evt == 'error') {
        setState(
              () => _status = 'Error: ${event['code'] ?? ''} ${event['msg'] ?? ''}',
        );
        _snack('Error: ${event['code'] ?? ''} ${event['msg'] ?? ''}');
      } else if (evt == 'usb_attached') {
        _snack('USB attached');
        _refreshDevices();
      } else if (evt == 'usb_detached') {
        _isConnected = false;
        _snack('USB detached');
      } else if (evt == 'weight') {
        _weightInfo = event;
        final w = (event['weight'] as num?)?.toDouble() ?? 0.0;
        setState(() => _weight = w);
      }
    });

    _requestPermissions().then((_) => _refreshDevices());
  }

  @override
  void dispose() {
    _eventSub?.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ACLAS Scale Demo')),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Device Info',
                      style: const TextStyle(
                        fontSize: 15,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    Text(
                      'statusOk: ${_deviceData["statusOk"]}',
                      style: const TextStyle(fontSize: 12),
                    ),
                    Text(
                      'gravity: ${_deviceData["gravity"]}',
                      style: const TextStyle(fontSize: 12),
                    ),
                    Text(
                      'protocol: ${_deviceData["protocol"]}',
                      style: const TextStyle(fontSize: 12),
                    ),
                    Text(
                      'id: ${_deviceData["id"]}',
                      style: const TextStyle(fontSize: 12),
                    ),
                    Text(
                      'fw: ${_deviceData["fw"]}',
                      style: const TextStyle(fontSize: 12),
                    ),
                    Text(
                      'sdk: ${_deviceData["sdk"]}',
                      style: const TextStyle(fontSize: 12),
                    ),
                    Text(
                      'rangeCode: ${_deviceData["rangeCode"]}',
                      style: const TextStyle(fontSize: 12),
                    ),

                    /*| rangeCode | rangeKg |
                | --------- | ------- |
                | 6         | 15 kg   |
                | 7         | 30 kg   |
                | 8         | 60 kg   |
                | 9         | 150 kg  |
                | 10        | 300 kg  |
                | 11        | 600 kg  |
                */
                  ],
                ),
                Column(
                  children: [
                    Text(
                      'Weight Info',
                      style: const TextStyle(
                        fontSize: 15,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    Text(
                      'weight: ${_weightInfo["weight"]}',
                      style: const TextStyle(fontSize: 12),
                    ),
                    Text(
                      'unit: ${_weightInfo["unit"]}',
                      style: const TextStyle(fontSize: 12),
                    ),
                    Text(
                      'decimal: ${_weightInfo["decimal"]}',
                      style: const TextStyle(fontSize: 12),
                    ),
                    Text(
                      'stable: ${_weightInfo["stable"]}',
                      style: const TextStyle(fontSize: 12),
                    ),
                    Text(
                      'tare: ${_weightInfo["tare"]}',
                      style: const TextStyle(fontSize: 12),
                    ),
                    Text(
                      'zero: ${_weightInfo["zero"]}',
                      style: const TextStyle(fontSize: 12),
                    ),
                  ],
                ),
              ],
            ),

            const SizedBox(height: 40),
            Text('Status: $_status', style: const TextStyle(fontSize: 18)),
            const SizedBox(height: 12),
            Text(
              'Weight: ${_weight.toStringAsFixed(2)}',
              style: const TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 24),
            Row(
              children: [
                const Text('Mode:'),
                const SizedBox(width: 12),
                DropdownButton<String>(
                  value: _type,
                  items: const [
                    DropdownMenuItem(value: 'usb', child: Text('USB')),
                    DropdownMenuItem(value: 'serial', child: Text('Serial')),
                    DropdownMenuItem(value: 'ble', child: Text('BLE')),
                  ],
                  onChanged: (v) {
                    if (v == null) return;
                    setState(() {
                      _type = v;
                    });
                    _refreshDevices();
                  },
                ),
                const Spacer(),
                ElevatedButton(
                  onPressed: _scanning ? null : _refreshDevices,
                  child: Text(_scanning ? 'Scanning...' : 'Scan'),
                ),
              ],
            ),
            const SizedBox(height: 12),
            Expanded(
              child: _devices.isEmpty
                  ? const Center(child: Text('No devices'))
                  : ListView.separated(
                itemCount: _devices.length,
                separatorBuilder: (_, __) => const Divider(height: 1),
                itemBuilder: (ctx, i) {
                  final name = _devices[i];
                  return ListTile(
                    title: Text(name),
                    onTap: () => _connectToIndex(i),
                  );
                },
              ),
            ),
            const SizedBox(height: 12),
            Wrap(
              spacing: 12,
              runSpacing: 12,
              children: [
                Visibility(
                  visible: _isConnected == true,
                  child: ElevatedButton(
                    onPressed: () async {
                      _snack('Disconnecting');
                      await _scale.disconnect();
                    },
                    child: const Text('Disconnect'),
                  ),
                ),
                ElevatedButton(
                  onPressed: () async {
                    final w = await _scale.getWeight();
                    setState(() => _weight = w);
                    _snack('Weight: $w');
                  },
                  child: const Text('Get Weight'),
                ),
                ElevatedButton(
                  onPressed: () async {
                    final ok = await _scale.zero();
                    _snack(ok ? "Zero Success" : "Zero Failed");
                  },
                  child: const Text('Zero'),
                ),
                ElevatedButton(
                  onPressed: () async {
                    final ok = await _scale.tare();
                    _snack(ok ? "Tare Success" : "Tare Failed");
                  },
                  child: const Text('Tare'),
                ),
                ElevatedButton(
                  onPressed: () async {
                    final ok = await _scale.stopLiveWeight();
                    _snack(
                      ok ? "Stop Live Weight Success" : "Stop Live Weight Fail",
                    );
                  },
                  child: const Text('Stop Live Weight'),
                ),
                ElevatedButton(
                  onPressed: () async {
                    final ok = await _scale.startLiveWeight();
                    _snack(
                      ok
                          ? "Start Live Weight Success"
                          : "Start Live Weight Fail",
                    );
                  },
                  child: const Text('Start Live Weight'),
                ),
                ElevatedButton(
                  onPressed: () async {
                    final ok = await _scale.isConnect();
                    _snack(
                      ok ? "Device is Connected" : "Device is Not Connected",
                    );
                  },
                  child: const Text('Check Connection'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Future<void> _refreshDevices() async {
    setState(() {
      _scanning = true;
      _devices = const [];
    });
    try {
      final list = await _scale.listDevices(
        type: _type,
        timeoutMs: _type == 'ble' ? 2500 : 500,
      );
      setState(() {
        _devices = list;
      });
    } catch (_) {
      setState(() {
        _devices = const [];
      });
    } finally {
      setState(() {
        _scanning = false;
      });
    }
  }

  Future<void> _connectToIndex(int index) async {
    bool ok = false;
    if (_type == 'serial') {
      final path = _devices.elementAt(index);
      ok = await _scale.connect(type: 'serial', path: path);
    } else if (_type == 'usb') {
      ok = await _scale.connect(type: 'usb', index: index);
    } else {
      ok = await _scale.connect(type: 'ble', index: index);
    }
  }

  Future<void> _requestPermissions() async {
    if (!Platform.isAndroid) return;
    final req = <Permission>[
      Permission.bluetoothScan,
      Permission.bluetoothConnect,
      Permission.locationWhenInUse,
    ];
    await req.request();
  }
}