为啥 NotifyParser 的任何更改都不会使用 Provider / ChangeNotifier / Streambuilder 在 UI 中呈现,而是来自服务类
Posted
技术标签:
【中文标题】为啥 NotifyParser 的任何更改都不会使用 Provider / ChangeNotifier / Streambuilder 在 UI 中呈现,而是来自服务类【英文标题】:Why won't any changes from NotifyParser render in the UI using Provider / ChangeNotifier / Streambuilder but will from a Service Class为什么 NotifyParser 的任何更改都不会使用 Provider / ChangeNotifier / Streambuilder 在 UI 中呈现,而是来自服务类 【发布时间】:2020-10-25 08:47:32 【问题描述】:根据示例代码,来自 Ble_Service 的任何数据都可以通过提供程序在 UI 中呈现更改。但是,如果我通过 changeParserInput() 将任何数据从 Ble_Service 传递给 NotifyParser,json 字符串就会结束,但无论我遵循哪种模式,我都无法让 UI 识别任何状态已更改并更新超出初始种子的 UI价值。
Ble_Service -> UI //没问题 Ble_Service -> NotifyParserBloc -> UI // 数据不成功。
为什么? (被注释掉的代码来自尝试过的许多不同的变体)
import 'package:flutter/material.dart';
import 'package:flutter_app_belt_provider/blocs/notifyParser_bloc.dart';
import 'package:flutter_app_belt_provider/services/bleBelt_service.dart';
import 'package:flutter_app_belt_provider/services/service_locator.dart';
import 'package:provider/provider.dart';
void main()
setupServiceLocator(); // <-- get it service locator function
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => BleService()),
ChangeNotifierProvider(create: (context) => NotifyParserBloc()),
//StreamProvider.value(value: NotifyParserBloc().parserInput)
],
child: MyApp(),
),
);
class MyApp extends StatelessWidget
// This widget is the root of your application.
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(),
);
class MyHomePage extends StatefulWidget
@override
_MyHomePageState createState() => _MyHomePageState();
class _MyHomePageState extends State<MyHomePage>
//final NotifyParserBloc _notify = NotifyParserBloc();
@override
Widget build(BuildContext context)
var _service = Provider.of<BleService>(context);
//var _bloc = Provider.of<NotifyParserBloc>(context, listen: false);
return Scaffold(
appBar: AppBar(
title: Text("poo"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
child: FlatButton(
child: Text(
"Scan",
),
onPressed: ()
_service.startScan();
,
)
),
Container(
child: FlatButton(
child: Text(
"Connect",
),
onPressed: ()
_service.connectToDevice();
,
)
),
Container(
child: FlatButton(
child: Text(
"Services",
),
onPressed: ()
_service.discoverServices();
,
)
),
Container(
child: FlatButton(
child: Text(
"Disconnect",
),
onPressed: ()
_service.disconnectFromDevice();
,
)
),
Consumer<NotifyParserBloc>(
builder: (context, notifyParserBloc, child) => Text(notifyParserBloc.parserInput)
),
Consumer<BleService>(
builder: (context, bleService, child) => Text(bleService.viaService)
),
StreamBuilder<String>(
initialData: "0",
stream: Provider.of<NotifyParserBloc>(context, listen: false).parserInputStream,
builder: (context, snapshot)
if(snapshot.data == null) return CircularProgressIndicator();
else return Text(
snapshot.data.toString(),
style: TextStyle(fontSize: 14.0, color: Colors.black),
textAlign: TextAlign.center,
);
),
// StreamProvider<String>.value(
// initialData: "go",
// value: _bloc.parserInput,
// child: ValueWidget(),
// ),
],
),
),
);
class ValueWidget extends StatelessWidget
@override
Widget build(BuildContext context)
//var model = Provider.of<NotifyParserBloc>(context);
return Column(
children: <Widget>[
Text('Listening a value :' + Provider.of<String>(context).toString(),),
],
);
ble_service
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter_app_belt_provider/services/service_locator.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'dart:convert' show utf8;
import 'package:rxdart/rxdart.dart';
import 'package:flutter_app_belt_provider/blocs/notifyParser_bloc.dart';
class BleService with ChangeNotifier
//NotifyParserBloc _bloc = NotifyParserBloc();
var notifyParser = sl<NotifyParserBloc>();
//class BleService
final String TARGET_DEVICE_NAME = "ESP32";
final String SERVICE_UUID = "4fafc201-1fb5-459e-8fcc-c5c9c331914b";
final String NOTIFY_UUID = "beb5483e-36e1-4688-b7f5-ea07361b26a8";
final String WRITE_UUID = "724b0547-3747-4c00-9710-5305a020018f";
FlutterBlue flutterBlue = FlutterBlue.instance;
StreamSubscription<ScanResult> scanSubScription;
BluetoothDevice beltDevice;
BluetoothCharacteristic characteristicNotify;
BluetoothCharacteristic characteristicWrite;
String bleNotifyString = "";
//BleBeltNotifyParserBloc _bloc = BleBeltNotifyParserBloc();
BehaviorSubject<String> _bleStatusFromBeltController = BehaviorSubject<String>.seeded("Disconnected");
Stream<String> get bleStatusFromBelt => _bleStatusFromBeltController.stream;
StreamController<String> _bleNotifyFromBeltController = BehaviorSubject<String>.seeded("BLE NOTIFY");
Stream<String> get bleNotifyFromBelt => _bleNotifyFromBeltController.stream;
Sink<String> get bleNotifyFromBeltSink => _bleNotifyFromBeltController.sink;
BehaviorSubject<String> _bleButtonTextController = BehaviorSubject<String>.seeded("button text");
Stream<String> get bleButtonTextGet => _bleButtonTextController.stream;
//BleService();
dispose()
_bleStatusFromBeltController.close();
_bleNotifyFromBeltController.close();
startScan()
stopScan();
// // SCANNING
scanSubScription = flutterBlue.scan().listen((scanResult) async
if (scanResult.device.name == TARGET_DEVICE_NAME)
stopScan();
// // FOUND
beltDevice = scanResult.device;
_bleStatusFromBeltController.add("Found");
//connectToDevice();
, onDone: () => stopScan());
stopScan()
flutterBlue.stopScan();
scanSubScription?.cancel();
scanSubScription = null;
_bleStatusFromBeltController.add("Disconnected");
print("print Disconnected");
connectToDevice() async
if (beltDevice == null) return;
// CONNECTING
await beltDevice.connect();
beltDevice.requestMtu(185);
print('print DEVICE CONNECTED');
print(" print BeltDevice $beltDevice");
_bleStatusFromBeltController.add("Connected");
print("print Connected");
//discoverServices();
discoverServices() async
print("discoverServices beltDevice name is $beltDevice");
if (beltDevice == null) return;
List<BluetoothService> services = await beltDevice.discoverServices();
services.forEach((service)
// do something with service
if (service.uuid.toString() == SERVICE_UUID)
service.characteristics.forEach((characteristic)
// set up notify characteristic
print("Service Found for $characteristic");
if (characteristic.uuid.toString() == NOTIFY_UUID)
characteristicNotify = characteristic;
// tell characteristic on server to notify
characteristicNotify.setNotifyValue(true);
print("notify set to true");
// listen, convert and put notify value in stream
characteristicNotify.value.listen((value)
bleNotifyString = utf8.decode(value);
//print("got characteristic $value");
print(bleNotifyString);
notifyParser.changeParserInput(bleNotifyString);
print("print Transmitting");
viaServiceChangeParserInput(bleNotifyString);
//_bleNotifyFromBeltController.sink.add(bleNotifyString);
//_bloc.parserInputSink.add(bleNotifyString);
//_bleStatusFromBeltController.add("Transmitting");
);
// COMMUNICATING
// Prepares characteristic for Write
if (characteristic.uuid.toString() == WRITE_UUID)
characteristicWrite = characteristic;
);
);
String _viaServiceChangeParserInput = "via Service";
String get viaService => _viaServiceChangeParserInput;
viaServiceChangeParserInput(String value)
_viaServiceChangeParserInput = value;
print("via Service $_viaServiceChangeParserInput");
notifyListeners();
disconnectFromDevice()
beltDevice.disconnect();
_bleStatusFromBeltController.add("Disconnected");
print("Disconnected");
// DISCONNECTED
bleButtonText() async
_bleStatusFromBeltController.listen((String data)
String buttonText;
if (data == "Disconnected") buttonText = "SCAN";
else if (data == "Found") buttonText = "CONNECT";
else if (data == "Connected") buttonText = "DISCONNECT";
return buttonText;
);
通知解析器
import 'package:flutter/foundation.dart';
import 'package:flutter_app_belt_provider/services/bleBelt_service.dart';
import 'dart:async';
import 'package:rxdart/rxdart.dart';
class NotifyParserBloc with ChangeNotifier
// NotifyParserBloc(@required this.bleService);
// final BleService bleService;
//final BleService bleService;
StreamController<String> _notifyParserController = BehaviorSubject<String>.seeded("parseNotify");
Stream<String> get parserInputStream => _notifyParserController.stream;
//
// Future<void> parseNotify(String data) async
// _notifyParserController.add(data);
// print("PARSENOTIFYBLOC: $data");
// _notifyParserController.stream.listen((event)
// print("NOTIFYPARSERSTREAM: $event");
// );
//
String _parserInput;
NotifyParserBloc()
_parserInput = "let's get this started";
String get parserInput => _parserInput;
void changeParserInput(String value)
_parserInput = value;
notifyListeners();
print("change parser value $_parserInput");
_notifyParserController.add(value);
_notifyParserController.stream.listen((event)
print("NOTIFYPARSERSTREAM: $event");
);
【问题讨论】:
【参考方案1】:我不知道sl<NotifyParserBloc>()
是什么,但 BleService 中的 var notifyParser 它与您在 Provider 中创建的实例不同(因此更新/通知对它的更改实际上不会通知取决于用户界面)。如果您想在其他提供者中保存一个实例,可以尝试这样的操作
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => NotifyParserBloc()),
ChangeNotifierProvider(create: (context) => BleService(Provider.of<NotifyParserBloc>(context, listen: false)),
],
child: MyApp(),
),
class BleService with ChangeNotifier
final notifyParser;
BleService(this.notifyParser); //save the instance in the constructor
...
notifyParser.changeParserInput(bleNotifyString); //now it will update the one using the UI
更新
您可以尝试同样的方法传递定位器而不是 Provider.of<NotifyParserBloc>(context, listen: false)
以提高可读性
MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => NotifyParserBloc()),
ChangeNotifierProvider(create: (context) => BleService(context.read)), //this is an extension method that allows us to use the Locator
],
child: MyApp(),
),
class BleService with ChangeNotifier
final notifyParser;
BleService(Locator locator) : notifyParser = locator<NotifyParserBloc>(); //save the instance in the constructor
...
notifyParser.changeParserInput(bleNotifyString); //now it will update the one using the UI
使用 GET 更新
如果您已经在 getit (sl()) 中有一个实例并且想要将其公开给提供者,只需将您的 ChangeNotifierProvider 更改为 .value
MultiProvider(
providers: [
ChangeNotifierProvider.value(value: sl<NotifyParserBloc>()), //this will expose the same instance of the get it to the UI
ChangeNotifierProvider(create: (context) => BleService())
],
child: MyApp(),
),
class BleService with ChangeNotifier
var notifyParser = sl<NotifyParserBloc>();
// No need for the constructor now
...
notifyParser.changeParserInput(bleNotifyString); //now it will update the one using the UI
这样,get_it 中对实例的更改将从 Provider 通知 UI
【讨论】:
感谢您的帮助。 sl以上是关于为啥 NotifyParser 的任何更改都不会使用 Provider / ChangeNotifier / Streambuilder 在 UI 中呈现,而是来自服务类的主要内容,如果未能解决你的问题,请参考以下文章
为啥 jQuery.val(value) 不会从 DOM 元素中分派任何事件?