flutter_industrial_scale 0.0.1
flutter_industrial_scale: ^0.0.1 copied to clipboard
Professional-grade weighing scale plugin. Connect via BLE, Bluetooth, USB, and Serial with built-in support for MT-SICS and CAS industrial protocols.
import 'package:flutter/material.dart';
import 'package:flutter_industrial_scale/flutter_industrial_scale.dart';
void main() {
runApp(
const MaterialApp(
home: ScaleExampleApp(),
debugShowCheckedModeBanner: false,
),
);
}
class ScaleExampleApp extends StatefulWidget {
const ScaleExampleApp({super.key});
@override
State<ScaleExampleApp> createState() => _ScaleExampleAppState();
}
class _ScaleExampleAppState extends State<ScaleExampleApp> {
final IndustrialScale _industrialScale = IndustrialScale.instance;
final List<ScaleDevice> _devices = [];
ScaleConnection? _connection;
WeightReading? _lastReading;
bool _isScanning = false;
ScaleParser _selectedParser = const AsciiParser();
final List<ScaleParser> _parsers = const [
AsciiParser(),
MtSicsParser(),
CasParser(),
];
@override
void initState() {
super.initState();
_startScan();
}
void _startScan() {
setState(() {
_devices.clear();
_isScanning = true;
});
_industrialScale.scan().listen(
(device) {
if (!_devices.any((d) => d.id == device.id)) {
setState(() {
_devices.add(device);
});
}
},
onDone: () => setState(() => _isScanning = false),
onError: (e) {
setState(() => _isScanning = false);
if (e is ScaleNativeException) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'Native Hardware Bridge Unavailable: $e\nRunning in Simulator (Mock) mode.',
),
duration: const Duration(seconds: 5),
),
);
}
},
);
}
Future<void> _connect(ScaleDevice device) async {
try {
if (_connection != null) {
await _connection!.disconnect();
}
final connection = await _industrialScale.connect(
device,
parser: _selectedParser,
);
setState(() {
_connection = connection;
_lastReading = null;
});
connection.weightStream.listen(
(reading) {
setState(() => _lastReading = reading);
},
onError: (e) {
if (!mounted) return;
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Stream Error: $e')));
},
);
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text('Connection Error: $e')));
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Industrial Scale Example'),
backgroundColor: Colors.blueGrey[900],
foregroundColor: Colors.white,
actions: [
if (_isScanning)
const Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
),
),
),
)
else
IconButton(onPressed: _startScan, icon: const Icon(Icons.refresh)),
],
),
body: Column(
children: [
_buildDisplayCard(),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: DropdownMenu<ScaleParser>(
initialSelection: _selectedParser,
expandedInsets: EdgeInsets.zero,
label: const Text('Communication Protocol / Parser'),
dropdownMenuEntries: _parsers.map((p) {
return DropdownMenuEntry(value: p, label: p.name);
}).toList(),
onSelected: (p) {
if (p != null) setState(() => _selectedParser = p);
},
),
),
const Divider(height: 1),
Expanded(child: _buildDeviceList()),
],
),
);
}
Widget _buildDisplayCard() {
return Container(
width: double.infinity,
margin: const EdgeInsets.all(16),
padding: const EdgeInsets.all(24),
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(12),
boxShadow: const [
BoxShadow(
color: Colors.black26,
blurRadius: 10,
offset: Offset(0, 4),
),
],
),
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
_connection != null ? 'CONNECTED' : 'DISCONNECTED',
style: TextStyle(
color: _connection != null ? Colors.green : Colors.red,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
if (_lastReading != null)
Text(
_lastReading!.isStable ? 'STABLE' : 'UNSTABLE',
style: TextStyle(
color: _lastReading!.isStable ? Colors.blue : Colors.amber,
fontWeight: FontWeight.bold,
fontSize: 12,
),
),
],
),
const SizedBox(height: 16),
FittedBox(
child: Text(
_lastReading?.value.toStringAsFixed(3) ?? '0.000',
style: const TextStyle(
color: Colors.greenAccent,
fontSize: 80,
fontFamily: 'monospace',
fontWeight: FontWeight.bold,
),
),
),
Text(
_lastReading?.unit ?? '--',
style: const TextStyle(color: Colors.greenAccent, fontSize: 24),
),
if (_connection != null) ...[
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton.icon(
onPressed: () => _connection?.tare(),
icon: const Icon(Icons.exposure_zero),
label: const Text('TARE'),
),
const SizedBox(width: 16),
ElevatedButton.icon(
onPressed: () async {
try {
await _connection?.disconnect();
} catch (e) {
debugPrint('Disconnect error: $e');
} finally {
setState(() => _connection = null);
}
},
icon: const Icon(Icons.close),
label: const Text('CLOSE'),
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red[900],
foregroundColor: Colors.white,
),
),
],
),
],
],
),
);
}
Widget _buildDeviceList() {
if (_devices.isEmpty && !_isScanning) {
return const Center(
child: Text(
'No devices found.\nTry bonded Bluetooth or USB devices.',
textAlign: TextAlign.center,
),
);
}
return ListView.builder(
itemCount: _devices.length,
itemBuilder: (context, index) {
final device = _devices[index];
return ListTile(
leading: CircleAvatar(
backgroundColor: _getColorForType(device.type),
child: Icon(_getIconForType(device.type), color: Colors.white),
),
title: Text(
device.name,
style: const TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text('${device.type.name.toUpperCase()} • ${device.id}'),
trailing: const Icon(Icons.connect_without_contact),
onTap: () => _connect(device),
);
},
);
}
IconData _getIconForType(TransportType type) {
switch (type) {
case TransportType.ble:
return Icons.bluetooth_audio;
case TransportType.bluetooth:
return Icons.bluetooth;
case TransportType.usb:
return Icons.usb;
case TransportType.serial:
return Icons.settings_input_component;
case TransportType.mock:
return Icons.bug_report;
}
}
Color _getColorForType(TransportType type) {
switch (type) {
case TransportType.ble:
return Colors.blue;
case TransportType.bluetooth:
return Colors.indigo;
case TransportType.usb:
return Colors.orange;
case TransportType.serial:
return Colors.purple;
case TransportType.mock:
return Colors.grey;
}
}
}