Flutterfire Provider Management With Other Screens - flutter

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,)

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.

Assistance needed with flutter app saving user authentication status

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

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.

Flutter State Management with Bloc/Cubit

for many of you this is an obvious / stupid question, but I've come to a point where I don't have a clue anymore. I have real difficulties understanding State Management with Bloc / Cubit.
Expectation: I have a page with a ListView (recipe_list) of all recipes and an 'add' button. Whenever I click on a ListItem or the 'add' button I go to the next page (recipe_detail). On this page I can create a new recipe (if clicked the 'add' button before), update or delete the existing recipe (if clicked on ListItem before). When I click the 'save' or 'delete' button the Navigator pops and I go back to the previous page (recipe_list). I used Cubit to manage the state of the recipe list. My expectation is that the ListView updates automatically after I clicked 'save' or 'delete'. But I have to refresh the App to display the changes.
main.dart
void main() {
runApp(const MyApp());
}
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 MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Recipe Demo',
home: BlocProvider<RecipeCubit>(
create: (context) => RecipeCubit(RecipeRepository())..getAllRecipes(),
child: const RecipeList(),
)
);
}
}
recipe_list.dart
class RecipeList extends StatefulWidget {
const RecipeList({Key? key}) : super(key: key);
#override
_RecipeListState createState() => _RecipeListState();
}
class _RecipeListState extends State<RecipeList> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 24.0
),
color: const Color(0xFFF6F6F6),
child: Stack(
children: [
Column(
children: [
Container(
margin: const EdgeInsets.only(
top: 32.0,
bottom: 32.0
),
child: const Center(
child: Text('Recipes'),
),
),
Expanded(
child: BlocBuilder<RecipeCubit, RecipeState>(
builder: (context, state) {
if (state is RecipeLoading) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (state is RecipeError) {
return const Center(
child: Icon(Icons.close),
);
} else if (state is RecipeLoaded) {
final recipes = state.recipes;
return ListView.builder(
itemCount: recipes.length,
itemBuilder: (context, index) {
return GestureDetector(
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) {
return BlocProvider<RecipeCubit>(
create: (context) => RecipeCubit(RecipeRepository()),
child: RecipeDetail(recipe: recipes[index]),
);
}
));
},
child: RecipeCardWidget(
title: recipes[index].title,
description: recipes[index].description,
),
);
},
);
} else {
return const Text('Loading recipes error');
}
}
),
),
],
),
Positioned(
bottom: 24.0,
right: 0.0,
child: FloatingActionButton(
heroTag: 'addBtn',
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) {
return BlocProvider<RecipeCubit>(
create: (context) => RecipeCubit(RecipeRepository()),
child: const RecipeDetail(recipe: null),
);
}
));
},
child: const Icon(Icons.add_rounded),
backgroundColor: Colors.teal,
),
)
],
),
),
),
);
}
}
recipe_detail.dart
class RecipeDetail extends StatefulWidget {
final Recipe? recipe;
const RecipeDetail({Key? key, required this.recipe}) : super(key: key);
#override
_RecipeDetailState createState() => _RecipeDetailState();
}
class _RecipeDetailState extends State<RecipeDetail> {
final RecipeRepository recipeRepository = RecipeRepository();
final int _recipeId = 0;
late String _recipeTitle = '';
late String _recipeDescription = '';
final recipeTitleController = TextEditingController();
final recipeDescriptionController = TextEditingController();
late FocusNode _titleFocus;
late FocusNode _descriptionFocus;
bool _buttonVisible = false;
#override
void initState() {
if (widget.recipe != null) {
_recipeTitle = widget.recipe!.title;
_recipeDescription = widget.recipe!.description;
_buttonVisible = true;
}
_titleFocus = FocusNode();
_descriptionFocus = FocusNode();
super.initState();
}
#override
void dispose() {
recipeTitleController.dispose();
recipeDescriptionController.dispose();
_titleFocus.dispose();
_descriptionFocus.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 24.0
),
color: const Color(0xFFF6F6F6),
child: Stack(
children: [
Column(
children: [
Align(
alignment: Alignment.topLeft,
child: InkWell(
child: IconButton(
highlightColor: Colors.transparent,
color: Colors.black54,
onPressed: () {
Navigator.pop(context);
},
icon: const Icon(Icons.arrow_back_ios_new_rounded),
),
),
),
TextField(
focusNode: _titleFocus,
controller: recipeTitleController..text = _recipeTitle,
decoration: const InputDecoration(
hintText: 'Enter recipe title',
border: InputBorder.none
),
style: const TextStyle(
fontSize: 26.0,
fontWeight: FontWeight.bold
),
onSubmitted: (value) => _descriptionFocus.requestFocus(),
),
TextField(
focusNode: _descriptionFocus,
controller: recipeDescriptionController..text = _recipeDescription,
decoration: const InputDecoration(
hintText: 'Enter recipe description',
border: InputBorder.none
),
),
],
),
Positioned(
bottom: 24.0,
left: 0.0,
child: FloatingActionButton(
heroTag: 'saveBtn',
onPressed: () {
if (widget.recipe == null) {
Recipe _newRecipe = Recipe(
_recipeId,
recipeTitleController.text,
recipeDescriptionController.text
);
context.read<RecipeCubit>().createRecipe(_newRecipe);
//recipeRepository.createRecipe(_newRecipe);
Navigator.pop(context);
} else {
Recipe _newRecipe = Recipe(
widget.recipe!.id,
recipeTitleController.text,
recipeDescriptionController.text
);
context.read<RecipeCubit>().updateRecipe(_newRecipe);
//recipeRepository.updateRecipe(_newRecipe);
Navigator.pop(context);
}
},
child: const Icon(Icons.save_outlined),
backgroundColor: Colors.amberAccent,
),
),
Positioned(
bottom: 24.0,
right: 0.0,
child: Visibility(
visible: _buttonVisible,
child: FloatingActionButton(
heroTag: 'deleteBtn',
onPressed: () {
context.read<RecipeCubit>().deleteRecipe(widget.recipe!.id!);
//recipeRepository.deleteRecipe(widget.recipe!.id!);
Navigator.pop(context);
},
child: const Icon(Icons.delete_outline_rounded),
backgroundColor: Colors.redAccent,
),
),
),
],
),
),
),
);
}
}
recipe_state.dart
part of 'recipe_cubit.dart';
abstract class RecipeState extends Equatable {
const RecipeState();
}
class RecipeInitial extends RecipeState {
#override
List<Object> get props => [];
}
class RecipeLoading extends RecipeState {
#override
List<Object> get props => [];
}
class RecipeLoaded extends RecipeState {
final List<Recipe> recipes;
const RecipeLoaded(this.recipes);
#override
List<Object> get props => [recipes];
}
class RecipeError extends RecipeState {
final String message;
const RecipeError(this.message);
#override
List<Object> get props => [message];
}
recipe_cubit.dart
part 'recipe_state.dart';
class RecipeCubit extends Cubit<RecipeState> {
final RecipeRepository recipeRepository;
RecipeCubit(this.recipeRepository) : super(RecipeInitial()) {
getAllRecipes();
}
void getAllRecipes() async {
try {
emit(RecipeLoading());
final recipes = await recipeRepository.getAllRecipes();
emit(RecipeLoaded(recipes));
} catch (e) {
emit(const RecipeError('Error'));
}
}
void createRecipe(Recipe recipe) async {
await recipeRepository.createRecipe(recipe);
final newRecipes = await recipeRepository.getAllRecipes();
emit(RecipeLoaded(newRecipes));
}
void updateRecipe(Recipe recipe) async {
await recipeRepository.updateRecipe(recipe);
final newRecipes = await recipeRepository.getAllRecipes();
emit(RecipeLoaded(newRecipes));
}
void deleteRecipe(int id) async {
await recipeRepository.deleteRecipe(id);
final newRecipes = await recipeRepository.getAllRecipes();
emit(RecipeLoaded(newRecipes));
}
}
It looks like you're creating another BlocProvider when you're navigating to RecipeDetail page. When you're pushing new MaterialPageRoute, this new page gets additionally wrapped in new RecipeCubit. Then, when you're calling context.read<RecipeCubit>(), you're referencing that provider (as this is closest BlocProvider in the widget tree). Your RecipeList can't react to those changes because it's BlocBuilder is looking for a BlocProvider declared above it in the widget tree (the one in MyApp).
Besides that, newly created provider gets removed from the widget tree anyway when you're closing RecipeDetail page as it is declared in the MaterialPageRoute which has just been pushed off the screen.
Try to remove the additional BlocProvider (the one in RecipeList, in OnTap function of RecipeCardWidget):
onTap: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) {
return BlocProvider<RecipeCubit>( // remove this BlocProvider
create: (context) => RecipeCubit(RecipeRepository()),
child: RecipeDetail(recipe: recipes[index]),
);
}
));
},

How can I get the input of my textfield inside my custom dialog widget?

I am working on a custom dialog called "Alertbox" where the user inserts a name into a textfield and after he pushes the button a function called "addTeam" created a team out of the string.
This is how I created my dialog "Alertbox":
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:trainings_app/config/palette.dart';
class Alertbox extends StatelessWidget {
final Function addTeamFunction;
const Alertbox(this.addTeamFunction);
#override
Widget build(BuildContext context) {
return Dialog(
backgroundColor: Colors.transparent,
elevation: 0,
insetPadding: EdgeInsets.all(10),
child: Center(
child: Container(
decoration: new BoxDecoration(
borderRadius: new BorderRadius.all(const Radius.circular(20)),
color: Colors.white,
),
width: 350,
height: 200,
child: Row(
children: [
SizedBox(width: 12),
Expanded(
child: TextField(
textAlign: TextAlign.center,
autofocus: true,
),
),
SizedBox(width: 12),
ElevatedButton(
onPressed: () => addTeamFunction(),
child: const Text('✓'),
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Palette.orange),
),
),
SizedBox(width: 8),
],
),
),
),
);
}
}
And here I am using it:
void newTeam() {
showDialog<AlertDialog>(
context: context,
builder: (BuildContext context) {
return Alertbox(() {
Navigator.of(context).pop();
});
},
);
}
void addTeam(String name) {
setState(() {
teams.add(name);
});
Navigator.of(context).pop();
sharedPreferences.setStringList('teams', teams);
}
But I can't find a way to parse the input from the textfield into the function "addTeam" where it is needed. Can anyone help me please?
You Should try below code hope its helps you:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Testing',
home: MyCustomForm(),
);
}
}
class MyCustomForm extends StatefulWidget {
const MyCustomForm({Key? key}) : super(key: key);
#override
_MyCustomFormState createState() => _MyCustomFormState();
}
class _MyCustomFormState extends State<MyCustomForm> {
final myController = TextEditingController();
#override
void dispose() {
myController.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Retrieve Text Input'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
controller: myController,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
content: Text(myController.text),
);
},
);
},
tooltip: 'Show the value!',
child: const Icon(Icons.add),
),
);
}
}
Your Screen like ->
Use a TextFormField instead of a TexiField widget contained in a Form widget that has a GlobalKey, which will be useful to you during validation!
How to get the value which is already entered on the keyboard?
Uses a TextEditingController or the onSaved method of the TextFormField.