Cannot Get StreamBuilder Data - flutter

I am trying to get the updated data from a stream but, even though I get data coming down in my future function, the snapshot.data give me this error:
type '_ControllerStream<dynamic>' is not a subtype of type 'Iterable<dynamic>'
Here is my function and stream:
Future getChat(orderId) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var _token = prefs.getString('token');
print('The Latest Order Token is $_token');
final Map<String, dynamic> body = {
"id": "$orderId",
};
final List _messageData = [];
var url = Uri.parse('$_server/api/driver/get/convo/?access_token=$_token');
await http.post(url, body: body, headers: {
"Content-Type": 'application/x-www-form-urlencoded'
}).then((http.Response response) {
print(response.body);
switch (response.statusCode) {
case 200:
final Map<String, dynamic> responseData = json.decode(response.body);
print("The ${response.body}");
var x = responseData['message_data'].split(",");
print(x);
for (int i = 0; i < x.length; i++) {
_messageData.add(x[i]);
print(x[i]);
}
print(x);
break;
default:
final Map<String, dynamic> responseData = json.decode(response.body);
print(responseData);
return _messageData;
}
return _messageData;
});
}
Stream getChatData(Duration refreshTime, id) async* {
while (true) {
await Future.delayed(refreshTime);
yield getChat(id).asStream();
}
}
I get this in the data response:
"message_data": ""11-12-21:09:01:14AM - Billy Fakename: fire
test,11-12-21:09:01:30AM - Test TEster: ewserasece,""
My stream builder is:
Stream _chatStream;
#override
void initState() {
_chatStream = getChatData(Duration(seconds: 3), orderid);
super.initState();
}
StreamBuilder(
stream: _chatStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
final messages = snapshot.data;
List<MessageBubble> messageWidgets = [];
for (var message in messages) {
final msgText = message;
final msgSender = message;
// final msgSenderEmail = message.data['senderemail'];
final currentUser = "loggedInUser.displayName";
// print('MSG'+msgSender + ' CURR'+currentUser);
final msgBubble = MessageBubble(
msgText: msgText,
msgSender: msgSender,
user: currentUser == msgSender);
messageWidgets.add(msgBubble);
}
return Expanded(
child: ListView(
reverse: true,
padding:
EdgeInsets.symmetric(vertical: 15, horizontal: 10),
children: messageWidgets,
),
);
} else {
return Center();
}
},
),
But, I get this error: type '_ControllerStream' is not a subtype of type 'Iterable' or the snapshot will be null.
How do I get the information that shows up in the future function, show up in the stream?

Could you show us where you define _chatStream ?
Your StreamBuilder uses _chatStream but you only showed us where you define the method
Future getChat(orderId)
and the method
Stream getChatData(Duration refreshTime, id)
where you create a stream that you do not use in the code you've shared.
Did you want to use getChatData in your StreamBuilder?
Did I miss something?

Related

How to add json to an autocomplete widget in flutter

Im trying to pass the data from an API to a list so that I can view it in the Autocomplete widget but I keep getting errors. I tried below code.
This is the code I have tried which passes data to the autocomplete as instance of 'Bus'
GetBuilder<BusesListController>(
init: BusesListController(),
builder: (_) {
_.viewPartners();
return DottedBorder(
child: Padding(
padding:
const EdgeInsets.only(left: 8.0),
child: Autocomplete<Bus>(
optionsBuilder: (TextEditingValue
textEditingValue) {
List<Bus> partnercos = [];
partnercos = _.partners.value as List<Bus>;
// (_.partners).map((value) => Bus.fromJson(value as Map<String, dynamic>)).toList();
print(partnercos);
return partnercos
.where((bus) => bus.company!
.toLowerCase()
.contains(textEditingValue
.text
.toLowerCase()))
.toList();
},
)),
);
}),
I also tried passing _.partners directly but it doesn't work either
Other fix I tried is passing _.partners instead of _.partners. Value above which invokes errors in arena.dart in void _tryToResolveArena which shows that state. Members.length == 1 hence scheduleMicrotask(() => _resolveByDefault(pointer, state));
Contoller code
class BusesListController extends GetxController {
var partners = [].obs;
var isLoaded = false.obs;
final loginController = Get.put(LoginController());
Future<void> viewPartners() async {
final token = loginController.rxToken.value;
var headers = {
'Authorization': 'Bearer $token'
};
try {
var url =
Uri.parse(ApiEndPoints.baseUrl + ApiEndPoints.endpoints.listBusAdmin);
http.Response response = await http.get(url, headers: headers);
if (response.statusCode == 200) {
final json = jsonDecode(response.body);
partners. Value =
(json as List).map((json) => Bus.fromJson(json)).toList();
isLoaded.value = true;
} else {
throw jsonDecode(response.body)["Message"] ?? "Unknown Error Occured";
}
} catch (error) {
// Get.snackbar('Error', error.toString());
}
}
#override
void onInit() {
super.onInit();
viewPartners();
}
}
I am able to print the response so I know the api works but I'm having problems with passing partners list into the autocomplete

call parameter function to get data in flutter

I'm learning and trying to add parameters when calling parameters in functions when getting data from the API, but I'm a bit confused about how I call them in widgets.
static Future<Map<String, DataKuliahModel>> getDataKuliah(String smt) async {
String url = Constant.baseURL;
String token = await UtilSharedPreferences.getToken();
await Future.delayed(const Duration(milliseconds: 1000));
// String responseJson = await rootBundle.loadString('assets/1.json');
Map<String, DataKuliahModel> finalResult = {};
final response = await http.get(
Uri.parse(
'$url/auth/mhs_siakad/perwalian/get_paket',
),
headers: {
'Authorization': 'Bearer $token',
},
);
final result = jsonDecode(response.body)['data'] as Map<String, dynamic>;
result.forEach((key, value) {
DataKuliahModel dataKuliah = DataKuliahModel.fromMap(value);
finalResult.addAll({
key: dataKuliah,
});
});
return finalResult;
}
and I want to call him here
When you declare a function with positional parameters you need to provide those parameters when you call that function.
import 'package:flutter/material.dart';
class Services {
static Future<String> greeting(String name) async {
/// this function doesn't need to be Future
/// but when you call API to get some data it should be a Future
return 'Hello $name';
}
}
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
#override
Widget build(BuildContext context) {
return FutureBuilder(
/// pass positional parameter to [greeting] here
future: Services.greeting('Dash'),
builder: (context, AsyncSnapshot<String> snapshot) {
return Center(
child: Text(snapshot.data ?? 'default'),
);
},
);
}
}
Result: Hello Dash
In your case smt seems to be an int not a String
and you have to pass it as query parameter to http request as follows
static Future<Map<String, DataKuliahModel>> getDataKuliah(int smt) async {
String url = Constant.baseURL;
String token = await UtilSharedPreferences.getToken();
await Future.delayed(const Duration(milliseconds: 1000));
// String responseJson = await rootBundle.loadString('assets/1.json');
Map<String, DataKuliahModel> finalResult = {};
final response = await http.get(
// Uri.parse(
// '$url/auth/mhs_siakad/perwalian/get_paket',
// ),
Uri.http(url, '/auth/mhs_siakad/perwalian/get_paket',
{'smt':smt}),
headers: {
'Authorization': 'Bearer $token',
},
);
final result = jsonDecode(response.body)['data'] as Map<String, dynamic>;
result.forEach((key, value) {
DataKuliahModel dataKuliah = DataKuliahModel.fromMap(value);
finalResult.addAll({
key: dataKuliah,
});
});
return finalResult;
}
Have you looked at the Uri replace method?
You can do the following:
Uri.parse('$url/auth/mhs_siakad/perwalian/get_paket').replace(queryParameters:{ "smt":"$smt"});
Update on FutureBuilder:
// Put this outside your build function
Future<Map<String, DataKuliahModel>> DK ;
// Put this in your initState if you want the future to run on page load or use it for events like onTap
DK = Service.getDataKuliah(<PARAM>);
// This is in your build method
FutureBuilder(
future:DK,
builder: (context, snapshot) {
// add wigets to display results here
}
)

LateInitializationError: Field 'check' has not been initialized

I'm trying to Use data that I fetched from database and i got an error : "LateInitializationError: Field 'check' has not been initialized. "
, i tried to remove the late word and adding " ? " and it gives another error "Expected a value of type 'num', but got one of type 'Null'
"
class _letterssState extends State<letterss> {
late var check;
Future getData() async{
var url = 'http://ip/getSpell.php';
http.Response response = await http.get(Uri.parse(url));
var data = jsonDecode(response.body);
check=data;
print(data.toString());
}
bool searchRes (String s){
int x=0;
for ( var i=0 ; i<check.length;i++ )
{
if (check[i]['letter']==s){
x=i;
}
}
if (check[x]['result']=='true')
{
return true;
}
else
{
return true;
}
}
initState()
{
getData();
}
It will take some frame to get data from getData future method and assigning on check.
It would better to use FutureBuilder for future methods. Follow this doc example
Future<List<yourDataType>?> getData() async {
var url = 'http://ip/getSpell.php';
http.Response response = await http.get(Uri.parse(url));
var data = jsonDecode(response.body);
return data;
}
late final future = getData();
#override
Widget build(BuildContext context) {
return FutureBuilder<List<YourDataType>?>(
future: future,
builder: (context, snapshot) {
if (snapshot.hasData) {
//todo:
}
return CircularProgressIndicator();
},
);
}

Future builder returns null although my list is not empty

I have this future builder which loads a list of movies in my provider class. Whenever I reload my screen, the movies do not get returned. Below is the future builder
FutureBuilder(
future: movieData.getTrendingMovies(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (snapshot.hasData) {
return Swiper(
itemBuilder: (BuildContext context, i) {
return ChangeNotifierProvider(
create: (context) => Movie(),
child: MovieContainer(
imageUrl: movieData.movies[i].imageUrl,
id: movieData.movies[i].id,
rate: movieData.movies[i].rate,
title: movieData.movies[i].title,
),
);
},
itemCount: movieData.movies.length,
viewportFraction: 0.25,
scale: 0.4,
);
} else {
return Text(snapshot.error.toString()); // it returns null on the screen
}
}),
Also in my homescreen where I display my movies, after the build method, I create a listener(moviesData) to listen to all changes in the movies provider.
final movieData = Provider.of<Movies>(context, listen: false);
Below is also the methos which fetches the movies from a restfulAPI using http get request
Future<void> getTrendingMovies() async {
List<String> movieTitles = [];
List<String> movieImageUrls = [];
List<String> movieDescriptions = [];
List<String> movieReleaseDates = [];
List<String> movieRates = [];
List<String> movieIds = [];
const _apiKey = '******************************';
const url =
'https://api.themoviedb.org/3/trending/all/week?api_key=$_apiKey';
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode >= 400) {
print(response.statusCode);
return;
}
final extractedData = json.decode(response.body);
List moviesList = extractedData['results'] as List;
List<Movie> loadedMovies = [];
for (int i = 0; i < moviesList.length; i++) {
String movieTitle = moviesList[i]['original_title'] ?? '';
String? movieImage =
'https://image.tmdb.org/t/p/w400${moviesList[i]['poster_path']}'; //results[0].poster_path
String movieDescription =
moviesList[i]['overview'] ?? ''; //results[0].overview
String movieReleaseDate = moviesList[i]['release_date'] ?? '';
String? movieRate = moviesList[i]['vote_average'].toString();
String? movieId = moviesList[i]['id'].toString();
movieTitles.add(movieTitle);
movieImageUrls.add(movieImage);
movieDescriptions.add(movieDescription);
movieReleaseDates.add(movieReleaseDate);
movieRates.add(movieRate);
movieIds.add(movieId);
loadedMovies.add(
Movie(
id: movieIds[i],
title: movieTitles[i],
imageUrl: movieImageUrls[i],
description: movieDescriptions[i],
rate: double.parse(movieRates[i]),
releaseDate: movieReleaseDates[i],
),
);
}
_movies = loadedMovies;
notifyListeners();
//print(_movies.last.title); //This prints the name of the last movie perfectly....This gets called unlimited times whenever I set the listen of the **moviesData** to true
} catch (error) {
print(error);
}
}
There's a couple of things to unpack here.
Instead of a ChangeNotifierProvider, I believe you should use a Consumer widget that listens to your Movies provided service when you call the notifyListeners call, so make it Consumer<Movie>.
You can still call it using the Provider.of above for the sake of making the async call via the FutureBuilder, but I believe because you're not returning anything out of the getTrendingMovies and is just a Future<void> and you're querying the snapshot.hasData, well there is no data coming through the snapshot. Maybe instead you should call snapshot.connectionState == ConnectionState.done as opposed to querying for whether it has data.
Make sure that the response.body is truly returning a JSON value, but I believe your issue is in one of the points above.

How to do stream builder to get data from bloc in flutter

I am new in BLOC and I am trying to read respond from api.. but whenever I call stream builder... my widget always stops in wait... here is my code
here is api provider file
class Provider {
final _url = '...';
Future<List<LoginRespon>> login(a, b) async {
List<LoginRespon> datalogin = [];
try {
bool trustSelfSigned = true;
HttpClient httpClient = new HttpClient()
..badCertificateCallback =
((X509Certificate cert, String host, int port) =>
trustSelfSigned);
IOClient http = new IOClient(httpClient);
final response = await http.post(_url,
headers: {
HttpHeaders.contentTypeHeader: 'application/json',
},
body: json.encode({
"aa": a,
"bb": b,
}));
Map<String, dynamic> responseJson = json.decode(response.body);
if (responseJson["status"] == "200") {
datalogin.add(LoginRespon(
status: responseJson['status'],
data: Data(
name: responseJson['data']['name'],
position: responseJson['data']['pos'])));
return datalogin;
} else {
print("ppp");
}
} on Exception {
rethrow;
}
return datalogin;
}
}
and here is for stream builder
isClick
? StreamBuilder(
stream: bloc.login(),
builder: (context, snapshot) {
if (snapshot.hasData) {
print(snapshot.data);
return Text("success");
} else if (snapshot.hasError) {
return Text(
snapshot.error.toString());
}
return Text("wait..");
},
)
: FlatButton(
child: Text("Login"),
onPressed: () {
setState(() {
isClick = true;
});
},
),
is there a way so that I can call print(snapshot.data) inside if (snapshot.hasData)
You need to pass argument which required in method otherwise it will not successfully responce (200) and it will throw error.