I have a task, I want to take the data through the web socket, and display them on the screen of the application. But I did not even find a normal example to do so. I will be grateful if someone helps to do it, or someone sets an example. I have the socket web address:
wss: //ws-sandbox.coinapi.io/v1/
I also have a body that I will send to the socket to return the data:
{ "type": "hello", "apikey": "C4F12EA7-D405-4619-99FD-62F4B00A8111", "heartbeat": false, "subscribe_data_type": ["exrate"], "subscribe_filter_asset_id": ["BTC / USD"] }
Here's what comes back in the web socket:
{
"time": "2022-04-28T16: 45: 31.0000000Z",
"asset_id_base": "BTC",
"asset_id_quote": "USD",
"rate": 39693.575,
"type": "exrate"
}
I want to display "rate" data on the screen. I will be grateful for your help)
My code
import 'package:flutter/material.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:web_socket_channel/io.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
WebSocketChannel channel = IOWebSocketChannel.connect("wss://ws-sandbox.coinapi.io/v1/");
@override
MyHomePageState createState() {
return MyHomePageState();
}
}
class MyHomePageState extends State<MyHomePage> {
TextEditingController _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Web Socket"),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Form(
child: TextFormField(
decoration: InputDecoration(labelText: "Send any message to the server"),
controller: _controller,
),
),
StreamBuilder(
stream: widget.channel.stream,
builder: (context, snapshot) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Text(snapshot.hasData ? '${snapshot.data}' : ''),
);
},
)
],
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.send),
onPressed: sendData,
),
);
}
void sendData() {
if (_controller.text.isNotEmpty) {
widget.channel.sink.add(_controller.text);
}
}
@override
void dispose() {
widget.channel.sink.close();
super.dispose();
}
}
The problem is that I return all the JSON(string). And I want to return only the data "rate"
My scrin:
CodePudding user response:
In this case you do not want to display the entire json body. So instead of using snapshot.data
you need to parse it first.
snapshot.data
has a dynamic type so I do not know what kind of data you are working with but if you somehow parse it to a Map<String, dynamic>
you can just do data['rate']
.
When using the regular http
library you can parse a http.Response
like this:
jsonDecode(response.body).cast<Map<String, dynamic>>();
If you tell me the type of snapshot.data
I might be able to give you more information.
Edit: I just tried your code and as suspected you are getting a String that we can parse with jsonDecode
:
jsonDecode(snapshot.data)["rate"]
If we apply this to your problem this should be the solution:
Your Widget should look like this:
Text(getExtractedRate(snapshot))
The getExtractedRate
method should looke like this:
String getExtractedRate(AsyncSnapshot<dynamic> snapshot) {
return snapshot.hasData ? '${jsonDecode(snapshot.data)["rate"]}' : '';
}
Keep in mind that I am using the dynamic
type to avoid typing conflicts. jsonDecode
requires a String while snapshot.data
is Object?
. In your production code you also might want to check if the key rate
is present before accessing it.