Flutter + PubNub + NodeRED + Arduino + Raspberry + IoT + Smart House (Parte 2 - Mobile)
Tags: flutter, mobile, pubnub, iot
Published: 2021-04-04 01:39:55

Aviso 1: Antes de ler este texto, indico a leitura da [primeira parte](https://dev.to/ricardoogliari/flutter-pubnub-nodered-arduino-raspberry-iot-smart-house-parte-1-159l).

Aviso 2: O código fonte da aplicação mobile está disponível [aqui](https://github.com/ricardoogliari/Smart-Party-House).

Notice 1: Before reading this text, I recommend reading the [first part] (https://dev.to/ricardoogliari/flutter-pubnub-nodered-arduino-raspberry-iot-smart-house-parte-1-159l) .

Notice 2: The source code of the mobile application is available [here] (https://github.com/ricardoogliari/Smart-Party-House).

**Aplicação Flutter. Flutter Application.**
A aplicação mobile foi feita com o [Flutter framework](https://flutter.dev/). A feature de controle de mídia for fortemente baseada no artigo [A Comprehensive Guide to Playing Local mp3 Files (With Seek Functionality) in Flutter](https://medium.com/@pongpiraupra/a-comprehensive-guide-to-playing-local-mp3-files-with-seek-functionality-in-flutter-7730a453bb1a). The mobile application was made with the [Flutter framework] (https://flutter.dev/). The media control feature is heavily based on the article [A Comprehensive Guide to Playing Local mp3 Files (With Seek Functionality) in Flutter] (https://medium.com/@pongpiraupra/a-comprehensive-guide-to-playing- local-mp3-files-with-seek-functionality-in-flutter-7730a453bb1a).

Como a interface é bem simples, podemos usar uma classe *StatelessWidget*, com seu ciclo de vida mais simples e sem necessidade de controle de status. The interface is very simple, then, we can use a *StatelessWidget* class, with its simplest life cycle and without the need for status control.

A construção da interface acontece na sobrescrita do *build*, usando o par *MaterialApp -> Scaffold*, com um *AppBar* e um *body*. O corpo é construído no método *_body*. Temos apenas um *Center -> Container -> Column*, contendo dois botões (criados no método *_btn*). Além das funções normais da mídia, fazemos uma chamada ao *publishToPubnub*, enviando por parâmetro a ação necessário. 1 indica que a lâmpada deve ser ligada e 0 que deve ser desligada. The interface construction takes place in the *build* method, using the pair *MaterialApp -> Scaffold*, with an *AppBar* and a *body*. The body is built using the *_body* method. We have only one *Center -> Container -> Column*, containing two buttons (created in the *_btn* method). In addition to the normal functions of the media, we make a call to *publishToPubnub*, sending the required action by parameter. 1 indicates that the lamp should be turned on and 0 that it should be turned off.

 ```
Widget _body() => Center(
      child: Container(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            _btn('Play', () {
              audioCache.play('one_horse_town.mp3');
              publishToPubNub(1);
            }),
            _btn('Stop', () {
              advancedPlayer.stop();
              publishToPubNub(0);
            }),
          ]
        ),
      ),
    );

  Widget _btn(String txt, VoidCallback onPressed) {
    return ButtonTheme(
        minWidth: 48.0,
        child: ElevatedButton(
            child: Text(txt),
            onPressed: onPressed
        )
    );
  }

  @override
  Widget build(BuildContext context) {
    initPlayer();
    pubnub = PubNub(defaultKeyset: myKeyset);
    myChannel = pubnub.channel('node-red');

    return Scaffold(
        appBar: AppBar(
          title: Text('Smart Party House'),
        ),
        body: _body()
      );
  }
```

O leitor deve ter visto que no *build*, criamos a instância de *PubNub* e *Channel*. O *PubNub* é um sistema de mensageria que segue o padrão *Publisher-Subscriber*. Sendo que, atuamos como *publisher* ou como *subscriber* em um dado canal. Neste caso, chamamos o mesmo de *node-red*. The reader must have seen that in *build* we created the instance of *PubNub* and *Channel*. *PubNub* is a messaging system that follows the *Publisher-Subscriber* pattern. Being that, we act as *publisher* or as *subscriber* in a given channel. In this case, we call it *node-red*.

Na instância de *PubNub* passamos uma instância de *Keyset*, que foi criada como uma constante da própria classe *MainWidget*. As chaves de publisher e subscriber são criadas no console do PubNub na aplicação criada neste mesmo local. *IMPORTANTE*: não confunda. Temos a aplicação Flutter e uma aplicação criada no console do PubNub, para termos acesso ao par de chaves pub-sub. In the instance of *PubNub* we pass an instance of *Keyset*, which was created as a constant of the *MainWidget* class itself. The publisher and subscriber keys are created in the PubNub console in the application created in that same location. *IMPORTANT*: do not confuse. We have the Flutter application and an application created on the PubNub console, to have access to the pub-sub key pair.

O método *publishToPubNub* é bem simples. Ele somente publica um dado no formato JSON para o canal criado previamente. The *publishToPubNub* method is quite simple. It only publishes data in JSON format for the previously created channel.

```
publishToPubNub(int action){
    myChannel.publish({ 'action': action });
}
```

Finalmente, faltou falar somente do método *initPlayer*. Neste local estamos apenas criando as instâncias de *AudioPlayer* e *AudioCache*. Também, criamos um *listener* para quando o áudio tocar completamente. Neste momento, apenas mandamos a ação de parar a música via PubNub. Finally, we only needed to talk about the *initPlayer* method. In this place we are just creating the instances of *AudioPlayer* and *AudioCache*. Also, we created a *listener* for when the audio plays completely. At this point, we just send the action to stop the music via PubNub.

```
void initPlayer(){
    advancedPlayer = AudioPlayer();
    audioCache = AudioCache(fixedPlayer: advancedPlayer);

    advancedPlayer.onPlayerCompletion.every((element) {
      publishToPubNub(0);
      return true;
    });
  }
```