为什么 NotifyParser 的任何更改都不会使用 Provider / ChangeNotifier / Streambuilder 在 UI 中呈现,而是来自服务 Class

Why won't any changes from NotifyParser render in the UI using Provider / ChangeNotifier / Streambuilder but will from a Service Class

根据示例代码,来自 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");
     });
  }
}

我不知道什么是 sl<NotifyParserBloc>() 但 BleService 中的 var notifyParser 它与您在 Provider 中创建的实例不同(因此 updating/notifying 更改为它不会实际上通知 UI 中的那个)。如果你想在其他提供者中保存一个实例,可以尝试这样的事情

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 IT

如果您在 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 中实例的更改将通知提供者 UI