I have a json file like this which I stored in the asset:
[
{
"topic":"Title One",
"subTopic":
[
"Overview",
"Install",
"Start"
]
},
{
"topic":"Title Two"
},
{
"topic":"Title Three",
"subTopic":
[
"Overview",
"Emulation",
"2. Install"
]
},
{
"topic":"Title Four",
"subTopic":
[
"Overview",
"Start",
"3. Try"
]
}
]
Which has an array and inside it also has array. I wonder how can I parse the "subTopic" as List and displayed it. My class:
class File {
String topic;
List<String> subTopic;
File({this.topic, this.subTopic});
File.fromJson(Map<String, dynamic> json) {
topic = json['topic'];
subTopic = json['subTopic'];
}
Map<String, dynamic> toJson() => {
'topic': topic,
'subTopic': subTopic,
};
}
What i did in maint.dart:
class MyHomePage extends StatelessWidget {
Future<List<File>> getData() async {
String response = await rootBundle.loadString('file.json');
return await Future.delayed(Duration(seconds: 2), () {
List<dynamic> data = jsonDecode(response);
//Iterate over list and map each object in list
List<File> files=
data.map((data) => File.fromJson(data)).toList();
return files;
});
}
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
appBar: AppBar(
title: Text("File"),
),
body: Container(
child: FutureBuilder<List<File>>(
future: getData(),
builder: (context, data) {
if (data.connectionState != ConnectionState.waiting &&
data.hasData) {
var fileList = data.data;
return Drawer(
child: ListView.builder(
itemCount: fileList.length,
itemBuilder: (context, index) {
var fileData = fileList[index];
return ExpansionTile(
// key: Key("$index"),
// title: Text(fileData.topics?? ""),
children: <Widget>[
Container(
width: double.infinity,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
//I want to display the list of subTopic here
],
),
),
)
],
);
}),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
),
),
);
}
}
I want to displayed the list of subtopic in the listView. How can I do this? Thanks in advance!
It is quite easy you can cast your json['subtype'] as an Iterable to generate a new List<String> object.
Code:
List<String>.from(json['subTopic'] as Iterable)
Full Sample:
class File {
String topic;
List<String> subTopic;
File({this.topic, this.subTopic});
File.fromJson(Map<String, dynamic> json) {
topic = json['topic'];
subTopic = List<String>.from(json['subTopic'] as Iterable);
}
Map<String, dynamic> toJson() => {
'topic': topic,
'subTopic': subTopic,
};
}
It's best if you don't start with an Array map in its json format.
Maybe that's what made it difficult.
enter image description here
https://app.quicktype.io/
It is better to use Provider and ChangeNotifier and you can update the File class as following.
class File {
String topic;
List<String> subTopic;
File({this.topic, this.subTopic});
File.fromJson(Map<String, dynamic> json) {
topic = json['topic'];
subTopic = (json["subTopic"] as List).map((n) => (n.toString())).toList());
}
Map<String, dynamic> toJson() => {
'topic': topic,
'subTopic': subTopic,
};
}
if you need to show topic and subtopic, you can use flutter_section_table_view
Related
Okay so my profile posts are working as intended right here, I am showing the users posts that only they have made to their very own profile
Now here is the code doing that
static Future<List<Post>> getUserPosts(String currentUserId) async {
QuerySnapshot userPostsSnap = await postsRef
.doc(currentUserId)
.collection('userPosts')
.orderBy('timestamp', descending: true)
.get();
List<Post> userPosts =
userPostsSnap.docs.map((doc) => Post.fromDoc(doc)).toList();
return userPosts;
}
and also to show them to the profile page as you see in the image:
showProfilePosts(UserModel author) {
return Expanded(
child: ListView.builder(
shrinkWrap: true,
physics: AlwaysScrollableScrollPhysics(),
itemCount: _allPosts.length,
itemBuilder: (context, index) {
return PostContainer(
post: _allPosts[index],
author: author,
currentUserId: widget.currentUserId,
);
}),
);
}
getAllPosts() async {
List<Post> userPosts =
await DatabaseMethods.getUserPosts(widget.visitedUserId);
if (mounted) {
setState(() {
_allPosts = userPosts;
});
}
}
#override
void initState() {
super.initState();
getAllPosts();
}
now my goal is to show every single post made by every user (I'm creating one big forum) so how can I show every single post made by everyone in the database to my home screen? My database also looks like this for some visuals, would I have to loop through?
here is my Home screen's code, where I wish to display every users posts
class HomeScreen extends StatefulWidget {
final String currentUserId;
const HomeScreen({Key? key, required this.currentUserId}) : super(key: key);
#override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
List _homeScreenPosts = [];
bool _loading = false;
buildHomeScreenPosts(Post post, UserModel author) {
return PostContainer(
post: post,
author: author,
currentUserId: widget.currentUserId,
);
}
showHomeScreenPosts(String currentUserId) {
List<Widget> homePostsList = [];
for (Post post in _homeScreenPosts) {
homePostsList.add(
FutureBuilder(
future: usersRef.doc(post.authorId).get(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
UserModel author = UserModel.fromSnap(snapshot.data);
return buildHomeScreenPosts(post, author);
} else {
return SizedBox.shrink();
}
},
),
);
}
return homePostsList;
}
setupHomeScreenPosts() async {
setState(() {
_loading = true;
});
List homeScreenPosts =
await DatabaseMethods.getHomeScreenPosts(widget.currentUserId);
if (mounted) {
setState(() {
_homeScreenPosts = homeScreenPosts;
_loading = false;
});
}
}
#override
void initState() {
super.initState();
setupHomeScreenPosts();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SearchScreen(
currentUserId: widget.currentUserId,
),
),
);
},
icon: Icon(Icons.search),
),
],
automaticallyImplyLeading: false,
title: Text('Home'),
centerTitle: true,
),
body: RefreshIndicator(
onRefresh: () => setupHomeScreenPosts(),
child: ListView(
physics: BouncingScrollPhysics(
parent: AlwaysScrollableScrollPhysics(),
),
children: [
_loading ? LinearProgressIndicator() : SizedBox.shrink(),
SizedBox(height: 5),
Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizedBox(height: 5),
Column(
children: _homeScreenPosts.isEmpty && _loading == false
? [
SizedBox(height: 5),
Padding(
padding: EdgeInsets.symmetric(horizontal: 25),
child: Text(
'There is No New posts',
style: TextStyle(
fontSize: 20,
),
),
),
]
: showHomeScreenPosts(widget.currentUserId),
),
],
)
],
),
),
);
}
}
Firebase does not have the a concept of "tables" the way relational/sql databases do. I.e. there is not a built-in method to access all documents labelled as a "post".
Because of this, you'll need to access each post through each of the user documents.
Assuming you have a List of all of your user Id's called allUserIds, then you can do something like the following:
List<Post> allPostsForAllUsers = [];
allUserIds.forEach((id) {
allPostsForAllUsers.addAll(await getUserPosts(id));
}
Okay so I figured it out myself, all I had to do was call the collectionGroup 'userPosts' like so
static Future<List<Post>> getHomeScreenPosts(String currentUserId) async {
QuerySnapshot homePostsSnap = await FirebaseFirestore.instance
.collectionGroup('userPosts')
.orderBy('timestamp', descending: true)
.get();
List<Post> homeScreenPosts =
homePostsSnap.docs.map((doc) => Post.fromDoc(doc)).toList();
return homeScreenPosts;
}
I am trying to parse json response data, in this response there are nested array list and i want to display table_id : 39 and on next line or under table_id : 39 i want to display table_id : 40. Now problem is when it completes on table_id : 39 then it should go to table_id : 40 and display but instead it shows 0floor is null. Can anyone help me in which step i am doing wrong.
JSON RESPONSE
{
"message": "floors returned",
"floorData": [
{
"0floor": [
{
"table_id": "39"
}
]
},
{
"1floor": [
{
"table_id": "40"
}
]
}
]
}
Model
class TablesFloorData0floor {
String? tableId;
TablesFloorData0floor({
this.tableId,
});
TablesFloorData0floor.fromJson(Map<String, dynamic> json) {
tableId = json['table_id']?.toString();
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['table_id'] = tableId;
return data;
}
}
class TablesFloorData {
List<TablesFloorData0floor?>? the0floor;
TablesFloorData({
this.the0floor,
});
TablesFloorData.fromJson(Map<String, dynamic> json) {
if (json['0floor'] != null) {
final v = json['0floor'];
final arr0 = <TablesFloorData0floor>[];
v.forEach((v) {
arr0.add(TablesFloorData0floor.fromJson(v));
});
the0floor = arr0;
}
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
if (the0floor != null) {
final v = the0floor;
final arr0 = [];
v!.forEach((v) {
arr0.add(v!.toJson());
});
data['0floor'] = arr0;
}
return data;
}
}
class Tables {
String? message;
List<TablesFloorData?>? floorData;
Tables({
this.message,
this.floorData,
});
Tables.fromJson(Map<String, dynamic> json) {
message = json['message']?.toString();
if (json['floorData'] != null) {
final v = json['floorData'];
final arr0 = <TablesFloorData>[];
v.forEach((v) {
arr0.add(TablesFloorData.fromJson(v));
});
floorData = arr0;
}
}
Map<String, dynamic> toJson() {
final data = <String, dynamic>{};
data['message'] = message;
if (floorData != null) {
final v = floorData;
final arr0 = [];
v!.forEach((v) {
arr0.add(v!.toJson());
});
data['floorData'] = arr0;
}
return data;
}
}
home.dart
Widget showTable(
List<TablesFloorData> data, StateSetter setStateBT, String lang) {
return Container(
child: ListView.builder(
shrinkWrap: true,
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return Column(
children: [
Text('test'),
ListView.builder(
shrinkWrap: true,
itemCount: data[index].the0floor!.length,
itemBuilder: (BuildContext context, int ind) {
return Text(data[index].the0floor![index]!.tableId!);
})
],
);
}));
}
Try this..
Create one floor class and use a key parameter to parse the floor level.
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: Home(),
);
}
}
class Home extends StatelessWidget {
const Home({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
var data = (json['floorData'] as List<Map<String, dynamic>>)
.map((f) => TablesFloorData.fromJson(f))
.toList();
return Scaffold(
body: Center(
child: ListView.builder(
shrinkWrap: true,
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return Column(
children: [
Text('test'),
ListView.builder(
shrinkWrap: true,
itemCount: data[index].tables.length,
itemBuilder: (BuildContext context, int ind) {
return Text(
data[index].tables[ind].id,
textAlign: TextAlign.center,
);
})
],
);
},
),
),
);
}
}
class TablesFloorData {
final List<TableDate> tables;
final int floor;
TablesFloorData({
required this.floor,
required this.tables,
});
factory TablesFloorData.fromJson(Map<String, dynamic> json) =>
TablesFloorData(
floor: int.parse(json.keys.first.replaceAll('floor', '')),
tables: json.values.first.map<TableDate>((tableJson) {
return TableDate.fromJson(tableJson);
}).toList());
Map<String, dynamic> toJson() {
return {'${floor}floor': tables.map((t) => t.toJson())};
}
}
class TableDate {
String id;
TableDate({
required this.id,
});
factory TableDate.fromJson(Map<String, dynamic> json) => TableDate(
id: json['table_id'] as String,
);
Map<String, dynamic> toJson() {
return <String, dynamic>{
'table_id': id,
};
}
}
var json = {
"message": "floors returned",
"floorData": [
{
"0floor": [
{"table_id": "39"}
]
},
{
"1floor": [
{"table_id": "40"}
]
}
]
};
I want to access imageUrl variable into Scaffold's body.
class _homePageState extends State<homePage> {
Stream<QuerySnapshot> _homeTopSliderData =
FirebaseFirestore.instance.collection("homeTopSliderData").snapshots();
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _homeTopSliderData,
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> querySnapshot) {
if (querySnapshot.hasError) {
return Scaffold(
body: Center(
child: Text("Something went wrong"),
),
);
} else if (querySnapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
querySnapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
var imageUrl = data['imageUrl'];
});
return Scaffold(
body: Image(
image: NetworkImage(imageUrl),
),
);
});
}
}
It shows error when I use it as network image's url,
I am new to flutter and dart
Thanks!!!
The scope of the variable imageUrl is local, for that reason the scaffold can't access to that variable. To get more info refer to: https://toastguyz.com/dart/dart-variable-scope
It should work in the next way:
class _homePageState extends State<homePage> {
Stream<QuerySnapshot> _homeTopSliderData =
FirebaseFirestore.instance.collection("homeTopSliderData").snapshots();
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _homeTopSliderData,
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> querySnapshot) {
if (querySnapshot.hasError) {
return Scaffold(
body: Center(
child: Text("Something went wrong"),
),
);
} else if (querySnapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
var imageUrl;
querySnapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
imageUrl = data['imageUrl'];
});
return Scaffold(
body: Image(
image: NetworkImage(imageUrl),
),
);
});
} }
You are streaming a collection, therefore, you will get a List of imageUrls (not 1, you could get up to 5 imageUrls). From your code, you are not using the list of imageUrls for anything, you are simply trying to use the last value of imageUrl. So, I do not think you want to stream a collection. I think you want a particular imageUrl.
To get a particular imageUrl, stream the document like this:
Stream<DocumentSnapshot<Map<String, dynamic>>> _homeTopSliderData =
FirebaseFirestore.instance
.collection("homeTopSliderData")
// TODO: replace DOCUMENTID with your document id.
.doc("DOCUMENTID")
.snapshots();
#override
Widget build(BuildContext context) {
return StreamBuilder<DocumentSnapshot>(
stream: _homeTopSliderData,
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> querySnapshot) {
if (querySnapshot.hasError) {
return Scaffold(
body: Center(
child: Text("Something went wrong"),
),
);
} else if (querySnapshot.connectionState == ConnectionState.waiting) {
return Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
Map<String, dynamic> data = querySnapshot.data.data();
print(data['imageUrl']);
return Scaffold(
body: Image(
image: NetworkImage(data['imageUrl']),
),
);
},
);
}
If you want to stream a collection (get all imageUrls in all documents) (bad idea since you are obviously not expecting multiple imageUrls), you can replace this part in your code:
var imageUrl;
querySnapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
print(data['imageUrl']);
if (data['imageUrl'] != null) {
imageUrl = data['imageUrl'];
}
});
return Scaffold(
body: Image(
image: NetworkImage(imageUrl),
),
);
I have a JSON and I have no problem displaying just Strings but my problem is arrays of strings. I have a Future function that takes data from the JSON and then display that in a futurebuilder.
So to be clear I want the usernames: "hugo" and "studentone" to be displayed in a listview.builder!
If someone can tell be how to do the Future Function that would help loads!
The JSON:
{
"id": 81,
"users": [
{
"username": "hugo",
"fullname": "Hugo Johnsson"
},
{
"username": "studentone",
"fullname": "Student One"
}
],
"title": "test med teacher chat",
"description": "This project does not have a description.",
"subject": "No subject",
"deadline": "2019-01-06",
"days_left": "91 days ago",
"overview_requests": [
{
"id": 28,
"user": {
"username": "hugo",
"fullname": "Hugo Johnsson"
},
"group": 81
}
]
},
The Future Function:
Future> _getUSERS() async {
var data = await http.get("IP");
var jsonData = json.decode(data.body);
List<USER> users;
for (var JData in jsonData) {
var jsonUsers = JData["users"];
for(int i = 0; i<users.length ; i++) {
var user = jsonUsers[i];
users.add(USER(user["username"], user["fullname"]));
}
}
The UI:
child: FutureBuilder(
future: _getUSERS(),
builder: (BuildContext context, AsyncSnapshot snapshot ) {
if (snapshot.data == null){
return Container(
child: Center(
child: CircularProgressIndicator(),
),
);
}
else return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(snapshot.data[index].username),
);
}
);
}
),
There are bugs in the future function that fetches the json data.
I have corrected and here it is:
Future<List<USER>> _getUSERS() async {
var data = await http.get("IP");
var jsonData = json.decode(data.body);
List<USER> users;
for (var JData in jsonData) {
var jsonUsers = JData["users"];
for(int i = 0; i<jsonUsers.length ; i++) {
var user = jsonUsers[i];
users.add(USER(user["username"], user["fullname"]));
}
}
return users;
fixes:
for(int i = 0; i<jsonUsers.length; i++)
return users;
This is something which I have done. You can take a reference from this
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_list_http/MyApp1.dart';
import 'package:flutter_list_http/XYZ.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:fluttertoast/fluttertoast.dart';
void main() => runApp(MyApp());
XYZ parsePosts(String responseBody) {
var myClass = XYZ.fromJson(jsonDecode(responseBody));
for (var result in myClass.data.toList()) {
print('result');
print(result?.id);
}
return myClass;
}
Future<XYZ> fetchPosts(http.Client client) async {
final response = await client.get(
'url',
headers: {
"Authorization":
"headers if any"
});
print(response.body);
// compute function to run parsePosts in a separate isolate
return compute(parsePosts, response.body);
}
class HomePage extends StatelessWidget {
final String title;
HomePage({Key key, this.title}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: FutureBuilder<XYZ>(
future: fetchPosts(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? ListViewPosts(posts: snapshot.data)
: Center(child: CircularProgressIndicator());
},
),
);
}
}
class ListViewPosts extends StatelessWidget {
final XYZ posts;
ListViewPosts({Key key, this.posts}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: ListView.builder(
itemCount: posts.data.length,
padding: const EdgeInsets.all(15.0),
itemBuilder: (context, position) {
return Column(
children: <Widget>[
Divider(height: 5.0),
ListTile(
title: Text(
'${posts.data[position].name}',
style: TextStyle(
fontSize: 22.0,
color: Colors.deepOrangeAccent,
),
),
onTap: () => _onTapItem(context, posts, position),
),
],
);
}),
);
}
void _onTapItem(BuildContext context, XYZ posts, int position) {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyApp1()),
);
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
final appTitle = 'JavaSampleApproach HTTP-JSON';
return MaterialApp(
title: appTitle,
home: HomePage(title: appTitle),
);
}
}
Hope it helps.
Hi I am currently trying to access a child of my database in Flutter so that I can link it to another child. I am able to pull the data from one child, but unable to link this data with my other child. I want to be able to access a field of the 'Record' child and link to the 'Volunteer' child using the "volunteer" field but am unable to get them to link together. Any help would be appreciated!
The database structure is as follows:
This is the code for My Volunteer class:
class Volunteer {
String volunteerID;
String name;
String role;
Volunteer(this.volunteerID, this.name, this.role);
Volunteer.fromSnapshot(DataSnapshot snapshot)
: volunteerID = snapshot.key,
name = snapshot.value["name"],
role = snapshot.value["role"];
toJson() {
return {
"key": volunteerID,
"name": name,
"role": role
};
}
}
And this is the code for my UI:
class SignInPageState extends State<SignInPage> {
List<Volunteer> volunteers;
Volunteer volunteer;
DatabaseReference dbRef;
DatabaseReference volunteerRef;
DatabaseReference recordRef;
#override
void initState() {
super.initState();
volunteers = new List();
volunteer = Volunteer("","", "");
final FirebaseDatabase database = FirebaseDatabase.instance;
dbRef = database.reference();
volunteerRef = database.reference().child('volunteer');
recordRef = database.reference().child('record');
dbRef.onChildAdded.listen(_onEntryAdded);
dbRef.onChildChanged.listen(_onEntryChanged);
volunteerRef.once().then((DataSnapshot snapshot) {
Map<dynamic, dynamic> getMap = snapshot.value;
getMap.forEach((key, values) {
String volunteerRecord = recordRef.child('volunteer').toString();
if (volunteerRecord == volunteer.volunteerID){
volunteer.role = volunteerRecord;
}
volunteers.add(volunteer);
});
});
}
_onEntryAdded(Event event) {
setState(() {
volunteers.add(Volunteer.fromSnapshot(event.snapshot));
});
}
_onEntryChanged(Event event) {
var old = volunteers.singleWhere((entry) {
return entry.volunteerID == event.snapshot.key;
});
setState(() {
volunteers[volunteers.indexOf(old)] = Volunteer.fromSnapshot(event.snapshot);
});
}
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
padding: EdgeInsets.all(10.0),
color: Colors.white,
child: Column(
children: <Widget>[
Expanded(
flex: 8,
child: Container(
child: FirebaseAnimatedList(
query: volunteerRef,
itemBuilder: (BuildContext context, DataSnapshot snapshot,
Animation<double> animation, int index) {
return new ListTile(
title: Text(
(volunteers[index].name + " " + volunteers[index].role),
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontWeight: FontWeight.bold ,color: Color.fromRGBO(139, 195, 68, 1)),
),
onTap: () {
SuccessSignInPage(volunteers[index].name);
},
);
},
),
)
),
],
)),
),
);
}
void Back() async {
await Navigator.of(context).pop();
}
void SuccessSignInPage(String name) async {
var route = new MaterialPageRoute(
builder: (BuildContext context) => new SignInSuccessPage(value: name));
await Navigator.of(context).push(route);
}
}