Блок Flutter Bloc A добавляет поток в блок B, но этот поток не будет отображаться в пользовательском интерфейсе через StreamBuilder.

Я пытаюсь перейти на архитектуру блока / поставщика для нового проекта флаттера. Я уже некоторое время устраняю проблему, когда потоки одного блока будут отображаться в пользовательском интерфейсе через StreamBuilder, а другой блок - нет. Прежде чем я продолжу, я бы хотел (и должен) узнать, почему.

У меня два блока, первый - ble_service. Я могу вызывать функции из пользовательского интерфейса в этот блок (строки app.dart 60-72), подключаясь к устройству и отображая то, что характеристика возвращает в пользовательском интерфейсе через StreamBuilder (строка 98). Простая визуализация возвращенной полезной нагрузки json с устройства ble в пользовательском интерфейсе. Он обновляется довольно часто, несколько раз в секунду.

Мой план состоял в том, чтобы создать блок парсера (bleBeltNotifyParser_bloc), который будет анализировать входящую полезную нагрузку json от ble_service, а затем передавать поток оттуда пользовательского интерфейса. В ble_service я передаю полезную нагрузку json в parserInputSink, поток из блока анализатора (строка 99 ble_service). В bleBeltNotifyParser.bloc я слушаю его в строке 21 и передаю в aTest2 (), где я планировал его проанализировать. Я остановился здесь, потому что я попытался отобразить эти данные в пользовательском интерфейсе (строка 108 app.dart), но независимо от того, какая другая комбинация передачи данных в parserInputController, пользовательский интерфейс отображает только исходные данные. Я подтвердил, что поток получает данные, распечатав их в строке 28.

Я также подтвердил, что могу попасть в блок Parser из пользовательского интерфейса, просто добавив некоторые данные в поток с помощью кнопок (строки 73 и 80). При нажатии этих кнопок эти данные добавляются в поток, а пользовательский интерфейс обновляется, как ожидалось.

Почему StreamBuilder работает с blue_service, а не с bleBeltNotifyParser_bloc? Я также попытался создать поток в сервисе, а затем прослушать его в парсере. Не повезло и их.

Мой main.dart

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  /// Starting here, everything is used regardless of dependencies
  var blocProvider = BlocProvider(
    bleBeltNotifyParserBloc: BleBeltNotifyParserBloc(),
    bleService: BleService(),

  );
  
  runApp(
    AppStateContainer(
      blocProvider: blocProvider,
      child:  App(),
    ),
  );
}

Мое приложение

import 'package:flutter/material.dart';
import 'package:flutterappbelt3/main.dart';
import 'package:flutterappbelt3/blocs/ble_service.dart';
import 'package:flutterappbelt3/blocs/bleBeltNotifyParser_bloc.dart';

class AppStateContainer extends StatefulWidget {
  final Widget child;
  final BlocProvider blocProvider;
  const AppStateContainer({
    Key key,
    @required this.child,
    @required this.blocProvider,
  }) : super(key: key);

  @override
  State<StatefulWidget> createState() => AppState();

  static AppState of(BuildContext context) {
    return (context.inheritFromWidgetOfExactType(_AppStoreContainer) as _AppStoreContainer).appData;
  }
}

class AppState extends State<AppStateContainer> {
  BlocProvider get blocProvider => widget.blocProvider;

  @override
  Widget build(BuildContext context) {
    return _AppStoreContainer(
      appData: this,
      blocProvider: widget.blocProvider,
      child: widget.child,
    );
  }

  void dispose() {
    super.dispose();
  }

}

class _AppStoreContainer extends InheritedWidget {
  final AppState appData;
  final BlocProvider blocProvider;

  _AppStoreContainer({
    Key key,
    @required this.appData,
    @required child,
    @required this.blocProvider,
  }) : super(key: key, child: child);

  @override
  bool updateShouldNotify(_AppStoreContainer oldWidget) => oldWidget.appData != this.appData;
}

class BlocProvider {
  BleBeltNotifyParserBloc bleBeltNotifyParserBloc = BleBeltNotifyParserBloc();
  BleService bleService;

  BlocProvider({
    @required this.bleBeltNotifyParserBloc,
    @required this.bleService,

  });
}

Мой app.dart

import 'package:flutter/material.dart';
import 'package:flutterappbelt3/blocs/bleBeltNotifyParser_bloc.dart';
import 'blocs/app_state.dart';
import 'blocs/ble_service.dart';

class App extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Position App',
      theme: new ThemeData(
        primarySwatch: Colors.red,
      ),
      home: PositionApp(),
    );
  }
}

class PositionApp extends StatefulWidget {
  @override
  _PositionAppState createState() => _PositionAppState();
}

class _PositionAppState extends State<PositionApp> {

  BleService _service = BleService();
  BleBeltNotifyParserBloc _bloc = BleBeltNotifyParserBloc();

  @override
  void initState() {
    super.initState();
     //_service.startScan();
    //_service.bleNotifyFromBelt.listen((String data) {_bloc.parserInputSink.add(data);});
  }

  @override
  Widget build(BuildContext context) {
    //SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
    BleService _bleServiceBloc = AppStateContainer.of(context).blocProvider.bleService;
    BleBeltNotifyParserBloc _bleBeltNotifyParserBloc = AppStateContainer.of(context).blocProvider.bleBeltNotifyParserBloc;
    return Scaffold(
      appBar: AppBar(
        title: Text("Position"),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            Container(
              child: Text("hello"),
            ),
            Container(
              child: FlatButton(onPressed: () {
                _bleServiceBloc.startScan();
              },
                child: Text("Scan & Connect"),
              ),
            ),
            Container(
              child: FlatButton(onPressed: () {
                _bleServiceBloc.discoverServices();
              },
                child: Text("discover services"),
              ),
            ),
            Container(
              child: FlatButton(onPressed: () {
                _bleServiceBloc.disconnectFromDevice();
              },
                child: Text("disconnect"),
              ),
            ),
            Container(
              child: FlatButton(onPressed: () {
                _bleBeltNotifyParserBloc.addToParserController1();
              },
                child: Text("Parser One"),
              ),
            ),
            Container(
              child: FlatButton(onPressed: () {
                _bleBeltNotifyParserBloc.addToParserController2();
              },
                child: Text("Parser Two"),
              ),
            ),
            Container(
              child: FlatButton(onPressed: () {
                _bleBeltNotifyParserBloc.aTest();
              },
                child: Text("aTest"),
              ),
            ),
            Container(
                child: StreamBuilder(
                    initialData: "0",
                    stream: _bleServiceBloc.bleNotifyFromBelt,
                    builder: (BuildContext context, AsyncSnapshot snapshot) {
                      if(snapshot.data == null) return CircularProgressIndicator();
                      else return Text(
                        snapshot.data.toString(),
                        style: TextStyle(fontSize: 14.0, color: Colors.black),
                        textAlign: TextAlign.center,
                      );}
                ),
              ),
            Container(
              child: StreamBuilder(
                  initialData: "0",
                  stream: _bleServiceBloc.bleStatusFromBelt,
                  builder: (BuildContext context, AsyncSnapshot snapshot) {
                    if(snapshot.data == null) return CircularProgressIndicator();
                    else return Text(
                      snapshot.data.toString(),
                      style: TextStyle(fontSize: 14.0, color: Colors.black),
                      textAlign: TextAlign.center,
                    );}
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Мой ble_service.dart


import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_blue/flutter_blue.dart';
import 'dart:convert' show utf8;
import 'package:rxdart/rxdart.dart';
import 'package:flutterappbelt3/blocs/bleBeltNotifyParser_bloc.dart';

class BleService {

//BleBeltNotifyParserBloc _bloc = BleBeltNotifyParserBloc();

  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("BLE STATUS");
  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;

  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 = await Future.value(scanResult.device).timeout(const Duration(seconds: 3));
        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);
              _bleNotifyFromBeltController.sink.add(bleNotifyString);
              _bloc.parserInputSink.add(bleNotifyString);
            });
            // COMMUNICATING
          }
          // Prepares characteristic for Write
          if (characteristic.uuid.toString() == WRITE_UUID) {
            characteristicWrite = characteristic;
          }
        });
      }
    });
  }
  disconnectFromDevice() {
    //if (beltDevice == null) return;
    //stopScan();
    beltDevice.disconnect();
    _bleStatusFromBeltController.add("Disconnect");
    print("Disconnect");
    // DISCONNECTED
  }
}

Мой bleBeltNotifyParser_bloc я не могу визуализировать

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import 'dart:math' as Math;
import 'package:flutterappbelt3/blocs/ble_service.dart';


class BleBeltNotifyParserBloc{

 // final BleService _bloc = BleService();

  StreamController<String> _parserInputController = BehaviorSubject<String>.seeded("Parser Input");
  Stream<String> get parserInput => _parserInputController.stream;
  Sink<String> get parserInputSink => _parserInputController.sink;

  BleBeltNotifyParserBloc(){


    _parserInputController.stream.listen(_aTest2);

    //_bloc.bleNotifyFromBelt.listen((String data) {parserInputSink.add(data); print('Got eem! Input Parser $data');}); Tried various things - such as listening to streams originating from ble_service.


  }

  void _aTest2(data) {
    print("WHAT!!! $data");
  }

  void aTest() {
    //_bloc.bleNotifyFromBelt.listen((String data) {_parserInputController.sink.add(data);});
  }

  void addToParserController1() {
    _parserInputController.sink.add("One");
  }

  void addToParserController2() {
    _parserInputController.sink.add("Two");
  }

  dispose() {
    _parserInputController.close();
  }
}


person nejas    schedule 29.06.2020    source источник


Ответы (1)


Я не хотел оставлять этот вопрос без ответа, поэтому хотел указать, что ответ на этот вопрос заключается в ответе на этот другой вопрос, который я разместил.

Почему не будет никаких изменений из NotifyParser отображать в пользовательском интерфейсе с помощью Provider / ChangeNotifier / Streambuilder, но будет из класса обслуживания

Комментарий к вопросу, на который дан ответ, который относится к этому вопросу.

Я прочитал это, но я не совсем понимаю проблему (вы внесли много изменений для тестирования потока, и я не знаю, в чем была реальная проблема без тестов), но я видел, что вы сделали то же самое, создав BleBeltNotifyParserBloc называется _bloc внутри BleService, и я считаю, что именно по этой причине он не обновил пользовательский интерфейс (по той же причине, что и здесь).

В этом примере я пробовал архитектуру Inherited Widget / Bloc с потоками, а затем перешел в другой вопрос к модели Provider / Bloc / Async, чтобы попытаться выяснить, почему пользовательский интерфейс не обновлялся при отправке данных из блока BleService в NotifyParser Блок.

Мне нужно поблагодарить https://stackoverflow.com/users/3547212/edwynzn за его ответ на связанный вопрос !!

person nejas    schedule 05.07.2020