Flutter自定义按钮插件flic_button的使用
Flutter自定义按钮插件flic_button的使用
flic_button
是一个用于Flutter和Dart的插件,它实现了Shortcut Labs Flic按钮协议,使Flutter应用能够在Android和iOS设备上使用Flic2按钮的功能。本文将详细介绍如何在Flutter项目中集成和使用flic_button
插件。
设备支持
当前支持
计划支持
- 可能会添加对原始Flic按钮的支持,但不确定有多少用户仍在使用这些旧版本按钮。
使用方法
该插件允许你搜索、连接、断开和忘记Flic 2按钮,并接收来自Flic 2库的回调以通知你按钮被按下。
请注意,目前没有后台服务来保持服务存活并在销毁时回调到Flutter。只要Flutter应用保持运行状态,Flic 2支持也会保持有效。
开始使用
修改Android的minSdkVersion
Flic2按钮仅兼容Android SDK 19及以上版本,因此需要在android/app/build.gradle
文件中修改以下内容:
android {
defaultConfig {
minSdkVersion: 19
}
}
添加蓝牙权限
我们需要添加蓝牙和位置访问权限:
Android
在android/app/src/main/AndroidManifest.xml
文件中添加以下权限:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
iOS
在ios/Runner/Info.plist
文件中添加以下权限:
<dict>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Need BLE permission</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>Need BLE permission</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Need Location permission</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Need Location permission</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Need Location permission</string>
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
</array>
</dict>
示例代码
以下是一个简单的示例代码,展示了如何在Flutter应用中使用flic_button
插件:
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(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> with Flic2Listener {
bool _isScanning = false;
final Map<String, Flic2Button> _buttonsFound = {};
Flic2ButtonClick? _lastClick;
FlicButtonPlugin? flicButtonManager;
@override
void initState() {
super.initState();
_startStopFlic2();
}
void _startStopScanningForFlic2() async {
if (!_isScanning) {
final isGranted = await Permission.bluetooth.request().isGranted &&
(!Platform.isAndroid ||
(await Permission.bluetoothScan.request().isGranted &&
await Permission.bluetoothConnect.request().isGranted));
if (!isGranted) {
print('cannot scan for a button when scanning is not permitted');
}
if (Platform.isAndroid && !await Permission.location.isGranted) {
await Permission.location.request();
}
flicButtonManager?.scanForFlic2();
} else {
flicButtonManager?.cancelScanForFlic2();
}
setState(() => _isScanning = !_isScanning);
}
void _startStopFlic2() {
if (null == flicButtonManager) {
setState(() => flicButtonManager = FlicButtonPlugin(flic2listener: this));
} else {
flicButtonManager?.disposeFlic2().then((value) => setState(() {
flicButtonManager = null;
}));
}
}
void _getButtons() {
flicButtonManager?.getFlic2Buttons().then((buttons) {
for (final button in buttons) {
_addButtonAndListen(button);
}
});
}
void _addButtonAndListen(Flic2Button button) {
setState(() {
_buttonsFound[button.uuid] = button;
flicButtonManager?.listenToFlic2Button(button.uuid);
});
}
Future<void> _connectDisconnectButton(Flic2Button button) async {
if (button.connectionState == Flic2ButtonConnectionState.disconnected) {
if (!await Permission.bluetoothConnect.request().isGranted) {
print('cannot connect to a button when bluetooth connect is not permitted');
}
flicButtonManager?.connectButton(button.uuid);
} else {
flicButtonManager?.disconnectButton(button.uuid);
}
}
void _forgetButton(Flic2Button button) {
flicButtonManager?.forgetButton(button.uuid).then((value) {
if (value != null && value) {
setState(() => _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?.invokation,
builder: (ctx, snapshot) {
if (snapshot.connectionState != ConnectionState.done) {
return Center(
child: ElevatedButton(
onPressed: () => _startStopFlic2(),
child: const Text('Start and initialize Flic2'),
),
);
} else {
return Column(
children: [
const SizedBox(height: 10),
const Text('Flic2 is initialized', style: TextStyle(fontSize: 20)),
ElevatedButton(onPressed: () => _startStopFlic2(), child: const Text('Stop Flic2')),
if (flicButtonManager != null)
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(onPressed: () => _getButtons(), child: const 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)
const Text('Hold down your flic2 button so we can find it now we are scanning...'),
Expanded(
child: ListView(
children: _buttonsFound.values.map((e) => ListTile(
key: ValueKey(e.uuid),
leading: const Icon(Icons.radio_button_on, size: 48),
title: Text('FLIC2 @${e.buttonAddr}'),
subtitle: Column(
children: [
Text('${e.uuid}\nname: ${e.name}\nbatt: ${e.battVoltage}V (${e.battPercentage}%)\nserial: ${e.serialNo}\npressed: ${e.pressCount}\n'),
Row(
children: [
ElevatedButton(
onPressed: () => _connectDisconnectButton(e),
child: Text(e.connectionState == Flic2ButtonConnectionState.disconnected ? 'connect' : 'disconnect'),
),
const SizedBox(width: 20),
ElevatedButton(onPressed: () => _forgetButton(e), child: const Text('forget')),
],
),
],
),
)).toList(),
),
),
],
);
}
},
),
),
);
}
@override
void onButtonClicked(Flic2ButtonClick buttonClick) {
print('button ${buttonClick.button.uuid} clicked');
setState(() {
_lastClick = buttonClick;
});
}
@override
void onButtonConnected() {
print('button connected');
setState(() {});
}
@override
void onButtonUpOrDown(Flic2ButtonUpOrDown button) {
print('button ${button.button.uuid} ${button.isDown ? 'down' : 'up'}');
}
@override
void onButtonDiscovered(String buttonAddress) {
print('button @$buttonAddress discovered');
flicButtonManager?.getFlic2ButtonByAddress(buttonAddress).then((button) {
if (button != null) {
print('button found with address $buttonAddress resolved to actual button data ${button.uuid}');
_addButtonAndListen(button);
}
});
}
@override
void onButtonFound(Flic2Button button) {
print('button ${button.uuid} found');
_addButtonAndListen(button);
}
@override
void onFlic2Error(String error) {
print('ERROR: $error');
}
@override
void onPairedButtonDiscovered(Flic2Button button) {
print('paired button ${button.uuid} discovered');
_addButtonAndListen(button);
}
@override
void onScanCompleted() {
setState(() {
_isScanning = false;
});
}
@override
void onScanStarted() {
setState(() {
_isScanning = true;
});
}
}
特性和问题
请在issue tracker提交功能请求和错误报告。
许可证
Dart flic_button
由Douglas Brain(Darker Waters LTD)根据Modified BSD License授权。
更多关于Flutter自定义按钮插件flic_button的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter自定义按钮插件flic_button的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,关于Flutter自定义按钮插件flic_button
的使用,以下是一个简单的代码案例来展示如何在Flutter项目中集成和使用该插件。需要注意的是,flic_button
这个名称在Flutter的官方插件库或常见社区插件中并不常见,这里假设它是一个自定义的或第三方插件,并基于通常的Flutter插件使用方法进行演示。
首先,确保你已经在pubspec.yaml
文件中添加了flic_button
插件的依赖(如果它是一个公开发布的插件):
dependencies:
flutter:
sdk: flutter
flic_button: ^x.y.z # 替换为实际的版本号
然后,运行flutter pub get
来安装依赖。
接下来,在你的Dart代码中导入并使用flic_button
。以下是一个简单的示例,展示如何创建一个自定义按钮:
import 'package:flutter/material.dart';
import 'package:flic_button/flic_button.dart'; // 假设插件的包名是flic_button
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flic Button Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Flic Button Demo'),
),
body: Center(
child: FlicButton(
// 假设FlicButton有这些参数,实际使用时应参考插件的文档
onPressed: () {
// 按钮点击事件处理
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Button clicked!')),
);
},
color: Colors.blue,
textColor: Colors.white,
icon: Icon(Icons.add), // 假设可以添加图标
label: Text('Click Me'), // 按钮标签
),
),
);
}
}
在这个示例中,我们创建了一个简单的Flutter应用,其中包含一个自定义的FlicButton
。请注意以下几点:
- 导入插件:通过
import 'package:flic_button/flic_button.dart';
导入插件。 - 使用插件:在
_MyHomePageState
的build
方法中,我们创建了一个FlicButton
实例,并设置了其点击事件处理函数、颜色、文本颜色、图标和标签。 - 点击事件处理:当按钮被点击时,会显示一个SnackBar提示。
请注意,由于flic_button
可能是一个假设的插件名称,实际使用时你需要参考该插件的官方文档来了解其API和可用的参数。如果它是一个私有插件或你正在开发它,你需要确保插件代码已经正确实现并发布了相应的包。