提供者和流出现问题:在null

时间:2020-04-29 07:35:55

标签: flutter dart mqtt flutter-layout android-contentprovider

第一次在这里发布,这是我最后的希望:(。我正在尝试制作一个非常简单的Mqtt应用程序,该应用程序使用Ubidots代理来管理消息。首先,我有一个MQTTManager类:


import 'package:mqttapp/Services/Mqtt_messages_notifier.dart';
import 'package:flutter/cupertino.dart';
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqttapp/MQTTStates.dart';
import 'package:mqtt_client/mqtt_server_client.dart';
import 'dart:io';
import 'package:flutter/foundation.dart';


class MQTTManager{
  MqttServerClient cliente;
  MQTTMessagesNotifier _messagesNotifier = MQTTMessagesNotifier();
  String identifier;
  String topic;
  String host;


  void initialize({String host, String identifier}){
    MqttServerClient _client = MqttServerClient(host, identifier);
    this.identifier = _client.clientIdentifier;
    this.host = host;
    _client.port = 1883;
    _client.keepAlivePeriod = 20;
    _client.onDisconnected = onDisconnected;
    _client.onConnected = onConnected;
    _client.onSubscribed = onSubscribed;
    _client.logging(on: false);

    final conMess = MqttConnectMessage()
        .withClientIdentifier(identifier)
        .keepAliveFor(20)
        .withWillTopic('willtopic')
        .withWillMessage('willmessage')
        .startClean()
        .withWillQos(MqttQos.atLeastOnce);

    _client.connectionMessage = conMess;
    this.cliente = _client;
  }

  Future connect()async{
    print(cliente.clientIdentifier);
    try {
      MqttClientConnectionStatus result = await cliente.connect('HERE GOES THE UBIDOTS TOKEN, REMOVED IT FOR SECURITY REASONS','');
      return result.state;
    } on Exception catch (e) {
      print('Something went wrong $e');
      disconnect();
      return null;
    }
  }

  void subscription({String topic}) {
    this.topic = topic;
    print('EXAMPLE::Subscribing to the $topic topic');
    cliente.subscribe(topic, MqttQos.atMostOnce);
  }

  void unsubscribe({String topic}) {
    print('unsubscribing from $topic');
    cliente.unsubscribe(topic);
    print('Unsubscribbed!');
  }

  void publish({String topic, String message}) async {
    final builder = MqttClientPayloadBuilder();
    builder.addString(message);
    cliente.publishMessage(topic, MqttQos.atMostOnce, builder.payload);
  }

  void disconnect() async {
    await MqttUtilities.asyncSleep(2);
    print('EXAMPLE::Disconnecting');
    cliente.disconnect();
  }

  /// The subscribed callback
  void onSubscribed(String topic) {
    print('EXAMPLE::Subscription confirmed for topic $topic');
    cliente.updates.listen((List<MqttReceivedMessage<MqttMessage>> c) {
      final MqttPublishMessage recMess = c[0].payload;
      final String message =
      MqttPublishPayload.bytesToStringAsString(recMess.payload.message);
      _messagesNotifier.messageAlert(message);
    });
  }

  /// The unsolicited disconnect callback
  void onDisconnected() {
    print('EXAMPLE::OnDisconnected client callback - Client disconnection');
    if (cliente.connectionStatus.returnCode == MqttConnectReturnCode.solicited) {
      print('EXAMPLE::OnDisconnected callback is solicited, this is correct');
    }
  }

  /// The successful connect callback
  void onConnected() {
    _messagesNotifier.managerAlert(this.cliente);
    print(
        'EXAMPLE::OnConnected client callback - Client connection was sucessful');
  }

  /// Pong callback
  void pong() {
    print('EXAMPLE::Ping response client callback invoked');
  }
}

管理连接状态。对于onSubscribed回调函数和onConnected回调函数,我使用在另一个类中声明的两个函数,该类管理提供程序:

import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:mqtt_client/mqtt_server_client.dart';

class MQTTMessagesNotifier with ChangeNotifier {
  String _messageOfNotifier='';
  MqttServerClient _client;

  void messageAlert(String text){
    this._messageOfNotifier = text;
    notifyListeners();
  }
  void managerAlert(MqttServerClient client){
    this._client = client;
    notifyListeners();
  }

  String get message => _messageOfNotifier;
  MqttServerClient get client => _client;

}

messageAlert函数的目的是检索消息,并将其存储在类属性_messageOfNotifier中。与managerAlert相同,但是从类中检索MqttServerClient cliente,并将其存储在名为_cliente的属性中。 我还有一个显示图形内容的类:

import 'package:flutter/material.dart';
import 'package:mqttapp/Services/MQTT.dart';
import 'package:mqttapp/screens/Mqtt_screens/Mqtt_Publish.dart';
import 'package:mqttapp/screens/Mqtt_screens/Mqtt_Subscribe.dart';
import 'package:provider/provider.dart';
import 'package:mqttapp/Services/Mqtt_messages_notifier.dart';

class ScreensWrapper extends StatefulWidget {
  @override
  _ScreensWrapperState createState() => _ScreensWrapperState();
}

class _ScreensWrapperState extends State<ScreensWrapper> {
  String selectedButton;
  MQTTManager _manager = MQTTManager();
  int _selectedPage = 0;

  final _pageOptions = [
    MQTTSubscribe(),
    MQTTPublish(),
  ];

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MQTTMessagesNotifier(),
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Colors.blueGrey,
          title: Text('Subscribe'),
          actions: <Widget>[
            Consumer<MQTTMessagesNotifier>(
              builder: (context, messagesNotifier, child){
                _manager.cliente = messagesNotifier.client;
                return FlatButton.icon(
                  onPressed: (){
                    _manager.disconnect();
                  },
                  icon: Icon(Icons.arrow_back),
                  label: Text('return'),
                );
              },
            ),
          ],
        ),
        body: _pageOptions[_selectedPage],
        bottomNavigationBar: BottomNavigationBar(
          backgroundColor: Colors.blueGrey[200],
          fixedColor: Colors.blueGrey[900],
          currentIndex: _selectedPage,
          type: BottomNavigationBarType.fixed,
          onTap: (int index){
            setState((){
              _selectedPage = index;
            });
          },
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.tap_and_play),
              title: Text('Subscribe'),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.publish),
              title: Text('Publish'),
            ),
          ],
        ),
      ),
    );
  }
}

该类具有changeNotifierProvider,我认为当notifyListeners()时,它提供了我的MQTTMessagesNotifier类的快照。叫做。此处包含的两个类如下:

import 'dart:convert';
import 'package:mqttapp/Services/Mqtt_messages_notifier.dart';
import 'package:flutter/material.dart';
import 'package:mqttapp/Services/MQTT.dart';
import 'package:mqttapp/Services/Mqtt_messages_notifier.dart';
import 'package:mqttapp/shared/templates.dart';
import 'package:provider/provider.dart';



class MQTTPublish extends StatefulWidget {
  @override
  _MQTTPublishState createState() => _MQTTPublishState();
}

class _MQTTPublishState extends State<MQTTPublish> {
  final _formKey = GlobalKey<FormState>();
  MQTTManager _manager = MQTTManager();
  String topic = '';
  String variable = '';
  String value = '';
  String message = '';
  String error;

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
      child: Form(
        key: _formKey,
        child: Column(
          children: <Widget>[
            SizedBox(height: 10.0),
            TextFormField(
              decoration: textInputTemplate.copyWith(hintText: 'Topic'),
              validator: (val) => val.isEmpty ? 'Enter a valid topic': null,
              onChanged: (val) {
                setState(() => topic = val);
              },
            ),
            SizedBox(height: 10.0),
            TextFormField(
              decoration: textInputTemplate.copyWith(hintText: 'Variable'),
              validator: (val) => val.isEmpty ? 'Enter a valid variable': null,
              onChanged: (val) {
                setState(() => variable = val);
              },
            ),
            SizedBox(height: 10.0),
            TextFormField(
              decoration: textInputTemplate.copyWith(hintText: 'Value'),
              validator: (val) => val.isEmpty ? 'Enter a valid value': null,
              onChanged: (val) {
                setState(() => value = val);
              },
            ),
            SizedBox(height: 10.0),
            Consumer<MQTTMessagesNotifier>(
              builder: (context, messagesNotifier, child){
                _manager.cliente = messagesNotifier.client;
                return RaisedButton(
                  onPressed: () async {
                    if(_formKey.currentState.validate()){
                      setState(() {
                        topic = '/v1.6/devices/' + topic;
                        message = '{"$variable" : $value}';
                        _manager.publish(topic: topic, message: message);
                      });
                    }
                  },
                  color: Colors.pink[900],
                  child: Text(
                    'Publish',
                    style: TextStyle(
                      color: Colors.pink[100],
                      fontSize: 15.0,
                    ),
                  ),
                );
              },
            ),
            SizedBox(height: 10.0),
            Consumer<MQTTMessagesNotifier>(
              builder: (context, messagesNotifier, child){
                _manager.cliente = messagesNotifier.client;
                return SingleChildScrollView(
                  child: Text(
                      messagesNotifier.message
                  ),
                );
              },
            ),

            Consumer<MQTTMessagesNotifier>(
              builder: (context, messagesNotifier, child){
                _manager.cliente = messagesNotifier.client;
                return Text(_manager.cliente.subscriptionsManager.subscriptions.toString());
              },
            ),
          ],
        ),
      ),
    );
  }
}

import 'package:flutter/material.dart';
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqttapp/Services/MQTT.dart';
import 'package:mqttapp/Services/Mqtt_messages_notifier.dart';
import 'package:mqttapp/shared/templates.dart';
import 'package:mqtt_client/mqtt_server_client.dart';
import 'package:provider/provider.dart';


class MQTTSubscribe extends StatefulWidget {
  @override
  _MQTTSubscribeState createState() => _MQTTSubscribeState();
}

class _MQTTSubscribeState extends State<MQTTSubscribe> {
  final _formKey = GlobalKey<FormState>();
  MQTTManager _manager = MQTTManager();
  String topic = '';
  String error = '';

  @override
  Widget build(BuildContext context) {
    //_manager = ModalRoute.of(context).settings.arguments;
    //MQTTMessagesNotifier messagesNotifier = Provider.of  <MQTTMessagesNotifier>(context);
    //_manager.client = messagesNotifier.client;
    return SafeArea(
      child: Container(
          padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
          child: Form(
            key: _formKey,
            child: Column(
              children: <Widget>[
                SizedBox(height: 10.0),
                TextFormField(
                  decoration: textInputTemplate.copyWith(hintText: 'Topic'),
                  validator: (val) => val.isEmpty ? 'Enter a valid topic': null,
                  onChanged: (val) {
                    setState(() => topic = val);
                  },
                ),
                SizedBox(height: 10.0),
                Consumer<MQTTMessagesNotifier>(
                  builder: (context, messagesNotifier, child){
                    _manager.cliente = messagesNotifier.client;
                    return RaisedButton(
                      onPressed: () async {
                        if(_formKey.currentState.validate()){
                          setState(() {
                            topic = '/v1.6/devices/' + topic;
                            _manager.subscription(topic: topic);
                          });
                          //Navigator.pushReplacementNamed(context, '/publish',arguments: _manager);
                        }else{
                          error='idk wtf man';
                        }
                      },
                      color: Colors.pink[900],
                      child: Text(
                        'Subscription to topics',
                        style: TextStyle(
                          color: Colors.pink[100],
                          fontSize: 15.0,
                        ),
                      ),
                    );
                  },
                ),
                SizedBox(height: 10.0),
                Text(error),
              ],
            ),
          ),
        ),
    );
  }
}

问题是,当我尝试显示代码的这一特定部分时:

Consumer<MQTTMessagesNotifier>(
              builder: (context, messagesNotifier, child){
                _manager.cliente = messagesNotifier.client;
                return Text(_manager.cliente.subscriptionsManager.subscriptions.toString());
              },
            ),

控制台会弹出以下警告:

════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building Consumer<MQTTMessagesNotifier>(dirty, dependencies: [_DefaultInheritedProviderScope<MQTTMessagesNotifier>]):
The getter 'subscriptionsManager' was called on null.
Receiver: null
Tried calling: subscriptionsManager

The relevant error-causing widget was: 
  Consumer<MQTTMessagesNotifier> file:///C:/Users/brand/Documents/Proyecto%20Final%20ITS/AppsFlutter/mqtt_app/lib/screens/Mqtt_screens/Mqtt_Publish.dart:97:13
When the exception was thrown, this was the stack: 
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:53:5)
#1      _MQTTPublishState.build.<anonymous closure> (package:mqttapp/screens/Mqtt_screens/Mqtt_Publish.dart:100:46)
#2      Consumer.buildWithChild (package:provider/src/consumer.dart:175:12)
#3      SingleChildStatelessWidget.build (package:nested/nested.dart:260:41)
#4      StatelessElement.build (package:flutter/src/widgets/framework.dart:4291:28)
...
════════════════════════════════════════════════════════════════════════════════════════════════════

我假设发送此消息是因为.cliente中的_manager.cliente.subscriptionsManager.subscriptions.toString()为空,这意味着它可能没有正确实例化(?)。我已经尝试通过执行以下操作来查看消费者提供的实际变量是否在抛出异常:

Consumer<MQTTMessagesNotifier>(
              builder: (context, messagesNotifier, child){
                print(messagesNotifier.client.clientIdentifier);
                _manager.cliente = messagesNotifier.client;
                return Text(_manager.cliente.subscriptionsManager.subscriptions.toString());
              },
            ),

但是实际上它什么都不打印。此时的注意事项是:

  1. initialize方法,connect方法,publish方法和subscribe方法运行正常,因为在另一个版本中,我使用导航器在屏幕之间导航,并将MQTTManager的实例作为参数传递,并且效果很好

  2. 最初的问题是类_messageOfNotifier中的变量MQTTMessagesNotifier无法正常工作,因为我从未在此部分收到消息:

Consumer<MQTTMessagesNotifier>(
              builder: (context, messagesNotifier, child){
                _manager.cliente = messagesNotifier.client;
                return SingleChildScrollView(
                  child: Text(
                      messagesNotifier.message
                  ),
                );
              },
            ),

我认为问题可能出在某些方面:

  • 在“消费者”小部件中执行此_manager.cliente = messagesNotifier.client;时,可能未正确实例化或初始化某些内容。

  • 当我在MQTTSubscribe,MQTTPublish和ScreensWrapper类中初始化MQTTManager _manager = MQTTManager();时,我做错了。

  • 在MQTTMessagesNotifier类中,我需要正确地实例化或初始化MqttServerClient _client,实际上我不这么认为,因为MqttServerCliente在构造函数中接收了两个参数:host和identifier,我无法接收,以防万一重构,每次运行MQTTMessagesNotifier时我都会创建一个新的MqttServerClient,并且需要引用MQTTManager类中已经创建的。

我的pubspec.yaml文件:

name: mqttapp
description: A new Flutter application.

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
version: 1.0.0+1

environment:
  sdk: ">=2.1.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter

  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^0.1.2
  mqtt_client: ^6.2.1
  provider: ^4.0.5

dev_dependencies:
  flutter_test:
    sdk: flutter


# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter.
flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  # assets:
  #  - images/a_dot_burr.jpeg
  #  - images/a_dot_ham.jpeg

  # An image asset can refer to one or more resolution-specific "variants", see
  # https://flutter.dev/assets-and-images/#resolution-aware.

  # For details regarding adding assets from package dependencies, see
  # https://flutter.dev/assets-and-images/#from-packages

  # To add custom fonts to your application, add a fonts section here,
  # in this "flutter" section. Each entry in this list should have a
  # "family" key with the font family name, and a "fonts" key with a
  # list giving the asset and other descriptors for the font. For
  # example:
  # fonts:
  #   - family: Schyler
  #     fonts:
  #       - asset: fonts/Schyler-Regular.ttf
  #       - asset: fonts/Schyler-Italic.ttf
  #         style: italic
  #   - family: Trajan Pro
  #     fonts:
  #       - asset: fonts/TrajanPro.ttf
  #       - asset: fonts/TrajanPro_Bold.ttf
  #         weight: 700
  #
  # For details regarding fonts from package dependencies,
  # see https://flutter.dev/custom-fonts/#from-packages

先谢谢大家!真的很想知道解决方案:(

0 个答案:

没有答案