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
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
Can you add your code to make the problem you encountered more clear.