get data from json based on index number in flutter - flutter

I have a json file from where I am collecting only email addresses, and want to print index number base output like 3rd record's email address to Text widget..
class _HomeScreenState extends State<HomeScreen> {
List<String> list = [];
Future<List<String>> getcomments() async {
Uri url =
Uri.parse('https://jsonplaceholder.typicode.com/posts/1/comments');
var response = await http.get(url);
if (response.statusCode == 200) {
var jsondata = json.decode(response.body);
list.clear();
for (var jdata in jsondata) {
list.add(jdata['email']);
}
}
return list;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('JSON'),
),
body: Center(
child: FutureBuilder(
future: getcomments(),
builder: (context, snapshot) {
return Text(snapshot.data[2].toString());
//here i want only 3rd data(index 2)
},
),
),
);
}
}

Can you try this?
FutureBuilder(
future: getcomments(),
builder: (context, snapshot) {
List<String> data = snapshot.data as List<String>;
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
else {
return Text(data[2]);
}
},
),

Related

Retrieving Firestore data in ListView but Failing

Currently struggling to make a ListView data retrieved from Firestore.
I am trying to get "kids name" saved under in the firestore as linked photo.
Firestore
No error message is shown up but the data is not retrieved correctly and shown blank screen...hope anyone can correct my code!
and here is my code:
class kidsNamePick extends StatefulWidget {
#override
_kidsNamePickState createState() => _kidsNamePickState();
}
class _kidsNamePickState extends State<kidsNamePick> {
List<Memo> kidsnamelist = [];
Future<void>fetchMemo()async{
final kidsnames = await FirebaseFirestore.instance.collection('useraccount').doc(FirebaseAuth.instance.currentUser!.uid)
.collection('kidsname').get();
final docs = kidsnames.docs;for (var doc in docs){
Memo fetchMemo = Memo(kidsname: doc.data()['kids name'],
);
kidsnamelist.add(fetchMemo);}
setState(() {
});}
#override
void initState(){
super.initState();
fetchMemo();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add/Select Kids'),
),
body: ListView.builder(
itemCount: kidsnamelist.length,
itemBuilder: (context, index){
return ListTile(
title: Text(kidsnamelist[index].kidsname),
);
},
)
);
}
}
The best way to call future method is using FutureBuilder, first change your fetchMemo to this:
Future<List<Memo>> fetchMemo() async {
try {
final kidsnames = await FirebaseFirestore.instance
.collection('useraccount')
.doc(FirebaseAuth.instance.currentUser!.uid)
.collection('kidsname')
.get();
final docs = kidsnames.docs;
return docs
.map((doc) => Memo(
kidsname: doc.data()['kids name'],
))
.toList();
} catch (e) {
return [];
}
}
then change your build method to this:
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Add/Select Kids'),
),
body: FutureBuilder<List<Memo>>(
future: fetchMemo(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<Memo> data = snapshot.data ?? [];
return ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(data[index].kidsname),
);
},
);
}
}
},
),
);
}

Appbar should show number of records using futurebuilder in flutter

I have just created a demo for better understanding future builder
scaffold body showing all users from api and appear should be shown with number of users
appear's title showing 0 when loaded but does not change...what to do to rebuild it
here is my code
class _withmodelState extends State<withmodel> {
List<UserModel> userlist=[];
Future<List<UserModel>> getdata() async {
final resp =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
if (resp.statusCode == 200) {
print('i ma called');
List<dynamic> dlist = json.decode(resp.body);
await Future.delayed(Duration(seconds: 2));
userlist= dlist.map((e) => UserModel.fromJson(e)).toList();
return userlist;
}
return userlist;
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(title: Text("Total users="+userlist.length.toString()),),
body: MyBody(
//MyBody returning FutureBuilder for showing userlist array;
),
));
}
You can use ChangeNotifier like this, first create a class like this:
class WithmodelDecl with ChangeNotifier {
ValueNotifier<int> totalUsers = ValueNotifier<int>(0);
}
WithmodelDecl withmodeldecl = new WithmodelDecl();
then use it like this:
return SafeArea(
child: Scaffold(
appBar: PreferredSize(
child: ValueListenableBuilder<int>(
valueListenable: withmodeldecl.totalUsers,
builder: (context, value, _) {
return AppBar(
title: Text("Total users=" + value.toString()),
);
}),
preferredSize: AppBar().preferredSize),
body: MyBody(
//MyBody returning FutureBuilder for showing userlist array;
),
));
and finally change your getdata to this:
Future<List<UserModel>> getdata() async {
final resp =
await http.get(Uri.parse('https://jsonplaceholder.typicode.com/users'));
if (resp.statusCode == 200) {
print('i ma called');
List<dynamic> dlist = json.decode(resp.body);
await Future.delayed(Duration(seconds: 2));
userlist= dlist.map((e) => UserModel.fromJson(e)).toList();
withmodeldecl.totalUsers.value = userlist.length;
return userlist;
}
return userlist;
}
You also need to rebuild the Text widget, that you are using to show the count, when the count is available, i.e., the Future completes.
You need to wrap that Text widget with FutureBuilder like this:
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: FutureBuilder<List<UserModel>>(
future: getdata(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
final List<UserModel> userlist = snapshot.data!;
return Text("Total users= ${userlist.length}");
// it's better to use String interpolation than "Total users=" + snapshot.data!.length.toString()
} else {
// return loading widget
}
},
),
),
body: MyBody(
//MyBody returning FutureBuilder for showing userlist array;
),
),
);
It is better to have the Future in a variable, and then use it like this, to avoid unwanted and repeated calling of it whenever the build() method is called:
late final Future<List<UserModel>> _userListFuture;
And initialize it in your initState(), like this:
#override
void initState() {
super.initState();
_userListFuture = Future<List<UserModel>>(getdata);
}
And use it with your FutureBuilder like this:
FutureBuilder<List<UserModel>>(
future: _userListFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// return your widget showing data
} else {
// return loading widget
}
},
)

How do I display JSON data to appear as a list view in flutter?

I have been working on solving an exception error(FormatException: Unexpected character (at character 1)) that is present at the json.decode(response.body) section of the code. I have no idea on how I can solve that. Any response from you guys is highly appreciated. Below is the code:
Future<List<Garage>> garagesFuture = getGarages();
static Future<List<Garage>> getGarages() async {
const url =
'https://console.firebase.google.com/project/atta-web-app-a5135/database/atta-web-app-a5135-default-rtdb/data/~2FGarages';
final response = await http.get(Uri.parse(url));
final body = json.decode(response.body);
return body.map<Garage>(Garage.fromJson).toList();
}
#override
Widget build(BuildContext context) {
final ref = dref.ref().child('Garages');
return Scaffold(
body: Center(
child: FutureBuilder<List<Garage>>(
future: garagesFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasData) {
final garages = snapshot.data!;
return buildGarage(garages);
} else {
return const Text('No Garages Available');
}
},
),
),
);
}
buildGarage(List<Garage> garages) {
ListView.builder(
itemCount: garages.length,
itemBuilder: (context, index) {
final garage = garages[index];
return Card(
child: ListTile(
title: Text(garage.garageName),
subtitle: Text(garage.officeNumber),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: ((context) => const MessageCenter())));
},
),
Seems your problem with decoder.
Try using
json.decode(utf8.decode(response.bodyBytes))
to convert the encoding to utf8

rebuilding listview.builder every time I scroll , and 'Stream has already been listened to' error

I am using a stream builder which has another stream builder inside it. Every time I get data from the first stream I use some of this data in the other stream to finally build a list view (POSTS), but I have a problem every time I scroll down I have this error:
if (!_isInitialState) {
throw StateError("Stream has already been listened to.");
}
I tried to listen to the second stream asBroadcastStream(), and I added the case that there is no data and every time I scroll I get the notification I made that there is no data any ideas?
This is my code:
StreamBuilder<QuerySnapshot>(
stream: posts.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
return SizedBox(
height: MediaQuery.of(context).size.height * 0.69,
child: ListView(
scrollDirection: Axis.vertical,
children:
snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return StreamBuilder<DocumentSnapshot>(
stream: users
.doc(data['Uid'])
.get()
.asStream()
.asBroadcastStream(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return const Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {}
if (!(snapshot.hasData)) {
print("no data");
return SizedBox(
width: 0,
);
}
if (snapshot.connectionState ==
ConnectionState.done) {
Map<String, dynamic> daata = snapshot.data!
.data() as Map<String, dynamic>;
String username = daata['Username'];
String userimage = daata['Userimage'];
return mypost(
context,
data['title'],
data['ImageUrl'],
data['context'],
username,
userimage,
data['nlikes'],
data['ncomments'],
data['date']
.toDate()
.toString()
.split(' ')
.first);
}
return const Text("loading");
});
}).toList(),
),
);
}),
if any could help I would be happy with that.
It might interest you to know that when I run the below code (basically your code, but with my streams and mypost() function) I don't get any errors!... It scrolls fine!
import 'package:firebase_core/firebase_core.dart';
import 'package:my_app/firebase_labels.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Screen(),
);
}
}
class MyFirebase {
static FirebaseFirestore storeObject = FirebaseFirestore.instance;
}
class Screen extends StatelessWidget {
Screen({Key? key}) : super(key: key);
// Let me just define some streams here, from the same CollectionReference:
final CollectionReference posts = MyFirebase.storeObject
.collection(kCollectionConversations);
final CollectionReference users = MyFirebase.storeObject
.collection(kCollectionConversations);
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: posts.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
return SizedBox(
height: MediaQuery.of(context).size.height * 0.69,
child: ListView(
scrollDirection: Axis.vertical,
children:
snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return StreamBuilder<DocumentSnapshot>(
stream: users
.doc(data['Uid'])
.get()
.asStream()
.asBroadcastStream(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return const Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {}
if (!(snapshot.hasData)) {
print("no data");
return SizedBox(
width: 0,
);
}
if (snapshot.connectionState ==
ConnectionState.done) {
Map<String, dynamic>? daata = snapshot.data!
.data() as Map<String, dynamic>?;
String username = '';
String userimage = '';
if (daata != null) {
username = daata['Username'];
userimage = daata['Userimage'];
}
return mypost(
data,
// context,
data['title'],
data['ImageUrl'],
data['context'],
username,
userimage,
data['nlikes'],
data['ncomments'],
// data['date']
// .toDate()
// .toString()
// .split(' ')
// .first
);
}
return const Text("loading");
});
}
).toList(),
),
);
}),
);
}
}
Widget mypost(var data1, var data2, var data3, var data4, var data5, var data6, var data7, var data8/*, var data9,*/) {
return Container(
// height: 50,
child: Text('$data1'),
decoration: BoxDecoration(border: Border.all(color: Colors.blue)),
);
}
If you copy-paste this code into yours, do you get errors?
What if you change the streams for yours and the mypost() for yours? Do you get errors then?

snapshot.data is null in Flutter

My snapshot.data is null. When I print the response it is displaying the retrieved data. But still snapshot.data is null.
Future _getUsers() async {
var data = await http.post("http://10.0.2.2/Flutter/abreport.php", body: {
{
"date": mydt,
});
var jsonData = json.decode(data.body); //edited
print(jsonData); // the data is printing here
return jsonData;
}
}
FutureBuilder(
future: _getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
debugPrint(snapshot.data);
if (snapshot.data == null) {
return Container(
child: Center(
child:Text("no data"),
)
);
} else {
//some code
}
)
You should use the format given in the documentation for FutureBuilder. You're not checking for the state of the future, so when the FutureBuilder is first built, it will display "no data". You haven't implemented your else branch, so by the time you have data, your build will probably not refresh anyway. Try this code instead:
FutureBuilder(
future: _getUsers(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) {
return Text('no data');
} else {
return Text('data present');
}
} else if (snapshot.connectionState == ConnectionState.error) {
return Text('Error'); // error
} else {
return CircularProgressIndicator(); // loading
}
}
)
with Flutter 2.2, this one returns an error
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data,);
Error: The argument type 'String?' can't be assigned to the parameter type 'String' because 'String?' is nullable and 'String' isn't.
return Text(snapshot.data,);
but this one dosen't
builder: (BuildContext context, AsyncSnapshot snapshot) {
When similar things happen, take the type "var" not "String" or other non-nullable type.
(If it was not Flutter, the compilers will do?)
Since i cannot see your complete code, i am assuming you are parsing your json data incorrectly after receiving it inside FutureBuilder. Below is an example which is similar to what you are doing. This example retrieves Date json data and displays using FutureBuilder,
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0),
child: FutureBuilder(
future: _getDate(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return Text('Date: ' + snapshot.data['date']
+ '\nMilliseconds Since Epoch: ' + snapshot.data['milliseconds_since_epoch'].toString()
+ '\nTime: ' + snapshot.data['time'],
style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.bold, color: Colors.grey));
} else {
return Center(child: CircularProgressIndicator());
}
},
))
]))));
}
Future _getDate() async {
var data = await http.post("http://date.jsontest.com/");
var jsonData = json.decode(data.body);
print(jsonData);
return jsonData;
}
}
Test screenshot:
Hope this helps.
Because your async function doesnt return anything..
Change it like this:
Future _getUsers() async {
return await http.post("http://10.0.2.2/Flutter/abreport.php", body: {
{
"date": mydt,
});
var jsonData = json.decode(data.body); //edited
print(jsonData); // the data is printing here
return jsonData;
}
}