bt_classic 1.0.2 copy "bt_classic: ^1.0.2" to clipboard
bt_classic: ^1.0.2 copied to clipboard

PlatformAndroid

A Flutter plugin for Bluetooth Classic communication with host/server functionality, text messaging, and file transfer capabilities.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:bt_classic/bt_classic.dart';
import 'dart:convert';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'BT Classic Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const BluetoothDemo(),
    );
  }
}

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

  @override
  State<BluetoothDemo> createState() => _BluetoothDemoState();
}

class _BluetoothDemoState extends State<BluetoothDemo>
    with TickerProviderStateMixin {
  late TabController _tabController;

  // Services
  late BluetoothClientService _clientService;
  late BluetoothHostService _hostService;

  // State
  bool _isBluetoothEnabled = false;
  bool _isServerRunning = false;
  bool _isConnected = false;
  String _deviceName = 'Unknown Device';
  String _connectedAddress = '';

  // UI Controllers
  final TextEditingController _messageController = TextEditingController();
  final List<String> _messages = [];
  final List<BluetoothDevice> _discoveredDevices = [];
  final List<BluetoothDevice> _pairedDevices = [];

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 2, vsync: this);
    _initializeServices();
    _checkBluetoothStatus();
  }

  void _initializeServices() {
    _clientService = BluetoothClientService();
    _hostService = BluetoothHostService();

    // Client callbacks
    _clientService.onDeviceFound = (device) {
      setState(() {
        if (!_discoveredDevices.any((d) => d.address == device.address)) {
          _discoveredDevices.add(device);
        }
      });
    };

    _clientService.onDiscoveryFinished = () {
      _showSnackBar('Discovery finished');
    };

    _clientService.onConnected = (address) {
      setState(() {
        _isConnected = true;
        _connectedAddress = address;
      });
      _showSnackBar('Connected to $address');
    };

    _clientService.onDisconnected = () {
      setState(() {
        _isConnected = false;
        _connectedAddress = '';
      });
      _showSnackBar('Disconnected');
    };

    _clientService.onMessageReceived = (message) {
      setState(() {
        _messages.add('Received: $message');
      });
    };

    _clientService.onFileReceived = (fileName, fileData) {
      setState(() {
        _messages.add('Received file: $fileName (${fileData.length} bytes)');
      });
    };

    _clientService.onError = (error) {
      _showSnackBar('Client Error: $error');
    };

    // Host callbacks
    _hostService.onClientConnected = (address) {
      setState(() {
        _isConnected = true;
        _connectedAddress = address;
      });
      _showSnackBar('Client connected: $address');
    };

    _hostService.onClientDisconnected = () {
      setState(() {
        _isConnected = false;
        _connectedAddress = '';
      });
      _showSnackBar('Client disconnected');
    };

    _hostService.onMessageReceived = (message) {
      setState(() {
        _messages.add('Received: $message');
      });
    };

    _hostService.onFileReceived = (fileName, fileData) {
      setState(() {
        _messages.add('Received file: $fileName (${fileData.length} bytes)');
      });
    };

    _hostService.onError = (error) {
      _showSnackBar('Host Error: $error');
    };
  }

  Future<void> _checkBluetoothStatus() async {
    final isEnabled = await _clientService.isBluetoothEnabled();
    final deviceName = await _hostService.getDeviceName();

    setState(() {
      _isBluetoothEnabled = isEnabled;
      _deviceName = deviceName;
    });
  }

  Future<void> _requestPermissions() async {
    final granted = await _clientService.requestPermissions();
    if (granted) {
      _showSnackBar('Permissions granted');
      _checkBluetoothStatus();
    } else {
      _showSnackBar('Permissions denied');
    }
  }

  Future<void> _startDiscovery() async {
    _discoveredDevices.clear();
    final started = await _clientService.startDiscovery();
    if (started) {
      _showSnackBar('Discovery started');
    }
  }

  Future<void> _getPairedDevices() async {
    final devices = await _clientService.getPairedDevices();
    setState(() {
      _pairedDevices.clear();
      _pairedDevices.addAll(devices);
    });
  }

  Future<void> _connectToDevice(String address) async {
    final connected = await _clientService.connectToDevice(address);
    if (!connected) {
      _showSnackBar('Failed to connect');
    }
  }

  Future<void> _makeDiscoverable() async {
    final success = await _hostService.makeDiscoverable();
    if (success) {
      _showSnackBar('Device is now discoverable');
    }
  }

  Future<void> _startServer() async {
    final started = await _hostService.startServer();
    if (started) {
      setState(() {
        _isServerRunning = true;
      });
      _showSnackBar('Server started');
    }
  }

  Future<void> _stopServer() async {
    final stopped = await _hostService.stopServer();
    if (stopped) {
      setState(() {
        _isServerRunning = false;
      });
      _showSnackBar('Server stopped');
    }
  }

  Future<void> _sendMessage() async {
    final message = _messageController.text.trim();
    if (message.isEmpty) return;

    final sent = _tabController.index == 0
        ? await _clientService.sendMessage(message)
        : await _hostService.sendMessage(message);

    if (sent) {
      setState(() {
        _messages.add('Sent: $message');
        _messageController.clear();
      });
    } else {
      _showSnackBar('Failed to send message');
    }
  }

  Future<void> _sendTestFile() async {
    // Create a test file
    final testData =
        utf8.encode('This is a test file content from bt_classic package!');

    final sent = _tabController.index == 0
        ? await _clientService.sendFile(
            Uint8List.fromList(testData), 'test.txt')
        : await _hostService.sendFile(Uint8List.fromList(testData), 'test.txt');

    if (sent) {
      setState(() {
        _messages.add('Sent file: test.txt (${testData.length} bytes)');
      });
    } else {
      _showSnackBar('Failed to send file');
    }
  }

  Future<void> _disconnect() async {
    if (_tabController.index == 0) {
      await _clientService.disconnect();
    } else {
      await _hostService.disconnect();
    }
  }

  void _showSnackBar(String message) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text(message)),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('BT Classic Demo'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        bottom: TabBar(
          controller: _tabController,
          tabs: const [
            Tab(text: 'Client', icon: Icon(Icons.smartphone)),
            Tab(text: 'Host', icon: Icon(Icons.router)),
          ],
        ),
      ),
      body: Column(
        children: [
          // Status bar
          Container(
            padding: const EdgeInsets.all(16),
            color: _isBluetoothEnabled
                ? Colors.green.shade100
                : Colors.red.shade100,
            child: Row(
              children: [
                Icon(
                  _isBluetoothEnabled
                      ? Icons.bluetooth
                      : Icons.bluetooth_disabled,
                  color: _isBluetoothEnabled ? Colors.green : Colors.red,
                ),
                const SizedBox(width: 8),
                Expanded(
                  child: Text(
                    'Device: $_deviceName | ${_isBluetoothEnabled ? 'Enabled' : 'Disabled'} | ${_isConnected ? 'Connected to $_connectedAddress' : 'Not connected'}',
                    style: const TextStyle(fontWeight: FontWeight.bold),
                  ),
                ),
              ],
            ),
          ),

          // Tab content
          Expanded(
            child: TabBarView(
              controller: _tabController,
              children: [
                _buildClientTab(),
                _buildHostTab(),
              ],
            ),
          ),

          // Messages section
          Container(
            height: 200,
            decoration: BoxDecoration(
              border: Border(top: BorderSide(color: Colors.grey.shade300)),
            ),
            child: Column(
              children: [
                Container(
                  padding: const EdgeInsets.all(8),
                  color: Colors.grey.shade100,
                  child: Row(
                    children: [
                      const Text('Messages',
                          style: TextStyle(fontWeight: FontWeight.bold)),
                      const Spacer(),
                      IconButton(
                        icon: const Icon(Icons.clear),
                        onPressed: () => setState(() => _messages.clear()),
                      ),
                    ],
                  ),
                ),
                Expanded(
                  child: ListView.builder(
                    itemCount: _messages.length,
                    itemBuilder: (context, index) {
                      return ListTile(
                        dense: true,
                        title: Text(_messages[index]),
                      );
                    },
                  ),
                ),
              ],
            ),
          ),

          // Message input
          if (_isConnected)
            Container(
              padding: const EdgeInsets.all(8),
              child: Row(
                children: [
                  Expanded(
                    child: TextField(
                      controller: _messageController,
                      decoration: const InputDecoration(
                        hintText: 'Type a message...',
                        border: OutlineInputBorder(),
                      ),
                      onSubmitted: (_) => _sendMessage(),
                    ),
                  ),
                  const SizedBox(width: 8),
                  IconButton(
                    icon: const Icon(Icons.send),
                    onPressed: _sendMessage,
                  ),
                  IconButton(
                    icon: const Icon(Icons.attach_file),
                    onPressed: _sendTestFile,
                  ),
                ],
              ),
            ),
        ],
      ),
    );
  }

  Widget _buildClientTab() {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          ElevatedButton(
            onPressed: _requestPermissions,
            child: const Text('Request Permissions'),
          ),
          const SizedBox(height: 8),
          Row(
            children: [
              Expanded(
                child: ElevatedButton(
                  onPressed: _startDiscovery,
                  child: const Text('Start Discovery'),
                ),
              ),
              const SizedBox(width: 8),
              Expanded(
                child: ElevatedButton(
                  onPressed: _getPairedDevices,
                  child: const Text('Get Paired'),
                ),
              ),
            ],
          ),
          const SizedBox(height: 16),
          const Text('Discovered Devices:',
              style: TextStyle(fontWeight: FontWeight.bold)),
          Expanded(
            child: ListView.builder(
              itemCount: _discoveredDevices.length + _pairedDevices.length,
              itemBuilder: (context, index) {
                BluetoothDevice device;
                bool isPaired = false;

                if (index < _pairedDevices.length) {
                  device = _pairedDevices[index];
                  isPaired = true;
                } else {
                  device = _discoveredDevices[index - _pairedDevices.length];
                }

                return ListTile(
                  leading: Icon(
                    isPaired ? Icons.bluetooth_connected : Icons.bluetooth,
                    color: isPaired ? Colors.blue : Colors.grey,
                  ),
                  title: Text(device.name),
                  subtitle: Text(device.address),
                  trailing: ElevatedButton(
                    onPressed: () => _connectToDevice(device.address),
                    child: const Text('Connect'),
                  ),
                );
              },
            ),
          ),
          if (_isConnected)
            ElevatedButton(
              onPressed: _disconnect,
              style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
              child: const Text('Disconnect'),
            ),
        ],
      ),
    );
  }

  Widget _buildHostTab() {
    return Padding(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          ElevatedButton(
            onPressed: _requestPermissions,
            child: const Text('Request Permissions'),
          ),
          const SizedBox(height: 8),
          ElevatedButton(
            onPressed: _makeDiscoverable,
            child: const Text('Make Discoverable'),
          ),
          const SizedBox(height: 8),
          ElevatedButton(
            onPressed: _isServerRunning ? _stopServer : _startServer,
            style: ElevatedButton.styleFrom(
              backgroundColor: _isServerRunning ? Colors.red : Colors.green,
            ),
            child: Text(_isServerRunning ? 'Stop Server' : 'Start Server'),
          ),
          const SizedBox(height: 16),
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Server Status',
                    style: Theme.of(context).textTheme.titleMedium,
                  ),
                  const SizedBox(height: 8),
                  Text('Running: ${_isServerRunning ? 'Yes' : 'No'}'),
                  Text('Connected: ${_isConnected ? 'Yes' : 'No'}'),
                  if (_isConnected) Text('Client: $_connectedAddress'),
                ],
              ),
            ),
          ),
          const Spacer(),
          if (_isConnected)
            ElevatedButton(
              onPressed: _disconnect,
              style: ElevatedButton.styleFrom(backgroundColor: Colors.red),
              child: const Text('Disconnect Client'),
            ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    _tabController.dispose();
    _messageController.dispose();
    super.dispose();
  }
}
1
likes
155
points
38
downloads

Publisher

verified publishermojtahedi.me

Weekly Downloads

A Flutter plugin for Bluetooth Classic communication with host/server functionality, text messaging, and file transfer capabilities.

Homepage
Repository (GitHub)
View/report issues

Topics

#bluetooth #communication #file-transfer #networking

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on bt_classic

Packages that implement bt_classic