Flutter自定义按钮插件flic_button的使用

发布于 1周前 作者 caililin 来自 Flutter

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

1 回复

更多关于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。请注意以下几点:

  1. 导入插件:通过import 'package:flic_button/flic_button.dart';导入插件。
  2. 使用插件:在_MyHomePageStatebuild方法中,我们创建了一个FlicButton实例,并设置了其点击事件处理函数、颜色、文本颜色、图标和标签。
  3. 点击事件处理:当按钮被点击时,会显示一个SnackBar提示。

请注意,由于flic_button可能是一个假设的插件名称,实际使用时你需要参考该插件的官方文档来了解其API和可用的参数。如果它是一个私有插件或你正在开发它,你需要确保插件代码已经正确实现并发布了相应的包。

回到顶部