FlutterでTCP通信


# Flutter Dashロボット


FlutterでTCP通信例を作ったので、備忘録です。
Flutter Dashさんが左右に動く装置を、遠隔操作します。
TCP通信なのか、TELNET通信なのか、正確な名称はよくわかりませんが、
TELNETで接続後、コマンド「left」「right」で、Dashさんが左右に動かすことができる装置をコントロールします。

通信部分は以下を参照しました。
https://medium.com/flutter-community/working-with-sockets-in-dart-15b443007bc9

# 通信仕様


| 項目       | 設定値    |
----|----
| サーバーIP | 127.0.0.1 |
| ポート     | 5000      |
| ログインID | なし      |
| パスワード | なし      |
| 終端文字   | \r\n      |


# 操作コマンド


| コマンド | 説明                   |
----|----
| left     | Dashさんが左に動きます |
| right    | Dashさんが右に動きます |


# 操作方法


1  装置を起動する

![Sheet1_B43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165394/9a7e4537-5c19-28f0-c7f5-6ed0b6ec5634.png)


2 コントローラを起動する

![Sheet1_B52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165394/0d3ac5d1-994c-6b6e-5edb-1ad7a8a67b14.png)


3 コントローラからConnectする

4 ボタンが有効になったら、左右に動かす


![Sheet1_B67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/165394/8d98266b-dfee-adcc-27ac-72027d1a62d4.png)

# コントローラ


```dart
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => TcpClient(),
      child: MaterialApp(
        home: ClientPage(),
        debugShowCheckedModeBanner: false,
      ),
    ),
  );
}

class TcpClient with ChangeNotifier {
  Socket? socket;
  bool isConnect = false;
  String serverResponse = '';
  Future<void> connect() async {
    try {
      socket = await Socket.connect('127.0.0.1', 5000);
    } catch (e) {
      throw Exception('connect error!!');
    }
    // connect server
    isConnect = true;
    notifyListeners();

    // listen for responses from the server
    socket?.listen(
      // handle data from the server
      (Uint8List data) {
        serverResponse = String.fromCharCodes(data).replaceAll('\r\n', '');
        notifyListeners();
      },
      // handle errors
      onError: (error) {
        socket?.destroy();
      },
      // handle server ending connection
      onDone: () {
        isConnect = false;
        socket?.destroy();
        notifyListeners();
      },
    );
  }

  Future<void> sendMessage(String message) async {
    socket?.write(message);
    await Future.delayed(Duration(milliseconds: 500));
  }
}

/// Fcontroller main page
class ClientPage extends StatefulWidget {
  const ClientPage({Key? key}) : super(key: key);
  @override
  _ClientPageState createState() => _ClientPageState();
}

class _ClientPageState extends State<ClientPage> {
  @override
  Widget build(BuildContext context) {
    var client = context.read<TcpClient>();

    return Scaffold(
      appBar: AppBar(
        title: const Text('Fobot controller'),
      ),
      body: Row(
        children: [
          TextButton(
              onPressed: () {
                client.connect();
              },
              child: Text('connect')),
          const Padding(padding: EdgeInsets.all(3)),
          ElevatedButton(
              child: Icon(Icons.arrow_left_rounded),
              onPressed: context.watch<TcpClient>().isConnect
                  ? () {
                      client.sendMessage('left');
                    }
                  : null),
          const Padding(padding: EdgeInsets.all(3)),
          ElevatedButton(
              child: Icon(Icons.arrow_right_rounded),
              onPressed: context.watch<TcpClient>().isConnect
                  ? () {
                      client.sendMessage('right');
                    }
                  : null),
        ],
      ),
    );
  }
}
```

# 装置


コントローラの動作確認用に作ったもの

```dart
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => TcpServer(),
      child: MaterialApp(
        home: ServerPage(),
      ),
    ),
  );
}

/// network server
class TcpServer with ChangeNotifier {
  String request = '';

  /// constructor
  TcpServer() {
    start();
  }

  void start() async {
    final server = await ServerSocket.bind("127.0.0.1", 5000);
    server.listen((client) {
      client.listen(
        (Uint8List data) async {
          request = String.fromCharCodes(data).replaceAll('\r\n', '');
          client.write('ret:' + request + '\r\n');
          notifyListeners();
        },
        // handle errors
        onError: (error) {
          client.close();
        },
        // handle the closing the connection
        onDone: () {
          client.close();
        },
      );
    });
  }
}

/// fobot server main page
class ServerPage extends StatefulWidget {
  const ServerPage({Key? key}) : super(key: key);

  @override
  _ServerPageState createState() => _ServerPageState();
}

class _ServerPageState extends State<ServerPage> {
  final positionMax = 10;
  int position = 2;

  @override
  Widget build(BuildContext context) {
    var req = context.watch<TcpServer>().request;
    if (req == 'left') {
      position = position - 1 < 0 ? 0 : position - 1;
    } else if (req == 'right') {
      position = position >= positionMax - 1 ? positionMax - 1 : position + 1;
    }
    return Scaffold(
        body: Row(
            children: List.generate(positionMax, (index) {
      return Container(
          decoration: BoxDecoration(
            border: Border.all(color: Colors.black),
          ),
          child: Icon(position == index ? Icons.flutter_dash : null));
    })));
  }
}
```