Flutter How to observ UserModel with GetX after 2.8.1 - firebase

I am using getX for fetching data from Firebase.
I used this one before 2.8 update. It was working.
class UserController extends GetxController {
final Rx<UserModel> _userModel = UserModel(
email: '',
name: '',
uid: '',
kullaniciAdi: '',
bildirim: 0,
userRole: 0,
).obs;
UserModel get user => _userModel.value;
set user(UserModel value) => _userModel.value = value;
void clear() {
_userModel.value = UserModel(
email: '',
name: '',
uid: '',
kullaniciAdi: '',
bildirim: 0,
userRole: 0,
);
}
}
I can observ this with GetX or Obx widget. But now, I can't do that anymore.
Here is my Firebase Database code fetching user codes:
Future getUserFromDB(String uid) async {
try {
var userData = await _firestore.collection("customers").doc(uid).get();
var map = userData.data();
//debugPrint(map!['email']);
return UserModel.fromData(userData.data());
} on FirebaseException catch (e) {
return e.message;
}
}
And this is my UserModel:
class UserModel {
String? uid;
String? name;
String? email;
int? bildirim;
int? userRole;
String? kullaniciAdi;
String? pphoto;
UserModel({
this.uid,
this.name,
this.email,
this.bildirim,
this.userRole,
this.kullaniciAdi,
this.pphoto,
});
UserModel.fromData(Map<String, dynamic>? dataMap)
: uid = dataMap!['id'],
name = dataMap['name'],
email = dataMap['email'],
bildirim = dataMap['bildirim'],
kullaniciAdi = dataMap['kullaniciAdi'];
Map<String, dynamic> toData() {
return {
'uid': uid,
'name': name,
'email': email,
'userRole': userRole,
'bildirim': bildirim,
'kullaniciAdi': kullaniciAdi,
'pphoto': pphoto,
};
}
}
I would like to listen this userModel after fetching data. But whenever I use Obx or GetX widget they returns an error.
Here is GetX widget and error;
GetX<UserController>(
initState: (_) async {
Get.find<UserController>().user = await FirebaseDatabase()
.getUserData(homeController.userVeri!.uid);
},
builder: (_) {
if (_.user.uid != null) {
return Text(
_.user.name.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.w500),
);
} else {
return const Text("...");
}
},
),
Error is:
Null check operator used on nulls value

It seems to me, that the only possible cause for the error is that you are getting the userVeri!.uid, with a null check(!). Inside the initState builder of your GetX widget. I would suggest first checking whether the userVeri.uid is not null before requesting their data. What I mean is more like this.
GetX<UserController>(
initState: (_) async {
if(homeController.userVeri != null){
Get.find<UserController>().user = await FirebaseDatabase()
.getUserData(homeController.userVeri!.uid);
}
},
builder: (_) {
if (_.user.uid != null && homeController.userVeri != null) {
return Text(
_.user.name.toString(),
style: const TextStyle(
color: Colors.white,
fontSize: 30,
fontWeight: FontWeight.w500),
);
} else {
return const Text("...");
}
},
),

Related

How to get data from database and into listview.builder

I am trying to get data from Firebase RTDB and then display them as a list using Listview.builder.
This worked well before, however I have added a new node into my database to have it be more stuctured. The problem is, inspite of there being data, it's showing up as empty when I try to retrieve it.
Database before:
Database Now:
Code:
Future<List> getParentDetails() async {
schoolName = await getSchoolName();
databaseReference
.child("users")
.child(schoolName.toString()) // 👈👈👈(newly added)
.child("parents")
.onValue
.listen(
(event) {
if (event.snapshot.exists) {
setState(
() {
var value = event.snapshot.value;
parentList = Map.from(value)
.values
.map((e) => Parents.fromJson(Map.from(e)))
.toList();
},
);
} else {
print("No Data Exists");
}
},
);
return parentList;
}
UI Code:
ListView.builder(
itemCount: parentList.length,
itemBuilder: (context, int index) {
final Parents parents = parentList[index];
final String parentEmail = parents.email;
final String parentName = parents.name;
final String parentPhone = parents.phone;
// final String parentUID = parents.uid;
return Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
elevation: 0.2,
child: ExpansionTile(
// collapsedBackgroundColor: Colors.grey,
title: Text(
parentName.toUpperCase(),
style: GoogleFonts.lexendMega(
fontSize: 12,
),
textAlign: TextAlign.center,
),
children: [
Column(
children: [
Text(
parentEmail,
textAlign: TextAlign.center,
style: GoogleFonts.lexendMega(fontSize: 12),
),
SizedBox(
height: 5,
),
Text(
parentPhone,
textAlign: TextAlign.center,
style: GoogleFonts.lexendMega(fontSize: 12),
),
SizedBox(
height: 5,
),
],
)
],
),
),
);
},
),
New Edit:
getSchoolName() async {
// ignore: unused_local_variable
final ref = FirebaseDatabase.instance.reference();
User user = auth.currentUser;
String adminUID = user.uid.toString();
print("Getting School Name");
databaseReference.child("users").child("admin").child(adminUID).once().then(
(DataSnapshot snapshot) {
setState(() {
schlName = snapshot.value["school"];
print(schlName); // 👈👈👈 (Prints - Highway Secondary School)
});
},
);
return await schlName;
}
Future<List> getParentDetails() async {
schoolName = await getSchoolName();
databaseReference
.child("users")
.child("Highway Secondary School")
.child("parents")
.onValue
.listen(
(event) {
if (event.snapshot.exists) {
setState(
() {
var value = event.snapshot.value;
print(value); // 👈👈👈 (See print value below)
parentList = Map.from(value)
.values
.map((e) => Parents.fromJson(Map.from(e)))
.toList();
},
);
} else {
print("No Data Exists");
}
},
);
return parentList;
}
Output:
{L9LnmRTZJgVJWjNhrcTqoRdMlas2: {driver: Locate the driver: , phone: ********, school: Highway Secondary School, name: Parent One, user uid: L9LnmRTZJgVJWjNhrcTqoRdMlas2, email: **********#gmail.com}, Z9nHn3HZ3MZqgS7RsKsFiofD4ty2: {driver: Locate the driver: , phone: ********, school: Highway Secondary School, name: Parent Two, user uid: Z9nHn3HZ3MZqgS7RsKsFiofD4ty2, email: ***********#gmail.com}}
Edit 2:
Future<List> getParentDetails() async {
schoolName = await getSchoolName();
print(schoolName); 👈👈👈 (Prints - null)
var ref = databaseReference.child("users/$schoolName/parents");
var snapshot = await ref.get();
if (snapshot.exists) {
setState(() {
var value = snapshot.value;
parentList = Map.from(value)
.values
.map((e) => Parents.fromJson(Map.from(e)))
.toList();
});
} else {
print("No Data Exists");
}
return parentList;
}
With newly added line of code above, the code doesn't get any data from the database (from the current database).
Without the newly added line of code, I get data from the old database without any issues.
The intention of the new database is to be more organized.
This will not work:
Future<List> getParentDetails() async {
schoolName = await getSchoolName();
databaseReference
.child("users")
.child("Highway Secondary School")
.child("parents")
.onValue
.listen(
(event) {
if (event.snapshot.exists) {
setState(
() {
var value = event.snapshot.value;
print(value); // 👈👈👈 (See print value below)
parentList = Map.from(value)
.values
.map((e) => Parents.fromJson(Map.from(e)))
.toList();
},
);
} else {
print("No Data Exists");
}
},
);
return parentList;
}
Calling listen starts an asynchronous process, but your main code continues to run. So your return parentList runs before the parentList = Map.from(value) is ever called. Adding some more print statements will show that in the order of they are output.
You also return a Future, which you can do only once, while listen can be called many times. If you only get about the current value, you should use get() instead of listen as shown in the documentation on reading data once.
With that, the code would look something like:
Future<List> getParentDetails() async {
schoolName = await getSchoolName();
var ref = databaseReference.child("users/Highway Secondary School/parents");
var snapshot = await ref.get();
if (snapshot.exists) {
setState(
() {
var value = snapshot.value;
parentList = Map.from(value)
.values
.map((e) => Parents.fromJson(Map.from(e)))
.toList();
},
);
} else {
print("No Data Exists");
}
return parentList;
}
Update: to also fix getSchoolName it's important to not mix then with async/await, but use one or the other only.
getSchoolName() async {
final ref = FirebaseDatabase.instance.reference();
User user = auth.currentUser;
String adminUID = user.uid.toString();
var snapshot = databaseReference.child("users/admin").child(adminUID).get();
schlName = snapshot.value["school"];
setState(() {});
return schlName;
}

Can't register user in firebase (firebase_auth/unknown: null)

I am registering a user in firebase and each time I try to register it shows me the mentioned error and donot send credentials to the firebase. Although it is getting the credentials from the firebase for login but shows error while storing values in firebase. Below is the code for only register that is getting the email address and password. I have another question that like password and email how could I store other details in firebase e.g Age, Gender etc. Kindly help me go through this.
class _ClientRegistrationScreenState extends State<ClientRegistrationScreen> {
bool showSpinner = false;
final _auth = FirebaseAuth.instance;
File image;
//final ImagePicker _picker = ImagePicker();
String password;
String confirmPassword;
String email;
String name;
bool _passwordVisible = false;
bool _confirmPasswordVisible = false;
#override
void initState() {
_passwordVisible = false;
_confirmPasswordVisible = false;
}
final _formKey = GlobalKey<FormState>();
Expanded(
child: Center(
child: TextFormField(
validator: (value) {
if (value == null || value.isEmpty) {
return '*Email Address Required';
}
return null;
},
),
),
),
),
Expanded(
child: Center(
child: TextFormField(
onChanged: (value){
password = value;
},
validator: (value) {
if (value == null || value.isEmpty) {
return '*Password Required';
}
if (password != confirmPassword) {
return 'Password Donot Match';
}
return null;
},
onPressed: () {
setState(() {
_passwordVisible = !_passwordVisible;
});
},
),
),
),
),
),
Expanded(
child: Center(
child: TextFormField(
onChanged: (value){
confirmPassword = value;
},
validator: (value) {
if (value == null || value.isEmpty) {
return '*Confirm Password';
}
if (password != confirmPassword) {
return 'Password Donot Match';
}
return null;
},
onPressed: () {
setState(() {
_confirmPasswordVisible = !_confirmPasswordVisible;
});
},
),
),
),
),
),
RoundedButton(
colour: Colors.yellow,
opColour: Colors.yellow.withOpacity(0.2),
title: 'Register',
buttonTextColor: Colors.black,
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() {
showSpinner = true;
});
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Processing Data')),
);
}
try {
final newUser = await _auth.createUserWithEmailAndPassword(
email: email, password: password);
if(newUser!=null){
Navigator.pushNamed(context, MainEmployeeScreen.id);
print(name);
}
setState(() {
showSpinner = false;
});
}
catch(e){
print(e);
}
}
),
Did you initialize firebase in main function?
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}

Null check operator cannot used on null value

this is my chatscreen code
import 'package:flutter/material.dart';
import 'package:messenger/helperfunction/sharedpref_helper.dart';
import 'package:messenger/services/database.dart';
import 'package:random_string/random_string.dart';
class ChatScreen extends StatefulWidget {
final String chatWithusername, name;
ChatScreen(this.chatWithusername, this.name);
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
String? chatRoomId, messageId = "";
String? myName, myProfilePic, myUserName, myEmail;
TextEditingController messageEditingController = TextEditingController();
getMyInfoFromSharedPreference() async {
myName = await SharedPreferenceHelper().getDisplayName();
myProfilePic = await SharedPreferenceHelper().getUserProfileUrl();
myUserName = await SharedPreferenceHelper().getUserName();
myEmail = await SharedPreferenceHelper().getUserEmail();
chatRoomId = getChatRoomIdByUsername(widget.chatWithusername, myUserName);
}
getChatRoomIdByUsername(String? a, String? b) {
if (a!.substring(0, 1).codeUnitAt(0) > b!.substring(0, 1).codeUnitAt(0)) {
return "$b\_$a";
} else {
return "$a\_$b";
}}
addMessage(bool sentClicked) {
if (messageEditingController.text != "") {
String message = messageEditingController.text;
var lastMessageTs = DateTime.now();
Map<String, dynamic> messageInfoMap = {
"message": message,
"sentBy": myUserName,
'ts': lastMessageTs,
"imgUrl": myProfilePic
};
//Gentrate the message id
if (messageId == "") {
messageId = randomAlphaNumeric(12);
}
DatabaseMethods()
.addMessage(chatRoomId!, messageId!, messageInfoMap)
.then((value) {
Map<String, dynamic>? lastMessageInfoMap = {
"lastmessage": message,
"lastMessageSendTs": lastMessageTs,
"lastMessageSendBy": myUserName
};
DatabaseMethods()
.updateLastmessageSend(chatRoomId!, lastMessageInfoMap);
if (sentClicked) {
messageEditingController.text = "";
messageId = "";
}
});
}
}
getAnfSentMessages() async {}
doThisOnlaunch() async {
await getMyInfoFromSharedPreference();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.name),
),
body: Container(
child: Stack(
children: [
Container(
alignment: Alignment.bottomCenter,
child: Container(
color: Colors.black.withOpacity(0.8),
padding: EdgeInsets.symmetric(
horizontal: 6,
vertical: 8,
),
child: Row(
children: [
Expanded(
child: TextField(
controller: messageEditingController,
onChanged: (value) {
addMessage(false);
},
style: TextStyle(color: Colors.white),
decoration: InputDecoration(
hintText: " Type a message",
hintStyle: TextStyle(
color: Colors.white.withOpacity(0.6))),
),
),
GestureDetector(
onTap: () {
addMessage(true);
},
child: Icon(Icons.send, color: Colors.white))
],
),
),
)
],
),
),
);
}
}
This is my DataBase;
import 'package:cloud_firestore/cloud_firestore.dart';
// ignore: unused_import
import 'package:firebase_core/firebase_core.dart';
class DatabaseMethods {
Future addUserInfoToDB(
String userId, Map<String, dynamic> userInfoMap) async {
return FirebaseFirestore.instance
.collection("users")
.doc(userId)
.set(userInfoMap);
}
Future<Stream<QuerySnapshot>> getUserByUserName(String username) async {
return FirebaseFirestore.instance
.collection("users")
.where("username", isEqualTo: username)
.snapshots();
}
Future addMessage(String chatRoomId, String messageId,
Map<String, dynamic> messageInfoMap) async {
return FirebaseFirestore.instance
.collection("chatRooms")
.doc(chatRoomId)
.collection("chats")
.doc(messageId)
.set(messageInfoMap);
}
updateLastmessageSend(
String chatRoomId, Map<String, dynamic> lastMessageInfoMap) {
return FirebaseFirestore.instance
.collection("chatRooms")
.doc(chatRoomId)
.update(lastMessageInfoMap);
}
createChatRoom(
String chatRoomId, Map<String, dynamic> chatRoomInfoMap) async {
final snapShot = await FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.get();
if (snapShot.exists) {
//chatroom already exits
return true;
} else {
//chatRoom does not exit:
return FirebaseFirestore.instance
.collection("chatrooms")
.doc(chatRoomId)
.set(chatRoomInfoMap);
}
}
}
when i run this code and start typing in textformfield, i am getting this following error:
'''
════════ Exception caught by widgets
═══════════════════════════════════════════
The following _CastError was thrown while calling onChanged:
Null check operator used on a null value
When the exception was thrown, this was the stack
#0 _ChatScreenState.addMessage
package:messenger/view/chatscreen.dart:54
#1 _ChatScreenState.build.<anonymous closure>
package:messenger/view/chatscreen.dart:103
#2 EditableTextState._formatAndSetValue
package:flutter/…/widgets/editable_text.dart:2298
#3 EditableTextState.updateEditingValue
package:flutter/…/widgets/editable_text.dart:1749
#4 TextInput._handleTextInputInvocation
package:flutter/…/services/text_input.dart:1351
'''
After typed, when i clicked sent icon i am getting error like this:
'''
════════ Exception caught by gesture
═══════════════════════════════════════════
Null check operator used on a null value
════════════════════════════════════════════════════════════════════════
'''
can anyone help to solve this issue
After digging into your code I can see that this function is never called
doThisOnlaunch() async {
await getMyInfoFromSharedPreference();
}
So the getMyInfoFromSharedPreference() function is never called
So chatRoomId is not initialized and it will always be null Because of this code
String? chatRoomId, messageId = "";
And in this method you are using the ! on chatRoomId which is null.
DatabaseMethods()
.addMessage(chatRoomId!, messageId!, messageInfoMap).then((value) {...
So this is what causes the error.
Solution
Call tis method on initState like
String? chatRoomId, messageId = "";
String? myName, myProfilePic, myUserName, myEmail;
TextEditingController messageEditingController = TextEditingController();
#override
void initState() {
super.initState();
doThisOnlaunch();
}
.
.
.
or just initiate chatRoomId with a value and declare them as not nullable String
String chatRoomId="", messageId = "";
Note that this second solution will just stop the error but your code will not function properly
Somewhere you used ! on a variable and that variable is null. Check the error where it is.
getChatRoomIdByUsername(String? a, String? b) {
if (a!.substring(0, 1).codeUnitAt(0) > b!.substring(0, 1).codeUnitAt(0)) {
return "$b\_$a";
} else {
return "$a\_$b";
}}
Why are you allowing nullable variables if the function depends on this?
getChatRoomIdByUsername({required String a,required String b}) {
if (a.substring(0, 1).codeUnitAt(0) > b.substring(0, 1).codeUnitAt(0)) {
return "$b\_$a";
} else {
return "$a\_$b";
}
}

Error appears only the first call, once the screen opens again, the error disappears,

As per the screenshot once I open the screen it gives me the error "The getter 'email' was called on null." but if I just click back button, then open the screen again, it works well without any error,
the purpose of the screen is the account owner see the notes that was sent to him from the admin account
This is the error appears only one time after opening the app
Here the same screen without any error
Here is the codes in dart where the tap to screen exists
import 'package:flutter/cupertino.dart' show CupertinoIcons;
import 'package:flutter/material.dart';
import 'package:notification_permissions/notification_permissions.dart';
import 'package:provider/provider.dart';
import 'package:rate_my_app/rate_my_app.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../../app.dart';
import '../../common/config.dart';
import '../../common/constants.dart';
import '../../common/tools.dart';
import '../../generated/l10n.dart';
import '../../models/index.dart' show AppModel, User, UserModel, WishListModel;
import '../../routes/flux_navigate.dart';
import '../../screens/blogs/post_screen.dart';
import '../../services/index.dart';
import '../../widgets/common/webview.dart';
import '../custom/smartchat.dart';
import '../index.dart';
import '../users/user_point.dart';
import 'currencies.dart';
import 'language.dart';
import 'notification.dart';
import '../../common/config.dart' as config;
import 'package:cespohm/screens/bywaleed/booking_admin_screen.dart';
import 'package:cespohm/screens/bywaleed/docor_note_tap.dart';
import 'package:cespohm/screens/bywaleed/user_search_new_screen.dart';
import 'package:cespohm/screens/bywaleed/doctor_notes_user_screen.dart';
class SettingScreen extends StatefulWidget {
final List<dynamic> settings;
final String background;
final User user;
final VoidCallback onLogout;
final bool showChat;
SettingScreen({
this.user,
this.onLogout,
this.settings,
this.background,
this.showChat,
});
#override
_SettingScreenState createState() {
return _SettingScreenState();
}
}
class _SettingScreenState extends State<SettingScreen>
with
TickerProviderStateMixin,
WidgetsBindingObserver,
AutomaticKeepAliveClientMixin<SettingScreen> {
#override
bool get wantKeepAlive => true;
final bannerHigh = 150.0;
bool enabledNotification = true;
final RateMyApp _rateMyApp = RateMyApp(
// rate app on store
minDays: 7,
minLaunches: 10,
remindDays: 7,
remindLaunches: 10,
googlePlayIdentifier: kStoreIdentifier['android'],
appStoreIdentifier: kStoreIdentifier['ios']);
void showRateMyApp() {
_rateMyApp.showRateDialog(
context,
title: S.of(context).rateTheApp,
// The dialog title.
message: S.of(context).rateThisAppDescription,
// The dialog message.
rateButton: S.of(context).rate.toUpperCase(),
// The dialog "rate" button text.
noButton: S.of(context).noThanks.toUpperCase(),
// The dialog "no" button text.
laterButton: S.of(context).maybeLater.toUpperCase(),
// The dialog "later" button text.
listener: (button) {
// The button click listener (useful if you want to cancel the click event).
switch (button) {
case RateMyAppDialogButton.rate:
break;
case RateMyAppDialogButton.later:
break;
case RateMyAppDialogButton.no:
break;
}
return true; // Return false if you want to cancel the click event.
},
// Set to false if you want to show the native Apple app rating dialog on iOS.
dialogStyle: const DialogStyle(),
// Custom dialog styles.
// Called when the user dismissed the dialog (either by taping outside or by pressing the "back" button).
// actionsBuilder: (_) => [], // This one allows you to use your own buttons.
);
}
#override
void initState() {
super.initState();
Future.delayed(Duration.zero, () async {
await checkNotificationPermission();
});
_rateMyApp.init().then((_) {
// state of rating the app
if (_rateMyApp.shouldOpenDialog) {
showRateMyApp();
}
});
}
// #override
// void dispose() {
// Utils.setStatusBarWhiteForeground(false);
// super.dispose();
// }
Future<void> checkNotificationPermission() async {
if (!isAndroid || isIos) {
return;
}
try {
await NotificationPermissions.getNotificationPermissionStatus()
.then((status) {
if (mounted) {
setState(() {
enabledNotification = status == PermissionStatus.granted;
});
}
});
} catch (err) {
printLog('[Settings Screen] : ${err.toString()}');
}
}
#override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
checkNotificationPermission();
}
}
/// Render the Admin Vendor Menu
Widget renderVendorAdmin() {
if (!(widget.user != null ? widget.user.isVendor ?? false : false)) {
return Container();
}
return Card(
color: Theme.of(context).backgroundColor,
margin: const EdgeInsets.only(bottom: 2.0),
elevation: 0,
child: ListTile(
onTap: () {
final String langCode =
Provider.of<AppModel>(context, listen: false).langCode;
if (unsupportedLanguages.contains(langCode)) {
final snackBar = SnackBar(
content: Text(
S.of(context).thisFeatureDoesNotSupportTheCurrentLanguage),
duration: const Duration(seconds: 1),
);
Scaffold.of(context).showSnackBar(snackBar);
return;
}
FluxNavigate.push(
MaterialPageRoute(
builder: (context) =>
Services().widget.getAdminVendorScreen(context, widget.user),
),
forceRootNavigator: true,
);
},
leading: Icon(
Icons.dashboard,
size: 24,
color: Theme.of(context).accentColor,
),
title: Text(
S.of(context).vendorAdmin,
style: const TextStyle(fontSize: 16),
),
trailing: Icon(
Icons.arrow_forward_ios,
size: 18,
color: Theme.of(context).accentColor,
),
),
);
}
/// Render the custom profile link via Webview
/// Example show some special profile on the woocommerce site: wallet, wishlist...
Widget renderWebViewProfile() {
if (widget.user == null) {
return Container();
}
var base64Str = Utils.encodeCookie(widget.user.cookie);
var profileURL = '${serverConfig["url"]}/my-account?cookie=$base64Str';
return Card(
color: Theme.of(context).backgroundColor,
margin: const EdgeInsets.only(bottom: 2.0),
elevation: 0,
child: ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => WebView(
url: profileURL, title: S.of(context).updateUserInfor),
),
);
},
leading: Icon(
CupertinoIcons.profile_circled,
size: 24,
color: Theme.of(context).accentColor,
),
title: Text(
S.of(context).updateUserInfor,
style: const TextStyle(fontSize: 16),
),
trailing: Icon(
Icons.arrow_forward_ios,
color: Theme.of(context).accentColor,
size: 18,
),
),
);
}
Widget renderItem(value) {
IconData icon;
String title;
Widget trailing;
Function() onTap;
bool isMultiVendor = kFluxStoreMV.contains(serverConfig['type']);
switch (value) {
case 'bookingAdmin':
if (widget.user == null ) {
return Container();
}
else if(widget.user.email == config.adminEmail || widget.user.email == config.adminEmailTwo && widget.user != null )
{
icon = FontAwesomeIcons.keyboard;
title = S.of(context).checkout;
trailing =
const Icon(Icons.arrow_forward_ios, size: 18, color: kGrey600);
onTap = () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => const BookingAdminScreen()),
);
}
else {
return Container();
}
break;
case 'addDoctorNote':
if (widget.user == null ) {
return Container();
}
else if(widget.user.isVendor && widget.user != null )
{
icon = FontAwesomeIcons.edit;
title = S.of(context).continueToShipping;
trailing =
const Icon(Icons.arrow_forward_ios, size: 18, color: kGrey600);
onTap = () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => const UserSearchNewScreen()),
);
}
else {
return Container();
}
break;
/// here is the tap to the screen where there is the error
case 'yourDoctorNotes':
if (widget.user == null || widget.user.email == config.adminEmail || widget.user.email == config.adminEmailTwo ) {
return Container();
}
else if(!widget.user.isVendor)
{
icon = FontAwesomeIcons.notesMedical;
title = S.of(context).french;
trailing =
const Icon(Icons.arrow_forward_ios, size: 18, color: kGrey600);
onTap = () => Navigator.push(
context,
MaterialPageRoute(builder: (context) => DoctorNoteUserScreen(senderUser: widget.user,)),
);
}
else {
return Container();
}
break;
case 'chat':
here is the dart code of the screen where is the error
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:date_format/date_format.dart';
import 'package:flutter/material.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_model.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_provider.dart';
import 'package:cespohm/models/entities/user.dart';
import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
import 'package:provider/provider.dart';
import '../../generated/l10n.dart' as word;
import '../../models/index.dart' show AppModel, Store, User, UserModel ;
final _fireStore = FirebaseFirestore.instance;
firebase_auth.User loggedInUser;
class DoctorNoteUserScreen extends StatelessWidget {
final User senderUser;
DoctorNoteUserScreen({
Key key,
this.senderUser,
}) : super(key: key);
#override
Widget build(BuildContext context) {
final userSearchProvider = Provider.of<UserSearchProvider>(context);
return Scaffold(
appBar: AppBar(
title: Text(
word.S.of(context).french,
style: TextStyle(
color: Colors.white, fontSize: 18.0, fontWeight: FontWeight.w400),
),
leading: Center(
child: GestureDetector(
child: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
),
onTap: () => Navigator.pop(context),
),
),
),
body: StreamBuilder<List<UserSearchModel>>(
stream: userSearchProvider.userstwo,
builder: (context, snapshot) {
if (!snapshot.hasData ) {
return Center(
child: CircularProgressIndicator(
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).primaryColor),
),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(7.0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
SizedBox(
height: 40.0,
),
Text(word.S.of(context).french,
style: TextStyle(fontSize: 18.0)),
SizedBox(
height: 15.0,
),
Divider(
thickness: 1.0,
)
],
),
),
Container(
child: ListTile(
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
width: 15.0,
),
Container(
child: Text(snapshot.data[index].doctorNote!=null?snapshot.data[index].doctorNote: 'No Notes Yet',
style: TextStyle(fontSize: 20.0)),
),
],
),
),
),
],
),
);
});
}
}),
);
}
}
here is the dart code of the service
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_model.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_provider.dart';
import 'package:cespohm/models/user_model.dart';
class UserSearchService {
final FirebaseFirestore _db = FirebaseFirestore.instance;
final userModel = UserModel();
//Get Entries
Stream<List<UserSearchModel>> getEntries(){
return _db
.collection('users')
.orderBy('firstName', descending: false)
.snapshots()
.map((snapshot) => snapshot.docs
.map((doc) => UserSearchModel.fromJson(doc.data()))
.toList());
}
/// here is the firebase collection where the error exists
Stream<List<UserSearchModel>> getEntriesTwo(){
return _db
.collection('users').where('email', isEqualTo: userModel.user.email)
.snapshots()
.map((snapshot) => snapshot.docs
.map((doc) => UserSearchModel.fromJson(doc.data()))
.toList());
}
}
here is the model
import 'package:cloud_firestore/cloud_firestore.dart';
class UserSearchModel {
final String date;
final String firstName;
final String lastName;
final String phone;
final String email;
final String searchKey;
final String doctorNote;
final String bYDoctor;
final String uID;
UserSearchModel(
{this.date,
this.firstName,
this.lastName,
this.phone,
this.email,
this.searchKey,
this.doctorNote,
this.bYDoctor,
this.uID});
// creating a Trip object from a firebase snapshot
UserSearchModel.fromSnapshot(DocumentSnapshot snapshot) :
date = snapshot['date'],
firstName = snapshot['firstName'].toDate(),
lastName = snapshot['lastName'].toDate(),
phone = snapshot['phone'],
email = snapshot['email'],
searchKey = snapshot['searchKey'],
doctorNote = snapshot['doctorNote'],
bYDoctor = snapshot['bYDoctor'],
uID = snapshot.id;
factory UserSearchModel.fromJson(Map<String, dynamic> json) {
return UserSearchModel(
date: json['createdAt'],
firstName: json['firstName'],
lastName: json['lastName'],
phone: json['phone'],
email: json['email'],
searchKey: json['searchKey'],
doctorNote: json['doctorNote'],
bYDoctor: json['bYDoctor'],
uID: json['uID']);
}
Map<String, dynamic> toMap() {
return {
'createdAt': date,
'firstName': firstName,
'lastName': lastName,
'phone': phone,
'email': email,
'searchKey': searchKey,
'doctorNote': doctorNote,
'bYDoctor': bYDoctor,
'uID': uID,
};
}
}
Here is the provider
import 'package:flutter/material.dart';
import 'package:cespohm/models/bywaleed_model/user_search_new_model.dart';
import 'package:cespohm/models/user_model.dart';
import 'package:cespohm/services/bywaleed/user_search_new_service.dart';
import 'package:uuid/uuid.dart';
class UserSearchProvider with ChangeNotifier {
final userSearchService = UserSearchService();
final userModel = UserModel();
DateTime _date;
String _firstName;
String _lastName;
String _phone;
String _email;
String _searchKey;
String _doctorNote;
String _bYDoctor;
String _uID;
var uuid = Uuid();
//Getters
DateTime get date => _date;
String get firstName => _firstName;
String get lastName => _lastName;
String get phone => _phone;
String get email => _email;
String get searchKey => _searchKey;
String get doctorNote => _doctorNote;
String get byDoctor => _bYDoctor;
String get uID => _uID;
Stream<List<UserSearchModel>> get users => userSearchService.getEntries();
Stream<List<UserSearchModel>> get userstwo => userSearchService.getEntriesTwo();
//Setters
set changeDate(DateTime date) {
_date = date;
notifyListeners();
}
set changeFirstName(String firstName) {
_firstName = firstName;
notifyListeners();
}
set changeLastName(String lastName) {
_lastName = lastName;
notifyListeners();
}
set changePhone(String phone) {
_phone = phone;
notifyListeners();
}
set changeEmail(String email) {
_email = email;
notifyListeners();
}
set changeSearchKey(String searchKey) {
_searchKey = searchKey;
notifyListeners();
}
set changeDoctorNote(String doctorNote) {
_doctorNote = doctorNote;
notifyListeners();
}
set changeBYDoctor(String bYDoctor) {
_bYDoctor = bYDoctor;
notifyListeners();
}
set changeuID(String uID) {
_uID = uID;
notifyListeners();
}
//Functions
loadAll(UserSearchModel userSearchModel) {
if (userSearchModel != null) {
_date = DateTime.parse(userSearchModel.date);
_firstName = userSearchModel.firstName;
_lastName = userSearchModel.lastName;
_phone = userSearchModel.phone;
_email = userSearchModel.email;
_searchKey = userSearchModel.searchKey;
_doctorNote = userSearchModel.doctorNote;
_bYDoctor = userModel.user.email;
_uID = userSearchModel.uID;
} else {
_date = DateTime.now();
_firstName = null;
_lastName = null;
_phone = null;
_email = null;
_searchKey = null;
_doctorNote = null;
_bYDoctor = null;
_uID = null;
}
}
saveEntry() {
if (_email == null) {
//Add
var newUserModel = UserSearchModel(
date: _date.toIso8601String(),
firstName: _firstName,
lastName: _lastName,
phone: _phone,
email: _email,
searchKey: _searchKey,
doctorNote: _doctorNote,
bYDoctor: _bYDoctor,
uID: _uID);
print(newUserModel.email);
userSearchService.setEntry(newUserModel);
} else {
//Edit
var updatedEntry = UserSearchModel(
date: _date.toIso8601String(),
firstName: _firstName,
lastName: _lastName,
phone: _phone,
email: _email,
searchKey: _searchKey,
doctorNote: _doctorNote,
bYDoctor: _bYDoctor,
uID: _uID);
userSearchService.setEntry(updatedEntry);
}
}
removeEntry(String entryId) {
userSearchService.removeEntry(entryId);
}
}
You need to make sure your User object is initialised. This error appears because user.email is accessed, but your User object is null. Make sure your code considers loading time for the user object if it comes from the API, and has appropriate checks for the case when user == null.

flutter validate form asynchronously

new TextFormField(
validator: (value) async{
if (value.isEmpty) {
return 'Username is required.';
}
if (await checkUser()) {
return 'Username is already taken.';
}
},
controller: userNameController,
decoration: InputDecoration(hintText: 'Username'),
),
I have a form for user, and I want to check if the user already exists in the firestore datebase.
Future checkUser() async {
var user = await Firestore.instance
.collection('users')
.document(userNameController.text)
.get();
return user.exists;
}
This is my function to check if the user document already exists in the database.
But validator gives me this error.
[dart] The argument type '(String) → Future' can't be assigned to the parameter type '(String) → String'.
How should I fix this issue?
At this time I think that you can't associate a Future to a validator.
What you can do is this verifying the data on a button click or in another way and set the state on the validator response var.
#override
Widget build(BuildContext context) {
// TODO: implement build
return Scaffold(
body: Form(
key: _formKey,
child: Column(children: [
new TextFormField(
validator: (value) {
return usernameValidator;
},
decoration: InputDecoration(hintText: 'Username')),
RaisedButton(
onPressed: () async {
var response = await checkUser();
setState(() {
this.usernameValidator = response;
});
if (_formKey.currentState.validate()) {}
},
child: Text('Submit'),
)
])));
}
I needed to do this for username validation recently (to check if a username already exists in firebase) and this is how I achieved async validation on a TextFormField ( without installation of any additional packages). I have a "users" collection where the document name is the unique username ( Firebase can't have duplicate document names in a collection but watch out for case sensitivity)
//In my state class
class _MyFormState extends State<MyForm> {
final _usernameFormFieldKey = GlobalKey<FormFieldState>();
//Create a focus node
FocusNode _usernameFocusNode;
//Create a controller
final TextEditingController _usernameController = new TextEditingController();
bool _isUsernameTaken = false;
String _usernameErrorString;
#override
void initState() {
super.initState();
_usernameFocusNode = FocusNode();
//set up focus node listeners
_usernameFocusNode.addListener(_onUsernameFocusChange);
}
#override
void dispose() {
_usernameFocusNode.dispose();
_usernameController.dispose();
super.dispose();
}
}
Then in my TextFormField widget
TextFormField(
keyboardType: TextInputType.text,
focusNode: _usernameFocusNode,
textInputAction: TextInputAction.next,
controller: _usernameController,
key: _usernameFormFieldKey,
onEditingComplete: _usernameEditingComplete,
validator: (value) => _isUsernameTaken ? "Username already taken" : _usernameErrorString,)
Listen for focus changes on the widget i.e when it loses focus. You can also do something similar for "onEditingComplete" method
void _onUsernameFocusChange() {
if (!_usernameFocusNode.hasFocus) {
String message = UsernameValidator.validate(_usernameController.text.trim());
//First make sure username is in valid format, if it is then check firebase
if (message == null) {
Firestore.instance.collection("my_users").document(_usernameController.text.trim()).get().then((doc) {
if (doc.exists) {
setState(() {
_isUsernameTaken = true;
_usernameErrorString = null;
});
} else {
setState(() {
_isUsernameTaken = false;
_usernameErrorString = null;
});
}
_usernameFormFieldKey.currentState.validate();
}).catchError((onError) {
setState(() {
_isUsernameTaken = false;
_usernameErrorString = "Having trouble verifying username. Please try again";
});
_usernameFormFieldKey.currentState.validate();
});
} else {
setState(() {
_usernameErrorString = message;
});
_usernameFormFieldKey.currentState.validate();
}
}
}
For completeness, this is my username validator class
class UsernameValidator {
static String validate(String value) {
final regexUsername = RegExp(r"^[a-zA-Z0-9_]{3,20}$");
String trimmedValue = value.trim();
if (trimmedValue.isEmpty) {
return "Username can't be empty";
}
if (trimmedValue.length < 3) {
return "Username min is 3 characters";
}
if (!regexUsername.hasMatch(trimmedValue)) {
return "Usernames should be a maximum of 20 characters with letters, numbers or underscores only. Thanks!";
}
return null;
}
}
I had the same problem while using Firebase's Realtime Database but I found a pretty good solution similar to Zroq's solution. This function creates a simple popup form to have the user input a name. Essentially, I was trying to see if a particular name for a specific user was already in the database and show a validation error if true. I created a local variable called 'duplicate' that is changed anytime the user clicks the ok button to finish. Then I can call the validator again if there is an error, and the validator will display it.
void add(BuildContext context, String email) {
String _name;
bool duplicate = false;
showDialog(
context: context,
builder: (_) {
final key = GlobalKey<FormState>();
return GestureDetector(
onTap: () => FocusScope.of(context).requestFocus(new FocusNode()),
child: AlertDialog(
title: Text("Add a Workspace"),
content: Form(
key: key,
child: TextFormField(
autocorrect: true,
autofocus: false,
decoration: const InputDecoration(
labelText: 'Title',
),
enableInteractiveSelection: true,
textCapitalization: TextCapitalization.sentences,
onSaved: (value) => _name = value.trim(),
validator: (value) {
final validCharacters =
RegExp(r'^[a-zA-Z0-9]+( [a-zA-Z0-9]+)*$');
if (!validCharacters.hasMatch(value.trim())) {
return 'Alphanumeric characters only.';
} else if (duplicate) {
return 'Workspace already exists for this user';
}
return null;
},
),
),
actions: <Widget>[
FlatButton(
child: const Text("Ok"),
onPressed: () async {
duplicate = false;
if (key.currentState.validate()) {
key.currentState.save();
if (await addToDatabase(_name, email) == false) {
duplicate = true;
key.currentState.validate();
} else {
Navigator.of(context).pop(true);
}
}
},
),
FlatButton(
child: const Text('Cancel'),
onPressed: () {
Navigator.of(context).pop(false);
},
),
],
),
);
});
}

Resources