flic_button 1.0.0 flic_button: ^1.0.0 copied to clipboard
An interface to the flic2 libraries for iOS and Android wrapping the offical ones supplied by flic.io (50ButtonsEach), implements the connection to the Flic2 manager, listening and finding buttons and [...]
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flic_button/flic_button.dart';
import 'package:permission_handler/permission_handler.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with Flic2Listener {
// flic2 starts and isn't scanning
bool _isScanning = false;
// as we discover buttons, lets add them to a map of uuid/button to show
final Map<String, Flic2Button> _buttonsFound = {};
// the last click to show we are hearing the button click
Flic2ButtonClick? _lastClick;
// the plugin manager to use while we are active
FlicButtonPlugin? flicButtonManager;
@override
void initState() {
super.initState();
// create the FLIC 2 manager and initialize it
_startStopFlic2();
}
void _startStopScanningForFlic2() async {
// start scanning for new buttons
if (!_isScanning) {
// not scanning yet - start - flic 2 needs permissions for FINE_LOCATION
// when on android to perform this action
if (Platform.isAndroid && !await Permission.location.isGranted) {
await Permission.location.request();
}
flicButtonManager!.scanForFlic2();
} else {
// are scanning - cancel that
flicButtonManager!.cancelScanForFlic2();
}
// update the UI
setState(() {
_isScanning = !_isScanning;
});
}
void _startStopFlic2() {
// start or stop the plugin (iOS doesn't stop)
if (null == flicButtonManager) {
// we are not started - start listening to FLIC2 buttons
setState(() => flicButtonManager = FlicButtonPlugin(flic2listener: this));
} else {
// started - so stop
flicButtonManager!.disposeFlic2().then((value) => setState(() {
// as the flic manager is disposed, signal that it's gone
flicButtonManager = null;
}));
}
}
void _getButtons() {
// get all the buttons from the plugin that were there last time
flicButtonManager!.getFlic2Buttons().then((buttons) {
// put all of these in the list to show the buttons
buttons.forEach((button) {
_addButtonAndListen(button);
});
});
}
void _addButtonAndListen(Flic2Button button) {
// as buttons are discovered via the various methods, add them
// to the map to show them in the list on the view
setState(() {
// add the button to the map
_buttonsFound[button.uuid] = button;
// and listen to the button for clicks and things
flicButtonManager!.listenToFlic2Button(button.uuid);
});
}
void _connectDisconnectButton(Flic2Button button) {
// if disconnected, connect, else disconnect the button
if (button.connectionState == Flic2ButtonConnectionState.disconnected) {
flicButtonManager!.connectButton(button.uuid);
} else {
flicButtonManager!.disconnectButton(button.uuid);
}
}
void _forgetButton(Flic2Button button) {
// forget the passed button so it disappears and we can search again
flicButtonManager!.forgetButton(button.uuid).then((value) {
if (value != null && value) {
// button was removed
setState(() {
// remove from the list
_buttonsFound.remove(button.uuid);
});
}
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flic Button Plugin Example'),
),
body: FutureBuilder(
future: flicButtonManager != null
? flicButtonManager!.invokation
: null,
builder: (ctx, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
// are not initialized yet, wait a sec - should be very quick!
return Center(
child: ElevatedButton(
onPressed: () => _startStopFlic2(),
child: Text('Start and initialize Flic2'),
),
);
} else {
// we have completed the init call, we can perform scanning etc
return Column(
children: [
SizedBox(
height: 10,
),
Text(
'Flic2 is initialized',
style: TextStyle(fontSize: 20),
),
ElevatedButton(
onPressed: () => _startStopFlic2(),
child: Text('Stop Flic2'),
),
if (flicButtonManager != null)
Row(
// if we are started then show the controls to get flic2 and scan for flic2
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () => _getButtons(),
child: Text('Get Buttons')),
ElevatedButton(
onPressed: () => _startStopScanningForFlic2(),
child: Text(_isScanning
? 'Stop Scanning'
: 'Scan for buttons')),
],
),
if (null != _lastClick)
Padding(
padding: const EdgeInsets.all(20),
child: Text(
'FLIC2 @${_lastClick!.button.buttonAddr}\nclicked ${_lastClick!.timestamp - _lastClick!.button.readyTimestamp}ms from ready state\n'
'${_lastClick!.isSingleClick ? 'single click\n' : ''}'
'${_lastClick!.isDoubleClick ? 'double click\n' : ''}'
'${_lastClick!.isHold ? 'hold\n' : ''}',
),
),
if (_isScanning)
Text(
'Hold down your flic2 button so we can find it now we are scanning...'),
// and show the list of buttons we have found at this point
Expanded(
child: ListView(
children: _buttonsFound.values
.map((e) => ListTile(
key: ValueKey(e.uuid),
leading:
Icon(Icons.radio_button_on, size: 48),
title: Text('FLIC2 @${e.buttonAddr}'),
subtitle: Column(
children: [
Text('${e.uuid}\n'
'name: ${e.name}\n'
'batt: ${e.battVoltage}V (${e.battPercentage}%)\n'
'serial: ${e.serialNo}\n'
'pressed: ${e.pressCount}\n'),
Row(
children: [
ElevatedButton(
onPressed: () =>
_connectDisconnectButton(e),
child: Text(e.connectionState ==
Flic2ButtonConnectionState
.disconnected
? 'connect'
: 'disconnect'),
),
SizedBox(width: 20),
ElevatedButton(
onPressed: () => _forgetButton(e),
child: Text('forget'),
),
],
),
],
),
))
.toList()),
),
],
);
}
},
)),
);
}
@override
void onButtonClicked(Flic2ButtonClick buttonClick) {
// callback from the plugin that someone just clicked a button
print('button ${buttonClick.button.uuid} clicked');
setState(() {
_lastClick = buttonClick;
});
}
@override
void onButtonConnected() {
super.onButtonConnected();
// this changes the state of our list of buttons, set state for this
setState(() {
print('button connected');
});
}
@override
void onButtonDiscovered(String buttonAddress) {
super.onButtonDiscovered(buttonAddress);
// this is an address which we should be able to resolve to an actual button right away
print('button @$buttonAddress discovered');
// but we could in theory wait for it to be connected and discovered because that will happen too
flicButtonManager!.getFlic2ButtonByAddress(buttonAddress).then((button) {
if (button != null) {
print(
'button found with address $buttonAddress resolved to actual button data ${button.uuid}');
// which we can add to the list to show right away
_addButtonAndListen(button);
}
});
}
@override
void onButtonFound(Flic2Button button) {
super.onButtonFound(button);
// we have found a new button, add to the list to show
print('button ${button.uuid} found');
// and add to the list to show
_addButtonAndListen(button);
}
@override
void onFlic2Error(String error) {
super.onFlic2Error(error);
// something went wrong somewhere, provide feedback maybe, or did you code something in the wrong order?
print('ERROR: $error');
}
@override
void onPairedButtonDiscovered(Flic2Button button) {
super.onPairedButtonDiscovered(button);
print('paired button ${button.uuid} discovered');
// discovered something already paired (getButtons will return these but maybe you didn't bother and
// just went right into a scan)
_addButtonAndListen(button);
}
@override
void onScanCompleted() {
super.onScanCompleted();
// scan completed, update the state of our view
setState(() {
_isScanning = false;
});
}
@override
void onScanStarted() {
super.onScanStarted();
// scan started, update the state of our view
setState(() {
_isScanning = true;
});
}
}