skip to Main Content

I have a project in Flutter. I would like to try to read multiple JSON files in realtime through websocket but I am experiencing difficulties.

I have tried several codes and, unfortunately, even asked ChatGPT for help but no luck. I would like to initially have the JSON information shown on the screen, later when a JSON is modified in realtime I would like to see the changes on the page. Could you explain how to do this in detail?

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:web_socket_channel/status.dart' as status;

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'JSON Fetch',
      theme: ThemeData(
        useMaterial3: true,
        primarySwatch: Colors.blue,
      ),
      home: const HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  late String _title1 = 'Loading...';
  late String _status1 = '';
  List _items1 = [];

  late String _title2 = 'Loading...';
  late String _status2 = '';
  List _items2 = [];

  late WebSocketChannel channel1;
  late WebSocketChannel channel2;
  late Timer _reconnectTimer1;
  late Timer _reconnectTimer2;

  @override
  void initState() {
    super.initState();
    _connectWebSocket1();
    _connectWebSocket2();
    readJson1();
    readJson2();
  }

  void _connectWebSocket1() {
    channel1 =             
    WebSocketChannel.connect(Uri.parse('ws://localhost:8080'));

    channel1.stream.listen((data) {
      _handleWebSocketData(data, 1);
    }, onError: (error) {
      print('WebSocket error: $error');
      _reconnectWebSocket1();
    }, onDone: () {
      print('WebSocket connection closed');
      _reconnectWebSocket1();
    });
  }

  void _connectWebSocket2() {
    channel2 =     
    WebSocketChannel.connect(Uri.parse('ws://localhost:8081'));

    channel2.stream.listen((data) {
      _handleWebSocketData(data, 2);
    }, onError: (error) {
      print('WebSocket error: $error');
      _reconnectWebSocket2();
    }, onDone: () {
      print('WebSocket connection closed');
      _reconnectWebSocket2();
    });
  }

  void _reconnectWebSocket1() {
    _reconnectTimer1 = Timer(const Duration(seconds: 5), () {
      if (mounted) {
        _connectWebSocket1();
      }
    });
  }

  void _reconnectWebSocket2() {
    _reconnectTimer2 = Timer(const Duration(seconds: 5), () {
      if (mounted) {
        _connectWebSocket2();
      }
    });
  }

  void _handleWebSocketData(dynamic data, int blockNumber) {
    final decodedData = json.decode(data);
    setState(() {
      if (blockNumber == 1) {
        _items1 = decodedData["blocks"][0]["items"];
        _title1 = decodedData["blocks"][0]["title"];
        _status1 = decodedData["blocks"][0]["status"];
      } else if (blockNumber == 2) {
        _items2 = decodedData["blocks"][0]["items"];
        _title2 = decodedData["blocks"][0]["title"];
        _status2 = decodedData["blocks"][0]["status"];
      }
    });
  }

  Future<void> readJson1() async {
    try {
      final response = await     
      http.get(Uri.parse('http://localhost/Flutter/data.json'));
      if (response.statusCode == 200) {
        final data = json.decode(response.body);
        setState(() {
          _items1 = data["blocks"][0]["items"];
          _title1 = data["blocks"][0]["title"];
          _status1 = data["blocks"][0]["status"];
        });
      } else {
        print('Failed to load data: ${response.statusCode}');
        throw Exception('Failed to load data');
      }
    } catch (e) {
      print('Error fetching data: $e');
    }
  }

  Future<void> readJson2() async {
    try {
      final response = await     
      http.get(Uri.parse('http://localhost/Flutter/data2.json'));
      if (response.statusCode == 200) {
        final data = json.decode(response.body);
        setState(() {
          _items2 = data["blocks"][0]["items"];
          _title2 = data["blocks"][0]["title"];
          _status2 = data["blocks"][0]["status"];
        });
      } else {
        print('Failed to load data: ${response.statusCode}');
        throw Exception('Failed to load data');
      }
    } catch (e) {
      print('Error fetching data: $e');
    }
  }

  Color _getStatusColor(String status) {
    switch (status) {
      case 'red':
        return Colors.red;
      case 'green':
        return Colors.green;
      case 'yellow':
        return Colors.yellow;
      default:
        return Colors.grey;
    }
  }

  @override
  void dispose() {
    _reconnectTimer1.cancel();
    _reconnectTimer2.cancel();
    channel1.sink.close(status.goingAway);
    channel2.sink.close(status.goingAway);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    double screenWidth = MediaQuery.of(context).size.width;
    double containerWidth = screenWidth * 0.3;
    return Scaffold(
      body: SafeArea(
       child: Align(
          alignment: Alignment.topLeft,
          child: Padding(
            padding: const EdgeInsets.only(top: 100, bottom: 50, left: 50, right: 50),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row( // Aggiunto Row per allineare i container uno accanto all'altro
                  children: [
                    Container(
                      width: containerWidth,
                      decoration: BoxDecoration(
                        color: Colors.grey.shade200,
                        border: Border.all(color: Colors.black),
                        borderRadius: BorderRadius.circular(10),
                      ),
                      padding: const EdgeInsets.all(16.0),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Row(
                            children: [
                              Expanded(
                                child: Center(
                                  child: Text(
                                    _title1,
                                    style: const TextStyle(
                                      fontSize: 20,
                                      fontWeight: FontWeight.bold,
                                    ),
                                  ),
                                ),
                              ),
                              Container(
                                width: 20,
                                height: 20,
                                decoration: BoxDecoration(
                                  shape: BoxShape.circle,
                                  color: _getStatusColor(_status1),
                                ),
                              ),
                            ],
                          ),
                          const SizedBox(height: 10),
                          for (var item in _items1)
                            Container(
                              margin: const EdgeInsets.only(top: 16.0),
                              padding: const EdgeInsets.all(16.0),
                              decoration: BoxDecoration(
                                color: Colors.white,
                                border: Border.all(color: Colors.black),
                                borderRadius: BorderRadius.circular(10),
                              ),
                              width: double.infinity,
                              child: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                              Text("ID: ${item['id']}"),
                              Text("Name: ${item['name']}"),
                              Text("Description: ${item['description']}"),
                            ],
                          ),
                        ),
                    ],
                  ),
                ),
                const SizedBox(width: 50), // Spazio tra i container
                Container(
                  width: containerWidth,
                  decoration: BoxDecoration(
                    color: Colors.grey.shade200,
                    border: Border.all(color: Colors.black),
                    borderRadius: BorderRadius.circular(10),
                  ),
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Row(
                        children: [
                          Expanded(
                            child: Center(
                              child: Text(
                                _title2,
                                style: const TextStyle(
                                  fontSize: 20,
                                  fontWeight: FontWeight.bold,
                                ),
                              ),
                            ),
                          ),
                          Container(
                            width: 20,
                            height: 20,
                            decoration: BoxDecoration(
                              shape: BoxShape.circle,
                              color: _getStatusColor(_status2),
                            ),
                          ),
                        ],
                      ),
                      const SizedBox(height: 10),
                      for (var item in _items2)
                        Container(
                          margin: const EdgeInsets.only(top: 16.0),
                          padding: const EdgeInsets.all(16.0),
                          decoration: BoxDecoration(
                            color: Colors.white,
                            border: Border.all(color: Colors.black),
                            borderRadius: BorderRadius.circular(10),
                          ),
                          width: double.infinity,
                          child: Column(
                            crossAxisAlignment: CrossAxisAlignment.start,
                            children: [
                              Text("ID: ${item['id']}"),
                              Text("Name: ${item['name']}"),
                              Text("Description: ${item['description']}"),
                            ],
                          ),
                        ),
                    ],
                  ),
                ),
              ],
            ),
          ],
        ),

Also the websocket code:

const WebSocket = require('ws');
const chokidar = require('chokidar');
const fs = require('fs');

const jsonFilePath1 = 'C:/xampp/htdocs/Flutter/data.json';
const jsonFilePath2 = 'C:/xampp/htdocs/Flutter/data2.json';

const wss1 = new WebSocket.Server({ port: 8080 });
const wss2 = new WebSocket.Server({ port: 8081 });

// WebSocket Server per il primo file JSON
wss1.on('connection', function connection(ws) {
  console.log('WebSocket Server 1 connected');

  // Inizialmente invia il contenuto del primo file JSON
  fs.readFile(jsonFilePath1, 'utf8', (err, data) => {
    if (err) {
      console.error('Error reading file:', err);
      return;
    }
    ws.send(data);
  });

  // Watcher per il primo file JSON
  const watcher1 = chokidar.watch(jsonFilePath1);
  watcher1.on('change', path => {
    console.log(`File ${path} changed`);
    fs.readFile(jsonFilePath1, 'utf8', (err, data) => {
      if (err) {
        console.error('Error reading file:', err);
        return;
      }
      ws.send(data);
    });
  });

  // Chiudi la connessione WebSocket quando il client si disconnette
  ws.on('close', () => {
    console.log('WebSocket Server 1 disconnected');
    watcher1.close();
  });
});

// WebSocket Server per il secondo file JSON
wss2.on('connection', function connection(ws) {
  console.log('WebSocket Server 2 connected');

  // Inizialmente invia il contenuto del secondo file JSON
  fs.readFile(jsonFilePath2, 'utf8', (err, data) => {
    if (err) {
      console.error('Error reading file:', err);
      return;
    }
    ws.send(data);
  });

  // Watcher per il secondo file JSON
  const watcher2 = chokidar.watch(jsonFilePath2);
  watcher2.on('change', path => {
    console.log(`File ${path} changed`);
    fs.readFile(jsonFilePath2, 'utf8', (err, data) => {
      if (err) {
        console.error('Error reading file:', err);
        return;
      }
      ws.send(data);
    });
  });

  // Chiudi la connessione WebSocket quando il client si disconnette
  ws.on('close', () => {
    console.log('WebSocket Server 2 disconnected');
    watcher2.close();
  });
});

console.log('WebSocket servers listening on ports 8080 and 8081');

2

Answers


  1. Chosen as BEST ANSWER

    Okay there is some news. Basically everything is working now but I'm handling a single JSON with a dedicated websocket server. I would like to have only one server to handle all my JSONs (it will be about 80). How can I do this? I add the code to the question


  2. Can you add your code to make the problem you encountered more clear.

    • You need to check whether an event has been received from the websocket or not
    • How do you manage state? You can use StreamBuilder to display the json file content whenever it is sent.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search