brady_flutter_plugin 3.3.0+1
brady_flutter_plugin: ^3.3.0+1 copied to clipboard
The official Flutter Plugin to integrate the Brady SDK into your Flutter application. The Brady SDK allows users to discover, connect, and print to Brady printers.
example/lib/main.dart
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:brady_flutter_plugin/brady_flutter_plugin.dart';
Uint8List? previewImage;
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _bradyFlutterPlugin = BradyFlutterPlugin();
bool showImage = false;
String _status = 'Ready';
String _details = '';
List<Map<String, String>> _printers = [];
List<String> _templateFiles = [];
String? _selectedTemplate;
List<String> _assetFiles = [];
String? _selectedAsset;
String? _connectedPrinterModel;
List<String> _availablePrintModes = [];
String? _selectedPrintMode;
bool get _isM811J {
return _connectedPrinterModel == 'M811J';
}
bool get _isPiclLite {
return _connectedPrinterModel == "M211" || _connectedPrinterModel == "M511";
}
bool get _isI7500 {
return _connectedPrinterModel == 'i7500';
}
final TextEditingController _placeholderNameController =
TextEditingController(text: '');
final TextEditingController _placeholderValueController =
TextEditingController(text: '');
final TextEditingController _shutoffTimeController =
TextEditingController(text: '30');
final TextEditingController _alignmentOffsetController =
TextEditingController(text: '0');
final TextEditingController _maintenanceStationController =
TextEditingController(text: 'Reset');
final TextEditingController _rfidOperationsController =
TextEditingController(text: '[]');
final TextEditingController _ipAddressController =
TextEditingController(text: '192.168.1.100');
final TextEditingController _printerModelController =
TextEditingController(text: 'M611');
@override
void initState() {
super.initState();
_loadAssets();
}
Future<void> _loadAssets() async {
try {
final manifestAsset = await rootBundle.load('AssetManifest.bin');
final manifestMap =
const StandardMessageCodec().decodeMessage(manifestAsset) as Map;
final assetFiles = manifestMap.keys
.whereType<String>()
.where((String key) =>
key.startsWith('assets/') && !key.endsWith("DS_Store"))
.toList();
setState(() {
_assetFiles = assetFiles;
if (_assetFiles.isNotEmpty) {
_selectedAsset = _assetFiles.first;
_callApi(() => _bradyFlutterPlugin.setTemplate(_selectedAsset!),
'setTemplate');
}
});
} catch (e) {
// Handle error loading asset manifest
if (kDebugMode) {
print("Error loading assets: $e");
}
}
}
Timer? _discoveryTimer;
Future<void> _startDiscovery() async {
setState(() {
_status = 'Starting discovery...';
_printers = [];
});
try {
_bradyFlutterPlugin.startBlePrinterDiscovery();
_bradyFlutterPlugin.startWifiPrinterDiscovery();
_discoveryTimer?.cancel();
_discoveryTimer = Timer.periodic(const Duration(milliseconds: 500), (timer) async {
try {
final printers = await _bradyFlutterPlugin.getPrinters();
if (mounted) {
setState(() {
_printers = printers;
});
}
} catch (e) {
if (kDebugMode) {
print("Error getting printers: $e");
}
}
});
} catch (e) {
setState(() {
_status = 'Discovery failed';
_details = e.toString();
});
}
}
Future<void> _connectToPrinter(String printerName, String protocol) async {
final bool isBluetooth = protocol == "BLE";
await _callApi(
() => _bradyFlutterPlugin.connect(printerName, isBluetooth), 'connect',
onSuccess: () async {
_discoveryTimer?.cancel();
_bradyFlutterPlugin.stopPrinterDiscovery();
_getAllDetails();
final model = await _bradyFlutterPlugin.getPrinterModel();
List<String> printModes = [];
try {
if (model == 'M811J') {
printModes = await _bradyFlutterPlugin.getAvailablePrintQualityModes() ?? [];
}
} catch (e) {
if (kDebugMode) {
print("Error getting print modes: $e");
}
}
setState(() {
_connectedPrinterModel = model;
_availablePrintModes = printModes;
if (_availablePrintModes.isNotEmpty) {
_selectedPrintMode = _availablePrintModes.first;
} else {
_selectedPrintMode = null;
}
});
});
}
Future<void> _callApi(Function apiCall, String name,
{VoidCallback? onSuccess}) async {
setState(() {
_status = 'Calling $name...';
_details = '';
showImage = false;
});
try {
final result = await apiCall();
setState(() {
_status = 'Success: $name';
_details = result.toString();
});
onSuccess?.call();
} on PlatformException catch (e) {
setState(() {
_status = 'Error: $name';
_details = "Where: ${e.message!}\nDetails: ${e.details}";
});
} catch (e) {
setState(() {
_status = 'Error: $name';
_details = e.toString();
});
}
}
Future<void> _getAllDetails() async {
setState(() {
_status = 'Getting all details...';
_details = '';
});
final detailsMap = <String, dynamic>{};
final functions = {
'getPrinterStatus': _bradyFlutterPlugin.getPrinterStatus,
'getPrinterStatusMessage': _bradyFlutterPlugin.getPrinterStatusMessage,
'getPrinterStatusMessageTitle':
_bradyFlutterPlugin.getPrinterStatusMessageTitle,
'getPrinterStatusRemedyExplanationMessage':
_bradyFlutterPlugin.getPrinterStatusRemedyExplanationMessage,
'getPrinterName': _bradyFlutterPlugin.getPrinterName,
'getPrinterModel': _bradyFlutterPlugin.getPrinterModel,
'getConnectionType': _bradyFlutterPlugin.getConnectionType,
'getBatteryLevelPercentage':
_bradyFlutterPlugin.getBatteryLevelPercentage,
'getIsAcConnected': _bradyFlutterPlugin.getIsAcConnected,
'getSupplyName': _bradyFlutterPlugin.getSupplyName,
'getSupplyRemainingPercentage':
_bradyFlutterPlugin.getSupplyRemainingPercentage,
'getSupplyWidth': _bradyFlutterPlugin.getSupplyWidth,
'getSupplyHeight': _bradyFlutterPlugin.getSupplyHeight,
'getSupplyColor': _bradyFlutterPlugin.getSupplyColor,
'getIsSupplyPreSized': _bradyFlutterPlugin.getIsSupplyPreSized,
'getIsSupplyDirectThermal': _bradyFlutterPlugin.getIsSupplyDirectThermal,
'getDotsPerInch': _bradyFlutterPlugin.getDotsPerInch,
'getPostPrintAccessoryType':
_bradyFlutterPlugin.getPostPrintAccessoryType,
'getYNumber': _bradyFlutterPlugin.getYNumber,
'getRibbonName': _bradyFlutterPlugin.getRibbonName,
'getRibbonRemainingPercentage':
_bradyFlutterPlugin.getRibbonRemainingPercentage,
'getRibbonColor': _bradyFlutterPlugin.getRibbonColor,
'getInkRemainingPercentage':
_bradyFlutterPlugin.getInkRemainingPercentage,
'getOutOfInkCmy': _bradyFlutterPlugin.getOutOfInkCmy,
'getIsCleanPrintheadMaintenanceRecommended':
_bradyFlutterPlugin.getIsCleanPrintheadMaintenanceRecommended,
'getCleanPrintheadSuccessful':
_bradyFlutterPlugin.getCleanPrintheadSuccessful,
'getMaintenanceStationWarningAway':
_bradyFlutterPlugin.getMaintenanceStationWarningAway,
'getMaintenanceStationErrorAway':
_bradyFlutterPlugin.getMaintenanceStationErrorAway,
'getAvailablePrintQualityModes':
_bradyFlutterPlugin.getAvailablePrintQualityModes,
'getValidInkSupply': _bradyFlutterPlugin.getValidInkSupply,
'getPrintheadInstalled': _bradyFlutterPlugin.getPrintheadInstalled,
'getAlignmentOffset': _bradyFlutterPlugin.getAlignmentOffset,
'suppliesMatch': _bradyFlutterPlugin.suppliesMatch,
'getErrorSeverity': _bradyFlutterPlugin.getErrorSeverity,
'getFirmwareVersion': _bradyFlutterPlugin.getFirmwareVersion,
'getLabelLibraryVersion': _bradyFlutterPlugin.getLabelLibraryVersion,
'getBluetoothVersion': _bradyFlutterPlugin.getBluetoothVersion,
'getWifiVersion': _bradyFlutterPlugin.getWifiVersion,
'isSupplyValid': _bradyFlutterPlugin.isSupplyValid,
'isRibbonValid': _bradyFlutterPlugin.isRibbonValid,
'isPreprintedSupply': _bradyFlutterPlugin.isPreprintedSupply,
'getAutomaticShutdownTime': _bradyFlutterPlugin.getAutomaticShutdownTime,
'getLastConnectedPrinterName':
_bradyFlutterPlugin.getLastConnectedPrinterName,
'getHaveOwnership': _bradyFlutterPlugin.getHaveOwnership,
'getTemplateSupplyName': _bradyFlutterPlugin.getTemplateSupplyName,
};
for (var entry in functions.entries) {
try {
final result = await entry.value();
detailsMap[entry.key] = result ?? 'null';
} catch (e) {
detailsMap[entry.key] = 'null';
}
}
try {
final templateData = await _bradyFlutterPlugin.getTemplateData();
detailsMap['getTemplateData'] = templateData;
} catch (e) {
// Ignore error from getTemplateData as it may be the source of the crash
}
final formattedDetails =
detailsMap.entries.map((e) => '${e.key}: ${e.value}').join('\n');
setState(() {
_status = 'Finished getting all details';
_details = formattedDetails;
});
}
Widget _buildSectionHeader(String title) {
return Padding(
padding: const EdgeInsets.fromLTRB(16, 24, 16, 8),
child: Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
color: Colors.black,
),
),
);
}
Widget _buildApiButton(String title, VoidCallback onPressed) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
child: ElevatedButton(
onPressed: onPressed,
child: Text(title, textAlign: TextAlign.center),
),
);
}
Widget _buildTextField(TextEditingController controller, String label) {
return TextField(
controller: controller,
style: const TextStyle(color: Colors.black),
decoration: InputDecoration(
labelText: label,
labelStyle: const TextStyle(color: Colors.black54),
enabledBorder: const UnderlineInputBorder(
borderSide: BorderSide(color: Colors.black54),
),
),
);
}
Widget _buildInputRow(
String buttonTitle, VoidCallback onPressed, List<Widget> textFields) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
child: Row(
children: [
Expanded(
flex: 2,
child: Column(children: textFields),
),
const SizedBox(width: 8),
Expanded(
flex: 1,
child: _buildApiButton(buttonTitle, onPressed),
),
],
),
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light().copyWith(
primaryColor: const Color(0xFF0D47A1),
scaffoldBackgroundColor: Colors.grey[200],
colorScheme: const ColorScheme.light(
primary: Color(0xFF0D47A1),
secondary: Color(0xFF0D47A1),
),
cardColor: Colors.white,
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF0D47A1),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.symmetric(vertical: 12),
),
),
appBarTheme: const AppBarTheme(
titleTextStyle: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w500,
),
),
),
home: Scaffold(
appBar: AppBar(
title: const Text('Brady SDK Flutter Wrapper'),
backgroundColor: const Color(0xFF1E1E1E),
elevation: 4,
),
backgroundColor: Colors.grey[200],
body: Column(
children: [
Card(
elevation: 4,
margin: const EdgeInsets.all(8.0),
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Status: $_status',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Colors.black),
),
const SizedBox(height: 8),
if (_details.isNotEmpty)
SizedBox(
height: 100, // Give it a fixed height
child: SingleChildScrollView(
child: Text(
_details,
style: const TextStyle(
fontSize: 14, color: Colors.black87),
),
),
),
],
),
),
),
Expanded(
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
children: [
_buildSectionHeader('Discovery & Connection'),
_buildApiButton('Start Discovery', _startDiscovery),
if (_printers.isNotEmpty)
SizedBox(
height: 150,
child: Card(
color: Colors.white,
child: ListView.builder(
itemCount: _printers.length,
itemBuilder: (context, index) {
Map<String, String> currentPrinter = _printers[index];
String key = currentPrinter.keys.first;
return ListTile(
title: Text(key,
style: const TextStyle(color: Colors.black)),
subtitle: Text(currentPrinter[key]!,
style:
const TextStyle(color: Colors.black87)),
onTap: () =>
_connectToPrinter(key, currentPrinter[key]!),
);
},
),
),
),
_buildInputRow(
'Connect w/ IP',
() => _callApi(
() => _bradyFlutterPlugin.connectWithManualIP(
_ipAddressController.text,
_printerModelController.text),
'connectWithManualIP', onSuccess: () async {
final model =
await _bradyFlutterPlugin.getPrinterModel();
List<String> printModes = [];
try {
if (model == 'M811J') {
printModes = await _bradyFlutterPlugin.getAvailablePrintQualityModes() ?? [];
}
} catch (e) {
if (kDebugMode) {
print("Error getting print modes: $e");
}
}
setState(() {
_connectedPrinterModel = model;
_availablePrintModes = printModes;
if (_availablePrintModes.isNotEmpty) {
_selectedPrintMode = _availablePrintModes.first;
} else {
_selectedPrintMode = null;
}
});
}),
[
_buildTextField(_ipAddressController, 'IP Address'),
_buildTextField(
_printerModelController, 'Printer Model'),
]),
_buildApiButton(
'Disconnect',
() =>
_callApi(_bradyFlutterPlugin.disconnect, 'disconnect',
onSuccess: () {
setState(() {
_connectedPrinterModel = null;
_availablePrintModes = [];
_selectedPrintMode = null;
});
})),
_buildApiButton(
'Disconnect Without Forget',
() => _callApi(
_bradyFlutterPlugin.disconnectWithoutForget,
'disconnectWithoutForget')),
_buildApiButton(
'Forget Last Printer',
() => _callApi(
_bradyFlutterPlugin.forgetLastConnectedPrinter,
'forgetLastConnectedPrinter')),
_buildSectionHeader('Printer Status & Info'),
_buildApiButton('Get All Details', _getAllDetails),
_buildSectionHeader('Template & Printing'),
if (_assetFiles.isNotEmpty)
Padding(
padding: const EdgeInsets.all(8.0),
child: DropdownButton<String>(
value: _selectedAsset,
isExpanded: true,
dropdownColor: Colors.grey[200],
onChanged: (String? newValue) {
setState(() {
_selectedAsset = newValue;
});
if (newValue != null) {
_callApi(
() => _bradyFlutterPlugin.setTemplate(newValue),
'setTemplate');
}
},
items: _assetFiles
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.split('/').last,
style: const TextStyle(color: Colors.black)),
);
}).toList(),
),
),
if (_templateFiles.isNotEmpty)
Padding(
padding: const EdgeInsets.all(8.0),
child: DropdownButton<String>(
value: _selectedTemplate,
isExpanded: true,
dropdownColor: Colors.grey[200],
onChanged: (String? newValue) {
setState(() {
_selectedTemplate = newValue;
});
if (newValue != null) {
_callApi(
() => _bradyFlutterPlugin.setTemplate(newValue),
'setTemplate');
}
},
items: _templateFiles
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.split('/').last,
style: const TextStyle(color: Colors.black)),
);
}).toList(),
),
),
_buildInputRow(
'Set Placeholder',
() => _callApi(
() => _bradyFlutterPlugin.setPlaceholderValue(
_placeholderNameController.text,
_placeholderValueController.text),
'setPlaceholderValue'),
[
_buildTextField(
_placeholderNameController, 'Placeholder Name'),
_buildTextField(
_placeholderValueController, 'Placeholder Value'),
]),
_buildApiButton(
'Get Preview',
() => _callApi(() async {
final base64 =
await _bradyFlutterPlugin.getPreview(200);
if (base64 != null) {
setState(() {
previewImage = base64Decode(base64);
showImage = true;
});
}
}, 'getPreview')),
_buildApiButton(
'Print',
() => _callApi(
() => _bradyFlutterPlugin.print(1, false, true, true),
'print')),
if (_isI7500)
_buildInputRow('Print with RFID', () {
try {
final ops = json.decode(_rfidOperationsController.text);
_callApi(
() => _bradyFlutterPlugin.printWithRfid(
ops, 1, "EndOfLabel", false, true),
'printWithRfid');
} catch (e) {
setState(() {
_status = 'Error: Invalid JSON';
_details = e.toString();
});
}
}, [
_buildTextField(
_rfidOperationsController, 'RFID Ops (JSON)')
]),
_buildApiButton(
'Print PDF',
() => _callApi(
() => _bradyFlutterPlugin.printPDF(
"assets/add_analytics_for_printer_status_screen_small.pdf",
0,
1,
"EndOfLabel",
true,
true),
'printPDF')),
if (_isPiclLite || _isM811J) ...[
_buildSectionHeader('Maintenance & Supply'),
_buildApiButton(
'Feed Supply',
() => _callApi(
_bradyFlutterPlugin.feedSupply, 'feedSupply')),
_buildApiButton(
'Cut Supply',
() => _callApi(
_bradyFlutterPlugin.cutSupply, 'cutSupply')),
_buildInputRow(
'Set Auto Shutoff',
() => _callApi(
() => _bradyFlutterPlugin.setAutomaticShutoffTime(
int.parse(_shutoffTimeController.text)),
'setAutomaticShutoffTime'),
[_buildTextField(_shutoffTimeController, 'Minutes')]),
],
if (_isM811J) ...[
_buildSectionHeader('M811J Specific Settings'),
Padding(
padding: const EdgeInsets.all(8.0),
child: DropdownButton<String>(
value: _selectedPrintMode,
isExpanded: true,
dropdownColor: Colors.white,
onChanged: (String? newValue) {
setState(() {
_selectedPrintMode = newValue;
});
},
items: _availablePrintModes
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
),
_buildApiButton('Set Print Mode', () {
if (_selectedPrintMode != null) {
_callApi(
() => _bradyFlutterPlugin
.setPrintQualityMode(_selectedPrintMode!),
'setPrintQualityMode');
}
}),
_buildApiButton(
'Clean Printhead',
() => _callApi(_bradyFlutterPlugin.cleanPrinthead,
'cleanPrinthead')),
_buildApiButton(
'Print Alignment Label',
() => _callApi(_bradyFlutterPlugin.printAlignmentLabel,
'printAlignmentLabel')),
_buildInputRow(
'Set Alignment Offset',
() => _callApi(
() => _bradyFlutterPlugin.setAlignmentOffset(
int.parse(_alignmentOffsetController.text)),
'setAlignmentOffset'),
[
_buildTextField(
_alignmentOffsetController, 'Offset')
]),
_buildInputRow(
'Set Maint. Property',
() => _callApi(
() => _bradyFlutterPlugin
.setMaintenanceStationProperty(
_maintenanceStationController.text),
'setMaintenanceStationProperty'),
[
_buildTextField(
_maintenanceStationController, 'Property')
]),
],
if (showImage && previewImage != null)
Card(
margin: const EdgeInsets.all(16),
color: Colors.white,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Image.memory(previewImage!),
),
),
],
),
),
],
),
),
);
}
@override
void dispose() {
_discoveryTimer?.cancel();
_placeholderNameController.dispose();
_placeholderValueController.dispose();
_shutoffTimeController.dispose();
_alignmentOffsetController.dispose();
_maintenanceStationController.dispose();
_rfidOperationsController.dispose();
_ipAddressController.dispose();
_printerModelController.dispose();
super.dispose();
}
}