Assistance needed with flutter app saving user authentication status - flutter

I have an app where Users can currently log in (using phone auth) and log out from the home screen. But whenever the app gets refreshed the user logs out automatically.
What I want to do is to keep the user logged in till he pressed the log out button.
Since I am new to flutter and coding in general it would be helpful if you can help me with the exact code.
main.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'login.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
Firebase.initializeApp();
runApp(const MaterialApp(
home: MyApp(),
debugShowCheckedModeBanner: false,
));
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return const LoginScreen();
}
}
login.dart
import 'package:flutter/material.dart';
import 'otp.dart';
import 'package:intl_phone_field/intl_phone_field.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({Key? key}) : super(key: key);
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
String Country = '';
final TextEditingController _controller2 = TextEditingController();
final TextEditingController _controller = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Phone Auth'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(children: [
Container(
margin: const EdgeInsets.only(top: 60),
child: const Center(
child: Text(
'Phone Authentication',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 28),
),
),
),
//Phone feild
IntlPhoneField(
decoration: const InputDecoration(
labelText: 'Phone Number',
border: OutlineInputBorder(
borderSide: BorderSide(),
),
),
controller: _controller,
initialCountryCode: 'IN',
onChanged: (phone) {
debugPrint('Below is phone number');
debugPrint(phone.completeNumber);
debugPrint('Country code is: ${phone.countryCode}');
Country = phone.countryCode;
// phone.countryCode = _controller2 as String;
debugPrint(Country);
},
),
// Container(
// margin: const EdgeInsets.only(top: 40, right: 10, left: 10),
// child: TextField(
// decoration: const InputDecoration(
// hintText: 'Phone Number',
// prefix: Padding(
// padding: EdgeInsets.all(4),
// child: Text('+91'),
// ),
// ),
// maxLength: 10,
// keyboardType: TextInputType.number,
// controller: _controller,
// ),
// )
]),
Container(
margin: const EdgeInsets.all(10),
width: double.infinity,
child: FlatButton(
color: Colors.blue,
onPressed: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) =>
OTPScreen(_controller.text, Country)));
},
child: const Text(
'Next',
style: TextStyle(color: Colors.white),
),
),
)
],
),
);
}
}
otp.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'home.dart';
import 'package:pinput/pinput.dart';
class OTPScreen extends StatefulWidget {
final String phone;
final String countryCode;
const OTPScreen(this.phone, this.countryCode, {Key? key}) : super(key: key);
#override
_OTPScreenState createState() => _OTPScreenState();
}
class _OTPScreenState extends State<OTPScreen> {
final GlobalKey<ScaffoldState> _scaffoldkey = GlobalKey<ScaffoldState>();
late String _verificationCode;
final TextEditingController _pinPutController = TextEditingController();
final FocusNode _pinPutFocusNode = FocusNode();
final BoxDecoration pinPutDecoration = BoxDecoration(
color: const Color.fromRGBO(43, 46, 66, 1),
borderRadius: BorderRadius.circular(10.0),
border: Border.all(
color: const Color.fromRGBO(126, 203, 224, 1),
),
);
#override
Widget build(BuildContext context) {
return Scaffold(
key: _scaffoldkey,
appBar: AppBar(
title: const Text('OTP Verification'),
),
body: Column(
children: [
Container(
margin: const EdgeInsets.only(top: 40),
child: Center(
child: Text(
'Verify ${widget.countryCode} ${widget.phone}',
style:
const TextStyle(fontWeight: FontWeight.bold, fontSize: 26),
),
),
),
Padding(
padding: const EdgeInsets.all(30.0),
child: Pinput(
length: 6,
// fieldsCount: 6,
// textStyle: const TextStyle(fontSize: 25.0, color: Colors.white),
// eachFieldWidth: 40.0,
// eachFieldHeight: 55.0,
// focusNode: _pinPutFocusNode,
// controller: _pinPutController,
// submittedFieldDecoration: pinPutDecoration,
// selectedFieldDecoration: pinPutDecoration,
// followingFieldDecoration: pinPutDecoration,
// pinAnimationType: PinAnimationType.fade,
onSubmitted: (pin) async {
debugPrint('submit pressed');
try {
await FirebaseAuth.instance
.signInWithCredential(PhoneAuthProvider.credential(
verificationId: _verificationCode, smsCode: pin))
.then((value) async {
if (value.user != null) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => Home()),
(route) => false);
}
});
} catch (e) {
FocusScope.of(context).unfocus();
_scaffoldkey.currentState!.showSnackBar(
const SnackBar(content: Text('invalid OTP')));
}
},
),
)
],
),
);
}
_verifyPhone() async {
await FirebaseAuth.instance.verifyPhoneNumber(
phoneNumber: '${widget.countryCode}${widget.phone}',
verificationCompleted: (PhoneAuthCredential credential) async {
await FirebaseAuth.instance
.signInWithCredential(credential)
.then((value) async {
if (value.user != null) {
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => Home()),
(route) => false);
}
});
},
verificationFailed: (FirebaseAuthException e) {
debugPrint(e.message);
},
codeSent: (String verficationID, int? resendToken) {
setState(() {
_verificationCode = verficationID;
});
},
codeAutoRetrievalTimeout: (String verificationID) {
setState(() {
_verificationCode = verificationID;
});
},
timeout: const Duration(seconds: 120));
}
#override
void initState() {
// TODO: implement initState
super.initState();
_verifyPhone();
}
}
home.dart
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'login.dart';
class Home extends StatefulWidget {
const Home({Key? key}) : super(key: key);
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
late String uid;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
actions: [
IconButton(
icon: const Icon(Icons.logout),
onPressed: () async {
await FirebaseAuth.instance.signOut();
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => const LoginScreen()),
(route) => false);
},
)
],
),
body: Center(
child: Text(uid),
),
);
}
#override
void initState() {
// TODO: implement initState
super.initState();
uid = FirebaseAuth.instance.currentUser!.uid;
}
}

One perfect way is to store the data in shared preferences. All you need in to add the flutter shared_preference package [https://pub.dev/packages/shared_preferences].
CONFIGURATION
After adding this package, create a shared_preference object like: * final preference = await SharedPreference.getInstance*
Next you can store the logs on the mobile storage using the preference object. For example, to store a variable isAuthenticated(which stores the authentication as bool) status of the user, do something like: * await preference.setBool("authentication", isAuthenticated)*
Step three, create another method to retrieve the data from storage.
first create another preference object loke in step one(1). But this time instead of setBool, use getBool like:* final isAlreadyAuthenticated = preferences.getBool("authentication")*
Now isAleadyAuthenticated holds the value whether is user already login or not and can you can use it to display a login screen of the value is false

Since you are using firebase, i recommed you add the firebase_auth package. And then you wrap the MyApp with a stream builder and supply FirebaseAuth.instance.authStateChanges as stream to the stream parameter which comes with the firebase_auth
So in the builder of the stream builder, return the MyApp() if snapshot.hasData, else return the OTP screen

Related

Loading different screens for different types of users in flutter

I have two types of users and I need to save the authorization session after close the app
I use Firebase and I added a bool variable to determine whether the user is "authorized" or not
When I launch the app again, I check the status.
In the code below, I can register as a "client," if the application is closed and then opened again, everything will work well
home: isLoggedIn ? const RegScreenWidget() : const StartScreenClientWidget(),
full code
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:geomap/screen/login_screen_client.dart';
import 'package:geomap/screen/logni_screen_company.dart';
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool isLoggedIn = false;
#override
void initState() {
checkStatusUser();
super.initState();
}
void checkStatusUser() {
if (FirebaseAuth.instance.currentUser == null) {
isLoggedIn = true;
} else {
isLoggedIn = false;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Promo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.lime,
),
routes: {
'/main': (context) => const RegScreenWidget(),
'/start_screen_company': (context) => const StartScreenCompanyWidget(),
'/start_screen_client': (context) => const StartScreenClientWidget(),
},
home: isLoggedIn ? const RegScreenWidget() : const StartScreenClientWidget(),
);
}
}
class RegScreenWidget extends StatefulWidget {
const RegScreenWidget({super.key});
#override
State<RegScreenWidget> createState() => _RegScreenWidgetState();
}
class _RegScreenWidgetState extends State<RegScreenWidget> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Registration'),
),
body: Center(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Registration screen',
style: TextStyle(fontSize: 16),
),
const SizedBox(
height: 15,
),
Container(
width: 200,
child: Column(
children: [
TextField(
controller: _emailController,
decoration: const InputDecoration(
hintText: 'email',
border: OutlineInputBorder(),
),
),
const SizedBox(
height: 15,
),
TextField(
controller: _passController,
decoration: const InputDecoration(
hintText: 'pass',
border: OutlineInputBorder(),
),
),
],
),
),
const SizedBox(
height: 15,
),
ElevatedButton(
onPressed: regCompany,
child: const Text('Registration as a Company'),
),
const SizedBox(
height: 10,
),
ElevatedButton(
onPressed: regClient,
child: const Text('Registration as a Client'),
),
const SizedBox(
height: 10,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("I'm already registered,"),
TextButton(
onPressed: () {},
child: Text('sign in'),
),
],
)
],
),
),
),
);
}
void regCompany() async {
var _emailCompany = _emailController.text;
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passController.text.trim(),
);
await FirebaseFirestore.instance
.collection('users')
.doc(_emailCompany)
.set({
'emailCompany': _emailCompany,
'typeUser': 'company',
});
_emailController.clear();
_passController.clear();
Navigator.of(context).pushNamed('/start_screen_company');
}
void regClient() async {
var _emailClient = _emailController.text;
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passController.text.trim(),
);
await FirebaseFirestore.instance.collection('users').doc(_emailClient).set({
'emailCompany': _emailClient,
'typeUser': 'client',
});
_emailController.clear();
_passController.clear();
Navigator.of(context).pushNamed('/start_screen_client');
}
}
But I have two types of users and I need to show two different screens. If I register as a "company," close the application, then open again, there will be an error, because I need a screen for the company.
I use the Firebase and after registration, I create a table in which the "type" of the user is displayed
image
I can check this data and use it: "client" or "company" is now authorized
How do I show different screens? I need a simple solution, I'm new and trying to understand the basic things.
My last full code, i have an error(
i think the screen loads faster than I get data on what type of user - authorized and I get an error, how i can change it?
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:geomap/screen/login_screen_client.dart';
import 'package:geomap/screen/logni_screen_company.dart';
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Map<String, Widget> screenByUser = {
'client': const StartScreenClientWidget(),
'company': const StartScreenCompanyWidget(),
};
var userType = '';
bool notLogInUser = true;
#override
void initState() {
checkStatusUser();
super.initState();
}
void checkStatusUser() async {
if (FirebaseAuth.instance.currentUser == null) {
return;
} else {
notLogInUser = false;
print('user is auth');
var _emailUser = FirebaseAuth.instance.currentUser!.email;
var document =
FirebaseFirestore.instance.collection('users').doc(_emailUser);
var snapshot = await document.get();
Map<String, dynamic>? data = snapshot.data();
var typeUserfromFBS = data!['typeUser'];
print(typeUserfromFBS);
userType = typeUserfromFBS;
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Promo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.lime,
),
routes: {
'/main': (context) => const RegScreenWidget(),
'/start_screen_company': (context) => const StartScreenCompanyWidget(),
'/start_screen_client': (context) => const StartScreenClientWidget(),
},
home: notLogInUser ? const RegScreenWidget() : screenByUser[userType],
);
}
}
class RegScreenWidget extends StatefulWidget {
const RegScreenWidget({super.key});
#override
State<RegScreenWidget> createState() => _RegScreenWidgetState();
}
class _RegScreenWidgetState extends State<RegScreenWidget> {
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Registration'),
),
body: Center(
child: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'Registration screen',
style: TextStyle(fontSize: 16),
),
const SizedBox(
height: 15,
),
Container(
width: 200,
child: Column(
children: [
TextField(
controller: _emailController,
decoration: const InputDecoration(
hintText: 'email',
border: OutlineInputBorder(),
),
),
const SizedBox(
height: 15,
),
TextField(
controller: _passController,
decoration: const InputDecoration(
hintText: 'pass',
border: OutlineInputBorder(),
),
),
],
),
),
const SizedBox(
height: 15,
),
ElevatedButton(
onPressed: regCompany,
child: const Text('Registration as a Company'),
),
const SizedBox(
height: 10,
),
ElevatedButton(
onPressed: regClient,
child: const Text('Registration as a Client'),
),
const SizedBox(
height: 10,
),
ElevatedButton(
onPressed: () {
FirebaseAuth.instance.signOut();
},
child: Text('Go Out')),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text("I'm already registered,"),
TextButton(
onPressed: () {},
child: Text('sign in'),
),
],
)
],
),
),
),
);
}
void regCompany() async {
var _emailCompany = _emailController.text;
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passController.text.trim(),
);
await FirebaseFirestore.instance
.collection('users')
.doc(_emailCompany)
.set({
'emailCompany': _emailCompany,
'typeUser': 'company',
});
_emailController.clear();
_passController.clear();
Navigator.of(context).pushNamed('/start_screen_company');
}
void regClient() async {
var _emailClient = _emailController.text;
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passController.text.trim(),
);
await FirebaseFirestore.instance.collection('users').doc(_emailClient).set({
'emailCompany': _emailClient,
'typeUser': 'client',
});
_emailController.clear();
_passController.clear();
Navigator.of(context).pushNamed('/start_screen_client');
}
}
Create Map key as String (user type) and value as Widget based on user type. like below.
Map<String, Widget> screenByUser = {
'client': StartScreenClientWidget(),
'company': StartScreenCompanyWidget()
}
use below in the home
home: isLoggedIn ? const RegScreenWidget() : screenByUser['HERE_USER_TYPE'],
This might work other wise create one more screen to handle.

How can I update my initialValue of TextFormField by setState() which is not happening?

This is my settings.dart
import 'package:flutter/material.dart';
import 'package:upm/file_manager.dart';
import 'package:upm/screens/home.dart';
import 'package:upm/screens/login.dart';
import 'package:upm/screens/signup.dart';
class Settings extends StatefulWidget {
static const FileStorage storage = FileStorage();
const Settings({Key? key}) : super(key: key);
#override
State<Settings> createState() => _SettingsState();
}
class _SettingsState extends State<Settings> {
late Map<String, dynamic> config;
#override
void initState() {
super.initState();
config = {};
getData().then((value) {
setState(() {
config = value;
});
});
}
Future<Map<String, dynamic>> getData() async {
config = await const FileStorage().readConfig();
print("Ye lo config ${config["serverIP"]}");
return config;
}
#override
Widget build(BuildContext context) {
const String name = "Settings";
return Scaffold(
appBar: AppBar(
title: const Text(name),
),
body: Container(
padding:
const EdgeInsets.only(left: 15, right: 15, bottom: 15, top: 15),
child: Center(
child: Column(
children: [
TextFormField(
initialValue: config["serverIP"].toString(),
decoration: const InputDecoration(
labelText: 'Server IP',
border: OutlineInputBorder(),
),
),
const SizedBox(
height: 15,
),
const TextField(
decoration: InputDecoration(
labelText: 'Server Port',
border: OutlineInputBorder(),
),
),
const SizedBox(
height: 25,
),
const ElevatedButton(
onPressed: null,
child: Text("Save"),
)
],
),
),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
ListTile(
title: const Text('Home'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const Home()),
);
},
),
ListTile(
title: const Text('Sign In'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const Login()),
);
},
),
ListTile(
title: const Text('Sign Up'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const Signup()),
);
},
),
],
),
),
);
}
}
This is my file_manager.dart
import 'dart:convert';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:path_provider/path_provider.dart';
class FileStorage extends StatelessWidget {
const FileStorage({Key? key}) : super(key: key);
Future<String> get _localPath async {
final directory = await getApplicationDocumentsDirectory();
return directory.path;
}
Future<File> get _localFileConfig async {
final path = await _localPath;
if (await File("$path/config.ini").exists() == false) {
File("$path/config.ini").create();
Map<String, dynamic> config = {
"serverIP": "0.0.0.0",
"port": 1234,
};
writeConfig(config);
}
File file = File("$path/config.ini");
return file;
}
Future<Map<String, dynamic>> readConfig() async {
try {
final file = await _localFileConfig;
String temp = await file.readAsString();
Map<String, dynamic> contents = await jsonDecode(temp);
print("Ye Lo contents $contents");
return contents;
} catch (e) {
print("Ye bhi Parh Lo-T $e -T");
return <String, dynamic>{};
}
}
Future<File> writeConfig(Map<String, dynamic> config) async {
final file = await _localFileConfig;
return file.writeAsString(json.encode(config));
}
#override
Widget build(BuildContext context) {
throw UnimplementedError();
}
}
I am trying to fetch a json map from a local file and using one of it keys and values as a variable for my initialValue in settings.dart. The problem I am facing is that after building all the widgets it does not rebuild my TextFormField widget with the new value of the config variable and the initialValue shows "null" and does not update.
You will be only able to set initial value 1st time, after that it won't response by default. It would be better to use TextEditingController instead.
Declare a variable on state class.
TextEditingController controller =
TextEditingController.fromValue(TextEditingValue(text: "initValue"));
#override
void initState() {
super.initState();
config = {};
getData().then((value) {
controller.text = value; // controller doent need setState to update
});
}
And use the controller
TextFormField(
controller: controller,
)

Flutterfire Provider Management With Other Screens

When I try to use Provider to look at whether the user is signed in or not, I'm getting an error. If the user is signed in, I want to go to the profile page. If the user isn't signed in, I want to go to the login page.
I created a class AuthenticationWrapper that returns to a route. I then navigate through that route.
Here is the error:
The following ProviderNotFoundException was thrown building AuthenticationWrapper(dirty):
Error: Could not find the correct Provider<User> above this AuthenticationWrapper Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that AuthenticationWrapper is under your MultiProvider/Provider<User>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>().toString()),
);
}
```
consider using `builder` like so:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context, child) {
// No longer throws
return Text(context.watch<Example>().toString());
}
);
}
```
If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
The relevant error-causing widget was
AuthenticationWrapper
Here is my main.dart:
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
import 'package:provider/provider.dart';
import 'Screens/login.dart';
import 'Screens/profile.dart';
import 'Screens/authentication.dart';
void main () async{
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform,);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
Provider<AuthenticationService>(create: (_) => AuthenticationService(FirebaseAuth.instance),),
StreamProvider(initialData: 5,create: (context) => context.read<AuthenticationService>().authStateChanges,)],
child: MaterialApp(
debugShowCheckedModeBanner: false,
initialRoute: '/',
routes: {
'/':(context) => const HomeScreen(),
'/login': (context) => const LoginScreen(),
'/profile': (context) => const ProfileScreen(),
'/userSignIn': (context) => const AuthenticationWrapper()
},
)
);
}
}
// Home Screen - First Run
class HomeScreen extends MyApp {
const HomeScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
bottomOpacity: 0.0,
flexibleSpace: Container(
decoration: const BoxDecoration(gradient: LinearGradient(begin: Alignment.topCenter, end:Alignment.bottomCenter, colors: [ Color(0xFFFE6E08), Colors.white]))
),
leading: IconButton(iconSize: 40.0, icon: const Icon(Icons.menu, color: Colors.black), tooltip: "Menu",
onPressed: () {
},
),
title: Image.asset('assets/topImage.png',scale: 8),
centerTitle: true,
actions: <Widget>[
IconButton(iconSize: 40.0, icon: const Icon(Icons.account_circle, color: Colors.black,), tooltip: "Click Here to Login or View Profile",
onPressed: () {
Navigator.pushNamed(context, '/userSignIn');
}, // onPressed
),
],
),
);
}
}
class AuthenticationWrapper extends StatelessWidget {
const AuthenticationWrapper({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
final firebaseUser = context.watch<User>();
if (firebaseUser != null) {
return const ProfileScreen();
}
return const LoginScreen();
}
}
Here is my login.dart:
```
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'authentication.dart';
class LoginScreen extends StatelessWidget {
const LoginScreen({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return WillPopScope(onWillPop: () async => false, child:
Scaffold(
appBar: AppBar(
elevation: 0,
bottomOpacity: 0.0,
flexibleSpace: Container(
decoration: const BoxDecoration(
gradient: LinearGradient(begin: Alignment.topCenter, end:Alignment.bottomCenter, colors: [ Color(0xFFFE6E08), Colors.white]),
),
),
leading: IconButton(iconSize: 40.0, icon: const Icon(Icons.menu, color: Colors.black), tooltip: "Menu",
onPressed: () {
},
),
title: IconButton(icon:Image.asset('assets/topImage.png'), iconSize: 65,
onPressed: () {
Navigator.pop(context);
},
),
centerTitle: true,
),
body: const Inputs()
)
);
}
}
class Inputs extends StatefulWidget {
const Inputs({Key? key}) : super(key: key);
#override
State<Inputs> createState() => _InputsState();
}
class _InputsState extends State<Inputs> {
final emailController = TextEditingController();
final passwordController = TextEditingController();
#override
void dispose() {
emailController.dispose();
passwordController.dispose();
super.dispose();
}
bool userauth(email, password) {
return false;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
// Top Image
Padding(
padding: const EdgeInsets.only(top: 60.0),
child: Center(
child: SizedBox(
width: 200,
height: 150,
child: Image.asset('assets/topImage.png')
),
)
),
// Email Input
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: TextField(
controller: emailController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Email',
hintText: 'Enter valid email id as abcgamil.com'
)
),
),
// Password Input
Padding(
padding: const EdgeInsets.only(
left: 15.0, right: 15.0, top: 15, bottom: 0
),
child: TextField(
controller: passwordController,
obscureText: true,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'Password',
hintText: 'Enter Secure Password'
),
)
),
// Forgot Password Text
TextButton(
onPressed: (){
// Add Forgot Password Thing Here
},
child: const Text('Forgot Password', style: TextStyle(color: Colors.black, fontSize: 15),),
),
// Submit Box
InkWell(
onTap: () {
print(emailController.text);
print(passwordController.text);
emailController.text = "";
passwordController.text = "";
context.read<AuthenticationService>().signIn(
email: emailController.text.trim(),
password: passwordController.text.trim(),
);
},
child: Ink(
height: 50,
width: 250,
decoration: BoxDecoration(
color: Colors.black,
borderRadius: BorderRadius.circular(20)
),
child: const Center(child: Text("Sign In", style: TextStyle(color: Colors.white)))
)
),
// Box for spacing
const SizedBox(height: 100),
// Text for Sign Up
const Text('New User? Create Account')
,]
),
)
);
}
}
Here is my authentication.dart:
import 'package:firebase_auth/firebase_auth.dart';
class AuthenticationService {
final FirebaseAuth _firebaseAuth;
AuthenticationService(this._firebaseAuth);
/// Changed to idTokenChanges as it updates depending on more cases.
Stream<User?> get authStateChanges => _firebaseAuth.idTokenChanges();
/// This won't pop routes so you could do something like
/// Navigator.of(context).pushNamedAndRemoveUntil('/', (Route<dynamic> route) => false);
/// after you called this method if you want to pop all routes.
Future<void> signOut() async {
await _firebaseAuth.signOut();
}
/// There are a lot of different ways on how you can do exception handling.
/// This is to make it as easy as possible but a better way would be to
/// use your own custom class that would take the exception and return better
/// error messages. That way you can throw, return or whatever you prefer with that instead.
Future<String?> signIn({required String email, required String password}) async {
try {
await _firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
return "Signed in";
} on FirebaseAuthException catch (e) {
return e.message;
}
}
/// There are a lot of different ways on how you can do exception handling.
/// This is to make it as easy as possible but a better way would be to
/// use your own custom class that would take the exception and return better
/// error messages. That way you can throw, return or whatever you prefer with that instead.
Future<String?> signUp({required String email, required String password}) async {
try {
await _firebaseAuth.createUserWithEmailAndPassword(email: email, password: password);
return "Signed up";
} on FirebaseAuthException catch (e) {
return e.message;
}
}
}
```
Thanks for the help :)
The problem seems to be while instantiating StreamProvider. You provided a number to its initialData argument which is wrong, as it's predicting the type to int. The type of StreamProvider should be User? and initialData can be null
StreamProvider<User?>(initialData: null,create: (context) => context.read<AuthenticationService>().authStateChanges,)

How to use google login in flutter and bypass emailverification

I am new to Flutter, I am currently using a logic i got from youtube to verify email first before logging in the app. The logic works just fine but now I want to make use of Google Login as an alternative.
First issue is, email address always returns null when i use google login. And i couldn't access the FirebaseAuth.instance.currentUser!.emailVerified when googleLogIn is used.
Secondly, there is no need for email verification again when using google to sign in to the app.
the App logic goes here:
Main App Page
class MyApp extends StatelessWidget {
const MyApp({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
scaffoldMessengerKey: Utils.messengerKey,
debugShowCheckedModeBanner: false,
navigatorKey: navigatorKey,
home: const AuthWrapper(),
);
}
}
class AuthWrapper extends StatelessWidget {
const AuthWrapper({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<User?>(
stream: FirebaseAuth.instance.authStateChanges(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
} else if (snapshot.hasError) {
return const Center(
child: Text('Something went wrong'),
);
}
if (snapshot.hasData) {
return const VerifyEmailPage();
} else {
return const AuthPage();
}
}),
);
}
}
VerifyEmailPage
class VerifyEmailPage extends StatefulWidget {
const VerifyEmailPage({Key? key}) : super(key: key);
#override
State<VerifyEmailPage> createState() => _VerifyEmailPageState();
}
class _VerifyEmailPageState extends State<VerifyEmailPage> {
bool isEmailVerified = false;
bool canResendEmail = false;
Timer? timer;
get label => null;
#override
void initState() {
super.initState();
//user needs to be created before
isEmailVerified = FirebaseAuth.instance.currentUser!.emailVerified;
if (!isEmailVerified) {
sendVerificationEmail();
timer = Timer.periodic(
const Duration(seconds: 10),
(_) => checkEmailVerified(),
);
}
}
#override
void dispose() {
timer?.cancel();
super.dispose();
}
Future checkEmailVerified() async {
//call after email verification
await FirebaseAuth.instance.currentUser!.reload();
setState(() {
isEmailVerified = FirebaseAuth.instance.currentUser!.emailVerified;
});
if (isEmailVerified) timer?.cancel();
}
Future sendVerificationEmail() async {
try {
final user = FirebaseAuth.instance.currentUser!;
await user.sendEmailVerification();
setState(() => canResendEmail = false);
await Future.delayed(const Duration(seconds: 5));
setState(() => canResendEmail = true);
} catch (e) {
Utils.showSnackBar(e.toString());
}
}
#override
Widget build(BuildContext context) {
return isEmailVerified
? const HomeScreen()
: Scaffold(
appBar: AppBar(
backgroundColor: lightblue,
centerTitle: true,
elevation: 0,
title: const Text(
'Verify Email',
),
),
body: SingleChildScrollView(
child: Container(
height: MediaQuery.of(context).size.height,
width: MediaQuery.of(context).size.width,
decoration: BoxDecoration(
gradient: backgroundgradient,
),
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
'A verification email has been sent to your email',
style: TextStyle(fontSize: 18, color: Colors.white),
textAlign: TextAlign.center,
),
const SizedBox(
height: 20,
),
ElevatedButton.icon(
label: const Text('Resent Email'),
icon: const Icon(
Icons.email,
size: 32,
),
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(50)),
onPressed:
canResendEmail ? sendVerificationEmail : null,
),
const SizedBox(
height: 8,
),
TextButton(
onPressed: () => FirebaseAuth.instance.signOut(),
child: const Text(
'Cancel',
style: TextStyle(fontSize: 14, color: Colors.white38),
),
)
],
),
),
),
));
}
}
Lastly my Google Login Button
Widget _googleSignInButton() {
return OutlinedButton.icon(
icon: Image.asset(
'assets/google_logo.png',
scale: 25,
),
label: const Text('Sign in with Google'),
onPressed: () async {
final newUser = await _googleSignIn.signIn();
final googleAuth = await newUser!.authentication;
final creds = GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken, idToken: googleAuth.idToken);
await FirebaseAuth.instance.signInWithCredential(creds);
},
style: OutlinedButton.styleFrom(
minimumSize: const Size.fromHeight(50),
padding: const EdgeInsets.all(15),
elevation: 1,
primary: Colors.white54,
side: const BorderSide(
color: Colors.white30,
),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(10),
),
),
),
);
}
}
I'd really appreciate an alternative or an easy work around to solve this.
Thank you in anticipation.

Hide icons before authorization and display after using BottomNavigationBar in flatter

I have a navigation menu that I made for the test application, there are some tabs as I want to hide if I have not logged in to the application. Once I log in, these tabs should appear. Here is my code for the navigation menu.
import 'package:flutter/material.dart';
import 'package:flutter_app_seals/Status.dart';
import 'package:flutter_app_seals/Seals_List.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_app_seals/auth_page.dart';
import 'package:flutter_app_seals/Setting_glob.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:flutter_app_seals/model/add_seals/JsonAddSeals.dart';
import 'package:flutter_app_seals/model/user_page/page.dart';
import 'package:flutter_app_seals/model/setting/globalvar.dart' as global;
class Main_Page extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new LoginPage());
}
}
class LoginPage extends StatefulWidget {
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
int _pageIndex = 0;
PageController _pageController;
List<Widget> tabPages = [
Login(),
Setting(),
UserPage(),
Status_Obj(),
Status_seals(),
JsonParseAddSeals(),
];
#override
void initState(){
super.initState();
_pageController = PageController(initialPage: _pageIndex);
}
#override
void dispose() {
_pageController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return WillPopScope(
child: new Scaffold(
resizeToAvoidBottomInset: false,
bottomNavigationBar: BottomNavigationBar(
showSelectedLabels: true, // <-- HERE
showUnselectedLabels: true, // <-- AND
currentIndex: _pageIndex,
onTap: onTabTapped,
backgroundColor: Colors.blue,
items: <BottomNavigationBarItem>[
new BottomNavigationBarItem( icon: Icon(Icons.admin_panel_settings_outlined), title: Text(" Вхід"),backgroundColor: Colors.lightBlue),
new BottomNavigationBarItem(icon: Icon(Icons.settings), title: Text("Налаштування"),backgroundColor: Colors.lightBlue),
if(global.nameUser ?.isNotEmpty == true)...[
new BottomNavigationBarItem(icon: Icon(Icons.person_pin), title: Text("Користувач"),backgroundColor: Colors.lightBlue),
new BottomNavigationBarItem(icon: Icon(Icons.home), title: Text("Пломбування"),backgroundColor: Colors.lightBlue),
new BottomNavigationBarItem(icon: Icon(Icons.qr_code), title: Text("Пломби"),backgroundColor: Colors.lightBlue),
new BottomNavigationBarItem(icon: Icon(Icons.add_outlined), title: Text("Акт приймання"),backgroundColor: Colors.lightBlue),
]
],
),
body: PageView(
children: tabPages,
onPageChanged: onPageChanged,
controller: _pageController,
),
),
);
}
void onPageChanged(int page) {
setState(() {
this._pageIndex = page;
});
}
void onTabTapped(int index) {
this._pageController.animateToPage(index,duration: const Duration(milliseconds: 500),curve: Curves.easeInOut);
}
}
The problem is that when I log in I go to the desired page, I do not see all the icons of the navigation menu. In order for them to appear, I need to click on one of the icons that are available (only 2 icons are available for authorization) and only then the navigation menu will be updated and everything will be as it should be. All 6 icons will be visible! Here is my auto authorization code:
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'Status.dart';
import 'dart:io';
import 'package:flutter_app_seals/model/setting/globalvar.dart' as global;
import 'package:flutter_aes_ecb_pkcs5/flutter_aes_ecb_pkcs5.dart';
import 'package:flutter/widgets.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_app_seals/model/user_page/page.dart' ;
void main() => runApp(Login());
class Login extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new LoginPage(
storage: Storage()
)
);
}
}
class LoginPage extends StatefulWidget {
final Storage storage;
LoginPage({Key key, #required this.storage}) : super(key: key);
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
//Info about users
String state;
#override
void initState() {
super.initState();
widget.storage.readData().then((String value) {
setState(() {
global.urlVar = value;
});
});
}
bool _isLoading = false;
#override
Widget build(BuildContext context) {
SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.light.copyWith(statusBarColor: Colors.transparent));
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [Colors.blue, Colors.white],
begin: Alignment.topCenter,
end: Alignment.bottomCenter),
),
child: _isLoading ? Center(child: CircularProgressIndicator()) : ListView(
children: <Widget>[
headerSection(),
textSection(),
buttonSection(),
],
),
),
);
}
signIn(String login, pass) async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
var AESLogin = login;
var AESpass = pass;
//generate a 16-byte random key
var key = '123111111111111111121212';
print(key);
//encrypt
var encryptLogin = await FlutterAesEcbPkcs5.encryptString(AESLogin, key);
var encryptPass = await FlutterAesEcbPkcs5.encryptString(AESpass, key);
HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
String url = 'testUrl';
Map map = {
"login": encryptLogin,
"pass": encryptPass
};
HttpClientRequest request = await client.postUrl(Uri.parse(url));
request.headers.set('content-type', 'application/json');
request.add(utf8.encode(json.encode(map)));
HttpClientResponse response = await request.close();
var responseBody = await response.transform(utf8.decoder).join();
Map jsonResponse = json.decode(responseBody);
print(jsonResponse);
if (response.statusCode == 200) {
if (jsonResponse['message'] ==
'200') { //if( jsonResponse['message'] == '200') {
setState(() {
_isLoading = false;
});
global.nameUser = jsonResponse['name'];
global.dataArea = jsonResponse['data_area'];
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => UserPage()),
);
}
else {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => Error_Auth()),
);
}
}
else {
setState(() {
_isLoading = false;
});
}
}
Container buttonSection() {
return Container(
width: MediaQuery.of(context).size.width,
height: 40.0,
padding: EdgeInsets.symmetric(horizontal: 15.0),
margin: EdgeInsets.only(top: 15.0),
child: RaisedButton(
onPressed: emailController.text == "" || passwordController.text == "" ? null : () {
setState(() {
_isLoading = true;
});
signIn(emailController.text, passwordController.text);
},
elevation: 0.0,
color: Colors.purple,
child: Text("Log in", style: TextStyle(color: Colors.white70)),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(5.0)),
),
);
}
final TextEditingController emailController = new TextEditingController();
final TextEditingController passwordController = new TextEditingController();
Container textSection() {
return Container(
padding: EdgeInsets.symmetric(horizontal: 15.0, vertical: 20.0),
child: Column(
children: <Widget>[
TextFormField(
controller: emailController,
cursorColor: Colors.white,
style: TextStyle(color: Colors.white70),
decoration: InputDecoration(
icon: Icon(Icons.login, color: Colors.white70),
hintText: "Name",
border: UnderlineInputBorder(borderSide: BorderSide(color: Colors.white70)),
hintStyle: TextStyle(color: Colors.white70),
),
),
SizedBox(height: 30.0),
TextFormField(
controller: passwordController,
cursorColor: Colors.white,
obscureText: true,
style: TextStyle(color: Colors.white70),
decoration: InputDecoration(
icon: Icon(Icons.lock, color: Colors.white70),
hintText: "Pass",
border: UnderlineInputBorder(borderSide: BorderSide(color: Colors.white70)),
hintStyle: TextStyle(color: Colors.white70),
),
),
],
),
);
}
Container headerSection() {
return Container(
alignment: Alignment.topCenter,
margin: EdgeInsets.only(top: 50.0),
child: Text("Пломби",
style: TextStyle(
color: Colors.white70,
fontSize: 40.0,
fontWeight: FontWeight.bold)),
);
}
}
class Error_Auth extends StatelessWidget {
#override
Widget build(BuildContext context) {
final AlertDialog dialog = AlertDialog(
title: Text('Помилка при авторизації'),
content:
Text('Повторити спробу авторизації'),
actions: [
FlatButton(
textColor: Color(0xFF6200EE),
onPressed: () => SystemNavigator.pop(),
child: Text('Ні'),
),
FlatButton(
textColor: Color(0xFF6200EE),
onPressed: () { Navigator.push(
context,MaterialPageRoute(builder: (context) => Login()),
);
},
child: Text('Так'),
),
],
);
return Scaffold(
body:dialog
);
}
}
class Storage {
Future<String> get localPath async {
final dir = await getApplicationDocumentsDirectory();
return dir.path;
}
Future<File> get localFile async {
final path = await localPath;
return File('$path/db.txt');
}
Future<String> readData() async {
try {
final file = await localFile;
String body = await file.readAsString();
return body;
} catch (e) {
return e.toString();
}
}
Future<File> writeData(String data) async {
final file = await localFile;
return file.writeAsString("$data");
}
}
After I log in to the application and go to the user page, the navigation menu is not updated and only 2 icons are available.
Solution 1: Pass the controller to the Login page
List<Widget> tabPages = [];
#override
void initState() {
super.initState();
_pageController = PageController(initialPage: _pageIndex);
tabPages = [
Login(_pageController),
Setting(),
UserPage(),
Status_Obj(),
Status_seals(),
JsonParseAddSeals(),
];
}
And instead of
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => UserPage()),
);
use
widget.pageController?.animateToPage(2,
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
Solution 2: Put your controller in your global dart.
Cool login page by the way :-)