You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修改Flutter_Blue读写按钮的界面设计

问题

我需要修改蓝牙设备界面上读写命令的前端样式。目前使用的是pauldemarco仓库中的Flutter_Blue代码,但该设备界面的前端设计不符合我的应用需求,请问如何修改界面上上传、下载信号按钮的设计?

import 'dart:async';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'widgets.dart';
import 'dart:ui' as ui;
import 'package:flutter/rendering.dart';
import 'package:dproject/qrcode.dart';
void main() {
  // RenderErrorBox.backgroundColor = Colors.transparent;
  ErrorWidget.builder = (FlutterErrorDetails details) => Scaffold(body:Center(child: Text("Click the Refresh Button"),));
  runApp(FlutterBlueApp());
  // static ErrorWidgetBuilder builder = _defaultErrorWidgetBuilder;
  // RenderErrorBox.textStyle = ui.TextStyle(color: Colors.transparent);
}

class FlutterBlueApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      color: Colors.lightBlue,
      home: StreamBuilder<BluetoothState>(
          stream: FlutterBlue.instance.state,
          initialData: BluetoothState.unknown,
          builder: (c, snapshot) {
            final state = snapshot.data;
            if (state == BluetoothState.on) {
              return FindDevicesScreen();
            }
            return BluetoothOffScreen(state: state);
          }),
    );
  }
}

class BluetoothOffScreen extends StatelessWidget {
  const BluetoothOffScreen({Key? key, this.state}) : super(key: key);

  final BluetoothState? state;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.lightBlue,
      body: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Icon(
              Icons.bluetooth_disabled,
              size: 200.0,
              color: Colors.white54,
            ),
            Text(
              'Bluetooth Adapter is ${state != null ? state.toString().substring(15) : 'not available'}.',
              // style: Theme.of(context)
              //     .primaryTextTheme
              //     .subhead
              //     ?.copyWith(color: Colors.white),
            ),
          ],
        ),
      ),
    );
  }
}

class FindDevicesScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Find Devices'),
      ),
      body: RefreshIndicator(
        onRefresh: () =>
            FlutterBlue.instance.startScan(timeout: Duration(seconds: 8)),
        child: SingleChildScrollView(
          child: Column(
            children: <Widget>[
              StreamBuilder<List<BluetoothDevice>>(
                stream: Stream.periodic(Duration(seconds: 2))
                    .asyncMap((_) => FlutterBlue.instance.connectedDevices),
                initialData: [],
                builder: (c, snapshot) => Column(
                  children: snapshot.data!
                      .map((d) => ListTile(
                            // title: Text(d.toString()),
                            // subtitle: Text(d.id.toString()),
                            trailing: StreamBuilder<BluetoothDeviceState>(
                              stream: d.state,
                              initialData: BluetoothDeviceState.disconnected,
                              builder: (c, snapshot) {
                                if (snapshot.data ==
                                    BluetoothDeviceState.connected) {
                                  return ElevatedButton(
                                    child: Text('OPEN'),
                                    onPressed: () => Navigator.of(context).push(
                                        MaterialPageRoute(
                                            builder: (context) =>
                                                DeviceScreen(device: d))),
                                  );
                                }
                                return Text(snapshot.data.toString());
                              },
                            ),
                          ))
                      .toList(),
                ),
              ),
              StreamBuilder<List<ScanResult>>(
                stream: FlutterBlue.instance.scanResults,
                initialData: [],
                builder: (c, snapshot){ 
                  List<ScanResult> data = [];
                  //  print("sh");
                  for(int i=0;i<snapshot.data!.length;i++)
                  { 
                    // print("sh");
                    // print(snapshot.data![i]);
                    if(snapshot.data![i].device.id.toString()=='FC:67:78:5D:96:EF'){
                      data.add(snapshot.data![i]);
                    }
                  }
                  // print(data);
                  return Column(
                  children: data
                      .map(
                        (r) => ScanResultTile(
                          result: r,
                          onTap: () => Navigator.of(context)
                              .push(MaterialPageRoute(builder: (context) {
                            r.device.connect();
                            return                         DeviceScreen(device: r.device); //FlutterBlueApp();
                          })),
                        ),
                      )
                      .toList(),
                );
                },
                ),
            ],
          ),
        ),
      ),
      floatingActionButton: StreamBuilder<bool>(
        stream: FlutterBlue.instance.isScanning,
        initialData: false,
        builder: (c, snapshot) {
          if (snapshot.data!) {
            return FloatingActionButton(
              child: Icon(Icons.stop),
              onPressed: () => FlutterBlue.instance.stopScan(),
              backgroundColor: Colors.red,
            );
          } else {
            return FloatingActionButton(
                child: Icon(Icons.search),
                onPressed: () => FlutterBlue.instance
                    .startScan(timeout: Duration(seconds: 8)));
          }
        },
      ),
    );
  }
}

class DeviceScreen extends StatelessWidget {
  const DeviceScreen({Key? key, required this.device}) : super(key: key);
  final BluetoothDevice device;

  List<int> _getRandomBytes() {
    final math = Random();
    return [
      math.nextInt(255),
      math.nextInt(255),
      math.nextInt(255),
      math.nextInt(255)
    ];
  }

  List<Widget> _buildServiceTiles(List<BluetoothService> services) {
    List<BluetoothService> data = [];
       print("sh%%%%%%%%%%%%%%%%%%%%%%@@@@");
      for(int i=0;i<services.length;i++)
      {
        print("sh####################################");
        // print(services[i]);
        if(i==2){
          data.add(services[i]);
        }
      }
      // print(data);
    return data //services
        .map(
          (s) => ServiceTile(
            service: s,
            characteristicTiles: s.characteristics
                .map(
                  (c) => CharacteristicTile(
                    characteristic: c,
                    onReadPressed: () {
                      c.read();
                    },
                    onWritePressed: () async {
                      await c.write([1], withoutResponse: false);
                      await c.read();
                    // ElevatedButton(
                    //   // style: style,
                    //   onPressed: null,
                    //   child: const Text('Disabled'),
                    // );
                    },
                    onNotificationPressed: () async {
                      await c.setNotifyValue(!c.isNotifying);
                      await c.read();
                    },
                    descriptorTiles: c.descriptors
                        .map(
                          (d) => DescriptorTile(
                            descriptor: d,
                            onReadPressed: () => d.read(),
                            onWritePressed: () => d.write([1]),
                          ),
                        )
                        .toList(),
                  ),
                )
                .toList(),
          ),
        )
        .toList();
        //  print(c.read);
  }
  //  @override
  //   State<DeviceScreen> createState() => _DeviceScreenState();
  // }
  // class _DeviceScreenState extends State<DeviceScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("BITLOCK"),//device.name
        actions: <Widget>[
          StreamBuilder<BluetoothDeviceState>(
            stream: device.state,
            initialData: BluetoothDeviceState.connecting,
            builder: (c, snapshot) {
              VoidCallback? onPressed;
              String text;
              switch (snapshot.data) {
                case BluetoothDeviceState.connected:
                  onPressed = () => device.disconnect();
                  text = 'DISCONNECT';
                  break;
                case BluetoothDeviceState.disconnected:
                  onPressed = () => device.connect();
                  text = 'CONNECT';
                  break;
                default:
                  onPressed = null;
                  text = snapshot.data.toString().substring(21).toUpperCase();
                  break;
              }
              return TextButton(
                  onPressed: onPressed,
                  child: Text(
                    text,
                    style: Theme.of(context)
                        .primaryTextTheme
                        .button
                        ?.copyWith(color: Colors.white),
                  ));
            },
          )
        ],
      ),
      body: SingleChildScrollView(
        child: Column(
          children: <Widget>[
            StreamBuilder<BluetoothDeviceState>(
              stream: device.state,
              initialData: BluetoothDeviceState.connecting,
              builder: (c, snapshot) => ListTile(
                leading: (snapshot.data == BluetoothDeviceState.connected)
                    ? Icon(Icons.bluetooth_connected)
                    : Icon(Icons.bluetooth_disabled),
                title: Text(((){
                    if(snapshot.data.toString().split('.')[1]=="connected"){
                      return 'Your Lock is ${snapshot.data.toString().split('.')[1]} to your device.';
                    }
                    return 'Your Lock is ${snapshot.data.toString().split('.')[1]} from your device.';
                    })()),
                // subtitle: Text('${device.id}'),
                trailing: StreamBuilder<bool>(
                  stream: device.isDiscoveringServices,
                  initialData: false,
                  builder: (c, snapshot) => IndexedStack(
                    index: snapshot.data! ? 1 : 0,
                    children: <Widget>[
                      IconButton(
                        icon: Icon(Icons.refresh),
                        onPressed: () => device.discoverServices(),
                      ),
                      IconButton(
                        icon: SizedBox(
                          child: CircularProgressIndicator(
                            valueColor: AlwaysStoppedAnimation(Colors.grey),
                          ),
                          width: 18.0,
                          height: 18.0,
                        ),
                        onPressed: null,
                      )
                    ],
                  ),
                ),
              ),
            ),
            StreamBuilder<int>(
              stream: device.mtu,
              initialData: 0,
              builder: (c, snapshot) => ListTile(
                // title: Text('MTU Size'),
                // subtitle: Text('${snapshot.data} bytes'),
                trailing: IconButton(
                  icon: Icon(Icons.lock_open),
                  onPressed: () => device.requestMtu(223),
                ),
              ),
            ),
            StreamBuilder<List<BluetoothService>>(
              stream: device.services,
              initialData: [],
              builder: (c, snapshot) {
                return Column(
                  children: _buildServiceTiles(snapshot.data!),
                );
              },
            ),
          ],
        ),
      ),
    );
  }
}

蓝牙设备界面截图

解决方案

要修改读写命令的按钮样式,核心是调整CharacteristicTile组件中的按钮实现——你的代码引用了项目中widgets.dart文件里的CharacteristicTile,直接修改这个组件即可:

  1. 定位CharacteristicTile定义
    打开项目中的widgets.dart文件,找到CharacteristicTile类,里面包含了读取、写入按钮的默认实现(通常是IconButton)。

  2. 替换为自定义按钮样式
    将默认按钮替换为符合需求的样式,以下是示例:

修改读取(下载)按钮

// 替换原读取按钮代码
ElevatedButton(
  style: ElevatedButton.styleFrom(
    backgroundColor: Colors.blueAccent,
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
    padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
  ),
  onPressed: onReadPressed,
  child: Row(
    mainAxisSize: MainAxisSize.min,
    children: const [
      Icon(Icons.download, size: 18),
      SizedBox(width: 6),
      Text("读取数据"),
    ],
  ),
)

修改写入(上传)按钮

// 替换原写入按钮代码
OutlinedButton(
  style: OutlinedButton.styleFrom(
    side: BorderSide(color: Colors.green),
    shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
    padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
  ),
  onPressed: onWritePressed,
  child: Row(
    mainAxisSize: MainAxisSize.min,
    children: const [
      Icon(Icons.upload, size: 18),
      SizedBox(width: 6),
      Text("上传数据"),
    ],
  ),
)
  1. 添加状态样式(可选)
    如果需要区分按钮禁用/激活状态,可在样式中补充状态配置:
ElevatedButton.styleFrom(
  backgroundColor: Colors.blueAccent,
  disabledBackgroundColor: Colors.grey.shade300,
  disabledForegroundColor: Colors.grey.shade600,
)
  1. 调整按钮布局(可选)
    若要改变按钮排列方式,用Row或Wrap包裹按钮并设置间距:
Row(
  mainAxisAlignment: MainAxisAlignment.end,
  children: [
    // 读取按钮
    SizedBox(width: 10),
    // 写入按钮
  ],
)

内容的提问来源于stack exchange,提问作者Shailesh Rathod

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 05:57:22