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.
Related
Hi guys I'm new to Flutter
I'm trying to fetch the data from http.post method using FutureBuilder and snapshot.data but it keeps returning CircularProgressIndicator which is means snapshot has no data.
Future postDataTransaksi() async {
try {
http.Response response = await http.post(
Uri.parse('https://ignis.rumahzakat.org/donol/listTransaksi'),
body: {
'---': '---',
'---': '---',
},
headers: {
'access-token': '---'
});
if (response.statusCode == 200) {
var data = jsonDecode(response.body);
} else {}
} catch (e) {
print(e.toString());
}
}
This is my post method code.
FutureBuilder(
future: postDataTransaksi(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return Container(child: Text(snapshot.data[0]['id']));
} else {
return const Center(child: CircularProgressIndicator());
}
})
And this is how I try to fetch the data
Change your builder to this:
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return Container(child: Text(snapshot.data[0]['id']));
} else {
return const Center(child: CircularProgressIndicator());
}
and also change your future to this"
Future<List> postDataTransaksi() async {
try {
http.Response response = await http.post(
Uri.parse('https://ignis.rumahzakat.org/donol/listTransaksi'),
body: {
'---': '---',
'---': '---',
},
headers: {
'access-token': '---'
});
if (response.statusCode == 200) {
var data = jsonDecode(response.body);
return data;// <--- add this
}
return [];// <--- add this
} catch (e) {
print(e.toString());
return [];// <--- add this
}
}
I am developing an application in Flutter. I am facing an issue with my Future. Data from future in future builder giving error Model has no getter length also it is showing in print as Instance of TopicsModel inspite of data.
Please help.
Below is my code:
topics_model.dart
class TopicsModel {
List<Topics> topics = [];
TopicsModel();
TopicsModel.fromJson(Map<String, dynamic> jsonMap) {
try {
topics = jsonMap['topics'] != null
? parseTopicsAttributes(jsonMap['topics'])
: [];
print(jsonMap['topics']);
} catch (e) {
topics = [];
}
}
static List<Topics> parseTopicsAttributes(attributesJson) {
List list = attributesJson;
print("in list making");
List<Topics> attrList = list.map((data) => Topics.fromJson(data)).toList();
return attrList;
}
}
class Topics {
int id;
String name;
Topics.fromJson(Map<String, dynamic> json) {
print("hash problem");
print(json);
id = json["sound_id"];
name = json["title"];
}
}
Future
Future<TopicsModel> getFavTopics() async {
print("get_only_fav_topics");
print(userRepo.currentUser.value.userId.toString());
print(userRepo.currentUser.value.token);
Uri uri = Helper.getUri('get_only_fav_topics');
uri = uri.replace(queryParameters: {
'user_id': userRepo.currentUser.value.userId == null
? "0"
: userRepo.currentUser.value.userId.toString(),
"app_token": userRepo.currentUser.value.token
});
try {
Map<String, String> headers = {
'Content-Type': 'application/json; charset=UTF-8',
'USER': '${GlobalConfiguration().get('api_user')}',
'KEY': '${GlobalConfiguration().get('api_key')}',
};
var response = await http.get(uri, headers: headers);
if (response.statusCode == 200) {
var jsonData = json.decode(response.body);
if (jsonData['status'] == 'success') {
print("topicssssssssssssssss");
print(jsonData);
return (json).decode(response.body)['data'];
}
}
} catch (e) {
print(e.toString());
}
}
FutureBuilder in view
child: FutureBuilder<TopicsModel>(
builder: (context, projectSnap) {
print("Projeccct");
print(projectSnap);
if (projectSnap.connectionState ==
ConnectionState.none &&
projectSnap.hasData == null) {
//print('project snapshot data is: ${projectSnap.data}');
return Container();
}
if (projectSnap.connectionState ==
ConnectionState.done &&
projectSnap.hasData) {
print("ind one");
print(projectSnap.data.toString());
return ListView.builder(
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: projectSnap.data.length,
itemBuilder: (context, index) {
return new ChoiceChip(
pressElevation: 0.0,
selectedColor: settingRepo
.setting
.value
.buttonColor,
selected: false,
backgroundColor: Colors.white,
label: Text(projectSnap
.data[index].name));
},
);
} else {
return Container();
}
},
future: getFavTopics(),
)
Json response:
{
"status":"success",
"data":{
"topics":[
{
"fav_id":1,
"sound_id":3321,
"user_id":0,
"created_at":"2021-05-07 10":"01":25,
"title":"title 1",
"sound_name":1620381685.mp3,
"cat_id":4,
"parent_id":0,
"duration":30,
"album":"Album 1",
"artist":,
"tags":null,
"used_times":0,
"deleted":0,
"active":1,
"image":fLNu9mZDAAHNYJcdNK6YRJPvPVxmpzPidHZRhhW5.jpg
},
{
"fav_id":41,
"sound_id":3319,
"user_id":0,
"created_at":"2021-05-07 09":"58":52,
"title":"Title 2",
"sound_name":1620381532.mp3,
"cat_id":2,
"parent_id":0,
"duration":15,
"album":"Album 1",
"artist":,
"tags":null,
"used_times":0,
"deleted":0,
"active":1,
"image":54PKLMXikjx0KDCHQSL8uep42oXxzF4qtvI7VpHE.jpg
}
]
}
}
You need generic
Change this
child: FutureBuilder(
to
child: FutureBuilder<List<Topics>>(
You call your future with Asynchronously
FutureBuilder<List<Topics>>(
future: getFavTopics(),
builder: (context, AsyncSnapshot<List<Topics>> projectSnap) {
For more you can read this article When should I use a FutureBuilder?
You didn't connect the Model class with your FutureBuilder. The data will come from model so call the model class like :
FutureBuilder<Topics>()
I'm trying to display data from the API to the screen, but I can't do it because of the 401 error, take a look at my code and tell me what exactly is wrong, I think that I wrote the API incorrectly. At the moment I am trying to find the information myself, but I think the problem is in the API, and if so, what exactly is the problem?
Code :
API:
class ApiService {
Dio dio = new Dio();
var token ="token";
var refresh_token ="token";
Future getUserCards() async {
try {
Response resp;
var get_cards = "https://example/api/cards";
resp = await dio.get(get_cards);
dio.options.headers["Authorization"] = "Bearer ${token}";
dio.options.headers['Content-Type'] = "application/json";
var json = (resp.data);
var value = json["id"]["row"]["seq_num"]["text"];
return value;
} catch (e) {
print(e);
}
Future loginUser(String username, String password) async {
var storage = new FlutterSecureStorage();
await storage.write(key: 'JWT', value: token);
var login = "https://example/users/login/";
final data = {"username": username, "password": password};
Response response;
response = await dio.post(login, data: data);
dio.options.headers["Authorization"] = "Bearer ${token}";
dio.options.headers['Content-Type'] = "application/json";
if (response.statusCode == 200) {
Get.to(CardScreen());
return response.data;
} else if (response.statusCode == 401) {
var refreshToken = await dio.post(
"https://example.api/cards/refresh/");
response = await dio.post(refresh_token, data: data);
dio.options.headers["Authorization"] =
"Bearer ${token},'Content-Type': 'application/json','refresh_token': '$refresh_token'";
storage = response.data["token"];
refresh_token = response.data["refresh_token"];
return loginUser("username", "password");
} else
return null;
}
}
UI :
children: [
Expanded(
child: FutureBuilder<dynamic>(
future: ApiService().getUserCards(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Card(
child: Text(snapshot.data[index]),
);
});
}
},
)
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?
from this link on my web server as
http://instamaker.ir/api/v1/getPersons
i'm trying to get result and printing avatar from that result, unfortunately my implementation with rxDart and Bloc don't get result from this response and i don't get any error
server response this simplified result:
{
"active": 1,
"name": "my name",
"email": " 3 ",
"loginType": " 3 ",
"mobile_number": " 3 ",
...
"api_token": "1yK3PvAsBA6r",
"created_at": "2019-02-12 19:06:34",
"updated_at": "2019-02-12 19:06:34"
}
main.dart file: (click on button to get result from server)
StreamBuilder(
stream: bloc.login,
builder: (context,
AsyncSnapshot<UserInfo>
snapshot) {
if (snapshot.hasData) {
parseResponse(snapshot);
}
},
);
void parseResponse(AsyncSnapshot<UserInfo> snapshot) {
debugPrint(snapshot.data.avatar);
}
LoginBlock class:
class LoginBlock{
final _repository = Repository();
final _login_fetcher = PublishSubject<UserInfo>();
Observable<UserInfo> get login=>_login_fetcher.stream;
fetchLogin() async{
UserInfo userInfo = await _repository.userInfo();
_login_fetcher.sink.add(userInfo);
}
dispose(){
_login_fetcher.close();
}
}
final bloc = LoginBlock();
Repository class:
class Repository {
final userInformation = InstagramApiProviders();
Future<UserInfo> userInfo() => userInformation.checkUserLogin();
}
my model:
class UserInfo {
int _active;
String _name;
...
UserInfo.fromJsonMap(Map<String, dynamic> map)
: _active = map["active"],
_name = map["name"],
...
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['active'] = _active;
data['name'] = _name;
...
return data;
}
//GETTERS
}
BaseUrl class:
class BaseUrl {
static const url = 'http://instamaker.ir';
}
and then InstagramApiProviders class:
class InstagramApiProviders {
Client client = Client();
Future<UserInfo> checkUserLogin() async {
final response = await client.get(BaseUrl.url+'/api/v1/getPersons');
print("entered "+BaseUrl.url+'/api/v1/getPersons');
if (response.statusCode == 200) {
return UserInfo.fromJsonMap(json.decode(response.body));
} else
throw Exception('Failed to load');
}
}
Well the answer here is part of the test that I make to get this done. I can put my all test here but I think that the problem cause was because as StreamBuilder is a widget his builder method callback is only called when the widget is in flutter widget tree. As in your sample you're just creating a StreamBuilder the builder method will never be called bacause this widget isn't in widget tree.
As advice first test your code changing only UI layer... do somenthing like:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(icon: Icon(Icons.assessment), onPressed: () => loginBlock.fetchLogin()),
],
),
body: StreamBuilder<UserInfo>(
stream: loginBlock.login,
builder: (context, snapshot){
if (snapshot.hasData){
parseResponse(snapshot);
return Text('user: ${snapshot.data.name} ');
}
if (snapshot.hasError)
return Text('${snapshot.error}');
else return Text('There is no data');
},
),
);
Here we're putting the StreamBuilder in widget tree so the builder callback is called and maybe you will see the results. If it fails, please comment that I update my answer with my full test code with this working.
Updating the answer with sources that I made tests.
Basic model
class UserInfo {
int _active;
String name;
UserInfo.fromJsonMap(Map<String, dynamic> map) {
_active = map["active"];
name = map["name"];
}
Map<String, dynamic> toJson() => {
'active' : _active,
'name' : name,
};
}
The provider class
class InstagramApiProviders {
Future<UserInfo> checkUserLogin() async {
UserInfo info;
try {
http.Response resp = await http.get("http://instamaker.ir/api/v1/getPersons");
if (resp.statusCode == 200){
print('get response');
print( resp.body );
info = UserInfo.fromJsonMap( Map.from( json.decode(resp.body ) ));
}
}
catch (ex) {
throw ex;
}
print('returning $info');
return info;
}
}
Repository
class Repository {
final userInformation = InstagramApiProviders();
Future<UserInfo> userInfo() => userInformation.checkUserLogin().then((user) => user);
}
BLoC class
class LoginBlock{
final _repository = Repository();
final _login_fetcher = PublishSubject<UserInfo>();
Observable<UserInfo> get login=>_login_fetcher.stream;
fetchLogin() async {
UserInfo info = await _repository.userInfo();
_login_fetcher.sink.add(info);
}
dispose(){
_login_fetcher.close();
}
}
Widget UI
This starts showing There is no data message but when you hit appBar button wait a little and then the data is fetched and updates the UI.
class WidgetToShowData extends StatelessWidget {
final LoginBlock bloc = LoginBlock();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: <Widget>[
IconButton(icon: Icon(Icons.assessment), onPressed: () => loginBlock.fetchLogin()),
],
),
body: StreamBuilder<UserInfo>(
stream: loginBlock.login,
builder: (context, snapshot){
if (snapshot.hasData){
parseResponse(snapshot);
return Text('user: ${snapshot.data.name} ');
}
if (snapshot.hasError)
return Text('${snapshot.error}');
else return Text('There is no data');
},
),
);
}
void parseResponse(AsyncSnapshot<UserInfo> snapshot) {
debugPrint(snapshot.data.name);
}
}