Flutter Dropdown value not updating and UI not updating when submitting - flutter

I am working on a flutter project to display a Scrumboard, this is my first time working with flutter. I have a TextButton that opens a dialog when onpressed. The dialog is a form where you can edit details about a Scrumtask. I have discovered 2 issues. 1. issue is when i use the dropdownmenu and select a value. The value doenst get updated. 2. Lets say i edit a Scrumtask and set the state from 'Todo' to 'In Progress' from the dropdownmenu and submit, the UI doesn't move that Task object to the 'In Progress' column.
I hope the code I have provided is enough otherwise let me know.
Scrumboardscreen.dart
TextButton(
child: Text('Change'),
onPressed: () {
showDialog(
context: context,
builder: ((context) {
return AlertDialog(
title: Text('Task Informations'),
content: Form(
key: _formKey,
child: Column(
children: [
Text('Task Name'),
TextFormField(
controller: textscontrollers[0],
decoration: InputDecoration(
hintText: data[listIndex]
.items[itemIndex]
.taskName,
),
validator: (text) {
if (text == null || text.isEmpty) {
return 'Task Name cant be empty';
}
return null;
},
),
Text('Task Description'),
TextFormField(
controller: textscontrollers[1],
decoration: InputDecoration(
hintText: data[listIndex]
.items[itemIndex]
.taskDescription,
),
validator: (text) {
if (text == null || text.isEmpty) {
return 'Task Description cant be empty';
}
return null;
},
),
Text('Story Points'),
TextFormField(
controller: textscontrollers[2],
decoration: InputDecoration(
hintText: data[listIndex]
.items[itemIndex]
.storyPoints
.toString(),
),
validator: (text) {
if (text == null || text.isEmpty) {
return 'Story Points cant be empty';
}
return null;
},
),
Text('Task State'),
DropdownButton<String>(
value: data[listIndex]
.items[itemIndex]
.taskState,
icon: const Icon(Icons.arrow_downward),
elevation: 16,
style: const TextStyle(
color: Colors.blue,
),
underline: Container(
height: 2,
color: Colors.blue,
),
onChanged: (String? value) {
setState(() {
data[listIndex]
.items[itemIndex]
.taskState = value!;
});
},
items: states
.map<DropdownMenuItem<String>>(
(String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!
.validate()) {
_UpdateTask(data[listIndex]
.items[itemIndex]);
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(
content:
Text('Updated Task')));
Navigator.of(context).pop();
}
},
child: Text('Submit Changes'),
)
],
),
),
);
}));
},
),
This method is being in the Futurebuilder and shows data on the UI
Future<List<BoardPostColumn>> GetData() async {
if (data.isEmpty) {
data = await manager.GetData();
}
return data;
}
The update method
void _UpdateTask(BoardPost task) async {
task.taskName = textscontrollers[0].text;
task.taskDescription = textscontrollers[1].text;
task.storyPoints = int.parse(textscontrollers[2].text);
BoardPost result = await manager.UpdateTask(task);
setState(() {
//My understanding that this would update the UI
task = result;
textscontrollers[0].clear();
textscontrollers[1].clear();
textscontrollers[2].clear();
});
}
Manager class
Future<BoardPost> UpdateTask(BoardPost task) async {
return handler.UpdateTask(task);
}
ApiHandler.dart
Future<BoardPost> UpdateTask(BoardPost task) async {
Response response = await post(
Uri.parse('https://localhost:7252/api/ScrumTask/UpdateScrumTask'),
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8',
},
body: jsonEncode(<String, dynamic>{
'id': task.id,
'taskName': task.taskName,
'taskDescription': task.taskDescription,
'storyPoints': task.storyPoints,
'taskState': task.taskState
}));
if (response.statusCode == 200) {
Map<String, dynamic> ScrumMap = jsonDecode(response.body);
return BoardPost.fromJson(ScrumMap);
} else {
throw Exception('Failed to Update');
}
}
class BoardPost {
int? id;
String? taskName;
String? taskDescription;
int? storyPoints;
String? taskState;
BoardPost(
{this.id,
this.taskName,
this.taskDescription,
this.storyPoints,
this.taskState});
BoardPost.fromJson(Map<String, dynamic> json) {
id = json['id'];
taskName = json['taskName'];
taskDescription = json['taskDescription'];
storyPoints = json['storyPoints'];
taskState = json['taskState'];
}
This class here is the one that will be displaying all the data
class BoardPostColumn {
String title;
List<BoardPost> items;
BoardPostColumn({
required this.title,
required this.items,
});
}
Manager class that returns the data provided from api
Future<List<BoardPostColumn>> GetData() async {
Response responseBody = await handler.GetData();
List<BoardPostColumn> data = [];
List<BoardPost> tasks = (json.decode(responseBody.body) as List)
.map((data) => BoardPost.fromJson(data))
.toList();
data.add(BoardPostColumn(
title: 'To do',
items:
tasks.where((e) => e.taskState?.toLowerCase() == 'todo').toList()));
data.add(BoardPostColumn(
title: 'In Progress',
items: tasks
.where((e) => e.taskState?.toLowerCase() == 'in progress')
.toList()));
data.add(BoardPostColumn(
title: 'Done',
items:
tasks.where((e) => e.taskState?.toLowerCase() == 'done').toList()));
return data;
}

Try to use StatefulBuilder inside showDialog to update the dialog ui.
showDialog(
context: context,
builder: (context) {
return StatefulBuilder(builder: (context, setStateSB) => AlertDialog(...),)
},
);
Use this setStateSB as setState to update the dialog ui, You may need to pass this setStateSB on the function or call it setStateSB((){}) at the end of the function call.

Related

Image uploads in Firebase but not the data fetched from TextFormFields and dropdownbutton

Flutter beginner here. Working on a flutter project where I can submit a form where I can upload it's content in firebase. I took the data from TextFormField and DropDownButton and the images from ImagePicker. I can upload the image file perfectly to the firebase but the data are not uploading. Here is the code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart' as path;
class AddDoctor extends StatefulWidget {
#override
State<AddDoctor> createState() => AddDoctorState();
}
class AddDoctorState extends State<AddDoctor> {
late String name;
late int age;
late String description;
String specialistValue = 'Select Specialist';
String hospitalValue = 'Select Hospital';
List<String> imageUrlList = [];
final controllerName = TextEditingController();
final controllerAge = TextEditingController();
final controllerDesciption = TextEditingController();
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final FirebaseStorage _firebaseStorage = FirebaseStorage.instance;
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
final ImagePicker _picker = ImagePicker();
XFile? image;
void pickDoctorImage() async {
try {
final pickedImage = await _picker.pickImage(source: ImageSource.gallery);
setState(() {
image = pickedImage!;
});
} catch (e) {}
}
Widget displayImage() {
return Image.file(File(image!.path));
}
Future<void> uploadImage() async {
Reference ref =
_firebaseStorage.ref('products/${path.basename(image!.path)}');
await ref.putFile(File(image!.path)).whenComplete(() async {
await ref.getDownloadURL().then((value) {
imageUrlList.add(value);
});
});
}
void uploadInfo() async {
CollectionReference infoRef = _firestore.collection('DoctorList');
await infoRef.doc().set({
'name': name,
'age': age,
'description': description,
'specialist': specialistValue,
'hospital': hospitalValue,
'doctorImage': imageUrlList,
}).whenComplete(() {
Navigator.pop(context);
});
}
void uploadDoctorInfo() async {
await uploadImage().whenComplete(() => uploadInfo);
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFD9E4EE),
appBar: AppBar(
title: const Text('Add Doctor'),
actions: [
IconButton(
icon: const Icon(Icons.menu),
onPressed: () {},
),
],
),
body: Form(
key: _formKey,
child: ListView(
padding: const EdgeInsets.all(16),
children: <Widget>[
TextFormField(
keyboardType: TextInputType.name,
validator: (value) {
if (value!.isEmpty) {
return 'Please Name must not be empty';
} else {
return null;
}
},
controller: controllerName,
decoration: const InputDecoration(
label: Text('Name'),
),
onSaved: (value) {
name = value!;
},
),
const SizedBox(height: 10),
TextFormField(
keyboardType: TextInputType.number,
validator: (value) {
if (value!.isEmpty) {
return 'Please Age must not be empty';
} else {
return null;
}
},
controller: controllerAge,
decoration: const InputDecoration(
label: Text('Age'),
),
onSaved: (value) {
age = int.parse(value!);
},
),
const SizedBox(height: 10),
DropdownButton(
borderRadius: BorderRadius.circular(30),
value: specialistValue,
items: specialistList.map<DropdownMenuItem<String>>((e) {
return DropdownMenuItem(
value: e,
child: Text(e),
);
}).toList(),
onChanged: (String? value) {
setState(() {
specialistValue = value!;
});
},
),
DropdownButton(
borderRadius: BorderRadius.circular(30),
value: hospitalValue,
items: hospitalList.map<DropdownMenuItem<String>>((e) {
return DropdownMenuItem(
value: e,
child: Text(e),
);
}).toList(),
onChanged: (String? value) {
setState(() {
hospitalValue = value!;
});
},
),
const SizedBox(height: 10),
TextFormField(
keyboardType: TextInputType.number,
validator: (value) {
if (value!.isEmpty) {
return 'Please Description must not be empty';
} else {
return null;
}
},
maxLength: 100,
maxLines: 3,
controller: controllerDesciption,
decoration: const InputDecoration(
label: Text('Description'),
),
onChanged: (value) {
description = value;
},
),
const SizedBox(height: 10),
// CircleAvatar(
// radius: 50,
// backgroundImage: image != null ? FileImage(image) : null,
// ),
InkWell(
onTap: () {
setState(() {
image = null;
});
},
child: Container(
padding: const EdgeInsetsDirectional.only(top: 60),
height: 150,
width: 150,
decoration: const BoxDecoration(
color: Colors.blue,
shape: BoxShape.circle,
),
child: Center(
child: image != null
? displayImage()
: const Text(
'You have not pick any image',
style: TextStyle(fontSize: 11),
textAlign: TextAlign.center,
),
)),
),
const SizedBox(height: 10),
ElevatedButton(
onPressed: () {
pickDoctorImage();
},
child: const Text('Upload Image'),
),
const SizedBox(height: 10),
ElevatedButton(
child: const Text('Submit'),
onPressed: () {
uploadDoctorInfo();
},
),
],
),
),
);
}
}
There aren't any error in the file either. I can't figure out where the source of the problem is.
Fixed it. The problem was in
void uploadDoctorInfo() async {
await uploadImage().whenComplete(() => uploadInfo);
}
I changed it to
void uploadDoctorInfo() async {
await uploadImage().whenComplete(uploadInfo);
}
And now it's working fine
You should add for the new entry instead set data in collection. Try using the following code
void uploadInfo() async {
CollectionReference infoRef = _firestore.collection('DoctorList');
await infoRef.add({
'name': name,
'age': age,
'description': description,
'specialist': specialistValue,
'hospital': hospitalValue,
'doctorImage': imageUrlList,
}).whenComplete(() {
Navigator.pop(context);
});
}
Edited
Your are referencing uploadInfo function instead of call that. updateInfo should be called by adding (), so uploadDoctorInfo will be look like.
void uploadDoctorInfo() async {
await uploadImage().whenComplete(() => uploadInfo());
}
or
void uploadDoctorInfo() async {
await uploadImage().whenComplete(uploadInfo);
}

How to give new input again in flutter_form_builder textfield if there is happen any error to submission data in server?

I am using flutter_form_builder package as input field in login screen. I submit data nicely, everything is working fine. But if there is any login failure happens then input field is unable to give new input later on. Could anyone please help me with sharing a solution? Thanks in advance! Here you go with my code-
FormBuilder(
key: _formKey,
enabled: !_isLoading,
autovalidateMode: AutovalidateMode.disabled,
onChanged: () {
_formKey.currentState!.save();
},
child: Column(
children: <Widget>[
const EmailTextField(
name: 'loginEmail',
hintText: 'Email',
prefixIcon: emailIcon,
),
sixteenVerticalSpace,
const PasswordInputField(
name: 'password',
hintText: 'Password',
),
Align(
alignment: Alignment.centerRight,
child: TextButton(
onPressed: () {
Get.to(()=> const ForgotPasswordScreen());
},
child: const Text(
'Forgot Password?',
style: fourteenBlackStyle,
)),
),
PrimaryButton(
buttonTitle: 'Sign in',
onTap: () async {
if (_formKey.currentState?.saveAndValidate() ?? false) {
try{
if (!_isLoading) {
setState(() {
_isLoading = true;
});
// Send request to server
var response = await sendLoginRequest(
_formKey.currentState?.value['loginEmail'],
_formKey.currentState?.value['password'],
);
var data = response['data'];
if (response['status'] == 'success') {
final accessKey = data['access_token'];
final userId = data['user']['id'].toString();
final levelId = data['user']['level_id'].toString();
await localStorage.write(
key: 'access_key',
value: accessKey);
await localStorage.write(
key: 'user_id',
value: userId);
await localStorage.write(
key: 'level_id',
value: levelId);
Navigator.pushReplacement(context, MaterialPageRoute(builder: (_) => const QuoteScreen()));
CustomSnack.successSnack('You are logged in Successfully');
} else {
CustomSnack.warningSnack('${data['message']}');
}
}
} catch(e) {
CustomSnack.warningSnack(e.toString());
} finally {
setState(() {
_isLoading = false;
});
// _formKey.currentState?.reset();
}
}
},
buttonColor: primaryColor,
isLoading: _isLoading,
),
_signUpOption(),
],
),
),

Filed Assertion: line 298 pos 10: 'data != null' : A non-null String must be provided to a Text Widget

I have this challenge. All I want to do is to display the item whose quantity was updated on the data table. I can display the item and the quantity. I can also reset the quantity but when I tried to click Save so it should populate on the datatable and perhaps make update request latter, it displays this error below:
And this is the List of the Items:
THis is the form that allows me update the quantity:
Also Instead of displaying the data in Listile. I want to display the data in ListBody with Divider but I don't know how to do it. All the methods I've tried its throwing and error; the widget.farmerBvn and widget.dc_dcOid, Username can be replaced with this parameters:
farmerBvn=22499183844 dcOid=11, agentName=johndoh
I've tried but I keep getting this error on this and how to change the view from Listile to maybe ListBody where I can display more attributes. Please can anyone help me I am new to flutter.
Here's the code that helps me display the Items on a List:
//List Start Here
child: ListView(
children: eops.map((e) {
return ListTile(
onTap: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.circular(20)),
title: Text(e.itemName),
content: TextField(
controller: quantity,
keyboardType:
TextInputType.number,
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius:
BorderRadius.circular(
7),
),
labelText:
'Input Quantity Collected',
hintText:
e.quantityAllocated),
),
actions: <Widget>[
FlatButton(
child: Text('Reset'),
onPressed: () {
setState(() {
quantity.text = '';
});
},
),
FlatButton(
child: Text('Save'),
onPressed: () {
bool neww = true;
for (EopLine n
in selectedEops) {
if (n.oid == e.oid) {
setState(() {
n.quantityCollected =
quantity.text;
});
neww = false;
break;
}
}
if (neww == true) {
setState(() {
selectedEops.add(EopLine(
oid: e.oid,
itemName: e.itemName,
quantityCollected: e
.quantityCollected,
createdBy:
e.createdBy,
createdOn:
DateTime.now()
.toString(),
itemType: e.itemType,
measuringUnit:
e.measuringUnit));
});
neww = false;
}
Navigator.pop(context);
},
),
],
);
});
},
leading: (
FittedBox(
fit: BoxFit.contain,
child:Text(e.itemName),
)
),
title: Text('${e.quantityAllocated}'),
trailing: Text('${e.quantityCollected}'),
);
// separatorBuilder:
// (context, index) {
// return Divider();
// };
}).toList(),
),
// ],
// ),
),
//And it ends here
And this is the datatable I want to populate:
//Table starts here
child: DataTable(
columns: [
DataColumn(
label: Text('S/N'),
),
DataColumn(
label: Text('EOP Items'),
),
DataColumn(
label: Text('Qty Collected'),
),
// Lets add one more column to show a delete button
DataColumn(
label: Text('Update'),
)
],
rows: selectedEops
.map(
(eop) => DataRow(
selected: selectedEops.contains(eop),
cells: [
DataCell(
Text('${eop.oid}'),
onTap: () {
print('Selected ${eop.oid}');
},
),
DataCell(
Text(eop.itemName),
onTap: () {
print(
'Selected ${eop.itemName}');
},
),
DataCell(
Text(eop.quantityCollected ?? 0),
onTap: () {
print(
'Selected ${eop.quantityCollected ?? 0}');
},
),
DataCell(
Text(eop.quantityAllocated.toString() ?? 0),
onTap: () {
print(
'Selected ${eop.quantityAllocated.toString() ?? 0}');
},
showEditIcon: true,
),
]),
)
.toList(),
),
),
),
///Table Ends here
In the arrays of products in this eops afer the map function I can see quantityAllocated shows null but other items are showing. any line below the eops.map(e) this quantityallocated and some other show null while the rest is showing its value.
children: eops.map((e)
this is the function that performs the http request:
Future<EopLine> get_farmer_eop() async {
SharedPreferences localStorage = await SharedPreferences.getInstance();
var userJson = localStorage.getString('loginRes');
user = json.decode(userJson);
print(user['UserName']);
final response = await http.get(
'http://api.ergagro.com:112/GenerateFarmersEop?farmerBvn=${widget.result}&dcOid=${widget.dc_result}&agentName=${user['UserName']}',
headers: _setHeaders());
print('${response.statusCode}popo');
if (response.statusCode == 200 && response.body != null) {
final jsonStatus = jsonDecode(response.body);
maineops = jsonStatus['Eop'];
List<dynamic> EopItems = maineops['EopLines'];
for (var i in EopItems) {
print('${i['Oid'].toString()} eopitemid');
setState(() {
eops.add(EopLine(
oid: i['Oid'],
itemType: i['EopType'].toString(),
itemName: i['ItemName'],
quantityAllocated: i['QuantityAllocated'].toString(),
quantityCollected: i['QuantityCollected'].toString(),
measuringUnit: i['MeasuringUnit'],
));
// r = maineops;
});
}
} else {
Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(Colors.green),
backgroundColor: Colors.grey,
),
);
}
throw Exception();
}
_setHeaders() => {
'Content-type': 'application/json',
'Accept': 'application/json',
};

Flutter DropdownButton NoSuchMethodError: The getter value was called on null

I'm trying to populate a dropdown button in my flutter app with data coming from my restful api. However i get the error above.
Here is my model;
class SavingsCategory extends Equatable{
final int id;
final String name;
SavingsCategory({
#required this.id,
#required this.name
});
#override
List<Object> get props => [name, id];
}
My repository fetching the data
#override
Future<List<SavingsCategory>> getSavingsCategory()
async {
var token = await tokenRepository.getToken();
final response = await http.get(
'$baseUrl/user/savings-category',
headers: {HttpHeaders.authorizationHeader: 'Bearer $token'},
);
if (response.statusCode == 200) {
var data = json.decode(response.body);
List<SavingsCategory> categoryList = data['savingsCategory'].map<SavingsCategory>((json) {
return SavingsCategory.fromJson(json);
}).toList();
return categoryList;
} else {
throw new Exception("Couldn't get any saving categories");
}
}
My bloc code
class SavingsCategoryBloc {
final repository = SavingsRepository();
final _savingsCategories = PublishSubject<List<SavingsCategory>>();
Stream<List<SavingsCategory>> get savingsCategories => _savingsCategories.stream;
fetchSavingsCategories() async {
final categories = await repository.getSavingsCategory();
_savingsCategories.sink.add(categories);
}
dispose(){
_savingsCategories.close();
}
}
Finally my widget
class _StartSavingPageState extends State<StartSavingPage> {
final SavingsCategoryBloc bloc = SavingsCategoryBloc();
#override
void initState() {
bloc.fetchSavingsCategories();
super.initState();
}
#override
Widget build(BuildContext context) {
....
Container(
padding: EdgeInsets.symmetric(
horizontal: 15.0, vertical: 10.0),
child: StreamBuilder<List<SavingsCategory>>(
stream: bloc.savingsCategories,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return DropdownButton<String>(
items: [
DropdownMenuItem<String>(
child: Text('No Savings Category'),
value: '',
),
],
onChanged: (String value) {
setState(() {
});
},
isExpanded: true,
hint: Text(
'SAVING FOR?',
style: TextStyle(
fontSize: 15.0, color: Colors.grey),
),
);
}
return DropdownButton(
value: category,
items: snapshot.data.map((category) {
DropdownMenuItem(
child: Text('${category.name}'),
value: category.id,
);
}).toList(),
onChanged: (value) {
setState(() {
category = value;
});
},
isExpanded: true,
hint: Text(
'SAVING FOR?',
style: TextStyle(
fontSize: 15.0, color: Colors.grey),
),
);
}),
),
}
}
How can i fix this error? I know the data fetching works just fine. I'm definitely missing something in my widget. Any help would be appreciated.
The DropdownButton value must in item values or must be null.
DropdownButton(
value: categoryId,
items: snapshot.data.map((category) {
DropdownMenuItem(
child: Text('${category.name}'),
value: category.id,
);
}).toList(),
onChanged: (value) {
setState(() {
categoryId = value;
});
},
isExpanded: true,
hint: Text(
'SAVING FOR?',
style: TextStyle(
fontSize: 15.0, color: Colors.grey),
),
);
The mistake you've made is not returning the DropdownMenuItem from the map.
So:
snapshot.data.map((category) {
DropdownMenuItem(
child: Text('${category.name}'),
value: category.id,
);
})
should instead be:
snapshot.data.map((category) =>
DropdownMenuItem(
child: Text('${category.name}'),
value: category.id,
);
)

Flutter SteamBuilder snapshot.data is null

I am new to flutter. I am trying to use SteamBuilder to retrieve snapshot data.
But snapshot.data is returning null.
snapshot.connectionState == ConnectionState.done
is false.
snapshot.hasData
is false.
This is the dart file in which I am trying to retrieve snapshot data
return StreamBuilder<UserData>(
stream: DatabaseService(uid: user.uid).userData,
builder: (context, snapshot) {
//print(snapshot.connectionState == ConnectionState.done);
//print(snapshot.data);
if(snapshot.hasData){
print('snapshot has data');
UserData userData = snapshot.data;
return Form(
key: _formKey,
child: Column(
children: <Widget>[
Text(
'Update your brew settings.',
style: TextStyle(fontSize: 18.0),
),
SizedBox(height: 20.0),
TextFormField(
initialValue: userData.name,
decoration: textInputDecorator,
validator: (val) => val.isEmpty ? 'Please enter a name' : null,
onChanged: (val) => setState(() => _currentName = val),
),
SizedBox(height: 10.0),
DropdownButtonFormField(
value: _currentSugars ?? userData.sugars,
decoration: textInputDecorator,
items: sugars.map((sugar) {
return DropdownMenuItem(
value: sugar,
child: Text('$sugar sugars'),
);
}).toList(),
onChanged: (val) => setState(() => _currentSugars = val ),
),
SizedBox(height: 10.0),
Slider(
value: _currentStrength.toDouble() ?? userData.strength,
activeColor: Colors.brown[_currentStrength ?? userData.strength],
inactiveColor: Colors.brown[_currentStrength ?? userData.strength],
min: 100.0,
max: 900.0,
divisions: 8,
onChanged: (val) => setState(() => _currentStrength = val.round()),
),
RaisedButton(
color: Colors.pink[400],
child: Text(
'Update',
style: TextStyle(color: Colors.white),
),
onPressed: () async {
print(_currentName);
print(_currentSugars);
print(_currentStrength);
}
),
],
),
);
} else {
print('snapshot doesnt have data');
return Loading();
}
}
);
This is the stream
Stream<UserData> get userData {
return brewCollection.document(uid).snapshots().map(_userDataFromSnapshot);
}
brewCollection-
final CollectionReference brewCollection = Firestore.instance.collection('brews');
_userDataFromSnapshot
UserData _userDataFromSnapshot(DocumentSnapshot snapshot) {
return UserData(
uid: uid,
name: snapshot.data['name'],
sugars: snapshot.data['sugars'],
strength: snapshot.data['strength']
);
}
UserData
class UserData {
final String uid;
final String name;
final String strength;
final int sugars;
UserData({ this.uid, this.sugars, this.strength, this.name });
}
Since I am new to Flutter I don't know how to approach this issue.
Please help me out. Thank you.
Normally you need to check 3 cases:
snapshot is null (before any stream or action is fired, usually right
when the Widget is created)
snapshot is not null and data is null/invalid (stream is registered but no data is emitted yet)
snapshot is not null and data is valid (received data)
The UserData class should have been
class UserData {
final String uid;
final String name;
final int strength; // type int not String
final String sugars; // type String not int
UserData({ this.uid, this.sugars, this.strength, this.name });
}
I got confused with datatypes of strength and sugars. My bad.
It was a stupid mistake.
Thank you for responding :)
Here you have not handle error so it will be difficult to know what happens by just seeing code so also catch error on snapshot as
if(snapshot.hasData){
//Show widget with data
}else if(snapshot.hasError){
return Center(
child:Text(snapshot.error.toString());
);
}else{
return CircularProgressIndicator();
}