iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 24
1
IoT

來與IoT譜寫一首戀愛樂章吧系列 第 24

op.24 《應用層》-Flutter 與 MQTT

op.24 想回到那年的時空

妳說妳想回到原來的時空,但無奈現在的妳已經跟以前不同了
已經不能用與我一樣地方式穿越了......
但是沒關係,為了妳我可以再找出方法的!

再度進入連假魔王啦~天啊依舊我還是沒回家呢XD

Flutter 與 MQTT

今天就來一點簡單的部分,來嘗試看看把 Flutter 跟 Broker 運行一起看看吧。

MQTT 套件安裝

首先需要打開 pubspec.yaml,將 mqtt_client 的套件安裝到專案裡,所以需要在 pubspec.yaml 裡新增程式碼。

dependencies:
  mqtt_client: ^8.0.0

https://ithelp.ithome.com.tw/upload/images/20201009/20129084oqZhtmA4jg.png

然後將視線移到螢幕右上角有個 Pub get 按下去,就可以將套件安裝進去。
(備註:其他套件基本上也是相同的方式)
https://ithelp.ithome.com.tw/upload/images/20201009/2012908433GWgaKrgN.png

稍微等個幾秒鐘,下方出現這段提示,表示安裝完成。
https://ithelp.ithome.com.tw/upload/images/20201009/20129084NGrr9zHIah.png

接著回到 main.dart 之中,在上方 import 區域加入以下的 import

import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_server_client.dart';

到這邊就完成對 mqtt_client 的安裝啦。


撰寫 MQTT Client

接下來進入到 mqtt_Client 的連線,會需要有以下幾項的參數需要設定,所以我們先宣告以下東西。

const url = '127.0.0.1';        //主機位置
const port = 1883;              //MQTT port
const clientID = 'Client01';    //Mqtt Client
const username = 'Client01';    //Mqtt username
const password = 'password';    //Mqtt password

final client = MqttServerClient(url, clientID);

接下來要和 C# 一樣寫連線的 Function

Future<MqttServerClient> connect() async {
  client.port = port;          //對應 port
  client.setProtocolV311();    //設置 mqtt 的版本
  client.logging(on: true);
  
  await client.connect(username, password);
  
  if (client.connectionStatus.state == MqttConnectionState.connected) {
    print('client connected');
  } else {
    print(
        'ERROR client connection failed - disconnecting, state is ${client.connectionStatus.state}');
    client.disconnect();
  }
  
  //以下是訂閱主題並監聽的該主題的訊息
  client.subscribe("Test", MqttQos.atLeastOnce);    //這裡訂閱了 Test 主題。
  
  //下段程式碼為監聽的部分
  client.updates.listen((List<MqttReceivedMessage<MqttMessage>> c) {
    final MqttPublishMessage message = c[0].payload;
    final payload = MqttPublishPayload.bytesToStringAsString(message.payload.message);
    print('${c[0].topic}:${payload}');
  });
}

這裡用到一個比較特殊的 Function,屬於 Future 型的,使用這個目的為的便是使用異步的方式來執行,讓 mqtt 去慢慢處裡,使用 Future 代表說會在未來的某個時間返回結果。

然後在 main 裡塞入剛剛寫的 connect()。

void main() {
  //runApp();
  connect();
}

到這裡我們運行之前寫的 Broker 與 MQTTLens 來測試,MQTTLens 是一款線上的 mqtt client 端,可由瀏覽器執行 (連結)

成功運行應該可以看到兩個 Client 連線了
https://ithelp.ithome.com.tw/upload/images/20201009/20129084PORPqkPuUT.png

我們從 MQTTLens 發布訊息看看。
https://ithelp.ithome.com.tw/upload/images/20201009/20129084w4SwGeeNhq.png

此時的 Andorid Studio的 Console 視窗會出現剛剛從 MQTTLens 發的訊息。
https://ithelp.ithome.com.tw/upload/images/20201009/2012908478kaLXiyOc.png

到這邊我們就完成了訂閱的部分,那麼看起來現在只剩下從 App 這裡發送的功能了。
那我們先在頁面上拉出一個輸入框與按鈕,在 Widgets build 裡,我們先用到昨天有使用到的 Scaffold ,先在程式的最上方加入 AppBar,並且給予一個 title。

return Scaffold(
      appBar: AppBar(
        title: Text("MQTT example"),
      ),

記得最重要的一句話, Flutter 就像樂高一樣,我們剛剛已經堆出前面的頁面,現在要來繼續堆剩下的頁面,在 body 裡塞入一個 Center 容器,裡面我們放入一個直立的 Column (垂直布局)。

body: Center(
        child: Column(
             
        ),
      ),

接著要在這 挖好(佈局) 的洞放入 輸入框 與按鈕,值得注意的是,輸入框會需要將輸入的內容抓出來,所以會需要在Wigets build 之前,宣告一個物件來儲存內容;

final TextEditingController txf1 = new TextEditingController();

然後再將 輸入框 放入 Column 裡。

TextField(
    controller: txf1,
    decoration: InputDecoration(hintText: 'Input...'),
),

按鈕算是一個互動元件,除了顯示文字外,按下去後要有對應的事件產生,所以要給予一個 onPressed,後面會對應到自己寫的 Function 。

RaisedButton(
    child: Text('Print'),
    onPressed: btnEvent,
)

此時 Widget build 應該會長這樣。

Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("MQTT example"),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            TextField(
              controller: txf1,
              decoration: InputDecoration(hintText: 'Input...'),
            ),
            RaisedButton(
              child: Text('Print'),
              onPressed: btnEvent,
            )
          ],
        ),
      ),
    );

再來,剛剛說到按下按鈕的事件,我們接 Widget build 後面寫,簡單地介紹下面程式碼的功用,將內容從輸入框中提取出來,然後透過 mqtt publish 發送出去。

void btnEvent(){
    const pubTopic = 'testpub';
    final builder = MqttClientPayloadBuilder();
    builder.addString(txf1.text);
    client.publishMessage(pubTopic, MqttQos.atLeastOnce, builder.payload);
  }

今天的完整程式碼如下:

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

const url = '127.0.0.1';
const port = 1883;
const clientID = 'Client01';
const username = 'Client01';
const password = 'password';

final client = MqttServerClient(url, clientID);

void main() {
  runApp(MyApp());
  connect();
}

class MyApp extends StatelessWidget {
  @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 TextEditingController txf1 = new TextEditingController();
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("MQTT example"),
      ),
      body: Center(
        child: Column(
          children: <Widget>[
            TextField(
              controller: txf1,
              decoration: InputDecoration(hintText: 'Input...'),
            ),
            RaisedButton(
              child: Text('Print'),
              onPressed: btnEvent,
            )
          ],
        ),
      ),
    );
  }
  void btnEvent(){
    print(txf1.text);
    const pubTopic = 'testpub';
    final builder = MqttClientPayloadBuilder();
    builder.addString(txf1.text);
    client.publishMessage(pubTopic, MqttQos.atLeastOnce, builder.payload);
  }
}

Future<MqttServerClient> connect() async {
  client.port = port;
  client.setProtocolV311();
  client.logging(on: true);
  await client.connect(username, password);
  if (client.connectionStatus.state == MqttConnectionState.connected) {
    print('client connected');
  } else {
    print(
        'client connection failed - disconnecting, state is ${client.connectionStatus.state}');
    client.disconnect();
  }
  client.subscribe("Test", MqttQos.atLeastOnce);
  client.updates.listen((List<MqttReceivedMessage<MqttMessage>> c) {
    final MqttPublishMessage message = c[0].payload;
    final payload = MqttPublishPayload.bytesToStringAsString(message.payload.message);
    print('${c[0].topic}:${payload}');
  });
}

當成功執行時,畫面會出現一個輸入框與按鈕
https://ithelp.ithome.com.tw/upload/images/20201009/20129084qfhMaX5atC.jpg
輸入文字後,Broker會接收到剛剛按下 Print 的內容。
https://ithelp.ithome.com.tw/upload/images/20201009/20129084lv6k1gnQKS.png

好啦,今天的部分就到此結束啦,也恭喜各位把 MQTT 成功地帶到 Flutter 上了,明天沒意外我們來玩玩資料視覺化,讓冷冰冰的數據變成好看一點的圖表吧。

今日的曲子:<<噶瑪蘭>>蘇文慶


上一篇
op.23 《應用層》-Flutter 元件基本介紹
下一篇
op.25 《應用層》-Flutter 資料視覺化呈現
系列文
來與IoT譜寫一首戀愛樂章吧30

尚未有邦友留言

立即登入留言