Issue with Flutter retrieving data from Firestore (Authenticating different users types from collection) - flutter

I am having an issue with trying to route different users to different views in flutter.
I will attach my code below. I have authentication working and routing to the main view. I am now trying to query a Users table in my database (screenshot attached). I want to check by email that the user isStudent and route to menu page. if not a student then route to a separate page.
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_tex/flutter_tex.dart';
import 'package:maths_for_computing/components/roundedButton.dart';
import 'package:maths_for_computing/screens/chat_screen.dart';
import 'package:flutter/material.dart';
import 'package:maths_for_computing/components/roundedButton.dart';
import 'package:maths_for_computing/constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:maths_for_computing/screens/latex_example.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import 'chat_screen.dart';
import 'menu_student.dart';
final GlobalKey<ScaffoldState> _key = GlobalKey(); // Create a key
final _firestore = FirebaseFirestore.instance;
class LoginScreen extends StatefulWidget {
static const String id = 'login_screen';
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
bool showSpinner = false;
final _auth = FirebaseAuth.instance;
late String email;
late String password;
Future getUsers(String email) async {
final users = await _firestore
.collection('Users')
.where('email', isEqualTo: email)
.get();
for (var user in users.docs) {
print(user.get("email"));
//return user.get("isStudent") as String;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Flexible(
child: Hero(
tag: 'logo',
child: Container(
height: 200.0,
child: Image.asset('images/logo.png'),
),
),
),
const SizedBox(
height: 48.0,
),
TextField(
keyboardType: TextInputType.emailAddress,
textAlign: TextAlign.center,
onChanged: (value) {
//Do something with the user input.
email = value;
},
decoration:
kTextFieldDecoration.copyWith(hintText: 'Enter your email'),
),
const SizedBox(
height: 8.0,
),
TextField(
obscureText: true,
textAlign: TextAlign.center,
onChanged: (value) {
//Do something with the user input.
password = value;
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Enter your password'),
),
const SizedBox(
height: 24.0,
),
RoundedButton(
title: 'Log In',
colour: Colors.lightBlueAccent,
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
final user = await _auth.signInWithEmailAndPassword(
email: email, password: password);
String userType = getUsers(email) ;
// getUsers(email);
if (user != null) {
if (isStudent == 'N') {
Navigator.pushNamed(context, ChatScreen.id);
} else {
Navigator.pushNamed(context, MenuScreen.id);
}
}
} catch (e) {
print(e);
}
setState(() {
showSpinner = false;
});
},
),
],
),
),
),
);
}
}
Any help or advice on this or best practice would be much appreciated. This is a small app so trying to stay away from Custom claims as I am very new to flutter and don't want to overcomplicate things.
Many thanks
E

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_tex/flutter_tex.dart';
import 'package:maths_for_computing/components/roundedButton.dart';
import 'package:maths_for_computing/screens/chat_screen.dart';
import 'package:flutter/material.dart';
import 'package:maths_for_computing/components/roundedButton.dart';
import 'package:maths_for_computing/constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:maths_for_computing/screens/latex_example.dart';
import 'package:modal_progress_hud_nsn/modal_progress_hud_nsn.dart';
import 'chat_screen.dart';
import 'menu_student.dart';
final GlobalKey<ScaffoldState> _key = GlobalKey(); // Create a key
final _firestore = FirebaseFirestore.instance;
class LoginScreen extends StatefulWidget {
static const String id = 'login_screen';
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
bool showSpinner = false;
final _auth = FirebaseAuth.instance;
late String email;
late String password;
Future getUsers(String email) async {
final users = await _firestore
.collection('Users')
.where('email', isEqualTo: email)
.get();
for (var user in users.docs) {
print(user.get("email"));
//return user.get("isStudent") as String;
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
body: ModalProgressHUD(
inAsyncCall: showSpinner,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Flexible(
child: Hero(
tag: 'logo',
child: Container(
height: 200.0,
child: Image.asset('images/logo.png'),
),
),
),
const SizedBox(
height: 48.0,
),
TextField(
keyboardType: TextInputType.emailAddress,
textAlign: TextAlign.center,
onChanged: (value) {
//Do something with the user input.
email = value;
},
decoration:
kTextFieldDecoration.copyWith(hintText: 'Enter your email'),
),
const SizedBox(
height: 8.0,
),
TextField(
obscureText: true,
textAlign: TextAlign.center,
onChanged: (value) {
//Do something with the user input.
password = value;
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Enter your password'),
),
const SizedBox(
height: 24.0,
),
RoundedButton(
title: 'Log In',
colour: Colors.lightBlueAccent,
onPressed: () async {
setState(() {
showSpinner = true;
});
try {
final _auth = FirebaseAuth.instance;
await _auth.signInWithEmailAndPassword(email: email, password: password).then((uid) => {
FirebaseFirestore.instance.collection("users").doc(uid.user.uid).get().then((value) {
Map<dynamic, dynamic> currentUser = value.data();
if(currentUser['isStudent']=="N") {
Navigator.pushNamed(context, ChatScreen.id);
}
else {
Navigator.pushNamed(context, MenuScreen.id);
}
}),
});
} catch (e) {
print(e);
}
setState(() {
showSpinner = false;
});
},
),
],
),
),
),
);
}
}

Related

State not triggering while clicking login button

Every thing is working fine except that the page is not navigating while I press login button. The user is logged in and The screen is changing once I reload it. But the screen has to change when I click the login button. I used the setState fuction But still not working.Please help me solve this. Thanks in advance.
This is my root page
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:login/main.dart';
import 'package:login/services/auth_service.dart';
class Root extends StatefulWidget {
const Root({Key? key}) : super(key: key);
#override
State<Root> createState() => _RootState();
}
enum AuthState { signedIn, signedOut }
class _RootState extends State<Root> {
AuthState _authState = AuthState.signedOut;
AuthService auth = AuthService();
String authuser = "ghnfgjy";
void signOut() async {
await auth.signOut();
setState(() {
_authState = AuthState.signedOut;
});
}
void signIn() async {
setState(() {
_authState = AuthState.signedIn;
});
}
#override
void initState() {
super.initState();
auth.currentUser().then((user) {
print("check check $user ");
setState(() {
_authState = user == null ? AuthState.signedOut : AuthState.signedIn;
});
});
}
#override
Widget build(BuildContext context) {
if (_authState == AuthState.signedOut) {
return Login();
} else {
return Scaffold(
body: Center(
child: Column(
children: [
SizedBox(
height: 69,
),
Text("Helllloooooooo $authuser"),
SizedBox(
height: 45,
),
FlatButton(
onPressed: () {
signOut();
},
child: Text("Sign out"))
],
),
));
}
}
}
This is my login.dart page
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:login/main.dart';
import 'package:login/services/auth_service.dart';
import 'package:login/share/constant.dart';
class Login extends StatefulWidget {
const Login({Key? key}) : super(key: key);
#override
_LoginState createState() => _LoginState();
}
enum FormType {
login,
register,
}
class _LoginState extends State<Login> {
dynamic _email;
dynamic _password;
final formkey = GlobalKey<FormState>();
FormType _formType = FormType.login;
dynamic result;
AuthService auth = AuthService();
bool validateandsave() {
final form = formkey.currentState;
if (form!.validate()) {
form.save();
print('form is valid');
print('$_email $_password');
return true;
} else {
print('form is not valid');
return false;
}
}
Future validateandsumit() async {
if (validateandsave()) {
if (_formType == FormType.register) {
auth.register(_email, _password);
setState(() {
_formType = FormType.login;
});
} else {
auth.signIn(_email, _password);
}
}
}
void moveToRegister() {
formkey.currentState!.reset();
setState(() {
_formType = FormType.register;
});
}
void moveToLogin() {
formkey.currentState!.reset();
setState(() {
_formType = FormType.login;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: const LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Colors.blueAccent,
Colors.purple,
],
),
),
padding: EdgeInsets.all(16),
child: Form(
key: formkey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisAlignment: MainAxisAlignment.center,
children: openText() + inputFields() + buttons(),
),
),
),
);
}
List<Widget> openText() {
if (_formType == FormType.register) {
return [
Text(
'Please Register to continue',
style: TextStyle(
fontSize: 35, color: Colors.white, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
SizedBox(
height: 40,
),
];
} else {
return [
Text('Please Login to continue',
style: TextStyle(
fontSize: 35, color: Colors.white, fontWeight: FontWeight.bold),
textAlign: TextAlign.center),
SizedBox(
height: 40,
),
];
}
}
List<Widget> inputFields() {
return [
TextFormField(
decoration: inputDecoration.copyWith(
labelText: 'Email Address',
),
style: TextStyle(color: Colors.black),
validator: (val) => val!.isEmpty ? 'Enter the email address' : null,
onSaved: (val) => _email = val),
SizedBox(
height: 20,
),
TextFormField(
obscureText: true,
decoration: inputDecoration.copyWith(
labelText: 'Password',
),
style: TextStyle(color: Colors.black),
validator: (val) => val!.isEmpty ? 'Enter the password' : null,
onSaved: (val) => _password = val),
SizedBox(
height: 60,
),
];
}
List<Widget> buttons() {
if (_formType == FormType.register) {
return [
FlatButton(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 60),
onPressed: validateandsumit,
child: Text(
'Register',
style: TextStyle(fontSize: 20, color: Colors.white),
),
color: Colors.pinkAccent,
),
SizedBox(
height: 20,
),
FlatButton(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 60),
onPressed: moveToLogin,
child: Text(
'Have an account? Login',
style: TextStyle(fontSize: 20, color: Colors.white),
),
color: Colors.pinkAccent,
),
];
} else {
return [
FlatButton(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 60),
onPressed: validateandsumit,
child: Text(
'Login',
style: TextStyle(fontSize: 20, color: Colors.white),
),
color: Colors.pinkAccent,
),
SizedBox(
height: 20,
),
FlatButton(
padding: EdgeInsets.symmetric(vertical: 10, horizontal: 60),
onPressed: moveToRegister,
child: Text(
'Register',
style: TextStyle(fontSize: 20, color: Colors.white),
),
color: Colors.pinkAccent,
),
];
}
}
}
This is my authservice file
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class AuthService {
FirebaseAuth _auth = FirebaseAuth.instance;
//Stream <User>? get authStateChanges => _auth.authStateChanges();
Stream<User?> get authStateChanges => _auth.authStateChanges();
CollectionReference users = FirebaseFirestore.instance.collection('users');
late User user;
//register in with email and password
Future register(email, password) async {
try {
UserCredential userCredential = await _auth
.createUserWithEmailAndPassword(email: email, password: password);
user = userCredential.user!;
print('Registered ${user.uid}');
var userdata = {
'email': email,
'password': password,
'role': 'user',
};
users.doc(user.uid).get().then((doc) {
if (doc.exists) {
doc.reference.update(userdata);
} else {
users.doc(user.uid).set(userdata);
}
});
} catch (e) {
print(e);
}
}
//sign in with email and password
Future signIn(email, password) async {
try {
UserCredential userCredential = await _auth.signInWithEmailAndPassword(
email: email, password: password);
user = userCredential.user!;
print('logged in ${user.uid}');
var userdata = {
'email': email,
'password': password,
'role': 'user',
};
users.doc(user.uid).get().then((doc) {
if (doc.exists) {
doc.reference.update(userdata);
} else {
users.doc(user.uid).set(userdata);
}
});
} catch (e) {
print(e);
}
}
Future currentUser() async {
try {
User user = await _auth.currentUser!;
return user.uid;
} catch (e) {
print(e);
}
}
//sign out
Future signOut() async {
try {
await _auth.signOut();
} catch (e) {
print(e);
}
}
}
I think the problem is that the build function in _RootState does not rebuild when you login.
One possible solution is to use StreamBuilder to rebuild whenever auth changes (login/logout).
Add this function to your AuthService class:
class AuthService {
final _auth = FirebaseAuth.instance;
...
Stream<User?> authStream() => _auth.authStateChanges(); // add this line
}
Then write your build function (in _RootState) as below:
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: auth.authStream(), // auth here is AuthService.
builder: (BuildContext context, AsyncSnapshot<User?> snapshot) {
// StreamBuilder will automatically rebuild anytime you log in or log
// logout. No need for your initState or signout & signin functions.
if (snapshot.hasError) return const Text('Something went wrong');
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
}
if (snapshot.data == null) return Login();
// snapshot.data is User.
// print(snapshot.data?.email); print(snapshot.data?.displayName);
return Scaffold(
body: Center(
child: Column(
children: [
const SizedBox(height: 69),
Text('Hello ${snapshot.data?.email}'),
const SizedBox(height: 45),
FlatButton(
onPressed: () async {
// no need to call sign out here.
await auth.signOut();
},
child: const Text('Sign out'),
)
],
),
),
);
},
);
// you can delete your initState, signin and signout functions in _RootState

Everything matches but I still get "Bad state: field does not exist within the DocumentSnapshotPlatform"

So i'm pretty lost trying to get my app to fetch not only the desired text from firestore but also the image i placed there. I know the error "Bad state: field does not exist within the DocumentSnapshotPlatform" implies that either I'm missing field or there's something wrong with at least one of those fields. There's only four things I'm trying to fetch from firestore "imgUrl, title, desc, organizer" i have checked for spelling and order and i can't figure it out. I have also checked firestore to see if the order and spelling was correct and i dont see what's the issue. Please help, Thank you so much in advanced.
////////////////////////////// News Class Starts\\\\\\\\\\\\\\
import 'package:myfuji/screens/CrudMethods.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'CrudMethods.dart';
import 'add_blog.dart';
class News extends StatefulWidget {
#override
_NewsState createState() => _NewsState();
}
class _NewsState extends State<News> {
CrudMethods crudMethods = CrudMethods();
late QuerySnapshot blogSnapshot;
#override
void initState() {
crudMethods.getData().then((result) {
blogSnapshot = result;
setState(() {});
});
super.initState();
}
Widget blogsList() {
return ListView.builder(
padding: const EdgeInsets.only(top: 24),
itemCount: blogSnapshot.docs.length,
itemBuilder: (context, index) {
return BlogTile(
organizer: blogSnapshot.docs[index].get('Organizer'),
desc: blogSnapshot.docs[index].get('desc'),
imgUrl: blogSnapshot.docs[index].get('imgUrl'),
title: blogSnapshot.docs[index].get('title'),
);
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Events"),
),
body: Container(
child: blogSnapshot != null
? blogsList()
: const Center(
child: CircularProgressIndicator(),
)),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
Navigator.push(
context, MaterialPageRoute(builder: (context) => AddBlog()));
},
),
);
}
}
class BlogTile extends StatelessWidget {
final String imgUrl, title, desc, organizer;
const BlogTile(
{required this.organizer,
required this.desc,
required this.imgUrl,
required this.title});
#override
Widget build(BuildContext context) {
return Container(
margin: const EdgeInsets.only(bottom: 24, right: 16, left: 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(8)),
child: Image.network(
imgUrl,
width: MediaQuery.of(context).size.width,
fit: BoxFit.cover,
height: 200,
),
),
),
const SizedBox(height: 16),
Text(
title,
style: const TextStyle(fontSize: 17),
),
const SizedBox(height: 2),
Text(
'$desc - By $organizer',
style: const TextStyle(fontSize: 14),
)
],
),
);
}
}
///////////////////////////////////////////// class AddBlog begins here \\\\\\\\\\\
import 'dart:io';
import 'package:myfuji/screens/CrudMethods.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:random_string/random_string.dart';
class AddBlog extends StatefulWidget {
#override
_AddBlogState createState() => _AddBlogState();
}
class _AddBlogState extends State<AddBlog> {
//
late File selectedImage;
final picker = ImagePicker();
bool isLoading = false;
CrudMethods crudMethods = new CrudMethods();
Future getImage() async {
final pickedFile = await picker.getImage(source: ImageSource.gallery);
setState(() {
if (pickedFile != null) {
selectedImage = File(pickedFile.path);
} else {
print('No image selected.');
}
});
}
Future<void> uploadBlog() async {
if (selectedImage != null) {
// upload the image
setState(() {
isLoading = true;
});
Reference firebaseStorageRef = FirebaseStorage.instance
.ref()
.child("events/")
.child("${randomAlphaNumeric(9)}.jpg");
final UploadTask task = firebaseStorageRef.putFile(selectedImage);
var imageUrl;
await task.whenComplete(() async {
try {
imageUrl = await firebaseStorageRef.getDownloadURL();
} catch (onError) {
print("Error");
}
print(imageUrl);
});
// print(downloadUrl);
Map<String, dynamic> blogData = {
"imgUrl": imageUrl,
"Organizer": authorTextEditingController.text,
"title": titleTextEditingController.text,
"desc": descTextEditingController.text
};
crudMethods.addData(blogData).then((value) {
setState(() {
isLoading = false;
});
Navigator.pop(context);
});
// upload the blog info
}
}
//
TextEditingController titleTextEditingController =
new TextEditingController();
TextEditingController descTextEditingController = new TextEditingController();
TextEditingController authorTextEditingController =
new TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Create Blog"),
actions: [
GestureDetector(
onTap: () {
uploadBlog();
},
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Icon(Icons.file_upload)),
)
],
),
body: isLoading
? Container(
child: Center(
child: CircularProgressIndicator(),
))
: SingleChildScrollView(
child: Container(
margin: EdgeInsets.symmetric(horizontal: 16),
child: Column(
children: [
GestureDetector(
onTap: () {
getImage();
},
child: selectedImage != null
? Container(
height: 150,
margin: EdgeInsets.symmetric(vertical: 24),
width: MediaQuery.of(context).size.width,
child: ClipRRect(
borderRadius:
BorderRadius.all(Radius.circular(8)),
child: Image.file(
selectedImage,
fit: BoxFit.cover,
),
),
)
: Container(
height: 150,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius:
BorderRadius.all(Radius.circular(8))),
margin: EdgeInsets.symmetric(vertical: 24),
width: MediaQuery.of(context).size.width,
child: Icon(
Icons.camera_alt,
color: Colors.white,
),
),
),
TextField(
controller: titleTextEditingController,
decoration: InputDecoration(hintText: "enter title"),
),
TextField(
controller: descTextEditingController,
decoration: InputDecoration(hintText: "enter desc"),
),
TextField(
controller: authorTextEditingController,
decoration:
InputDecoration(hintText: "enter author name"),
),
],
)),
),
);
}
}
///////////////////////////////////////////// class CrudMethods begins here \\\\\\\\\\\
import 'package:cloud_firestore/cloud_firestore.dart';
class CrudMethods {
Future<void> addData(blogData) async {
print(blogData);
FirebaseFirestore.instance
.collection("events/")
.add(blogData)
.then((value) => print(value))
.catchError((e) {
print(e);
});
}
getData() async {
return await FirebaseFirestore.instance.collection("events/").get();
}
}
/////////////////////////////////////////////firestore\\\\\\\\\\\\\
This maybe related to having “/“ after collection name here:
.collection("events/")
Instead try this:
.collection("events")
Also it may be best to change child to collection here:
Reference firebaseStorageRef = FirebaseStorage.instance
.ref()
.child("events/")
Try to see if you get data back by running this:
itemCount: blogSnapshot.docs.length,
itemBuilder: (context, index) {
QuerySnapshot snap = blogSnapshot.data; // Snapshot
List<DocumentSnapshot> items = snap.documents; // List of Documents
DocumentSnapshot item = items[index]; Specific Document
return BlogTile(
organizer: item.data['Organizer'],
desc: item.data['desc'],
imgUrl: item.data['imgUrl'],
title: item.data['title'],
);
},
I think you need to utilize a QueryDocumentSnapshot to access the data in the document.

I'm implementing google sign in using flutter and firestore. On click, user should should sign in with google and navigate user to a create user page

I have a google_sign_in button which when clicked should present the google sign in then the user should be redirected to a CreateAccountPage. The page has an input and a submit button. When the user submits their preferred username, the google account data together with the created username should be stored as a document in users collection cloud firestore database and the user redirected to another page, say TimelinePage.
Below is my home.dart file:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:musagram/pages/activity_feed.dart';
import 'package:musagram/pages/create_account.dart';
import 'package:musagram/pages/profile.dart';
import 'package:musagram/pages/search.dart';
import 'package:musagram/pages/timeline.dart';
import 'package:musagram/pages/upload.dart';
final GoogleSignIn googleSignIn = GoogleSignIn();
final usersRef = FirebaseFirestore.instance.collection('users');
final DateTime timestamp = DateTime.now();
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
bool isAuth = false;
PageController pageController;
int pageIndex = 0;
#override
void initState() {
super.initState();
pageController = PageController();
// Detects when user signed in
googleSignIn.onCurrentUserChanged.listen((account) {
handleSignIn(account);
}, onError: (err) {
print('Error signing in: $err');
});
// Reauthenticate user when app is opened
googleSignIn.signInSilently(suppressErrors: false).then((account) {
handleSignIn(account);
}).catchError((err) {
print('Error signing in: $err');
});
}
handleSignIn(GoogleSignInAccount account) {
if (account != null) {
createUserInFirestore();
setState(() {
isAuth = true;
});
} else {
setState(() {
isAuth = false;
});
}
}
createUserInFirestore() async {
// 1) check if user exists in users collection in database (according to their id)
final GoogleSignInAccount user = googleSignIn.currentUser;
final DocumentSnapshot doc = await usersRef.doc(user.id).get();
if (!doc.exists) {
// 2) if the user doesn't exist, then we want to take them to the create account page
final username = await Navigator.push(
context, MaterialPageRoute(builder: (context) => CreateAccount()));
// 3) get username from create account, use it to make new user document in users collection
usersRef.doc(user.id).set({
"id": user.id,
"username": username,
"photoUrl": user.photoUrl,
"email": user.email,
"displayName": user.displayName,
"bio": "",
"timestamp": timestamp
});
}
}
#override
void dispose() {
pageController.dispose();
super.dispose();
}
login() {
googleSignIn.signIn();
}
logout() {
googleSignIn.signOut();
}
onPageChanged(int pageIndex) {
setState(() {
this.pageIndex = pageIndex;
});
}
onTap(int pageIndex) {
pageController.animateToPage(
pageIndex,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
Scaffold buildAuthScreen() {
return Scaffold(
body: PageView(
children: <Widget>[
// Timeline(),
ElevatedButton(
child: Text('Logout'),
onPressed: logout,
),
ActivityFeed(),
Upload(),
Search(),
Profile(),
],
controller: pageController,
onPageChanged: onPageChanged,
physics: NeverScrollableScrollPhysics(),
),
bottomNavigationBar: CupertinoTabBar(
currentIndex: pageIndex,
onTap: onTap,
activeColor: Theme.of(context).primaryColor,
items: [
BottomNavigationBarItem(icon: Icon(Icons.whatshot)),
BottomNavigationBarItem(icon: Icon(Icons.notifications_active)),
BottomNavigationBarItem(
icon: Icon(
Icons.photo_camera,
size: 35.0,
),
),
BottomNavigationBarItem(icon: Icon(Icons.search)),
BottomNavigationBarItem(icon: Icon(Icons.account_circle)),
]),
);
// return RaisedButton(
// child: Text('Logout'),
// onPressed: logout,
// );
}
Scaffold buildUnAuthScreen() {
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Theme.of(context).accentColor,
Theme.of(context).primaryColor,
],
),
),
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Text(
'MusaGram',
style: TextStyle(
fontFamily: "Signatra",
fontSize: 90.0,
color: Colors.white,
),
),
GestureDetector(
onTap: login,
child: Container(
width: 260.0,
height: 60.0,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage(
'assets/images/google_signin_button.png',
),
fit: BoxFit.cover,
),
),
),
)
],
),
),
);
}
#override
Widget build(BuildContext context) {
return isAuth ? buildAuthScreen() : buildUnAuthScreen();
}
}
And here's the create_account.dart
import 'package:flutter/material.dart';
import 'package:musagram/widgets/header.dart';
class CreateAccount extends StatefulWidget {
#override
_CreateAccountState createState() => _CreateAccountState();
}
class _CreateAccountState extends State<CreateAccount> {
final _formKey = GlobalKey<FormState>();
String username;
submit() {
_formKey.currentState.save();
Navigator.pop(context, username);
}
#override
Widget build(BuildContext parentContext) {
return Scaffold(
appBar: header(context, titleText: "Set up your profile"),
body: ListView(
children: <Widget>[
Container(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 25.0),
child: Center(
child: Text(
"Create a username",
style: TextStyle(fontSize: 25.0),
),
),
),
Padding(
padding: EdgeInsets.all(16.0),
child: Container(
child: Form(
key: _formKey,
child: TextFormField(
onSaved: (val) => username = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: "Username",
labelStyle: TextStyle(fontSize: 15.0),
hintText: "Must be at least 3 characters",
),
),
),
),
),
GestureDetector(
onTap: submit,
child: Container(
height: 50.0,
width: 350.0,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(7.0),
),
child: Center(
child: Text(
"Submit",
style: TextStyle(
color: Colors.white,
fontSize: 15.0,
fontWeight: FontWeight.bold,
),
),),
),
),
],
),
),
],
),
);
}
}
method of google sign in looks like this. It will first authenticate the user then it will check is users data present on the firestore. if it's present data will not be overridden and else data will be added.
also at the end you can navigate to your createAccountPage. and you can pass the required values to the constructor. directly map of data or values.
Future<UserCredential> signInWithGoogle() async {
//first trigger authentication
final GoogleSignInAccount googleUser = await GoogleSignIn().signIn();
//obtain the auth details
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
//create a new credentials
final GoogleAuthCredential credential = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken, idToken: googleAuth.idToken);
//saving the user data to shared preferences
SharedPrefsHelper _sharedpref = new SharedPrefsHelper();
// // sign in method
try {
UserCredential firebaseUser =
await FirebaseAuth.instance.signInWithCredential(credential);
if (firebaseUser != null) {
// Check is already sign up
final QuerySnapshot result = await FirebaseFirestore.instance
.collection('users')
.where('id', isEqualTo: firebaseUser.user.uid)
.get();
//userdata to update at firebase
//passing this via constructor to the next page
Map<String, dynamic> userdata = {
'useremail': firebaseUser.user.email,
'displayname': firebaseUser.user.displayName,
'photoUrl': firebaseUser.user.photoURL,
'userid': firebaseUser.user.uid,
};
// navigate . if you don't have context here you can pass this as a paramater to the
method. no issue if you call inside build or init
//userdata is map. just for handling easily
//you can also create simple variables and pass each value to them.
Navigator.pushReplacement(context, CreateUserAccount(userdata));
}
return firebaseUser;
} catch (error) {
print(error);
}
}
Inside the CreateUserAccount
class CreateUserAccount extends StatefulWidget {
final Map<String, dynamic> userdata;
CreateUserAccount(this.userdata);
#override
CreateUserAccountState createState() => CreateUserAccountState();
}
class CreateUserAccountState extends State<CreateUserAccount> {
TextEditingController _textController = new TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
children: [
Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 20),
child: TextField(
controller: _textController,
),
),
ElevatedButton(
onPressed: () async{
//get the username from textfield
//and update data to firebase
widget.userdata['username'] = _textController.text;
await FirebaseFirestore.instance
.collection('users')
.doc(widget.userdata['userId'])
.set(
widget.userdata,
SetOptions(merge: true),
);
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => TimeLinePage());
},
child: Text("submit"),
),
],
),
));
}
}
Inside the CreateUserAccount we simply added the username in map and added record in firebase. Then you can navigate to the timeline after this.

Flutter pressing back button pops up previous snackBar from Login page again

I have a LoginPage in Flutter. After login, it shows a small snackbar with "success" or "failure.." if password is wrong, then it navigates to the todo list.
When I now press the "back" button on an Android device, it navigates back to the login screen. However, there is still the snackbar popping up and saying "Login successful, redirecting..", and also, my textfields are not emptied and still have the values from the first login, why? That should not happen, but I cannot figure out why that is... here is my code:
import 'package:flutter/material.dart';
import 'package:todoey_flutter/components/rounded_button.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:todoey_flutter/util/file_handler.dart';
import 'package:provider/provider.dart';
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
String username;
String password;
String hashedPW;
// Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
var _nameController = TextEditingController();
var _pwController = TextEditingController();
#override
Widget build(BuildContext context) {
CryptOid cy = Provider.of<CryptOid>(context, listen: true);
FileHandler fh = Provider.of<FileHandler>(context, listen: true);
return Scaffold(
backgroundColor: Colors.white,
body: Builder(
builder: (BuildContext scaffoldBuildContext) {
return Container(
//inAsyncCall: isSpinning,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 34.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
/*
Flexible(
child: Hero(
tag: 'logo',
child: Container(
height: 200.0,
child: Image.asset('images/logo.png'),
),
),
),*/
SizedBox(
height: 48.0,
),
TextField(
controller: _nameController,
style: TextStyle(color: Colors.black54),
onChanged: (value) {
//Do something with the user input.
username = value.toLowerCase();
},
decoration: InputDecoration(
hintText: 'Enter your username',
),
),
SizedBox(
height: 8.0,
),
TextField(
controller: _pwController,
obscureText: true,
style: TextStyle(color: Colors.black54),
onChanged: (value) {
//Do something with the user input.
password = value;
},
decoration: InputDecoration(
hintText: 'Enter your password',
),
),
SizedBox(
height: 24.0,
),
RoundedButton(
title: 'Login',
colour: Colors.lightBlueAccent,
onPressed: () async {
Scaffold.of(scaffoldBuildContext).removeCurrentSnackBar();
print("user: $username, pw: $password");
if ((username != '' && username != null) && (password != '' && password != null)) {
SharedPreferences prefs = await SharedPreferences.getInstance();
// cy.test();
if ((username != '' && username != null) && prefs.containsKey(username)) {
hashedPW = prefs.getString(username);
bool decryptPW = await cy.deHash(hashedPW, password);
if (decryptPW) {
cy.setUsername(username);
fh.setUser(username);
prefs.setString('activeUser', username);
Scaffold.of(scaffoldBuildContext).showSnackBar(
SnackBar(
content: Text("Login successful! redirecting.."),
),
);
Navigator.pushNamed(context, 'taskScreen');
} else {
Scaffold.of(scaffoldBuildContext).showSnackBar(
SnackBar(
content: Text("Wrong password for user $username!"),
),
);
}
} else {
String hashedPW = await cy.hashPW(password);
prefs.setString('activeUser', username);
prefs.setString(username, hashedPW);
cy.setUsername(username);
fh.setUser(username);
Scaffold.of(scaffoldBuildContext).showSnackBar(
SnackBar(
content: Text("User created successful! redirecting.."),
),
);
Navigator.pushNamed(context, 'taskScreen');
//prefs.setString(username, hashedPW);
}
_nameController.clear();
_pwController.clear();
} else {
Scaffold.of(scaffoldBuildContext).showSnackBar(
SnackBar(
content: Text("User and password may not be empty.."),
),
);
_nameController.clear();
_pwController.clear();
return;
}
},
),
],
),
),
);
},
),
);
}
}
You should create a ScaffoldState GlobalKey then assign the to the scaffold.
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldKey,
body: Container());
}
The use the key to showSnackBar
void _showInSnackBar(String value) {
_scaffoldKey.currentState
.showSnackBar(new SnackBar(content: new Text(value)));
}
So your full code would look like this:
import 'package:flutter/material.dart';
import 'package:todoey_flutter/components/rounded_button.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:todoey_flutter/util/file_handler.dart';
import 'package:provider/provider.dart';
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
String username;
String password;
String hashedPW;
// Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
var _nameController = TextEditingController();
var _pwController = TextEditingController();
#override
Widget build(BuildContext context) {
CryptOid cy = Provider.of<CryptOid>(context, listen: true);
FileHandler fh = Provider.of<FileHandler>(context, listen: true);
return Scaffold(
key: _scaffoldKey,
backgroundColor: Colors.white,
body: Builder(
builder: (BuildContext scaffoldBuildContext) {
return Container(
//inAsyncCall: isSpinning,
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 34.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
/*
Flexible(
child: Hero(
tag: 'logo',
child: Container(
height: 200.0,
child: Image.asset('images/logo.png'),
),
),
),*/
SizedBox(
height: 48.0,
),
TextField(
controller: _nameController,
style: TextStyle(color: Colors.black54),
onChanged: (value) {
//Do something with the user input.
username = value.toLowerCase();
},
decoration: InputDecoration(
hintText: 'Enter your username',
),
),
SizedBox(
height: 8.0,
),
TextField(
controller: _pwController,
obscureText: true,
style: TextStyle(color: Colors.black54),
onChanged: (value) {
//Do something with the user input.
password = value;
},
decoration: InputDecoration(
hintText: 'Enter your password',
),
),
SizedBox(
height: 24.0,
),
RoundedButton(
title: 'Login',
colour: Colors.lightBlueAccent,
onPressed: () async {
_scaffoldKey.currentState.removeCurrentSnackBar();
print("user: $username, pw: $password");
if ((username != '' && username != null) &&
(password != '' && password != null)) {
SharedPreferences prefs =
await SharedPreferences.getInstance();
// cy.test();
if ((username != '' && username != null) &&
prefs.containsKey(username)) {
hashedPW = prefs.getString(username);
bool decryptPW = await cy.deHash(hashedPW, password);
if (decryptPW) {
cy.setUsername(username);
fh.setUser(username);
prefs.setString('activeUser', username);
_showInSnackBar("Login successful! redirecting..");
Navigator.pushNamed(context, 'taskScreen');
} else {
_showInSnackBar(
"Wrong password for user $username!");
}
} else {
String hashedPW = await cy.hashPW(password);
prefs.setString('activeUser', username);
prefs.setString(username, hashedPW);
cy.setUsername(username);
fh.setUser(username);
_showInSnackBar(
"User created successful! redirecting..");
Navigator.pushNamed(context, 'taskScreen');
//prefs.setString(username, hashedPW);
}
_nameController.clear();
_pwController.clear();
} else {
_showInSnackBar("User and password may not be empty..");
_nameController.clear();
_pwController.clear();
return;
}
},
),
],
),
),
);
},
),
);
}
void _showInSnackBar(String value) {
_scaffoldKey.currentState
.showSnackBar(new SnackBar(content: new Text(value)));
}
}

Flutter: How to assign other value (object) as Text Editing Controller in Another Field?

I'm building a form that has contact name and phone number fields. User will be able to choose (tap) contact from previously saved contact list, and this should display name and phone numbers at their respective fields.
To achieve that I'm using TypeAheadFormField from Flutter_Form_Builder Package version: 3.14.0 to build my form.
I successfully assign _nameController from local database in the default TypeAheadFormField controller.
But I can't assign _mobileController from the same choice I tapped to FormBuilderTextField.
I managed to get "name'-value with TypeAheadFormField, but everytime I switch the choices from suggestions,
the _mobileController.text didn't update on FormBuilderTextField
My code as follow:
import 'package:myApp/customer.dart';
import 'package:myApp/db_helper.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
class MyForm extends StatefulWidget {
#override
_MyFormState createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
DatabaseHelper _dbHelper;
Customer _customer = Customer();
List<Customer> _customerList = [];
final _formKey = GlobalKey<FormBuilderState>();
final _cfKey = GlobalKey<FormBuilderState>();
final _nameController = TextEditingController();
final _inputContactNameController = TextEditingController();
final _inputContactPhoneController = TextEditingController();
var _mobileController = TextEditingController();
#override
void initState() {
super.initState();
_refreshBikeSellerList();
setState(() {
_dbHelper = DatabaseHelper.instance;
});
_mobileController = TextEditingController();
_mobileController.addListener(() {
setState(() {});
});
}
#override
void dispose() {
_mobileController.dispose();
_nameController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Container(
child: FormBuilder(
key: _formKey,
child: Column(
children: [
FormBuilderTypeAhead(
attribute: 'contact_person',
initialValue: _customer,
controller: _nameController,
onChanged: (val) {},
itemBuilder: (context, Customer _customer) {
return ListTile(
title: Text(_customer.name),
subtitle: Text(_customer.mobile),
);
},
selectionToTextTransformer: (Customer c) => c.name,
suggestionsCallback: (query) {
if (query.isNotEmpty) {
var lowercaseQuery = query.toLowerCase();
return _customerList.where((_customer) {
return _customer.name
.toLowerCase()
.contains(lowercaseQuery);
}).toList(growable: false)
..sort((a, b) => a.name
.toLowerCase()
.indexOf(lowercaseQuery)
.compareTo(
b.name.toLowerCase().indexOf(lowercaseQuery)));
} else {
return _customerList;
}
},
textFieldConfiguration: TextFieldConfiguration(
autofocus: true,
style: DefaultTextStyle.of(context).style.copyWith(
fontSize: 17,
letterSpacing: 1.2,
color: Colors.black,
fontWeight: FontWeight.w300,
),
// controller: guessMotor1,
),
onSuggestionSelected: (val) {
if (val != null) {
return _customerList.map((_customer) {
setState(() {
_mobileController.text = _customer.mobile;
});
}).toList();
} else {
return _customerList;
}
},
),
FormBuilderTextField(
controller: _mobileController,
attribute: 'mobile',
readOnly: true,
style: TextStyle(fontSize: 17),
decoration: InputDecoration(
hintText: 'mobile',
),
),
SizedBox(height: 40),
Container(
child: RaisedButton(
onPressed: () async {
await manageContact(context);
},
child: Text('Manage Contact'),
),
),
],
),
),
);
}
manageContact(BuildContext context) async {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
'Manage Contact',
textAlign: TextAlign.center,
),
titleTextStyle: TextStyle(
fontWeight: FontWeight.w500,
fontSize: 17,
color: Colors.black45,
letterSpacing: 0.8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(12))),
content: FormBuilder(
key: _cfKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// SizedBox(height: 10),
InkResponse(
onTap: () {},
child: CircleAvatar(
radius: 30,
child: Icon(
Icons.person_add,
color: Colors.grey[100],
),
backgroundColor: Colors.grey[500],
),
),
SizedBox(height: 10),
Container(
width: MediaQuery.of(context).size.width * 0.5,
margin: EdgeInsets.symmetric(horizontal: 15),
child: FormBuilderTextField(
maxLength: 20,
controller: _inputContactNameController,
textAlign: TextAlign.start,
keyboardType: TextInputType.text,
textCapitalization: TextCapitalization.words,
attribute: 'contact_person',
decoration: InputDecoration(
prefixIcon: Icon(
Icons.person_outline,
size: 22,
)),
onChanged: (val) {
setState(() {
_customer.name = val;
_formKey
.currentState.fields['contact_person'].currentState
.validate();
});
},
autovalidateMode: AutovalidateMode.always,
validators: [
FormBuilderValidators.required(),
FormBuilderValidators.maxLength(20),
FormBuilderValidators.minLength(2),
],
),
),
Container(
width: MediaQuery.of(context).size.width * 0.5,
margin: EdgeInsets.symmetric(horizontal: 15),
child: FormBuilderTextField(
attribute: 'phone_number',
controller: _inputContactPhoneController,
textAlign: TextAlign.start,
keyboardType: TextInputType.number,
decoration: InputDecoration(
prefixIcon: Icon(
Icons.phone_android,
size: 22,
)),
onChanged: (val) {
setState(() {
_customer.mobile = val;
_formKey.currentState.fields['phone_number'].currentState
.validate();
});
},
validators: [
FormBuilderValidators.required(),
FormBuilderValidators.numeric(),
],
valueTransformer: (text) {
return text == null ? null : num.tryParse(text);
},
),
),
SizedBox(height: 20),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
RaisedButton(
color: Colors.white,
child: Text('Cancel'),
onPressed: () {
Navigator.of(context).pop();
}),
RaisedButton(
color: Colors.grey[400],
child: Text(
'Save',
style: TextStyle(color: Colors.white),
),
onPressed: () async {
try {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
if (_customer.id == null)
await _dbHelper.insertBikeContact(_customer);
else
await _dbHelper.updateCustomer(_customer);
_refreshBikeSellerList();
_formKey.currentState.reset();
_inputContactNameController.clear();
_inputContactPhoneController.clear();
Navigator.of(context).pop();
}
} catch (e) {
print(e);
}
},
)
],
),
],
),
),
),
);
}
_refreshBikeSellerList() async {
List<Customer> x = await _dbHelper.getCustomer();
setState(() {
_customerList = x;
});
}
}
Is there any possible way to update _mobileController as I tap?
Any help would be much appreciated.
Thank you in advance.
EDITED:
class where I save the customer data:
class Customer {
int id;
String name;
String mobile;
static const tblCustomer = 'Customer';
static const colId = 'id';
static const colName = 'name';
static const colMobile = 'mobile';
Customer({
this.id,
this.name,
this.mobile,
});
Map<String, dynamic> toMap() {
var map = <String, dynamic>{colName: name, colMobile: mobile};
if (id != null) map[colId] = id;
return map;
}
Customer.fromMap(Map<String, dynamic> map) {
id = map[colId];
name = map[colName];
mobile = map[colMobile];
}
#override
bool operator ==(Object other) =>
identical(this, other) ||
other is Customer &&
runtimeType == other.runtimeType &&
name == other.name;
#override
int get hashCode => name.hashCode;
#override
String toString() {
return name;
}
}
Here is my database:
import 'dart:async';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import 'customer.dart';
class DatabaseHelper {
static const _databaseVersion = 1;
static const _databaseName = 'Kiloin.db';
DatabaseHelper._();
static final DatabaseHelper instance = DatabaseHelper._();
Database _database;
Future<Database> get database async {
if (_database != null) return _database;
_database = await _initDatabase();
return _database;
}
_initDatabase() async {
Directory dataDirectory = await getApplicationDocumentsDirectory();
String dbPath = join(dataDirectory.path, _databaseName);
return await openDatabase(
dbPath,
version: _databaseVersion,
onCreate: _onCreateDB,
);
}
_onCreateDB(Database db, int version) async {
await db.execute('''
CREATE TABLE ${Customer.tblCustomer}(
${Customer.colId} INTEGER PRIMARY KEY AUTOINCREMENT,
${Customer.colName} TEXT NOT NULL,
${Customer.colMobile} TEXT NOT NULL
)
''');
}
Future<int> insertBikeContact(Customer customer) async {
Database db = await database;
return await db.insert(Customer.tblCustomer, customer.toMap());
}
Future<List<Customer>> getCustomer() async {
Database db = await database;
List<Map> contact = await db.query(Customer.tblCustomer);
return contact.length == 0
? []
: contact.map((e) => Customer.fromMap(e)).toList();
}
Future<int> updateCustomer(Customer customer) async {
Database db = await database;
return await db.update(Customer.tblCustomer, customer.toMap(),
where: '${Customer.colId}=?', whereArgs: [customer.id]);
}
Future<int> deleteContact(int id) async {
Database db = await database;
return await db.delete(Customer.tblCustomer,
where: '${Customer.colId}=?', whereArgs: [id]);
}
}
The value that you get from onSuggestionSelected is the customer. Use that value to update _mobileController.text.
onSuggestionSelected: (customer) {
if (customer != null) {
setState(() {
_mobileController.text = customer.mobile;
});
}
}