I am very new to Flutter and Dart.
I have a signup page and I would like to show error in the App. My backend page is returning the errors and status in JSON format. Like below.
{"errors":{"Password1":"Password could not be empty",
"Email1":"Invalid Email Format",
"Name":"Your name must be between 3 to 30 characters!"},
"success":false}
I created a file for JSON parsing like below.
import 'dart:convert';
Signup signupFromJson(String str) => Signup.fromJson(json.decode(str));
String signupToJson(Signup data) => json.encode(data.toJson());
class Signup {
Errors errors;
bool success;
Signup({
this.errors,
this.success,
});
factory Signup.fromJson(Map<String, dynamic> json) => Signup(
errors: Errors.fromJson(json["errors"]),
success: json["success"],
);
Map<String, dynamic> toJson() => {
"errors": errors.toJson(),
"success": success,
};
}
class Errors {
String password1;
String email1;
String name;
Errors({
this.password1,
this.email1,
this.name,
});
factory Errors.fromJson(Map<String, dynamic> json) => Errors(
password1: json["Password1"],
email1: json["Email1"],
name: json["Name"],
);
Map<String, dynamic> toJson() => {
"Password1": password1,
"Email1": email1,
"Name": name,
};
}
Now I need to show this data to App after Async call.
Future userRegistration() async{
try{
// Showing CircularProgressIndicator.
setState(() {
visible = true ;
});
// Getting value from Controller
String name = nameController.text;
String email = emailController.text;
String password = passwordController.text;
// SERVER API URL
var url = 'http://192.168.100.10:8080/app/registerexec.php';
// Store all data with Param Name.
var data = {'name': name, 'email': email, 'password' : password};
// Starting Web API Call.
var response = await http.post(url, body: json.encode(data));
// Getting Server response into a variable.
final message = signupFromJson(response.body);
if(response.statusCode == 200){
setState(() {
visible = false;
});
}
// Showing Alert with Response JSON Message.
}catch(e){
return userRegistration();
}
}
How can I show the JSON data to SnackBar?
Edit
I managed to get the data in Print after manually defining it. Like below. But I want to automate it. So, if there are any errors it can show and if its successful then a different message.
print(message.errors.email1);
print(message.errors.name);
print(message.errors.password1);
print(message.success);
you could use FutureBuilder at your snackBar. I've edited from the code available here:
class SnackBarPage extends StatelessWidget {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: userRegistration,
initialData: '',
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
// snapshot.data = yourData from your userRegistration
// print(snapshot.data) to show your data here
return snackBar = SnackBar(
content: Text('Yay! A SnackBar!'),
action: SnackBarAction(
label: 'Undo',
onPressed: () {
},
)
};
)
},
),
}
}
Related
I'm trying to get afield value into a list of string from documents in the firebase that match the WHERE of a field value from a value of my own.
(I wanna show images above each other to create a full picture depending on the data, using two fields to be exact and then creating a list of images to show as positioned inside a Stack)
my code:
Implant Class:
class Implant {
final String id;
final String pid;
final String aptid;
late String type;
late double? sizew;
late int? sizel;
late String? positionQ;
late int? positionN;
Implant(this.id, this.pid, this.aptid, this.type, this.sizew, this.sizel,
this.positionQ, this.positionN);
Map<String, dynamic> toJson() => {
'ID': id,
'PID': pid,
'APTID': aptid,
'TYPE': type,
'SIZEW': sizew,
'SIZEL': sizel,
'POSITIONQ': positionQ,
'POSITIONN': positionN,
};
factory Implant.fromJson(Map<String, dynamic> json) {
return Implant(
json['ID'],
json['PID'],
json['APTID'],
json['TYPE'],
json['SIZEW'],
json['SIZEL'],
json['POSITIONQ'],
json['POSITIONN'],
);
}
static Map<String, dynamic> toMap(Implant implant) => {
'ID': implant.id,
'PID': implant.pid,
'APTID': implant.aptid,
'TYPE': implant.type,
'SIZEW': implant.sizew,
'SIZEL': implant.sizel,
'POSITIONQ': implant.positionQ,
'POSITIONN': implant.positionN,
};
static String encode(List<Implant> implant) => json.encode(
implant
.map<Map<String, dynamic>>((implant) => Implant.toMap(implant))
.toList(),
);
static List<Implant> decode(String implants) =>
(json.decode(implants) as List<dynamic>)
.map<Implant>((implant) => Implant.fromJson(implant))
.toList();
}
Future of list of string function:
static Future<List<String>> getImplants(Patient patient) async {
List<String> result = ['assets/images/teeth/empty.png'];
var collection = FirebaseFirestore.instance.collection('implants');
var docSnapshot = await collection.doc(patient.id).get();
if (docSnapshot.exists) {
Map<String, dynamic> data = docSnapshot.data()!;
result.add(data['POSITIONQ'] + data['POSITIONN']);
}
return result;
}
How I translate them into Stack and Positioned:
static Future<void> showPatientImplants(BuildContext context, Patient patient,
{bool spaces = true}) async {
List<String> myImplants;
myImplants = await getImplants(patient);
List<Widget> x = [Image.asset('assets/images/teeth/empty.png')];
myImplants.forEach((element) {
x.add(Image.asset(element));
});
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
scrollable: true,
content: SingleChildScrollView(
child: GestureDetector(
onTap: () {
log(myImplants.toString());
},
child: Stack(
children: x,
),
),
),
);
});
}
The only problem I'm facing -I think- is that the Future<List> function doesn't get me the values I need.
I tried using other functions I found on SO and google, nothing worked.
I guess I can try using the stream and Listview.builder just to get the files names but it seems like exactly how it shouldn't be done.
any help is appreciated.
Never mind Solved it using the following code:
static Future<List<String>> getImplants(Patient patient) async {
List<String> result = ['assets/images/teeth/empty.png'];
log('ID:${patient.id}');
var collection = FirebaseFirestore.instance
.collection('implants')
.where('PID', isEqualTo: patient.id);
var docSnapshot = await collection.get();
for (var element in docSnapshot.docs) {
if (element.exists) {
Map<String, dynamic> data = element.data();
result.add(data['POSITIONQ'].toString() + data['POSITIONN'].toString());
}
}
return result;
}
but if anyone has a better idea I appreciate it along with everyone who has a similar problem.
I am using two apis, one is to use get method which I am using to fetch Image from server and display using ListView builder, and another api which I need to use to delete the image.
This model class is for Fetching data:
List<DisplayImageModels> displayImageModelsFromJson(String str) =>
List<DisplayImageModels>.from(
json.decode(str).map((x) => DisplayImageModels.fromJson(x)));
String displayImageModelsToJson(List<DisplayImageModels> data) =>
json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class DisplayImageModels {
DisplayImageModels({
this.id,
this.category,
this.documentImage,
this.document,
this.user,
});
int? id;
int? category;
String? documentImage;
int? document;
int? user;
factory DisplayImageModels.fromJson(Map<String, dynamic> json) =>
DisplayImageModels(
id: json["id"],
category: json["category"],
documentImage: json["document_image"],
document: json["document"],
user: json["user"],
);
Map<String, dynamic> toJson() => {
"id": id,
"category": category,
"document_image": documentImage,
"document": document,
"user": user,
};
}
This is model class for delete method
DeleteImageModels deleteImageModelsFromJson(String str) =>
DeleteImageModels.fromJson(json.decode(str));
String deleteImageModelsToJson(DeleteImageModels data) =>
json.encode(data.toJson());
class DeleteImageModels {
DeleteImageModels({
required this.ids,
});
List<int> ids;
factory DeleteImageModels.fromJson(Map<String, dynamic> json) =>
DeleteImageModels(
ids: List<int>.from(json["ids"].map((x) => x)),
);
Map<String, dynamic> toJson() => {
"ids": List<dynamic>.from(ids.map((x) => x)),
};
}
And this is API class for Delete method, which apparently is throwing exception
Future<DeleteImageModels> deleteAlbum() async {
var preferences = await SharedPreferences
.getInstance(); // This is Shared preference which is used to store the tokens of the users
var getToken = preferences.getString("token");
print("This is access: $getToken");
final http.Response response = await http.delete(
Uri.parse('http://10.0.2.2:8000/api_vi/deletedocument/'),
headers: {
'Context-Type': 'application/json',
'Accept': 'application/json',
'Authorization': 'Bearer $getToken',
},
);
if (response.statusCode == 200) {
return DeleteImageModels.fromJson(jsonDecode(response.body));
} else {
// If the server did not return a "200 OK response",
// then throw an exception.
throw Exception('Failed to delete album.');
}
}
}
The id is only common thing between two models and I am supposed to delete using that id, now how do I do it? I did go through flutter official documents for deleting but it doesn't relate to mine.
Here in this image below we can see some numbers on the image, those are id which is displayed from get API now how do I pass that ID in delete api and and delete those image?
AFAIK your question is how to pass the image id by clicking on the remove icon.
If you are using the List.builder for representing your images then in the end you must return a widget that represents your image. This widget must contain id of image and pressable button where you can register click handler. You can place logic responsible for deleting the image inside of this handler.
I make a brief example that highlights the main points here.
Image widget example
class ImageWidget extends StatelessWidget {
final String id;
final String url;
ImageWidget(this.id, this.url);
#override
Widget build(BuildContext context) {
return SizedBox(
width: 64,
height: 64,
child: Stack(children: [
Positioned(child: IconButton(icon: ..., onPressed: {
// here delete logic
// api.deleteImgae(id);
},)),
Positioned.fill(child: Image(image: image))
],),
)
}
}
You can specify delete logic in onPressed callback of IconButton or any other (InkWell, GestureDetector, ElevatedButton and etc)
ListView example:
ListView.builder(itemBuilder: (_, i) => ImageWidget(images[i].id, images[i].url))
I am using MultiProvider Stream with a database service with the firebase calls and a data model and using to and fromJson.
I am calling the Firestore User in my main file, and have a wrapper to call the user's document which holds the reference to the company they belong to, once the user data is retrieved we then call the company document in a userType screen, I then use the reference and pass it to the getter of the company document, but the document is being called before the document ref is passed.
I have tried to change the Stream to a Future but then I get an error on Provider.
Database Service
Stream<Companies> streamCompanies() {
_userRef.get().then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
print('HERE IS THE SNAPSHOT');
print(documentSnapshot.get('companyID'));
thisUserCompany = documentSnapshot.get('companyID');
}
});
debugPrint('Database Service => GETTING COMPANY STREAM');
debugPrint(thisUserCompany);
return _database
.collection('companies')
.doc(thisUserCompany)
.snapshots()
.map(
(snapshot) =>
Companies.fromJson(snapshot.data() as Map<String, dynamic>),
);
}
Comapnies Class
class Companies {
final String uid;
final String companyName;
final String logoForPDF;
Companies ({required this.uid, required this.companyName,
required this.logoForPDF});
Companies.fromJson(Map<String, dynamic> json)
: uid = json['uid'],
companyName = json['companyName'],
logoForPDF = json['logoForPDF'];
Map<String, dynamic> toJson() => {
'uid': uid,
'companyName': companyName,
'logoForPDF': logoForPDF,
};
factory Companies.initialData() {
return Companies(
uid: 'Loading',
companyName: 'Loading',
logoForPDF: 'Loading',
);
}
}
MultiProvider
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
#override
Widget build(BuildContext context) {
final user = Provider.of<UserData?>(context);
return user != null
? MultiProvider(providers: [
StreamProvider<Companies?>.value(
initialData: Companies.initialData(),
value: user != null
? DatabaseService(uid: user.uid, companyID: user.companyID)
.streamCompanies()
: null),
], child: const ProfileSelector())
: const CircularProgressIndicator();
}
}
Ok so a little more perseverance I now have this working but I do not know if this is the correct way of doing this.
In my database file, I have changed the code to the following
Stream<Companies> streamCompanies() async* {
await _userRef.get().then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
thisUserCompany = documentSnapshot.get('companyID');
}
});
yield* _database
.collection('companies')
.doc(thisUserCompany)
.snapshots()
.map((snapshot) =>
Companies.fromJson(snapshot.data() as Map<String, dynamic>),
);
}
I'm trying to use https://pub.dev/packages/flappy_search_bar#-readme-tab- to create a list of data which I plan on getting from an api(just testing now), but I can't seem to add the list created from the response json to the search widget.
The argument type 'Future<List<Album>> Function()' can't be assigned to the parameter type 'Future<List<Album>> Function(String)'.
class Album {
final int userId;
final int id;
final String title;
Album({this.userId, this.id, this.title});
factory Album.fromJson(Map<String, dynamic> json) {
return Album(
userId: json['userId'],
id: json['id'],
title: json['title'],
);
}
}
Future<List<Album>> fetchAlbum() async {
final response =
await http.get('https://jsonplaceholder.typicode.com/albums/1');
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
List jsonResponse = json.decode(response.body);
return jsonResponse.map((job) => Album.fromJson(job)).toList();
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load album');
}
}
#override
Widget build(BuildContext context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: SearchBar<Album>(
onSearch: fetchAlbum, <------ error here
onItemFound: (Album post, int index) {
return ListTile(
onTap: () => widget.setProviderData(post.title),
title: Text(post.title),
subtitle: Text(post.id.toString()),
);
},
),
),
);
}
Can anyone help me with this, please?
I can't test your code right now, but at first glance the problem is onSearch expects a function that gets String parameter and your code doesn't provide it.
You should modify this line Future<List<Album>> fetchAlbum() async as follows:
Future<List<Album>> fetchAlbum(String album) async
this is my cloud firestore looks like:
Error Message: Unhandled Exception: Converting object to an encodable
object failed: Photography
used jsonSerialization for my database
import 'package:json_annotation/json_annotation.dart';
part 'Model.g.dart';
#JsonSerializable()
class Photography{
String couplePhoto;
String female;
String image_url;
String info;
String male;
AllImages all_images;
Photography();
factory Photography.fromJson(Map<String, dynamic> json) => _$PhotographyFromJson(json);
Map<String,dynamic> toJson() => _$PhotographyToJson(this);
}
#JsonSerializable()
class AllImages {
List<String> imageUrl = List<String>();
AllImages();
factory AllImages.fromJson(Map<String, dynamic> json) => _$AllImagesFromJson(json);
Map<String,dynamic> toJson() => _$AllImagesToJson(this);
}
By running flutter pub run build_runner build in the project root, I generated JSON serialization code for my Photography and AllImages whenever they are needed.
Model.g.dart
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'Model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
Photography _$PhotographyFromJson(Map<String, dynamic> json) {
return Photography()
..couplePhoto = json['couplePhoto'] as String
..female = json['female'] as String
..image_url = json['image_url'] as String
..info = json['info'] as String
..male = json['male'] as String
..all_images = json['all_images'] == null
? null
: AllImages.fromJson(json['all_images'] as Map<String, dynamic>);
}
Map<String, dynamic> _$PhotographyToJson(Photography instance) =>
<String, dynamic>{
'couplePhoto': instance.couplePhoto,
'female': instance.female,
'image_url': instance.image_url,
'info': instance.info,
'male': instance.male,
'all_images': instance.all_images
};
AllImages _$AllImagesFromJson(Map<String, dynamic> json) {
return AllImages()
..imageUrl = (json['imageUrl'] as List)?.map((e) => e as String)?.toList();
}
Map<String, dynamic> _$AllImagesToJson(AllImages instance) =>
<String, dynamic>{'imageUrl': instance.imageUrl};
After that, I created the DB class,
How to use the model class?
class DB {
final db = Firestore.instance;
// Stream<QuerySnapshot> initStream() {
// return db.collection('photography').snapshots();
// }
getPhotography() async {
return db.collection('photography')
.document("0yUc5QBGHNNq6WK9CyyF")
.setData(jsonDecode(jsonEncode(Photography)));
}
}
DB db = DB();
my photography_bloc class
class PhotographyBloc extends BlocBase{
//PhotographyBloc(){
// db.initStream().listen((data) => inFirestore.add(data));
//}
PhotographyBloc(){
init();
}
Photography photography;
//final _firestoreController = StreamController<Photography>();
//Stream<Photography> get outFirestore => _firestoreController.stream;
//Sink<Photography> get inFirestore => _firestoreController.sink;
final _firestoreController = StreamController<Photography>();
Stream<Photography> get outFirestore => _firestoreController.stream;
Sink<Photography> get inFirestore => _firestoreController.sink;
void init() async{
photography = db.getPhotography();
inFirestore.add(photography);
}
#override
void dispose() {
_firestoreController.close();
}
}
my StreamBuilder Widget
How to get data using JSON serialization
child: StreamBuilder<Photography>(
stream: bloc.outFirestore,
initialData: null,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Column(
children: buildItem(snapshot.data, bloc));
// children: snapshot.data.documents
// .map<Widget>((doc) => buildItem(doc, bloc))
// .toList());
} else {
return SizedBox();
}
}),
builderItem() method,
buildItem(Photography doc, PhotographyBloc bloc) {
...
child: ClipRRect(
borderRadius: BorderRadius.circular(20.0),
child: FadeInImage.assetNetwork(
placeholder: "assets/images/photography.jpg",
image: doc.couplePhoto,
// image: doc.data['couplePhoto'],
fit: BoxFit.fill,
),
),
According to the package source :
/// Writes to the document referred to by this [DocumentReference].
///
/// If the document does not yet exist, it will be created.
///
/// If [merge] is true, the provided data will be merged into an
/// existing document instead of overwriting.
Future<void> setData(Map<String, dynamic> data, {bool merge = false}) {
return Firestore.channel.invokeMethod<void>(
'DocumentReference#setData',
<String, dynamic>{
'app': firestore.app.name,
'path': path,
'data': data,
'options': <String, bool>{'merge': merge},
},
);
}
You must give a <String, dynamic> Map to setData(x) method.
So in your case you should maybe do it like this :
getPhotography() async {
return db.collection('photography')
.document("0yUc5QBGHNNq6WK9CyyF")
.setData(photography.toJson());
}