Flutter结合Riverpod:使用服务类刷新视图遇问题求助
问题原因及解决方案
你的代码里ListView没反应,核心问题有两个:
- 用了
Provider来管理蓝牙服务,但Provider是不可变状态提供者,内部数据变化时不会通知Riverpod触发UI刷新 modulesScanned是普通List,直接添加元素不会让Riverpod感知到状态变化
修复步骤
1. 把蓝牙服务改成StateNotifier+StateNotifierProvider
StateNotifier是Riverpod专门用来管理可变状态的类,状态更新时会自动通知UI重建。
修改BluetoothService.dart:
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_reactive_ble/flutter_reactive_ble.dart'; import 'Module.dart'; // 改成StateNotifierProvider,泛型第一个是服务类,第二个是状态类型(这里是Module列表) final bluetoothServiceProvider = StateNotifierProvider<BluetoothService, List<Module>>((ref) => BluetoothService(ref)); class BluetoothService extends StateNotifier<List<Module>> { final Ref ref; final FlutterReactiveBle flutterReactiveBle = FlutterReactiveBle(); StreamSubscription? subscriptionDiscoverDevices; // 初始化状态为空列表 BluetoothService(this.ref) : super([]); Future<void> discoverDevices() async { // 先清空之前的扫描结果,避免重复 state = []; // 取消之前的订阅,防止多次扫描导致内存泄漏 subscriptionDiscoverDevices?.cancel(); subscriptionDiscoverDevices = flutterReactiveBle.scanForDevices(withServices: [SERVICE_UUID]).listen((device) { // 必须创建新列表更新state(StateNotifier要求状态不可变) // 可选:去重,避免同一设备多次添加 if (!state.any((module) => module.name == device.name)) { state = [...state, Module(name: device.name)]; } }, onError: (error) { print("蓝牙扫描错误: $error"); }); } // 销毁时取消订阅,释放资源 @override void dispose() { subscriptionDiscoverDevices?.cancel(); super.dispose(); } }
2. 更新UI层代码
现在可以直接watch状态列表,简化MyView.dart:
class ModulesDiscoverView extends ConsumerWidget { const ModulesDiscoverView({Key? key}) : super(key: key); @override Widget build(BuildContext context, WidgetRef ref) { // 直接watch状态列表 final scannedModules = ref.watch(bluetoothServiceProvider); return Scaffold( body: Column( children: [ ElevatedButton( // 通过notifier调用服务方法 onPressed: () => ref.read(bluetoothServiceProvider.notifier).discoverDevices(), child: const Text("Discover devices"), ), Expanded( child: ListView.builder( itemCount: scannedModules.length, itemBuilder: (context, index) { final module = scannedModules[index]; return Text(module.name); }, ), ), ], ), ); } }
额外注意事项
- 确保
SERVICE_UUID是正确的蓝牙服务UUID,否则扫描不到目标设备 - 检查蓝牙权限:Android需要位置权限+蓝牙权限,iOS需要蓝牙权限,权限不足会导致扫描失败
- 如果设备名称为空,可以改用
device.id显示,避免UI出现空白文本
内容的提问来源于stack exchange,提问作者Naografix
相关产品推荐
相关产品推荐

