How to show comments/details of the post according to the post id from an API? - flutter

I am trying to retrieve "comments" data from the API. But the main problem is I want to see the comments of the post according to the post I've selected or pressed instead of providing a static postId as the query parameter.
Here's my code:
Comment Model
class Comment {
int postId;
int id;
String name;
String email;
String body;
Comment({
required this.postId,
required this.id,
required this.name,
required this.email,
required this.body,
});
factory Comment.fromJson(Map<String, dynamic> json) {
return Comment(
postId: json['postId'],
id: json['id'],
name: json['name'],
email: json['email'],
body: json['body'],
);
}
}
Comment Provider (Riverpod and Dio used). Note that I've used postId = 1 as query parameter
final commentProvider = StateNotifierProvider<CommentProvider, List<Comment>>(
(ref) => CommentProvider());
List<Comment> commentList = [];
class CommentProvider extends StateNotifier<List<Comment>> {
CommentProvider() : super([]) {
getComments(postId: 1);
}
Future<void> getComments({required int postId}) async {
final dio = Dio();
try {
final response = await dio.get(Api.commentApi, queryParameters: {
'postId': postId,
});
state = (response.data as List).map((e) => Comment.fromJson(e)).toList();
commentList = state;
print(response.data);
} on DioError catch (err) {
print(err);
}
}
}
This is my main screen where I'm fetching the posts data from the API
Consumer(
builder: (context, ref, child) {
final posts = ref.watch(postProvider);
return ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
final data = posts[index];
return Container(
child: Column(
children: [
InkWell(
onTap: () {
Get.to(() => DetailScreen(), transition: Transition.zoom);
},
child: Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(AppPadding.p16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
CircleAvatar(
backgroundColor: Colors.grey[300],
radius: 13,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(data.userId.toString(), style: TextStyle(color: Colors.black, fontSize: 13),),
],
),
),
Text('#' + data.id.toString(), style: TextStyle(color: Colors.black, fontSize: 13),),
],
),
Text(
data.title.toString(),
style: getSemiBoldStyle(color: Colors.black),
),
Text(
data.body.toString(),
style: getRegularStyle(color: Colors.grey).copyWith(fontSize: FontSize.s14),
maxLines: 3,
overflow: TextOverflow.fade,
),
],
),
),
),
This is where I want to show the Comments data of the post according to the post id provided
Consumer(
builder: (context, ref, child) {
final posts = ref.watch(commentProvider);
return ListView.builder(
itemCount: posts.length,
itemBuilder: (context, index) {
final data = posts[index];
return Container(
child: Column(
children: [
Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.all(AppPadding.p16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Row(
// children: [
// Text(
// data.email.toString(),
// style: TextStyle(
// color: Colors.black, fontSize: 13),
// ),
// Text(
// '#' + data.id.toString(),
// style: TextStyle(
// color: Colors.black, fontSize: 13),
// ),
// ],
// ),
// Text(
// data.body.toString(),
// style: getSemiBoldStyle(color: Colors.black),
// ),
// Text(
// data.body.toString(),
// style: getRegularStyle(color: Colors.grey)
// .copyWith(fontSize: FontSize.s14),
// maxLines: 3,
// overflow: TextOverflow.fade,
// ),
],
),
),
I know how to pass the post Id from one screen to another but I got no idea how to show the comments according to the passed post id using a provider. For example: when I press on post with a post id with 3, I want to see the comments of the post id 3 in the next page. At the moment, I am just able to show the comments by providing static post id value.
If you didn't understand my problem then please feel free to ask. I'll kindly elaborate. Any state management solution can be provided but Flutter Riverpod would be much appreciated.
The API was taken from here:
Posts data : (https://jsonplaceholder.typicode.com/posts)
Comments data :(https://jsonplaceholder.typicode.com/posts/{post_id}/comments) or
Alternative (https://jsonplaceholder.typicode.com/comments?postId=1)

You can use the modifier .family. For example:
final commentsProvider = FutureProvider.family<List<Comment>, int>((ref, postId) async {
final dio = Dio();
try {
final response = await dio.get(Api.commentApi, queryParameters: {
'postId': postId,
});
return (response.data as List).map((e) => Comment.fromJson(e)).toList();
} on DioError catch (err) {
print(err);
return [];
}
return [];
});
In widget:
Consumer(
builder: (context, ref, child) {
...
final comments = ref.watch(commentsProvider(postId));
...
}
)
You can read more here: family modifier

Related

Change the id in the URL according to the selected card

Up!
I am a beginner in Flutter and I am making an application in which I have a first "boats" route. I access to the list of my boats contained in "Card" thanks to this route. Then, I have a second route, accessible from a button in each of these "Cards" allowing me to access the details of each boat.
To retrieve my data, I use a fake API to perform my tests (mockapi.io). For the list of boats, the route is "/boats". Then for the details of each boat, it becomes "/boats/:id/details". Here is my problem, how do I handle the fact that the id changes depending on the boat selected?
Here is the method to access the API:
Future<List<ListDetails>?> getListDetails({required id}) async
{
var client = http.Client();
var uri = Uri.parse('https://63e21b26109336b6cbff9ce9.mockapi.io/api/v1/boats_control/$id/details');
var response = await client.get(uri);
if(response.statusCode == 200)
{
var json = response.body;
return listDetailsFromJson(json);
}
}
And here is my boat_details view, where I try to display the API data:
class ControlListDetailViewState extends State<ControlListDetailView> {
#override
Widget build(BuildContext context) {
late final id = ModalRoute.of(context)?.settings.arguments;
List<ListDetails>? listDetails;
var isLoaded = false;
print(listDetails);
getData() async {
listDetails = await RemoteService().getListDetails(id: id);
if (listDetails != null) {
setState(() {
isLoaded = true;
});
}
}
#override
void initState() {
super.initState();
getData();
}
And here is my model :
import 'dart:convert';
List<BoatsControl> boatsControlFromJson(String str) => List<BoatsControl>.from(json.decode(str).map((x) => BoatsControl.fromJson(x)));
String boatsControlToJson(List<BoatsControl> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class BoatsControl {
BoatsControl({
required this.model,
required this.location,
required this.id,
});
String model;
String location;
String id;
factory BoatsControl.fromJson(Map<String, dynamic> json) => BoatsControl(
model: json["model"],
location: json["location"],
id: json["id"],
);
Map<String, dynamic> toJson() => {
"model": model,
"location": location,
"id": id,
};
}
Finally, here is my widget where I use this model:
Widget build(BuildContext context) {
print('ControlViewState - build');
return Scaffold(
drawer: NavDrawableWidget(), // Hamburger menu
bottomNavigationBar: FooterWidget(), // Footer Copyright
appBar: AppBar(
title: Text("${AppSettings.APP_NAME} | ${AppSettings.APP_VERSION}",
style: TextStyle(fontSize: 16)),
),
body: Column(
children: [
Text('\n${AppSettings.strings.controlTitle}',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 18)
),
Visibility(
visible: isLoaded,
replacement: const Center(
child: CircularProgressIndicator(),
),
child: const Text(''),
),
if(isLoaded)
Expanded(
child: ListView.builder(
itemCount: boatsControl?.length ?? 0,
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.all(8.0),
child: Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
side: BorderSide(color: Colors.black12, width: 2),
),
child: Padding(
padding: EdgeInsets.all(8.0),
child: Column(
children: <Widget>[
ListTile(
title: Text('${boatsControl![index].id} | ${boatsControl![index].model}'),
leading: Icon(Icons.anchor),
),
Row(
children: <Widget>[
Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(boatsControl![index].location,
style: TextStyle(fontSize: 14)),
],
),
),
Align(
alignment: Alignment.centerRight,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
CustomFloatingActionControlButton(
onPressed: () =>
{
Navigator.pushNamed(
context, '/controlListDefect',
arguments: boatsControl![index].id
)
},
tag: 'listDefectButton',
icon: Icons.equalizer,
),
],
),
),
],
),
],
),
),
),
);
},
),
)
]
),
);
}
Thank you in advance if you take the time to answer me and help me.

How to fetch data from REST API and display on Listview, Flutter

I am fairly new to using REST API to GET data and display in a Listview in Flutter. I have since been working on something like this, But i get Lost along the line. Hence I need Help with this.
My code Goes thus
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class TransactionDetails {
final String avatar;
final String name;
final String date;
final String amount;
TransactionDetails({required this.avatar, required this.name, required this.date, required this.amount});
factory TransactionDetails.fromJson(Map<String, dynamic> json) {
return TransactionDetails(
avatar: json['avatar'],
name: json['name'],
date: json['date'],
amount: json['amount']);
}
}
class BaseScreen extends StatelessWidget {
const BaseScreen({Key? key}) : super(key: key);
Future<TransactionDetails> fetchTransaction() async {
final response = await http
.get('https://brotherlike-navies.000webhostapp.com/people/people.php');
if (response.statusCode == 200) {
return TransactionDetails.fromJson(json.decode(response.body));
} else {
throw Exception('Request Failed.');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: double.infinity,
height: 150,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
Container(
margin: const EdgeInsets.all(15),
width: 319,
height: 100,
color: Colors.green,
alignment: Alignment.center,
child: const Text(
'\$5200.00',
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
Container(
margin: const EdgeInsets.all(15),
width: 319,
height: 100,
color: Colors.green,
alignment: Alignment.center,
child: const Text(
'\$1200.00',
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
SizedBox(height: 24),
],
),
),
Padding(
padding: EdgeInsets.all(15),
child: Text(
"Recent Transactions",
style: TextStyle(
fontSize: 14, fontWeight: FontWeight.bold, color: Colors.green),
),
),
ListView() //Display the data here from the REST API
],
)));
}
}
How do I Display it on the Listview? Please I need help with this. Just for the sake of clarification as I am learning here with this.
Try below code:
Your TransactionDetails Class
class TransactionDetails {
String? avatar;
String? name;
String? date;
String? amount;
TransactionDetails({
this.avatar,
this.name,
this.date,
this.amount,
});
TransactionDetails.fromJson(Map<String, dynamic> json) {
avatar = json['avatar'];
name = json['name'];
date = json['date'];
amount = json['amount'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['avatar'] = avatar;
data['name'] = name;
data['date'] = date;
data['amount'] = amount;
return data;
}
}
API Call:
Future<List<TransactionDetails>> fetchAlbum() async {
final response = await http.get(Uri.parse(
'https://brotherlike-navies.000webhostapp.com/people/people.php'));
if (response.statusCode == 200) {
final List result = json.decode(response.body);
return result.map((e) => TransactionDetails.fromJson(e)).toList();
} else {
throw Exception('Failed to load data');
}
}
Your Widget:
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: double.infinity,
height: 150,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
Container(
margin: const EdgeInsets.all(15),
width: 319,
height: 100,
color: Colors.green,
alignment: Alignment.center,
child: const Text(
'\$5200.00',
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
Container(
margin: const EdgeInsets.all(15),
width: 319,
height: 100,
color: Colors.green,
alignment: Alignment.center,
child: const Text(
'\$1200.00',
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold),
),
),
const SizedBox(height: 24),
],
),
),
const Padding(
padding: EdgeInsets.all(15),
child: Text(
"Recent Transactions",
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.green),
),
),
Center(
child: FutureBuilder<List<TransactionDetails>>(
future: fetchAlbum(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return ListTile(
leading: CircleAvatar(
child: Image.network(
snapshot.data![index].avatar.toString()),
),
title: Text(snapshot.data![index].name.toString()),
trailing:
Text(snapshot.data![index].amount.toString()),
subtitle: Text(snapshot.data![index].date.toString()),
);
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
return const CircularProgressIndicator();
},
),
),
],
),
Result Screen->
There are multiple ways you can achieve this.
Using FutureBuilder
Using StatefulWidget & setState
Currently, you are using a StatelessWidget (BaseScreen) so let's just go with FutureBuilder.
On hitting the url that you have given:
https://brotherlike-navies.000webhostapp.com/people/people.php
It gives the following response:
[{"avatar":"https:\/\/static.vecteezy.com\/system\/resources\/thumbnails\/002\/002\/403\/small\/man-with-beard-avatar-character-isolated-icon-free-vector.jpg","name":"Kayo Johnson","date":"09\/29\/2022","amount":"5000.00"},{"avatar":"https:\/\/static.vecteezy.com\/system\/resources\/thumbnails\/002\/002\/403\/small\/man-with-beard-avatar-character-isolated-icon-free-vector.jpg","name":"Kuta Joy","date":"09\/29\/2022","amount":"5000.00"},{"avatar":"https:\/\/static.vecteezy.com\/system\/resources\/thumbnails\/001\/993\/889\/small\/beautiful-latin-woman-avatar-character-icon-free-vector.jpg","name":"Timmi Phillips","date":"09\/28\/2022","amount":"3500.00"}]
It returns a list of transaction details objects. Hence, you should add another method in your model TransactionDetail which will return a list of TransactionDetails as follows:
class TransactionDetails {
final String avatar;
final String name;
final String date;
final String amount;
TransactionDetails(
{required this.avatar,
required this.name,
required this.date,
required this.amount});
factory TransactionDetails.fromJson(Map<String, dynamic> json) {
return TransactionDetails(
avatar: json['avatar'],
name: json['name'],
date: json['date'],
amount: json['amount']);
}
static List<TransactionDetails> fromJsonList(dynamic jsonList) {
final transactionDetailsList = <TransactionDetails>[];
if (jsonList == null) return transactionDetailsList;
if (jsonList is List<dynamic>) {
for (final json in jsonList) {
transactionDetailsList.add(
TransactionDetails.fromJson(json),
);
}
}
return transactionDetailsList;
}
}
Update your fetchTransaction method as follows:
Future<List<TransactionDetails>> fetchTransaction() async {
final response = await http
.get('https://brotherlike-navies.000webhostapp.com/people/people.php');
if (response.statusCode == 200) {
return TransactionDetails.fromJsonList(json.decode(response.body));
} else {
throw Exception('Request Failed.');
}
}
Just wrap your ListView widget with a FutureBuilder widget as follows:
FutureBuilder(
future: fetchTransaction(),
builder: (context, AsyncSnapshot<List<TransactionDetails>> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.data == null) {
return const Center(child: Text('Something went wrong'));
}
return ListView.builder(
itemCount: snapshot.data?.length ?? 0,
itemBuilder: (context, index) {
return ListTile(
title: Text(snapshot.data![index].name),
subtitle: Text(snapshot.data![index].amount),
);
});
}
return const CircularProgressIndicator();
},
),

Material Button is not calling post method function

i am beginner in FLutter , i have a json that i want to send to internet by flutter
this is the json i want to send:
{
"userid": 41,
"name": "dhya",
"price": 11,
"players": [
{
"id":1,
"firstname":"aa",
"lastname":"ee",
"position":"df",
"price":12.1,
"appearences":2,
"goals":1,
"assists":1,
"cleansheets":1,
"redcards":1,
"yellowcards":1,
"image":"qq"
},
{
"id":2,
"firstname":"aa",
"lastname":"ee",
"position":"df",
"price":12.1,
"appearences":2,
"goals":1,
"assists":1,
"cleansheets":1,
"redcards":1,
"yellowcards":1,
"image":"qq"
}
]
}
As a beginner , i did not have any idea about how to send an object that the server will receive in the json format that i wanted so i used a quicktype and i created this model
// To parse this JSON data, do
//
// final clubJson = clubJsonFromJson(jsonString);
import 'dart:convert';
ClubJson clubJsonFromJson(String str) => ClubJson.fromJson(json.decode(str));
String clubJsonToJson(ClubJson data) => json.encode(data.toJson());
class ClubJson {
ClubJson({
this.userid,
this.name,
this.price,
this.players,
});
int userid;
String name;
int price;
List<Player> players;
factory ClubJson.fromJson(Map<String, dynamic> json) => ClubJson(
userid: json["userid"],
name: json["name"],
price: json["price"],
players: List<Player>.from(json["players"].map((x) => Player.fromJson(x))),
);
Map<String, dynamic> toJson() => {
"userid": userid,
"name": name,
"price": price,
"players": List<dynamic>.from(players.map((x) => x.toJson())),
};
}
class Player {
Player({
this.id,
this.firstname,
this.lastname,
this.position,
this.price,
this.appearences,
this.goals,
this.assists,
this.cleansheets,
this.redcards,
this.yellowcards,
this.image,
this.clubid,
});
int id;
String firstname;
String lastname;
String position;
double price;
int appearences;
int goals;
int assists;
int cleansheets;
int redcards;
int yellowcards;
String image;
int clubid;
factory Player.fromJson(Map<String, dynamic> json) => Player(
id: json["id"],
firstname: json["firstname"],
lastname: json["lastname"],
position: json["position"],
price: json["price"].toDouble(),
appearences: json["appearences"],
goals: json["goals"],
assists: json["assists"],
cleansheets: json["cleansheets"],
redcards: json["redcards"],
yellowcards: json["yellowcards"],
image: json["image"],
clubid: json["clubid"],
);
Map<String, dynamic> toJson() => {
"id": id,
"firstname": firstname,
"lastname": lastname,
"position": position,
"price": price,
"appearences": appearences,
"goals": goals,
"assists": assists,
"cleansheets": cleansheets,
"redcards": redcards,
"yellowcards": yellowcards,
"image": image,
"clubid": clubid,
};
}
And now in the end the only remaining step is the function that i wrote in FLutter:
Future <void> PostRequest() async {
// set up POST request arguments
final url = Uri.parse('http://localhost:3000/api/questions/addQuestion');
Map<String, String> headers = {"Content-type": "application/json"};
ClubJson club = ClubJson(userid: 1, name: "dsds", price: 55.2,players: null );
for(var item in widget.selectedPlayers){
club.players.add(Player(id:item.playerID,firstname:item.firstName,lastname:item.lastName,position:item.position,price:item.price,appearences:item.appearances,goals:item.goals,assists:item.assists,cleansheets:item.cleanSheets,redcards:item.redCards,yellowcards:item.yellowCards,image:item.image));
}
String json = club.toJson().toString();
print(club);
// make POST request
Response response = await post(url, headers: headers, body: json);
// check the status code for the result
int statusCode = response.statusCode;
// this API passes back the id of the new item added to the body
String body = response.body;
}
After i clicked on the button to call the function PostRequest() and send the object to internet i got a strange message and the call is not done:
======== Exception caught by gesture ===============================================================
The following assertion was thrown while handling a gesture:
Scaffold.of() called with a context that does not contain a Scaffold.
No Scaffold ancestor could be found starting from the context that was passed to Scaffold.of(). This usually happens when the context provided is from the same StatefulWidget as that whose build function actually creates the Scaffold widget being sought.
There are several ways to avoid this problem. The simplest is to use a Builder to get a context that is "under" the Scaffold. For an example of this, please see the documentation for Scaffold.of():
https://api.flutter.dev/flutter/material/Scaffold/of.html
A more efficient solution is to split your build function into several widgets. This introduces a new context from which you can obtain the Scaffold. In this solution, you would have an outer widget that creates the Scaffold populated by instances of your new inner widgets, and then in these inner widgets you would use Scaffold.of().
A less elegant but more expedient solution is assign a GlobalKey to the Scaffold, then use the key.currentState property to obtain the ScaffoldState rather than using the Scaffold.of() function.
The context used was: CreateTeamView
state: _CreateTeamViewState#c4658
When the exception was thrown, this was the stack:
#0 Scaffold.of (package:flutter/src/material/scaffold.dart:1472:5)
#1 _CreateTeamViewState.build.<anonymous closure> (package:footyappp/Fantazyy/create_team_view.dart:325:36)
#2 _InkResponseState._handleTap (package:flutter/src/material/ink_well.dart:993:19)
#3 _InkResponseState.build.<anonymous closure> (package:flutter/src/material/ink_well.dart:1111:38)
#4 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:183:24)
...
Handler: "onTap"
Recognizer: TapGestureRecognizer#eeb07
debugOwner: GestureDetector
state: possible
won arena
finalPosition: Offset(187.1, 658.5)
finalLocalPosition: Offset(187.1, 25.1)
button: 1
sent tap down
====================================================================================================
Here the full code of Flutter:
import 'package:flutter/material.dart';
import 'package:footyappp/Fantazyy/club_json.dart';
import 'package:footyappp/Fantazyy/controller.dart';
import 'package:footyappp/Fantazyy/styles.dart';
import 'package:footyappp/Fantazyy/player%20copy.dart';
import 'package:footyappp/Fantazyy/players_creation_details_view.dart';
import 'package:http/http.dart';
class CreateTeamView extends StatefulWidget {
List<Playerr> selectedPlayers;
CreateTeamView({
Key key,
players,
selectedPlayers,
}) : selectedPlayers = (selectedPlayers == null) ? new List<Playerr>.generate(16, (int index) => null) : selectedPlayers;
#override
_CreateTeamViewState createState() => _CreateTeamViewState();
}
class _CreateTeamViewState extends State<CreateTeamView> {
Future <void> PostRequest() async {
// set up POST request arguments
final url = Uri.parse('http://localhost:3000/api/questions/addQuestion');
Map<String, String> headers = {"Content-type": "application/json"};
ClubJson club = ClubJson(userid: 1, name: "dsds", price: 55.2,players: null );
for(var item in widget.selectedPlayers){
club.players.add(Player(id:item.playerID,firstname:item.firstName,lastname:item.lastName,position:item.position,price:item.price,appearences:item.appearances,goals:item.goals,assists:item.assists,cleansheets:item.cleanSheets,redcards:item.redCards,yellowcards:item.yellowCards,image:item.image));
}
String json = club.toJson().toString();
print(club);
// make POST request
Response response = await post(url, headers: headers, body: json);
// check the status code for the result
int statusCode = response.statusCode;
// this API passes back the id of the new item added to the body
String body = response.body;
}
final double _checkboxHeight = 30.0;
double _startingBudget = 107.0;
double _budget = 107.0;
bool _everyTeam = false, _minThreeFreshers = false, _maxThreeSameTeam = true, _isTeamNameLong = false, _buttonEnabled = true;
String _teamName = "";
Widget _saveChanges = Text("Press to save changes");
#override
void initState() {
Map<int,int> teamCount = new Map<int, int>();
int fresherCount = 0;
for (Playerr player in widget.selectedPlayers) {
if (player != null) {
_budget -= player.price;
/* if (teamCount[player.club] == null) {
teamCount[player.teams] = 1;
} else {
teamCount[player.teams]++;
if (teamCount[player.club] > 3) _maxThreeSameTeam = false;
}*/
}
}
_minThreeFreshers = (fresherCount >= 3);
_everyTeam = (teamCount.length >=7);
super.initState();
}
emptyPlayer(int index) {
Playerr player = widget.selectedPlayers[index];
Widget playerView;
if (player == null) {
playerView = Image.asset("Assets/shirt_blank.png", fit: BoxFit.fitHeight,);
} else {
playerView = Column(
children: <Widget>[
Expanded(
child: Image.asset(player.image, fit: BoxFit.fitHeight,),
),
Container(
color: Colors.green,
child: Text(player.firstName.substring(0,1) + ". " + player.lastName, textAlign: TextAlign.center, softWrap: false, overflow: TextOverflow.fade,),
),
Container(
color: Colors.green,
child: Text("£${player.price}m", textAlign: TextAlign.center),
),
],
);
}
return Expanded(
child: InkWell(
onTap: () => Navigator.pushReplacement(context, MaterialPageRoute(builder: (BuildContext context) {return PlayersCreationDetailsView(selectedPlayers: widget.selectedPlayers, playerIndex: index,);})),
child: Padding(padding: EdgeInsets.only(left: 3.0, right: 3.0), child:playerView,)
),
);
}
#override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async => false,
child: Scaffold(
appBar: AppBar(title: Text("Create your team"),),
body: Stack(
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
child: Stack(
children: <Widget>[
Positioned.fill(
child: Image.asset("Assets/pitch.jpg", fit: BoxFit.fitWidth, alignment: Alignment.topLeft,)
)
]
)
),
],
),
Column( //players
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Expanded(
flex: 1,
child: Container()
),
Expanded(
flex: 6,
child: Padding(
padding: EdgeInsets.only(left: 40.0, right: 40.0), child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: List.generate(2, (index) => emptyPlayer(index)),
),
)
),
Expanded(
flex: 1,
child: Container()
),
Expanded(
flex: 6,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: List.generate(5, (index) => emptyPlayer(index+2)),
)
),
Expanded(
flex: 1,
child: Container()
),
Expanded(
flex: 6,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: List.generate(5, (index) => emptyPlayer(index+7)),
)
),
Expanded(
flex: 1,
child: Container()
),
Expanded(
flex: 6,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: List.generate(4, (index) => emptyPlayer(index+12)),
)
),
Expanded(
flex: 1,
child: Container()
),
Container(
color: Styles.colorAccentDark,
padding: EdgeInsets.only(left: 8.0, right: 8.0),
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 4.0, bottom: 4.0),
child: Row(
children: <Widget>[
Expanded(
child: Text("Remaining Budget", style: Styles.budgetLabel,),
),
Text("£${_budget}m", style: Styles.budgetLabel,)
],
),
),
Row(
children: <Widget>[
Expanded(
child: Text("At least one player from every team:", style: Styles.checkboxLabel),
),
Container(
height: _checkboxHeight,
child: Checkbox(
value: _everyTeam,
onChanged: (bool) => null,
),
)
],
),
Row(
children: <Widget>[
Expanded(
child: Text("At least two freshers:", style: Styles.checkboxLabel),
),
Container(
height: _checkboxHeight,
child: Checkbox(
value: _minThreeFreshers,
onChanged: (bool) => null,
),
)
],
),
Row(
children: <Widget>[
Expanded(
child: Text("Max three players from same team:", style: Styles.checkboxLabel),
),
Container(
height: _checkboxHeight,
child: Checkbox(
value: _maxThreeSameTeam,
onChanged: (bool) => null,
),
)
],
),
Padding(
padding: EdgeInsets.only(bottom: 4.0),
child: Row(
children: <Widget>[
Expanded(
child: TextField(
onChanged: (string) {
if (string.length >= 4) {
_teamName = string;
setState(() {
_isTeamNameLong = true;
});
} else {
setState(() {
_isTeamNameLong = false;
});
}
},
decoration: InputDecoration(
fillColor: Styles.colorBackgroundLight,
filled: true,
hintText: "Team Name",
),
)
),
Container(
height: _checkboxHeight,
child: Checkbox(
value: _isTeamNameLong,
onChanged: (bool) => null,
),
)
],
),
)
],
),
)
,
new MaterialButton(
height: 50.0,
minWidth: double.infinity,
color: Styles.colorButton,
splashColor: Colors.teal,
textColor: Colors.white,
child: _saveChanges,
onPressed: () {
if (_buttonEnabled) {
String message = "";
if (!_everyTeam) {
message +=
"You need at least one player from every team \n";
}
if (!_minThreeFreshers) {
message +=
"You need at least 3 freshers in your team \n";
}
if (!_maxThreeSameTeam) {
message +=
"You can have at most 3 players from the same team \n";
}
if (!_isTeamNameLong) {
message +=
"Your team name must be at least 4 characters long \n";
}
if (_budget < 0) {
message += "You can't exceed the budget \n";
}
if (message != "") {
final snackBar = SnackBar(
content: Text(message),
duration: Duration(seconds: 2)
);
Scaffold.of(context).showSnackBar(snackBar);
setState(() {
_saveChanges = FutureBuilder(
future: PostRequest(),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
_buttonEnabled = true;
return Text("Press to save changes");
}
// By default, show a loading spinner and disable button
_buttonEnabled = false;
return CircularProgressIndicator();
},
);
}); }
}
}
),
],
),
],
)
)
);
}
}
try adding a key for scaffold widget like this
final GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
Scaffold(
key: scaffoldKey,
body:....
scaffoldKey.currentState.showSnackBar(snackBar);
or if you are using flutter 2 use this to show SnackBar
ScaffoldMessenger.of(context).showSnackBar(snackBar)

Failed assertion: line 1916 pos 14: 'children != null': is not true.?

I have been trying to run a flutter app and it keeps giving me Failed assertion error. below is my code. I just want to show the list the second code
class Post extends StatefulWidget {
final String postId;
final String ownerId;
final String username;
final String location;
final String description;
final String mediaUrl;
final dynamic likes;
Post({
this.postId,
this.ownerId,
this.username,
this.location,
this.description,
this.mediaUrl,
this.likes,
});
factory Post.fromDocument(DocumentSnapshot doc) {
return Post(
postId: doc['postId'],
ownerId: doc['ownerId'],
username: doc['username'],
location: doc['location'],
description: doc['description'],
mediaUrl: doc['mediaUrl'],
likes: doc['likes'],
);
}
int getLikeCount(likes) {
// if no likes, return 0
if (likes == null) {
return 0;
}
int count = 0;
// if the key is explicitly set to true, add a like
likes.values.forEach((val) {
if (val == true) {
count += 1;
}
});
return count;
}
#override
_PostState createState() => _PostState(
postId: this.postId,
ownerId: this.ownerId,
username: this.username,
location: this.location,
description: this.description,
mediaUrl: this.mediaUrl,
likes: this.likes,
likeCount: getLikeCount(this.likes),
);
}
class _PostState extends State<Post> {
final String postId;
final String ownerId;
final String username;
final String location;
final String description;
final String mediaUrl;
Int likeCount;
Map likes;
_PostState({
this.postId,
this.ownerId,
this.username,
this.location,
this.description,
this.mediaUrl,
this.likes,
this.likeCount,
});
Widget postingHeading() {
return FutureBuilder(
future:
FirebaseFirestore.instance.collection('User').doc(ownerId).get(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return CircularProgressIndicator();
}
return ListTile(
onTap: () => print('go check the url'),
leading: CircleAvatar(
backgroundImage:
NetworkImage("${snapshot.data.data()['photoUrl']}"),
),
title: Text('${snapshot.data.data()['username']}'),
subtitle: Text(location),
trailing: IconButton(
onPressed: () => print('deleting post'),
icon: Icon(Icons.more_vert),
),
);
});
}
Widget postImagePicture() {
return Stack(
alignment: Alignment.center,
children: [
Image.network(mediaUrl),
],
);
}
Widget postDetailsComment() {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
IconButton(icon: Icon(Icons.favorite_border), onPressed: () {}),
IconButton(icon: Icon(Icons.favorite_border), onPressed: () {})
],
),
Row(
children: <Widget>[
Container(
margin: EdgeInsets.only(left: 20.0),
child: Text(
"$likeCount likes",
style: TextStyle(
color: Colors.black,
fontWeight: FontWeight.bold,
),
),
),
],
),
Row(
children: [
Text(username),
SizedBox(
width: 1,
),
Flexible(child: Text(description))
],
)
],
);
}
#override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
postingHeading(),
postImagePicture(),
postDetailsComment(),
],
);
}
}
here is where I convert it to List... I don't even no what is wrong.... please help.... thanks in advance
class Profile extends StatefulWidget {
final String currentUser;
Profile({this.currentUser});
#override
_ProfileState createState() => _ProfileState();
}
class _ProfileState extends State<Profile> {
final _firestore = FirebaseFirestore.instance;
int postLenght;
List<Post> post;
bool pleaseWait;
#override
void initState() {
super.initState();
getUsersPicsDetails();
}
getUsersPicsDetails() async {
setState(() {
pleaseWait = false;
});
QuerySnapshot _getPost = await _firestore
.collection('post')
.doc(widget.currentUser)
.collection('userPost')
.orderBy('timeStamp', descending: true)
.get();
setState(() {
pleaseWait = true;
postLenght = _getPost.docs.length;
post = _getPost.docs.map((e) => Post.fromDocument(e)).toList();
print(postLenght);
});
}
fellowEditButtton({String test, Function press}) {
return TextButton(
onPressed: press,
child: Container(
alignment: Alignment.center,
height: 25,
width: 250,
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
border: Border.all(),
borderRadius: BorderRadius.circular(8.0)),
child: Text(
test,
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
)),
);
}
gettingUserprofile() {
return StreamBuilder(
stream: _firestore.collection('User').doc(widget.currentUser).snapshots(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(child: CircularProgressIndicator());
}
if (snapshot.connectionState != ConnectionState.active) {
return Text('this stuff en');
}
return ListView.builder(
itemCount: 1,
itemBuilder: (context, index) => Container(
padding: EdgeInsets.all(8.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
FullScreenWidget(
child: Hero(
tag: 'smallImage',
child: CircleAvatar(
backgroundImage: NetworkImage(
'${snapshot.data.data()['photoUrl']}'),
radius: 50.0,
),
),
),
Expanded(
flex: 1,
child: Column(
children: [
Row(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
children: [
topColum(buttom: 'Post', number: 1),
topColum(buttom: 'Fellowers', number: 2),
topColum(buttom: 'Following', number: 0),
],
),
Column(
crossAxisAlignment:
CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20.0,
),
child: widget.currentUser ==
_auth.currentUser.uid
? fellowEditButtton(
test: 'Edit Profile',
press: profileEditingButton)
: fellowEditButtton(
test: 'Fellow', press: () {})),
],
),
],
),
),
],
),
Divider(),
//! this is i call the list
//getUserPicture(),
getUserPicture(),
],
),
));
},
);
}
profileEditingButton() {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => EditProfile(
userProfile: widget.currentUser,
)));
}
Widget topColum({int number, String buttom}) {
return Column(
children: [
Text(
'$number',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
Text(
buttom,
style: TextStyle(
fontSize: 20,
),
),
],
);
}
//! this is the where i return the list
Widget getUserPicture() {
if (pleaseWait = false) {
return CircularProgressIndicator();
}
return Column(
children: post,
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: header(context, titleString: "Profile"),
body:
//Text('${widget.currentUser}')
gettingUserprofile());
// userPictursPost(),
}
}
I want to the get the post at the bottom... thanks in advance
below is the error image
enter image description here
When you get an error, please post it along with your question. When you are getting an error, it means that something is wrong with your code,and most likely not the flutter engine. Both are important for debugging, the error+your code.
Try changing this
QuerySnapshot _getPost = await _firestore
.collection('post')
.doc(widget.currentUser)
.collection('userPost')
.orderBy('timeStamp', descending: true)
.get();
setState(() {
pleaseWait = true;
postLenght = _getPost.docs.length;
post = _getPost.docs.map((e) => Post.fromDocument(e)).toList();
print(postLenght);
});
into this:
QuerySnapshot _getPost = await _firestore
.collection('post')
.doc(widget.currentUser)
.collection('userPost')
.orderBy('timeStamp', descending: true)
.get();
if(_getPost.docs.isNotEmpty){
List<Post> tempPost = _getPost.docs.map((e) => Post.fromDocument(e)).toList();
setState(() {
pleaseWait = true;
post =tempPost
print(postLenght);
});
}else{
print('The List is empty);}
You are not checking if the Query result has data or not. If it's empty, you will pass an empty List post down your tree, and you will get the error you are having.
For people facing similar issues, let me tell what I found in my code:
The error says that the children is null, not empty !
So if you are getting the children for the parent widget like Row or Column from a separate method, just check if you are returning the constructed child widget from the method.
Row(
children: getMyRowChildren()
)
.
.
.
getMyRowChildren(){
Widget childWidget = ... //constructed
return childWidget; //don't forget to return!
}
Else it would return null, which results in the children being null and we get the mentioned above error!

Flutter/dart accessing listdata within another list

I have a JSON response from RestApi like this:
[{"_id":"5eee4b3630cff64ee216e4fb",
"assignee_user_id":"5eab4a435647780af311d3a7",
"task_name":"Another Test work",
"task_description":"Test Description",
"assignee_name":"Test Assignee",
"status":"assigned",
"assignment_date":"20-06-2020 11:15",
"assignor_name":"Test Assignor",
"assignor_remarks":[{"commentTime":"21-06-2020 05:17","comment":"Test Comment"}]}]
and the podo class build is like this:
import 'dart:convert';
List<Work> workFromMap(String str) => List<Work>.from(json.decode(str).map((x) => Work.fromMap(x)));
String workToMap(List<Work> data) => json.encode(List<dynamic>.from(data.map((x) => x.toMap())));
class Work {
Work({
this.id,
this.assigneeUserId,
this.taskName,
this.taskDescription,
this.assigneeName,
this.status,
this.assignmentDate,
this.assignorName,
this.assignorRemarks,
});
String id;
String assigneeUserId;
String taskName;
String taskDescription;
String assigneeName;
String status;
String assignmentDate;
String assignorName;
List<AssignorRemark> assignorRemarks;
factory Work.fromMap(Map<String, dynamic> json) => Work(
id: json["_id"],
assigneeUserId: json["assignee_user_id"],
taskName: json["task_name"],
taskDescription: json["task_description"],
assigneeName: json["assignee_name"],
status: json["status"],
assignmentDate: json["assignment_date"],
assignorRemarks: List<AssignorRemark>.from(json["assignor_remarks"].map((x) => AssignorRemark.fromMap(x))),
);
Map<String, dynamic> toMap() => {
"_id": id,
"assignee_user_id": assigneeUserId,
"task_name": taskName,
"task_description": taskDescription,
"assignee_name": assigneeName,
"status": status,
"assignment_date": assignmentDate,
"assignor_remarks": List<dynamic>.from(assignorRemarks.map((x) => x.toMap())),
};
}
class AssignorRemark {
AssignorRemark({
this.commentTime,
this.comment,
});
String commentTime;
String comment;
factory AssignorRemark.fromMap(Map<String, dynamic> json) => AssignorRemark(
commentTime: json["commentTime"],
comment: json["comment"],
);
Map<String, dynamic> toMap() => {
"commentTime": commentTime,
"comment": comment,
};
}
and my api call is like this:
import './work.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
Future<List<Work>> fetchWork() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final userid = prefs.getString('user_id');
final response =
await http.get("https://myserver/api/work-monitor/work/?id=$userid",
);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
print(response.body);
return workFromMap(response.body);
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load Profile');
}
}
and my screen file content is like this:
class WorkMonitor extends StatefulWidget {
#override
_WorkMonitorState createState() => _WorkMonitorState();
}
class _WorkMonitorState extends State<WorkMonitor> {
Future<Work> futureMyWorkMonitor;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Work Monitor'),
),
body: Container(
child: FutureBuilder(
future: fetchWork(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
shrinkWrap: true,
itemBuilder: (BuildContext context, index) {
Work work = snapshot.data[index];
// return Text('${work.taskName}');
return Container(
padding: const EdgeInsets.all(5),
width: double.infinity,
child: Card(
child: Container(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Subject: ',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
)),
Text(
'${work.taskName}',
style: TextStyle(
fontSize: 20, color: Colors.black),
),
]),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Description: ',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
)),
Flexible(
child: Text(
'${work.taskDescription}',
)),
]),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Assigned Date: ',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
)),
Text(
'${work.assignmentDate}' ?? " ",
style: TextStyle(fontSize: 16),
),
]),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('Description: ',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
)),
Text(
'${work.status}' ?? "Gone",
style: TextStyle(fontSize: 16),
),
]),
Text(
'${work.assignorRemarks[index]}'
),
]),
),
),
);
},
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
);
}
}
Only issue I am facing here is, I am not able to access/iterate over comments. How I can do this?
You need to somehow also iterate through each comment inside each list item. One way (probably only for a small amount of remarks), would be a Column (or if you need scrolling you could use a SingleChildScrollView with a Column). Example:
Column(
children: work.assignorRemarks.map<Text>((remark) {
return Text(remark.comment);
}).toList()
)
If you have a lot of items you are probably better of using a nested ListView