Первая публикация здесь, и вы моя последняя надежда :(. Я пытаюсь создать очень простое приложение 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');
}
}
, который управляет состоянием соединений. Для функции обратного вызова onSubscribeed и для функции обратного вызова 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
. У меня также есть класс, который отображает graphi c stuff:
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, который, как я полагаю, предоставляет снимок моего класса MQTTMessagesNotifier при notifyListeners (); Здесь содержатся два класса:
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),
],
),
),
),
);
}
}
Проблема в том, что t, когда я пытаюсь отобразить эту указанную c часть кода:
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()
равно null, что означает, что он, вероятно, не был создан правильно (?). Я пытался увидеть, генерирует ли фактическая переменная, полученная потребителем, что-то вроде этого:
Consumer<MQTTMessagesNotifier>(
builder: (context, messagesNotifier, child){
print(messagesNotifier.client.clientIdentifier);
_manager.cliente = messagesNotifier.client;
return Text(_manager.cliente.subscriptionsManager.subscriptions.toString());
},
),
Но на самом деле он ничего не печатает. На этом этапе необходимо учитывать следующее:
Метод инициализации, метод соединения, метод publi sh и метод подписки работают должным образом, поскольку в отдельной версии я перемещался между экраны с использованием Navigator и передачей экземпляра MQTTManager в качестве аргументов, и он работал хорошо.
Изначально проблема заключалась в том, что переменная _messageOfNotifier
в классе MQTTMessagesNotifier
не работала , так как я никогда не получал сообщение в этой части:
Consumer<MQTTMessagesNotifier>(
builder: (context, messagesNotifier, child){
_manager.cliente = messagesNotifier.client;
return SingleChildScrollView(
child: Text(
messagesNotifier.message
),
);
},
),
Я предполагаю, что проблема может быть в некоторых из этих пунктов:
Когда я делаю это _manager.cliente = messagesNotifier.client;
в виджетах Consumer, что-то может неправильно создаваться или инициализироваться.
Когда я инициализирую MQTTManager _manager = MQTTManager();
в классах MQTTSubscribe, MQTTPubli sh и ScreensWrapper , Я делаю это неправильно.
В классе MQTTMessagesNotifier мне нужно правильно создать экземпляр или инициализировать MqttServerClient _client
, что я, честно говоря, так не считаю, так как MqttServ erCliente получает в конструкторе два параметра: хост и идентификатор, которые я не могу получить, и в случае, если я сделаю рефакторинг, я буду создавать новый MqttServerClient каждый раз, когда я запускаю MQTTMessagesNotifier, и мне нужно сослаться на уже созданный в класс MQTTManager.
My pubspe c .yaml file:
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
Заранее спасибо, ребята! Действительно отчаянно, чтобы узнать решение: (