I am trying to implement a simple login/signup screen on flutter, connected to firebase.
The flow is supposed to be as follows:
Main => Wrapper
Wrappper => if user==NULL, then Authenticate, else Home
Authenticate => Login or Signup
The issue is occuring in the Wrapper file. When the code is run, initially the user will be NULL and has to go to the Login screen. However, my code goes directly to the Home screen.
The codes for each file are as follows:
main.dart
void main() async {
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamProvider<MyUser>.value(
value: AuthService().user,
initialData: null,
child: MaterialApp(
home: Wrapper(),
debugShowCheckedModeBanner: false,
)
);
}
}
wrapper.dart
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<MyUser>(context);
if (user == null) {
return Authenticate();
} else {
//return PatientHome();
}
}
}
authenticate.dart
class Authenticate extends StatefulWidget {
#override
_AuthenticateState createState() => _AuthenticateState();
}
class _AuthenticateState extends State<Authenticate> {
bool showSignIn = true;
void toggleView() {
setState(() => showSignIn = !showSignIn);
}
#override
Widget build(BuildContext context) {
if (showSignIn) {
return Login(toggleView: toggleView);
} else {
return Register(toggleView: toggleView);
}
}
}
auth.dart
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
// create user obj based on firebase user
MyUser _userFromFirebaseUser(User user) {
return user != null ? MyUser(uid: user.uid) : null;
}
// auth change user stream
Stream<MyUser> get user {
return _auth.authStateChanges()
//.map((FirebaseUser user) => _userFromFirebaseUser(user));
.map(_userFromFirebaseUser);
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
User user = result.user;
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
}
}
// register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
User user = result.user;
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString()+"oollala");
return null;
}
}
// sign out
Future signOut() async {
try {
return await _auth.signOut();
} catch (error) {
print(error.toString());
return null;
}
}
}
register.dart
class Register extends StatefulWidget {
final Function toggleView;
Register({this.toggleView});
#override
RegisterPatient createState() => RegisterPatient();
}
class RegisterPatient extends State<Register> {
final AuthService _auth = AuthService();
final _formKey = GlobalKey<FormState>();
bool loading = false;
String email = '';
String password = '';
String error = '';
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
backgroundColor: Colors.deepPurple,
elevation: 0.0,
title: Text('Register'),
actions: <Widget>[
TextButton.icon(
onPressed: () => widget.toggleView(),
icon: Icon(Icons.person),
label: Text('Register as Patient'))
],
),
body: Container(
padding: EdgeInsets.symmetric(vertical: 20.0, horizontal: 50.0),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(height: 20.0),
TextFormField(
validator: (val) => val.isEmpty ? 'Enter email' : null,
onChanged: (val) {
setState(() => email = val);
},
),
SizedBox(height: 20.0),
TextFormField(
obscureText: true,
validator: (val) => val.length < 6 ? 'Enter a password 6+ chars long' : null,
onChanged: (val) {
setState(() => password = val);
},
),
SizedBox(height: 20.0),
RaisedButton(
color: Colors.deepPurple,
child: Text(
'Register',
style: TextStyle(color: Colors.white),
),
onPressed: () async {
if(_formKey.currentState.validate()){
setState(() => loading = true);
dynamic result = await _auth.registerWithEmailAndPassword(email, password);
if(result == null) {
setState(() {
error = 'Email/password incorrect';
loading = false;
});
} else {
Navigator.of(context).pop();
loading = false;
}
}
}
),
SizedBox(height: 12.0),
Text(
error,
style: TextStyle(color: Colors.red, fontSize: 14.0),
)
],
),
),
),
);
}
}
login.dart
similar to register.dart
The issue is that whenever I run this code, it always takes me to HomePage, even though it is supposed to go through the Authenticate file as user is supposed to be NULL.
EDIT 1:
I thought it was directing to Homepage rather than to Authenticate, however my screen is simply white. It does not do anything other than that.
Any idea what could be the issue?
EDIT 2:
The code runs like so, shows a white screen, and never terminated unless I forcibly do so. I changed from emulator to my phone, but it still the same. Any idea what could be the issue? I'm fairly new to flutter so I don't understand the error...
Launching lib\main.dart on sdk gphone x86 arm in debug mode...
Running Gradle task 'assembleDebug'...
√ Built build\app\outputs\flutter-apk\app-debug.apk.
Debug service listening on ws://127.0.0.1:64181/vsky5JbFCKM=/ws
Syncing files to device sdk gphone x86 arm...
E/flutter ( 8057): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: Null check operator used on a null value
E/flutter ( 8057): #0 MethodChannel.binaryMessenger (package:flutter/src/services/platform_channel.dart:142:86)
E/flutter ( 8057): #1 MethodChannel._invokeMethod (package:flutter/src/services/platform_channel.dart:148:36)
E/flutter ( 8057): #2 MethodChannel.invokeMethod (package:flutter/src/services/platform_channel.dart:331:12)
E/flutter ( 8057): #3 MethodChannel.invokeListMethod (package:flutter/src/services/platform_channel.dart:344:41)
E/flutter ( 8057): #4 MethodChannelFirebase._initializeCore (package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart:30:37)
E/flutter ( 8057): #5 MethodChannelFirebase.initializeApp (package:firebase_core_platform_interface/src/method_channel/method_channel_firebase.dart:77:13)
E/flutter ( 8057): #6 Firebase.initializeApp (package:firebase_core/src/firebase.dart:41:47)
E/flutter ( 8057): #7 main (package:hospicare_mobile/main.dart:14:18)
E/flutter ( 8057): #8 _runMainZoned.<anonymous closure>.<anonymous closure> (dart:ui/hooks.dart:142:25)
E/flutter ( 8057): #9 _rootRun (dart:async/zone.dart:1354:13)
E/flutter ( 8057): #10 _CustomZone.run (dart:async/zone.dart:1258:19)
E/flutter ( 8057): #11 _runZoned (dart:async/zone.dart:1789:10)
E/flutter ( 8057): #12 runZonedGuarded (dart:async/zone.dart:1777:12)
E/flutter ( 8057): #13 _runMainZoned.<anonymous closure> (dart:ui/hooks.dart:138:5)
E/flutter ( 8057): #14 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:283:19)
E/flutter ( 8057): #15 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
E/flutter ( 8057):
I think using shared preferences might help for saving some data inside the app even though the app is closed or restarted by the user .Even you can retrieve it again and if it has something in it you can navigate to homepage , if it is null you can navigate to authenticate page.
Visit :
https://pub.dev/packages/shared_preferences
Follow the steps to use it:
Add the following inside your authenticate page or signin page
SharedPreferences
data=awaitSharedPreferences.getInstance();
data.setString('your_key',your_data);
Add the following inside the main function before runapp function and make the main function as async
SharedPreferences data = await
SharedPreferences.getInstance();
var your_variable = data.getString('your_key');
Related
Try To use Api in flutter with Dio, and find some error.
the api url is :
https://newsapi.org/v2/top-headlines?country=eg&category=business&apiKey=f984863b10ca46d59631152aa6cc9377
Dio Helper dart file
import 'package:dio/dio.dart';
class DioHelper {
static var dio = Dio();
//https://newsapi.org/v2/top-headlines?country=eg&category=business&apiKey=f984863b10ca46d59631152aa6cc9377
static init() {
dio = Dio(
BaseOptions(
baseUrl: 'https://newsapi.org/',
receiveDataWhenStatusError: true,
),
);
print('Connected');
}
static Future<Response> getData ({
required String url,
required Map<String,dynamic> query,
}) async {
return await dio.get(url, queryParameters: query);
}
}
FloatingActionButton Onpressed to get the data.
floatingActionButton: FloatingActionButton(
onPressed: (){
DioHelper.getData(
url: 'v2/top-headlines?',
query: {
'country':'eg',
'category':'business',
'apiKey':'f984863b10ca46d59631152aa6cc9377',
}).then((value) {
print(value.data.toString());
}).catchError((error) {
print(error.toString());
});
},
child: Icon(Icons.add),
),
and find an error when run:
I/flutter (13162): DioError [DioErrorType.response]: Http status error [400]
I/flutter (13162): Source stack:
I/flutter (13162): #0 DioMixin.fetch (package:dio/src/dio_mixin.dart:488:35)
I/flutter (13162): #1 DioMixin.request (package:dio/src/dio_mixin.dart:483:12)
I/flutter (13162): #2
Expect to find a solve to my problem
I'm kinda new to Flutter + Dart, for my second app I want to work on a spotify app for android.
I found the spotify sdk on pub dev and did my best to work with the docs from spotify and from pub dev but I dont get the token :(
First of all: I have a debug.keystore in my ../android/app/ and put the debug sha1 fingerprint on spotify console with a callback url.
I did everything from the spotify_sdk readme.
I tried it with different callback methods like:
redirectUrl: http://bundle_id/callback,
redirectUrl: bundle_id://callback,
redirectUrl: http://bundleid/auth,
and everything with the client_id and client_secret (because the docs are not clear for me what I should use).
It would be great if you guys could help out. I'm alone and dont have dev friends to ask D:
This is my code
import 'package:flutter/material.dart';
import 'package:spotify_sdk/spotify_sdk.dart';
void main() {
runApp(const MaterialApp(
home: HomePage(),
));
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('spotify auth test'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Align(
alignment: Alignment.center,
child: ElevatedButton(
onPressed: getAccessToken,
child: const Text('GIMMI TOKEN PLS'),
),
),
],
),
);
}
Future<String> getAccessToken() async {
try {
var authenticationToken = await SpotifySdk.getAccessToken(
clientId: 'client_id',
redirectUrl: 'redirectUrl',
scope: 'app-remote-control, '
'user-modify-playback-state, '
'playlist-read-private, '
'playlist-modify-public,user-read-currently-playing');
print('Got a token: $authenticationToken');
return authenticationToken;
} catch (e) {
print('$e.code, message $e.message');
return Future.error('$e.code: $e.message');
} /*catch (e) {
print('not implemented');
return Future.error('not implemented');
}*/
}
}
And this is my error log message:
I/flutter ( 7987): ┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
I/flutter ( 7987): │ #0 SpotifySdk._logException (package:spotify_sdk/spotify_sdk.dart:633:15)
package:spotify_sdk/spotify_sdk.dart:633
I/flutter ( 7987): │ #1 SpotifySdk.getAccessToken (package:spotify_sdk/spotify_sdk.dart:128:7)
package:spotify_sdk/spotify_sdk.dart:128
I/flutter ( 7987): ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
I/flutter ( 7987): │ ⛔ getAccessToken failed with: Authentication went wrong
I/flutter ( 7987): │ ⛔ AUTHENTICATION_SERVICE_UNKNOWN_ERROR
I/flutter ( 7987): └───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
I/flutter ( 7987): PlatformException(authenticationTokenError, Authentication went wrong, AUTHENTICATION_SERVICE_UNKNOWN_ERROR, null).code, message PlatformException(authenticationTokenError, Authentication went wrong, AUTHENTICATION_SERVICE_UNKNOWN_ERROR, null).message
E/flutter ( 7987): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: PlatformException(authenticationTokenError, Authentication went wrong, AUTHENTICATION_SERVICE_UNKNOWN_ERROR, null).code: PlatformException(authenticationTokenError, Authentication went wrong, AUTHENTICATION_SERVICE_UNKNOWN_ERROR, null).message
E/flutter ( 7987):
I have two collections which are called Users and Employee. I need to get data from specific field from those collections. "Users" and "Employee" collections have the same field like "password". I have been trying to get the password from Users and Employee collections and use it to navigate me different screen . My codes:
QuerySnapshot snap = await FirebaseFirestore.instance
.collection ("Employee").where ('id', isEqualTo: id).get();
QuerySnapshot snap2 = await FirebaseFirestore.instance
.collection ("Users").where ('uid', isEqualTo: id).get();
print(snap.docs[0]['id']);
print(snap2.docs[0]['uid']);
User.employeeId = id;
try {
if (password == snap.docs[0]['password'] ) {
sharedPreferences = await SharedPreferences.getInstance();
sharedPreferences.setString('employeeId', id).then((_){
navigateNext(HomeScreen());
});
}else if (password == snap2.docs[0]['upassword']) {
sharedPreferences = await SharedPreferences.getInstance();
sharedPreferences.setString('userId', id).then((_){
navigateNext(AdminScreen());
});
error:
E/flutter ( 7498): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: RangeError (index): Invalid value: Valid value range is empty: 0
E/flutter ( 7498): #0 List.[] (dart:core-patch/growable_array.dart:264:36)
E/flutter ( 7498): #1 _LoginScreenState.build. (package:attendanceapp/loginscreen.dart:126:38)
E/flutter ( 7498):
E/flutter ( 7498):
I saw there is a collection inside the document(Users or Employee), I am not sure whether you could get them properly.
Here is my code with a similar setup as you posted. If you could not get the data properly, I recommended you to assign the value to variable "id" before getting value from FirebaseFirestore.
I normally desiged a button for debugging. Hope it could help you.
void getMessagesTest222() async{
final id = "1";
QuerySnapshot querySnapshot = await _firestore.collection('User').where('uid', isEqualTo: id).get();
final allData1 = querySnapshot.docs.map((doc) => doc.get('uid')).toList();
final allData2 = querySnapshot.docs.map((doc) => doc.get('upassword')).toList();
print('uid = >$allData1');
print('upassword = > $allData2');
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Record'),
leading: null,
actions: <Widget>[
IconButton(
icon: const Icon(Icons.add),
onPressed: (){
Navigator.pushNamed(context, creationCategory.id);
},
),
IconButton(
icon: const Icon(Icons.settings),
onPressed: () async {
getMessagesTest222();
// await getData();
// await getMessagesTestTest();
// Navigator.push(
// context,
// MaterialPageRoute(builder: (context) => const ItemDetailsScrrent()),
// );
},
),
],
),
body: SafeArea(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text('Trahs Collection',
style: const TextStyle(
color: Colors.black38, fontWeight: FontWeight.bold, fontSize: 40),
),
ItemStream(),
],
),
),
),
);
}
Fig1.Firebase structure
Fig2.print out the data from firestore
Updated Code for the second question from this user.
As you mentioned, you created two pages that are for different users
We could get the data to make sure whether this account is existing, and then we could create two if loops to login to different pages.
RoundButton(
title: 'Log In',
colour: Colors.lightBlueAccent,
onPressed: () async{
try {
final user = await _auth.signInWithEmailAndPassword(email: email, password: password);
// if (user != null) {
// // Navigator.pushNamed(context, ChatScreen.id);
// Navigator.pushNamed(context, RecordScreen.id);
// }
if (user.user?.email.toString() == 'add#123.com') {
// Navigator.pushNamed(context, ChatScreen.id);
Navigator.pushNamed(context, administrors.id);
}
if (user.user?.email.toString() == 'operator#123.com') {
// Navigator.pushNamed(context, ChatScreen.id);
Navigator.pushNamed(context, user.id);
}
}
catch(e) {
print(e);
}
},
),
I am trying to check firebase database using flutter to see if user who is logged in is registered or not so checking to see if firstname exist in user database but what is happening is that the activeListeners function is always executing after rmn() function which should not happen. activeListeners should execute before rmn() .
Here is my code below:
import 'package:flutter/material.dart';
import 'package:after_layout/after_layout.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_database/firebase_database.dart';
import 'package:ayto_driver_x/signup.dart';
import 'package:ayto_driver_x/mainpage.dart';
import 'package:ayto_driver_x/login.dart';
import 'package:fluttertoast/fluttertoast.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'After Layout - Good Example',
home: new LoginState(),
);
}
}
class LoginState extends StatefulWidget {
#override
HomeScreenState createState() => new HomeScreenState();
}
class HomeScreenState extends State<LoginState> with AfterLayoutMixin<LoginState> {
FirebaseAuth auth = FirebaseAuth.instance;
String srm="";
final _database=FirebaseDatabase.instance.ref();
#override
void initState() {
// TODO: implement initState
super.initState();
activeListeners();
}
void activeListeners(){
final User? user=auth.currentUser;
final id=user?.uid ;
_database.child('Driver/${user?.uid}/FirstName').onValue.listen((event) {
final Object? description=event.snapshot.value ?? false;
setState(() {
print('DescriptionLS=$description');
srm='$description';
print('SRMx1=$srm');
});
});
}
#override
Widget build(BuildContext context) {
activeListeners();
return new Scaffold(body: new Container(color: Colors.white));
}
#override
void afterFirstLayout(BuildContext context) {
// Calling the same function "after layout" to resolve the issue.
activeListeners();
rmn();
}
void rmn()
{
FirebaseAuth.instance
.authStateChanges()
.listen((User? user) {
if (user == null) {
print('User is currently signed out!');
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => LoginScreen(),
),
(e) => false,
);
}
else {
activeListeners();
print('User is signed in!');
print('SRMxc=$srm');
Fluttertoast.showToast(
msg: srm,
toastLength: Toast.LENGTH_SHORT,
gravity: ToastGravity.CENTER,
timeInSecForIosWeb: 1,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0,
);
if(srm.compareTo('false')==0) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const SignupScreen(),
),
);
}
else {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => const MainPage(),
),
(e) => false,
);
}
}
});
}
}
Log File :
I/flutter ( 5748): User is signed in!
I/flutter ( 5748): SRMxc=
I/flutter ( 5748): DescriptionLS=false
I/flutter ( 5748): SRMx1=false
I/flutter ( 5748): DescriptionLS=false
I/flutter ( 5748): SRMx1=false
I/flutter ( 5748): DescriptionLS=false
I/flutter ( 5748): SRMx1=false
I/flutter ( 5748): DescriptionLS=false
I/flutter ( 5748): SRMx1=false
Please suggest me a solution to execute activeListeners() before rmn().
I'm working on a finance app and I'd like a custom textentry field and keyboard for currency entry with an inbuilt calculator.
I've tried using a BottomSheet, both persistent and modal. The modal behaviour is ideal, but it always shows a barrier. The persistent one is what I have now, using a focus node to show and hide it, but it's throwing strange errors:
I/flutter (30319): The following NoSuchMethodError was thrown while dispatching notifications for FocusNode:
I/flutter (30319): The method 'removeLocalHistoryEntry' was called on null.
I/flutter (30319): Receiver: null
I/flutter (30319): Tried calling: removeLocalHistoryEntry(Instance of 'LocalHistoryEntry')
I/flutter (30319):
I/flutter (30319): When the exception was thrown, this was the stack:
I/flutter (30319): #0 Object.noSuchMethod (dart:core/runtime/libobject_patch.dart:46:5)
I/flutter (30319): #1 LocalHistoryEntry.remove (package:flutter/src/widgets/routes.dart:296:12)
I/flutter (30319): #2 _NumpadFieldState.initState.<anonymous closure> (file:///D:/code/financepie/lib/widgets/numpad/numpadfield.dart:30:32)
...
In any case, the bottom sheet behaviour (dragging down) isn't really ideal to copy the android/ios soft keyboard. Any better solutions? Current code below:
import 'package:flutter/material.dart';
import 'numpad.dart';
class NumpadField extends StatefulWidget {
#override
_NumpadFieldState createState() {
return new _NumpadFieldState();
}
}
class _NumpadFieldState extends State<NumpadField> {
ValueNotifier<List<String>> state;
FocusNode focusNode;
PersistentBottomSheetController bottomSheetController;
#override initState() {
super.initState();
state = ValueNotifier<List<String>>([]);
state.addListener(() => setState((){}));
focusNode = FocusNode();
focusNode.addListener(() {
print(focusNode);
if (focusNode.hasFocus) {
bottomSheetController = showBottomSheet(
context: context,
builder: (context) => Numpad(state: state),
);
} else {
bottomSheetController?.close(); ///this line causing the error
}
});
}
#override dispose() {
state.dispose();
focusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(focusNode);
},
child: Container(
child: Text(state.value.fold<String>("", (str, e) => "$str $e")),
constraints: BoxConstraints.expand(height: 24.0),
decoration: BoxDecoration(
border: BorderDirectional(bottom: BorderSide())
),
),
);
}
}
bottomSheetController?.close(); is nullable. Since the line causes the error, you can add a null-check to prevent this issue.
else {
if(bottomSheetController != null)
bottomSheetController!.close();
}