I was updating UI of my Data table by assigning fetch result of an api and assigning it to Getx List. this works fine but when trying to delete, create and update data the ui is not responding. according to my code i was calling the get controller and assigning the value is by using initState() . so this only call it once initially. when i try to delete the whole page will reload even the widgets outside the data table. but those widgets display the data from the same api result. I'll post my code now.
user model
import 'role_model.dart';
import 'dart:convert';
List<User> usersFromJsonFromModel(String str) =>
List<User>.from(json.decode(str).map((json) => User.fromJson(json)));
String usersToJsonFromModel(List<User> data) =>
json.encode(List<dynamic>.from(data.map((e) => e.toJson())));
class User {
int? id;
String? name;
String? username;
String? email;
String? emailVerifiedAt;
String? twoFactorConfirmedAt;
String? currentTeamId;
String? profilePhotoPath;
String? warehouseId;
String? createdAt;
String? updatedAt;
String? profilePhotoUrl;
List<Roles>? roles;
User(
{this.id,
this.name,
this.username,
this.email,
this.emailVerifiedAt,
this.twoFactorConfirmedAt,
this.currentTeamId,
this.profilePhotoPath,
this.warehouseId,
this.createdAt,
this.updatedAt,
this.profilePhotoUrl,
this.roles});
User.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
username = json['username'];
email = json['email'];
emailVerifiedAt = json['email_verified_at'];
twoFactorConfirmedAt = json['two_factor_confirmed_at'];
currentTeamId = json['current_team_id'];
profilePhotoPath = json['profile_photo_path'];
warehouseId = json['warehouse_id'];
createdAt = json['created_at'];
updatedAt = json['updated_at'];
profilePhotoUrl = json['profile_photo_url'];
if (json['roles'] != null) {
roles = <Roles>[];
json['roles'].forEach((v) {
roles!.add(Roles.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['id'] = id;
data['name'] = name;
data['username'] = username;
data['email'] = email;
data['email_verified_at'] = emailVerifiedAt;
data['two_factor_confirmed_at'] = twoFactorConfirmedAt;
data['current_team_id'] = currentTeamId;
data['profile_photo_path'] = profilePhotoPath;
data['warehouse_id'] = warehouseId;
data['created_at'] = createdAt;
data['updated_at'] = updatedAt;
data['profile_photo_url'] = profilePhotoUrl;
if (roles != null) {
data['roles'] = roles!.map((v) => v.toJson()).toList();
}
return data;
}
}
API call
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:store_mgt_project/controller/user_controller.dart';
import 'package:store_mgt_project/utils/const.dart';
import '../users_model.dart';
Future<List<User>> fetchUsers() async {
Uri url = Uri.parse("${BASE_URL}user");
final response = await http.get(url, headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer $TOKEN',
});
var userview = <User>[];
if (response.statusCode == 200) {
var jsonres = json.decode(response.body);
for (var res in jsonres) {
userview.add(User.fromJson(res));
}
}
return userview;
}
Future createUser(String name, String username, String email, String password,
String roles) async {
final UserController userController = Get.put(UserController());
Uri url = Uri.parse("${BASE_URL}user");
var userview = <User>[];
var jsonres;
final response = await http
.post(url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer $TOKEN',
},
body: jsonEncode(<String, dynamic>{
'name': name,
'email': email,
'username': username,
'password': password,
'roles': roles.toString()
}))
.then((value) => {
if (value.statusCode == 200)
{
userController.setuserPress(true),
Get.back(),
userController.setuserPress(false)
}
else if (value.statusCode == 422)
{
Get.snackbar("Cannot create $name",
"username or email is already been taken",
duration: const Duration(seconds: 2),
colorText: Colors.black,
backgroundColor: Colors.white),
}
else
{
throw Get.snackbar("failed", "failed to create user $name",
duration: const Duration(seconds: 2),
colorText: Colors.black,
backgroundColor: Colors.white),
}
});
}
Future updateUser(var id, String name, String username, String email,
String password, String roles) async {
final UserController userController = Get.put(UserController());
Uri url = Uri.parse("${BASE_URL}user/$id");
final response = await http
.patch(
url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer $TOKEN',
},
body: jsonEncode(<String, String>{
'name': name,
'email': email,
'username': username,
// 'password': password,
'roles': roles
}),
)
.then((value) => {
if (value.statusCode == 200)
{
userController.setuserPress(true),
Get.back(),
userController.setuserPress(false)
}
else
{
throw Get.snackbar("failed", "failed to update user $name",
duration: const Duration(seconds: 2),
colorText: Colors.black,
backgroundColor: Colors.white),
}
});
}
Future deleteUser(var id) async {
final UserController userController = Get.put(UserController());
Uri url = Uri.parse("${BASE_URL}user/$id");
final response = await http.delete(url, headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer $TOKEN',
}).then((value) => {
userController.setuserPress(true),
//usersToJsonFromModel(jsonDecode(value.body)),
userController.setuserDelete(jsonDecode(value.body)),
Get.snackbar(
"Deleted $id", "You've deleted id of ${jsonDecode(value.body)}",
duration: const Duration(seconds: 30),
colorText: Colors.black,
backgroundColor: Colors.white),
});
}
userController page
import 'package:get/get.dart';
import 'package:store_mgt_project/models/api/user_api.dart';
import 'package:store_mgt_project/models/users_model.dart';
class UserController extends GetxController {
final _userPress = false.obs;
get userPress => _userPress.value;
set userPress(value) => _userPress.value = value;
List<User> _user = <User>[].obs;
List<User> _userDisplay = <User>[].obs;
get user => _user;
set user(value) => _user = value;
get userDisplay => _userDisplay;
set userDisplay(value) => _userDisplay = value;
setuserPress(bool value) {
userPress = value;
update();
}
setuser(List<User> value) {
user = value;
update();
}
setuserDelete(User value) {
_user.remove(value);
_user.join();
refresh();
}
setuserDisplay(List<User> value) {
userDisplay = value;
update();
}
}
finally homepage
DataTableSource dataSource(List<User> userList) =>
MyTable(datasList: userList, context: context);
final UserController userController = Get.put(UserController());
#override
void initState() {
// TODO: implement initState
super.initState();
setState(() {
fetchUsers().then((value) => {
setState(() {
userController.setuser(value);
userController.setuserDisplay(userController.user);
})
});
});
}
...
return SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: width * 0.02),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Obx(() {
return userController.userPress
? const Center(child: CircularProgressIndicator())
: StreamBuilder(
stream: fetchUsers().asStream(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return Container(
height: height * 0.15,
width: width / 1.5,
decoration: BoxDecoration(
color: const Color.fromRGBO(30, 119, 66, 1),
borderRadius: BorderRadius.circular(10)),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: width * 0.015),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${snapshot.data.length}",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: height * 0.05),
),
SizedBox(
height: height * 0.008,
),
Text(
"Total Users",
style: TextStyle(
color: Colors.white,
fontSize: height * 0.02,
fontWeight: FontWeight.w600),
)
],
),
),
);
} else {
return CircularProgressIndicator();
}
});
}),
SizedBox(
height: height * 0.02,
),
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(15),
border: Border.all(color: Colors.grey)),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: width * 0.01),
child: Obx(() {
return userController.userPress
? const Center(child: CircularProgressIndicator())
: PaginatedDataTable(
sortColumnIndex: sortColumnIndex,
sortAscending: isAscending,
columns: const [
DataColumn(
label: Text("Id"),
),
DataColumn(label: Text("Name")),
DataColumn(
label: Text("Username"),
),
DataColumn(label: Text("Email")),
DataColumn(label: Text("Roles")),
DataColumn(label: Text("Actions")),
],
header: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: width * 0.6,
child: TextField(
decoration:
InputDecoration(hintText: "search"),
onChanged: (text) {
text = text.toLowerCase();
setState(() {
userController.userDisplay =
userController.user
.where((element) {
var _name =
element.name!.toLowerCase();
var _username =
element.username!.toLowerCase();
return _name.contains(text) ||
_username.contains(text);
}).toList();
});
},
),
),
MaterialButton(
onPressed: () {
showMaterialModalBottomSheet(
context: context,
builder: (context) => SizedBox(
height: height * 0.9,
child: BottomSheetWidget(),
),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight:
Radius.circular(15))));
},
color: const Color.fromRGBO(30, 119, 66, 1),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10)),
child: Text(
"Add User",
style: TextStyle(
color: Colors.white,
fontSize: width * 0.03),
),
)
],
),
source: dataSource(userController.userDisplay));
})),
)
],
),
));
...
class MyTable extends DataTableSource {
MyTable({required this.datasList, required this.context});
final List<User> datasList;
BuildContext context;
final UserController userController = Get.put(UserController());
Widget Button(String title, Color color, String id, bool delete, String? name,
String? email, String? username, String? role) {
return MaterialButton(
onPressed: () async {
final height = MediaQuery.of(context).size.height;
delete
? await deleteUser(id)
: await showMaterialModalBottomSheet(
context: context,
builder: (context) => SizedBox(
height: height * 0.9,
child: BottomSheetEditWidget(
name: name,
email: email,
username: username,
role: role,
id: id,
),
),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15))));
userController.setuserPress(false);
},
child: Text(
title,
style: const TextStyle(color: Colors.white),
),
color: color,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
);
}
#override
DataRow? getRow(int index) {
return DataRow.byIndex(index: index, cells: [
DataCell(Text("${index + 1}".toString())),
DataCell(
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 100),
child: Text(
datasList[index].name.toString(),
overflow: TextOverflow.ellipsis,
),
),
),
DataCell(ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 100),
child: Text(
datasList[index].username.toString(),
overflow: TextOverflow.ellipsis,
))),
DataCell(ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 100),
child: Text(
datasList[index].email.toString(),
overflow: TextOverflow.ellipsis,
))),
DataCell(ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 100),
child: Text(
datasList[index].roles![0].name.toString(),
overflow: TextOverflow.ellipsis,
))),
DataCell(Row(
children: [
Button(
"Edit",
Colors.lightBlue,
datasList[index].id.toString(),
false,
datasList[index].name,
datasList[index].email,
datasList[index].username,
datasList[index].roles![0].name),
const SizedBox(
width: 5,
),
Button(
"Delete",
Colors.red,
datasList[index].id.toString(),
true,
datasList[index].name,
datasList[index].email,
datasList[index].username,
datasList[index].roles![0].name),
],
)),
]);
}
#override
bool get isRowCountApproximate => false;
#override
int get rowCount => datasList.length;
#override
int get selectedRowCount => 0;
}
so how can i use crud operation and update ui at the same time?
[update of the new approach]
Obx(() {
return userController.userPress
? const Center(child: CircularProgressIndicator())
: StreamBuilder(
stream: fetchUsers().asStream(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return Container(
height: height * 0.15,
width: width / 1.5,
decoration: BoxDecoration(
color: const Color.fromRGBO(30, 119, 66, 1),
borderRadius: BorderRadius.circular(10)),
child: Padding(
padding: EdgeInsets.symmetric(
horizontal: width * 0.015),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"${snapshot.data.length}",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
fontSize: height * 0.05),
),
SizedBox(
height: height * 0.008,
),
Text(
"Total Users",
style: TextStyle(
color: Colors.white,
fontSize: height * 0.02,
fontWeight: FontWeight.w600),
)
],
),
),
);
} else {
return CircularProgressIndicator();
}
});
}),
You can use Obx for this.
For this example I'll use the UserPress variable to explain to you. And you create an appropriate variable to do the action...
In your Ui you only needed put an obx in place that you want update:
Obx(() =>
(userController.userPress)
?
return MaterialButton(
onPressed: () async {
final height = MediaQuery.of(context).size.height;
delete
? await deleteUser(id)
: await showMaterialModalBottomSheet(
context: context,
builder: (context) => SizedBox(
height: height * 0.9,
child: BottomSheetEditWidget(
name: name,
email: email,
username: username,
role: role,
id: id,
),
),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15))));
userController.setuserPress(false);
}
: return MaterialButton(
onPressed: null,
),
),
In this example, if userPress = true the button can be clicked.
If userPress = false, the button don't be clicked.
Related
So basically it is a comment box that comes with or without an image. So the img url is nullable. I am trying to deserialize it.
Here is the Comment Model.
// To parse this JSON data, do
//
// final productComment = productCommentFromJson(jsonString);
import 'package:meta/meta.dart';
import 'dart:convert';
ProductComment productCommentFromJson(String str) =>
ProductComment.fromJson(json.decode(str));
String productCommentToJson(ProductComment data) => json.encode(data.toJson());
class ProductComment {
const ProductComment({
this.id,
this.comment,
this.owner,
this.productId,
this.imgsUrl,
this.imgsName,
this.createdAt,
});
final int? id;
final String? comment;
final String? owner;
final int? productId;
final List<String>? imgsUrl;
final List<String>? imgsName;
final DateTime? createdAt;
factory ProductComment.fromJson(Map<String, dynamic> json) => ProductComment(
id: json["id"],
comment: json["comment"],
owner: json["owner"],
productId: json["product_id"],
// imgsUrl: List<String>.from(json["imgs_url"].map((x) => x)),
imgsUrl: json["imgs_url"] == null
? null
: List<String>.from(json["imgs_url"].map((x) => x)),
imgsName: List<String>.from(json["imgs_name"].map((x) => x)),
createdAt: DateTime.parse(json["created_at"]),
);
Map<String, dynamic> toJson() => {
"id": id,
"comment": comment,
"owner": owner,
"product_id": productId,
"imgs_name": List<dynamic>.from(imgsName!.map((x) => x)),
// "imgs_url": List<dynamic>.from(imgsUrl!.map((x) => x)),
"imgs_url":
imgsUrl == null ? null : List<dynamic>.from(imgsUrl!.map((x) => x)),
"created_at": createdAt!.toIso8601String(),
};
}
I am trying to decode the imgs_url wether it has an image or not.
Wrap(
children: <Widget>[
Card(
margin: const EdgeInsets.all(8),
child: Container(
decoration: const BoxDecoration(
color: Color.fromARGB(255, 221, 217, 217),
borderRadius: BorderRadius.all(Radius.circular(10))),
padding:
const EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
child: Column(
children: <Widget>[
Container(
width: double.infinity,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: Isize().borderRadius),
child: Text(
widget.comment,
overflow: flag ? TextOverflow.ellipsis : null,
style: const TextStyle(
fontSize: 15,
),
),
),
InkWell(
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Text(
flag ? "show more" : "show less",
style: const TextStyle(
color: lilac, fontFamily: 'FiraCode'),
),
],
),
onTap: () {
setState(() {
flag = !flag;
});
},
),
Builder(builder: (context) {
if (widget.image!.isNotEmpty) {
return Container();
}
return SizedBox(
height: 100,
child: GridView.count(
mainAxisSpacing: 1,
crossAxisSpacing: 1,
crossAxisCount: 6,
children:
List.generate(widget.image!.length, (index) {
return Center(
child: CachedNetworkImage(
memCacheHeight: 20,
memCacheWidth: 20,
imageUrl: widget.image![index]!,
imageBuilder: (context, imageProvider) =>
Container(
width: 50,
height: 50,
decoration: BoxDecoration(
borderRadius: Isize().borderRadius,
image: DecorationImage(
image: imageProvider,
fit: BoxFit.cover,
colorFilter: const ColorFilter.mode(
Colors.black45,
BlendMode.darken,
),
),
),
),
placeholder: (context, url) => Center(
child: Image.asset(IhIcon().ihemyilogo),
),
errorWidget: (context, url, error) =>
const Icon(
Icons.error,
color: Colors.deepOrangeAccent,
),
),
);
}),
));
})
],
)),
),
],
);
For the repository which also has a map:
Future<List<ProductComment>> productCommentsByID(int productID) async {
final uri = Uri.parse('${ep.mainUrl}/api/v1/products/comments/$productID');
debugPrint('Comments by Product ID = $uri');
final response = await http.get(uri);
if (response.statusCode == 200) {
if (response.body.isNotEmpty) {
final comments = List<ProductComment>.from(
json.decode(response.body).map(
(data) => ProductComment.fromJson(data),
),
);
debugPrint('Fucker says hello ${comments.toString()}');
return comments;
} else {
throw ErrorEmptyResponse();
}
} else {
throw ErrorGettingGames(
'Error getting Comments With Product ID $productID');
}
}
Please i need help.I cant understand which of the maps that has the issue. I just get this error:
I/flutter ( 3603): Transition { currentState: CommentState([], Instance of 'ProductComment', 0, CommentStatus.error), event: CommentsByProductID(1), nextState: CommentError(NoSuchMethodError: The method 'map' was called on null.
I/flutter ( 3603): Receiver: null
I think your imgsName may be null too, try change this:
imgsName: List<String>.from(json["imgs_name"].map((x) => x)),
to this:
imgsName: json["imgs_name"] == null? null: List<String>.from(json["imgs_name"].map((x) => x)),
I'm using FutureBuilder for fetching API request. but it only builds once and it doesn't update ui unless hot reloded. i aslo tried StreamBuilder and convert the future method to stream. so how can i update my ui. please i already asked this question and got no answer. whay is this happening?
model
import 'dart:convert';
List<User> usersFromJson(String str) =>
List<User>.from(json.decode(str).map((json) => User.fromJson(json)));
String usersToJson(List<User> data) =>
json.encode(List<dynamic>.from(data.map((e) => e.toJson())));
class User {
int? id;
String? name;
String? username;
String? email;
String? role;
User({this.id, this.name, this.username, this.email, this.role});
#override
toString() => 'User: $name';
factory User.fromJson(Map<String, dynamic> json) => User(
email: json['email'],
name: json['name'],
id: json['id'],
username: json['username'],
role: json['role']);
Map<String, dynamic> toJson() =>
{"name": name, "username": username, "email": email, "role": role};
}
api call
Future fetchUsers() async {
Uri url = Uri.parse("${BASE_URL}user");
final response = await http.get(url, headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer $TOKEN',
});
var userview = <User>[];
if (response.statusCode == 200) {
var jsonres = json.decode(response.body);
for (var res in jsonres) {
userview.add(User.fromJson(res));
}
}
return userview;
}
Future createUser(String name, String username, String email, String password,
String roles) async {
Uri url = Uri.parse("${BASE_URL}user");
final response = await http
.post(url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
'Authorization': 'Bearer $TOKEN',
},
body: jsonEncode(<String, dynamic>{
'name': name,
'email': email,
'username': username,
'password': password,
'roles': roles
}))
.then((value) => fetchUsers());
}
and my page
StreamBuilder(
stream: fetchUsers().asStream(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: [
PaginatedDataTable(
sortColumnIndex: sortColumnIndex,
sortAscending: isAscending,
columns: [
DataColumn(
label: const Text("Id"),
onSort: onSort),
DataColumn(
label: const Text("Name"),
onSort: onSort),
DataColumn(
label: const Text("Username"),
onSort: onSort),
DataColumn(
label: const Text("Email"),
onSort: onSort),
DataColumn(
label: const Text("Roles"),
onSort: onSort),
DataColumn(
label: const Text("Actions"),
onSort: onSort),
],
header: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: [
Text(
"Manage Users",
style: TextStyle(
fontSize: width * 0.04,
fontWeight: FontWeight.normal),
),
MaterialButton(
onPressed: () {
showMaterialModalBottomSheet(
context: context,
builder: (context) => SizedBox(
height: height * 0.9,
child:
BottomSheetWidget(),
),
shape:
const RoundedRectangleBorder(
borderRadius:
BorderRadius.only(
topLeft: Radius
.circular(
15),
topRight: Radius
.circular(
15))));
},
color: const Color.fromRGBO(
30, 119, 66, 1),
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(10)),
child: Text(
"Add User",
style: TextStyle(
color: Colors.white,
fontSize: width * 0.03),
),
)
],
),
source: dataSource(
snapshot.data! as List<User>))
],
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return const Center(
child: CircularProgressIndicator());
})
additionally i'm using paginated datatable and this is the code for that.(it's related)
in the page
DataTableSource dataSource(List<User> userList) =>
MyTable(datasList: userList, context: context);
and the datasource page i'm calling the createUser here.
class MyTable extends DataTableSource {
MyTable({required this.datasList, required this.context});
final List<User> datasList;
BuildContext context;
Widget Button(String title, Color color, String id) {
return MaterialButton(
onPressed: () {
//deleteUser(id);
//updateUser(id, title);
createUser("name2", "user2", "email2#email.com", "password", "Admin");
},
child: Text(
title,
style: const TextStyle(color: Colors.white),
),
color: color,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
);
}
#override
DataRow? getRow(int index) {
return DataRow.byIndex(index: index, cells: [
DataCell(Text(datasList[index].id.toString())),
DataCell(ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 100),
child: Text(
datasList[index].name.toString(),
overflow: TextOverflow.ellipsis,
),
)),
DataCell(ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 100),
child: Text(
datasList[index].username.toString(),
overflow: TextOverflow.ellipsis,
))),
DataCell(ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 100),
child: Text(
datasList[index].email.toString(),
overflow: TextOverflow.ellipsis,
))),
DataCell(ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 100),
child: Text(
datasList[index].role.toString(),
overflow: TextOverflow.ellipsis,
))),
DataCell(Row(
children: [
Button("Edit", Colors.lightBlue, datasList[index].id.toString()),
const SizedBox(
width: 5,
),
Button("Delete", Colors.red, datasList[index].id.toString()),
],
)),
]);
}
#override
bool get isRowCountApproximate => false;
#override
int get rowCount => datasList.length;
#override
int get selectedRowCount => 0;
}
StreamBuilder class
Widget rebuilding is scheduled by each interaction, using
State.setState, but is otherwise decoupled from the timing of the
stream.
i see the code and can't found any issue. but im bit doubt with asStream().
as documentation said: asStream method:
Creates a Stream containing the result of this future.
maybe you can try with this : stream.fromFuture
found the issue here: https://stackoverflow.com/a/55169382/12838877
since your fetch method is not common , it is a Future method that return value of Future value.
Stream.fromFuture(fetchUsers)
it's just my doubt. hope solve the problem. because it's too long to explain in the comments, so I'll put it in the answer section
I wanted to do the classic login with a JSON but it does not work, in the sense that even if you put the wrong credentials from 200 (ie ok). I only put a JSON file (login.json), at this point I doubt that we need to create a particular environment to manage the JSON .. the server where the JSON resides is a HOSTING on the L.A.M.P. (Linux / Apache / MySQL / PHP)
login_page.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_http_post_request/api/api_service.dart';
import 'package:flutter_http_post_request/model/login_model.dart';
import '../ProgressHUD.dart';
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
bool hidePassword = true;
bool isApiCallProcess = false;
GlobalKey<FormState> globalFormKey = GlobalKey<FormState>();
LoginRequestModel loginRequestModel;
final scaffoldKey = GlobalKey<ScaffoldState>();
#override
void initState() {
super.initState();
loginRequestModel = new LoginRequestModel();
}
#override
Widget build(BuildContext context) {
return ProgressHUD(
child: _uiSetup(context),
inAsyncCall: isApiCallProcess,
opacity: 0.3,
);
}
Widget _uiSetup(BuildContext context) {
return Scaffold(
key: scaffoldKey,
backgroundColor: Theme.of(context).accentColor,
body: SingleChildScrollView(
child: Column(
children: <Widget>[
Stack(
children: <Widget>[
Container(
width: double.infinity,
padding: EdgeInsets.symmetric(vertical: 30, horizontal: 20),
margin: EdgeInsets.symmetric(vertical: 85, horizontal: 20),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Theme.of(context).primaryColor,
boxShadow: [
BoxShadow(
color: Theme.of(context).hintColor.withOpacity(0.2),
offset: Offset(0, 10),
blurRadius: 20)
],
),
child: Form(
key: globalFormKey,
child: Column(
children: <Widget>[
SizedBox(height: 25),
Text(
"Login",
style: Theme.of(context).textTheme.headline2,
),
SizedBox(height: 20),
new TextFormField(
keyboardType: TextInputType.emailAddress,
onSaved: (input) => loginRequestModel.email = input,
validator: (input) => !input.contains('#')
? "Email Id should be valid"
: null,
decoration: new InputDecoration(
hintText: "Email Address",
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context)
.accentColor
.withOpacity(0.2))),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).accentColor)),
prefixIcon: Icon(
Icons.email,
color: Theme.of(context).accentColor,
),
),
),
SizedBox(height: 20),
new TextFormField(
style:
TextStyle(color: Theme.of(context).accentColor),
keyboardType: TextInputType.text,
onSaved: (input) =>
loginRequestModel.password = input,
validator: (input) => input.length < 3
? "Password should be more than 3 characters"
: null,
obscureText: hidePassword,
decoration: new InputDecoration(
hintText: "Password",
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context)
.accentColor
.withOpacity(0.2))),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).accentColor)),
prefixIcon: Icon(
Icons.lock,
color: Theme.of(context).accentColor,
),
suffixIcon: IconButton(
onPressed: () {
setState(() {
hidePassword = !hidePassword;
});
},
color: Theme.of(context)
.accentColor
.withOpacity(0.4),
icon: Icon(hidePassword
? Icons.visibility_off
: Icons.visibility),
),
),
),
SizedBox(height: 30),
FlatButton(
padding: EdgeInsets.symmetric(
vertical: 12, horizontal: 80),
onPressed: () {
if (validateAndSave()) {
print(loginRequestModel.toJson());
setState(() {
isApiCallProcess = true;
});
APIService apiService = new APIService();
apiService.login(loginRequestModel).then((value) {
if (value != null) {
setState(() {
isApiCallProcess = false;
});
if (value.token.isNotEmpty) {
final snackBar = SnackBar(
content: Text("Login Successful"));
scaffoldKey.currentState
.showSnackBar(snackBar);
} else {
final snackBar =
SnackBar(content: Text(value.error));
scaffoldKey.currentState
.showSnackBar(snackBar);
}
}
});
}
},
child: Text(
"Login",
style: TextStyle(color: Colors.white),
),
color: Theme.of(context).accentColor,
shape: StadiumBorder(),
),
SizedBox(height: 15),
],
),
),
),
],
),
],
),
),
);
}
bool validateAndSave() {
final form = globalFormKey.currentState;
if (form.validate()) {
form.save();
return true;
}
return false;
}
}
api_service.dart
import 'package:http/http.dart' as http;
import 'dart:convert';
import '../model/login_model.dart';
class APIService {
Future<LoginResponseModel> login(LoginRequestModel requestModel) async {
//String url = "https://reqres.in/api/login";
String url = "https://www.nextesa.it/api/login";
final response = await http.post(url, body: requestModel.toJson());
if (response.statusCode == 200 || response.statusCode == 400) {
return LoginResponseModel.fromJson(
json.decode(response.body),
);
} else {
throw Exception('Failed to load data!');
}
}
}
login_mode.dart
class LoginResponseModel {
final String token;
final String error;
LoginResponseModel({this.token, this.error});
factory LoginResponseModel.fromJson(Map<String, dynamic> json) {
return LoginResponseModel(
token: json["token"] != null ? json["token"] : "",
error: json["error"] != null ? json["error"] : "",
);
}
}
class LoginRequestModel {
String email;
String password;
LoginRequestModel({
this.email,
this.password,
});
Map<String, dynamic> toJson() {
Map<String, dynamic> map = {
'email': email.trim(),
'password': password.trim(),
};
return map;
}
}
File json
{
"email": "eve.holt#reqres.in",
"password": "cityslicka"
}
My Json response status code is 200 and the object is also created but when I try to bind it with UI in ListView it doesn't show anything. I have used quicktype.io to parse my json data that I received from the API call.
Here's my API_manager.dart file:
import 'package:http/http.dart' as http;
import 'package:aritic/models/contactsModel.dart';
// ignore: camel_case_types
class API_Manager {
Future<ContactsModel> getContacts() async {
var client = http.Client();
var contactsModel;
String contacts_url =
'https://exampleapi.com';
String basicAuth = 'Basic auth key example';
try {
var response = await client.get(contacts_url,
headers: <String, String>{'authorization': basicAuth});
print(response.statusCode);
if (response.statusCode == 200) {
var jsonString = response.body;
var jsonMap = json.decode(jsonString);
contactsModel = contactsModel.fromJson(jsonMap);
}
} catch (Exception) {
return contactsModel;
}
return contactsModel;
}
}
My UI code:
import 'package:aritic/models/contactsModel.dart';
import 'package:aritic/services/api_manager.dart';
class ContactsPage extends StatefulWidget {
#override
_ContactsPageState createState() => _ContactsPageState();
}
class _ContactsPageState extends State<ContactsPage>
with SingleTickerProviderStateMixin {
Future<ContactsModel> _contactsModel;
bool isSearching = false;
TabController _tabController;
#override
void initState() {
// TODO: implement initState
super.initState();
_tabController = TabController(length: 2, initialIndex: 0, vsync: this);
_tabController.addListener(_handleTabIndex);
}
#override
void dispose() {
_tabController.removeListener(_handleTabIndex);
_tabController.dispose();
super.dispose();
}
void _handleTabIndex() {
setState(() {});
}
#override
Widget build(BuildContext context) {
_contactsModel = API_Manager().getContacts();
return DefaultTabController(
length: 2,
child: Scaffold(
appBar: AppBar(
title: Text('Contacts'),
bottom: PreferredSize(
child: Align(
alignment: Alignment.centerLeft,
child: TabBar(
controller: _tabController,
isScrollable: true,
unselectedLabelColor: Colors.white.withOpacity(0.3),
indicatorColor: Colors.white,
tabs: [
Tab(
child: Text('Contacts'),
),
Tab(
child: Text('Companies'),
)
],
),
),
preferredSize: Size.fromHeight(40.0)),
actions: <Widget>[
Padding(
padding: const EdgeInsets.only(right: 16.0),
child: IconButton(
icon: Icon(Icons.search),
color: Colors.white,
onPressed: () {},
),
),
],
),
body: TabBarView(controller: _tabController, children: <Widget>[
Container(
height: double.infinity,
child: FutureBuilder<ContactsModel>(
future: _contactsModel,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
padding: const EdgeInsets.all(6),
itemCount: snapshot.data.contacts.length,
itemBuilder: (context, index) {
var contact = snapshot.data.contacts[index];
return Container(
height: 100,
color: Colors.white,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(contact.owner.username,
style: TextStyle(fontSize: 16))
],
),
);
});
} else
return Center(child: CircularProgressIndicator());
})),
Container(
height: double.infinity,
child: ListView(
padding: const EdgeInsets.all(6),
children: <Widget>[
InkWell(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (_) => ViewCompany()));
},
child: Container(
height: 50,
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'Example company',
style: TextStyle(fontSize: 16),
),
Text(
'Example company',
style: TextStyle(fontSize: 14),
)
],
),
),
),
SizedBox(
height: 5,
),
InkWell(
onTap: () {
Navigator.push(context,
MaterialPageRoute(builder: (_) => ViewCompany()));
},
child: Container(
height: 50,
color: Colors.white,
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
'example',
style: TextStyle(fontSize: 16),
),
Text(
'example',
style: TextStyle(fontSize: 14),
)
],
),
),
),
],
)),
]),
floatingActionButton: _bottomButtons(),
));
}
Widget _bottomButtons() {
return _tabController.index == 0
? FloatingActionButton(
shape: StadiumBorder(),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return AddContacts();
}));
},
backgroundColor: Colors.cyan,
child: Icon(
Icons.person_add,
color: Colors.white,
))
: FloatingActionButton(
shape: StadiumBorder(),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (_) {
return AddCompany();
}));
},
backgroundColor: Colors.cyan,
child: Icon(
Icons.add,
color: Colors.white,
),
);
}
}
Created model(contactsModel.dart):
import 'dart:convert';
ContactsModel contactsModelFromJson(String str) => ContactsModel.fromJson(json.decode(str));
String contactsModelToJson(ContactsModel data) => json.encode(data.toJson());
class ContactsModel {
ContactsModel({
this.total,
this.contacts,
});
String total;
Map<String, Contact> contacts;
factory ContactsModel.fromJson(Map<String, dynamic> json) => ContactsModel(
total: json["total"],
contacts: Map.from(json["contacts"]).map((k, v) => MapEntry<String, Contact>(k, Contact.fromJson(v))),
);
Map<String, dynamic> toJson() => {
"total": total,
"contacts": Map.from(contacts).map((k, v) => MapEntry<String, dynamic>(k, v.toJson())),
};
}
class Contact {
Contact({
this.isPublished,
this.dateAdded,
this.dateModified,
this.createdBy,
this.createdByUser,
this.modifiedBy,
this.modifiedByUser,
this.id,
this.points,
this.color,
this.fields,
this.lastActive,
this.owner,
this.ipAddresses,
this.tags,
this.utmtags,
this.stage,
this.dateIdentified,
this.preferredProfileImage,
this.doNotContact,
this.frequencyRules,
});
bool isPublished;
DateTime dateAdded;
DateTime dateModified;
int createdBy;
String createdByUser;
int modifiedBy;
String modifiedByUser;
int id;
int points;
dynamic color;
Fields fields;
dynamic lastActive;
Owner owner;
List<dynamic> ipAddresses;
List<Tag> tags;
List<dynamic> utmtags;
Stage stage;
DateTime dateIdentified;
dynamic preferredProfileImage;
List<dynamic> doNotContact;
List<dynamic> frequencyRules;
factory Contact.fromJson(Map<String, dynamic> json) => Contact(
isPublished: json["isPublished"],
dateAdded: DateTime.parse(json["dateAdded"]),
dateModified: json["dateModified"] == null ? null : DateTime.parse(json["dateModified"]),
createdBy: json["createdBy"],
createdByUser: json["createdByUser"],
modifiedBy: json["modifiedBy"] == null ? null : json["modifiedBy"],
modifiedByUser: json["modifiedByUser"] == null ? null : json["modifiedByUser"],
id: json["id"],
points: json["points"],
color: json["color"],
fields: Fields.fromJson(json["fields"]),
lastActive: json["lastActive"],
owner: Owner.fromJson(json["owner"]),
ipAddresses: List<dynamic>.from(json["ipAddresses"].map((x) => x)),
tags: List<Tag>.from(json["tags"].map((x) => Tag.fromJson(x))),
utmtags: List<dynamic>.from(json["utmtags"].map((x) => x)),
stage: Stage.fromJson(json["stage"]),
dateIdentified: DateTime.parse(json["dateIdentified"]),
preferredProfileImage: json["preferredProfileImage"],
doNotContact: List<dynamic>.from(json["doNotContact"].map((x) => x)),
frequencyRules: List<dynamic>.from(json["frequencyRules"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"isPublished": isPublished,
"dateAdded": dateAdded.toIso8601String(),
"dateModified": dateModified == null ? null : dateModified.toIso8601String(),
"createdBy": createdBy,
"createdByUser": createdByUser,
"modifiedBy": modifiedBy == null ? null : modifiedBy,
"modifiedByUser": modifiedByUser == null ? null : modifiedByUser,
"id": id,
"points": points,
"color": color,
"fields": fields.toJson(),
"lastActive": lastActive,
"owner": owner.toJson(),
"ipAddresses": List<dynamic>.from(ipAddresses.map((x) => x)),
"tags": List<dynamic>.from(tags.map((x) => x.toJson())),
"utmtags": List<dynamic>.from(utmtags.map((x) => x)),
"stage": stage.toJson(),
"dateIdentified": dateIdentified.toIso8601String(),
"preferredProfileImage": preferredProfileImage,
"doNotContact": List<dynamic>.from(doNotContact.map((x) => x)),
"frequencyRules": List<dynamic>.from(frequencyRules.map((x) => x)),
};
}
class ......{}
Output Screen: here
Json Viewer: here
Dart Devtools: here
This is probably not a problem with your json parsing, but a UI issue. Putting Columns or Rows inside ListViews or the opposite. If there was something wrong with the json, you'd be seeing the return Center(child: CircularProgressIndicator());,
But instead, you're getting an empty UI.
Check your debug console, if you have RenderView errors, or failed to layout render-box issues, this will be the cause of them.
Try using the property of shrink wrap in your lists:
ListView(shrinkwrap:true...
and
ListView.builder(shirinkrap:true...
Also, wrap your Row that inside the ListView with a LimitedBox
i.e
LimitedBox(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
I have logged in from an Api of login. Then i got a response data of json. I need to show that json data in a list view. and need to show the array count in notification icon for counting it. How can i do it ? please help.
The response json format is -
{
"id": 1,
"name": "Mr Admin",
"email": "admin2#gmail.com",
"username": "admin2",
"api_token": "oYfajebhRzlxpMZV8dHI6w5R8CrpgybaGqX2ZaIXkGpumE9hZSgLVVINAgaF",
"user_types_id": null,
"created_at": "2020-01-21 16:21:48",
"updated_at": "2020-10-14 11:31:10",
"deleted_at": null,
"unread_notifications": [
{
"id": "d54ee0cc-054a-4d51-a53b-5f6f658841ae",
"type": "App\\Notifications\\HandSlipStatusNotification",
"notifiable_id": 1,
"notifiable_type": "App\\User",
"data": {
"payment_id": 471,
"generate_payment_id": "10200471",
"message": "Hand Slip Settled.",
"amount": 850
},
"read_at": null,
"created_at": "2020-10-12 15:50:38",
"updated_at": "2020-10-12 15:50:38"
},
{
"id": "aedb7880-4201-4805-b017-62242dfed741",
"type": "App\\Notifications\\HandSlipStatusNotification",
"notifiable_id": 1,
"notifiable_type": "App\\User",
"data": {
"payment_id": 471,
"generate_payment_id": "10200471",
"message": "Hand Slip Disbursed.",
"amount": 850
},
"read_at": null,
"created_at": "2020-10-12 15:50:25",
"updated_at": "2020-10-12 15:50:25"
},
i can show the id , name , email etc but cann't access unread_notifications.
my code -
api_service.dart ->
class LoginResponseModel {
final String token;
final String error;
LoginResponseModel({this.token, this.error});
factory LoginResponseModel.fromJson(Map<String, dynamic> json) {
return LoginResponseModel(
token: json["token"] != null ? json["token"] : "",
error: json["error"] != null ? json["error"] : "",
);
}
}
class LoginRequestModel {
String email;
String password;
String username;
LoginRequestModel({
this.email,
this.password,
this.username,
});
Map<String, dynamic> toJson() {
Map<String, dynamic> map = {
// 'email': email.trim(),
'username': username.trim(),
'password': password.trim(),
};
return map;
}
}
login_model
class LoginResponseModel {
final String token;
final String error;
LoginResponseModel({this.token, this.error});
factory LoginResponseModel.fromJson(Map<String, dynamic> json) {
return LoginResponseModel(
token: json["token"] != null ? json["token"] : "",
error: json["error"] != null ? json["error"] : "",
);
}
}
class LoginRequestModel {
String email;
String password;
String username;
LoginRequestModel({
this.email,
this.password,
this.username,
});
Map<String, dynamic> toJson() {
Map<String, dynamic> map = {
// 'email': email.trim(),
'username': username.trim(),
'password': password.trim(),
};
return map;
}
}
login.dart
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'homepage.dart';
class Login extends StatefulWidget {
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
var allData ;
TextEditingController _userController = TextEditingController();
TextEditingController _passwordController = TextEditingController();
bool _isLoading = false;
// arrange method for api log in
signIn( String username,String password) async {
// String url = "https://reqres.in/api/login";
String url = "my url";
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
Map body = { "username": username, "password": password
};
var notificatiion;
var jsonResponse;
var res = await http.post(url, body: body);
//need to check the api status
if (res.statusCode == 200) {
jsonResponse = json.decode(res.body);
notificatiion = jsonResponse['unread_notifications'];
print("Response status: ${res.statusCode}");
print("Response status: ${res.body}");
if (jsonResponse != null) {
setState(() {
_isLoading = false;
});
sharedPreferences.setString("token", jsonResponse['token']);
Navigator.of(context).pushAndRemoveUntil(
MaterialPageRoute(builder: (BuildContext) =>
HomePage(
email: jsonResponse['email'],
name: jsonResponse['name'],
username : jsonResponse['username'],
notification: notificatiion,
),
),
(Route<dynamic> route) => false);
}
} else {
setState(() {
_isLoading == false;
});
print(" Response status : ${res.body}");
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: SingleChildScrollView(
child: Center(
child: Container(
padding: EdgeInsets.fromLTRB(20, 100, 20, 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text("Login",
style: TextStyle(fontSize: 32),
),
SizedBox(
height: 30,
),
Card(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
child: Container(
height: 220,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(30),
child: TextField(
controller: _userController,
decoration: InputDecoration(hintText: "username"),
),
),
Padding(
padding: const EdgeInsets.all(30),
child: TextField(
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(hintText: "Password"),
),
),
],
),
),
),
SizedBox(
height: 60,
width: MediaQuery.of(context).size.width,
child: RaisedButton(
color: Colors.lightBlue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
child: Text("Sign In"),
onPressed: _userController.text == ""||
_passwordController.text == ""
? null
: () {
setState(() {
_isLoading = true ;
});
signIn(_userController.text, _passwordController.text);
},
),
),
SizedBox(
height: 20,
),
FlatButton(
child: Text("Forgot password"),
onPressed: (){
},
),
],
),
),
),
),
),
);
}
}
I want to show all the response value in home page .In notification's icon i want to show array count.
homepage.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'login.dart';
import 'login.dart';
class HomePage extends StatelessWidget {
String email;
String name;
String username;
List<dynamic> notification;
HomePage({this.email, this.name, this.username, this.notification, });
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("Cash-Management"),
backgroundColor: Colors.blue,
actions: [
IconButton(icon: Icon(Icons.notifications), onPressed: () {}),
IconButton(
icon: Icon(Icons.exit_to_app),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Login()),
);
}),
],
),
body: ListView(
children: <Widget>[
Container(
height: 200,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Text(
" $email ",
style: TextStyle(fontSize: 16),
),
Text(" $name "),
Text(" $username "),
],
),
),
Container(
height: 300,
child: ListView.builder(
itemCount: notification == null ? 0 : notification.length,
itemBuilder: (context, index){
return ListTile(
title: Text(notification[index] ["id"]),
subtitle: Text(notification[index]["type"]),
);
}),
),
],
),
),
);
}
}