How to use google login in flutter and bypass emailverification - flutter

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.

Related

Can't read the arguments on a second screen using PushNamed in Flutter

I have the following code that is passing an object as an Argument to another screen using Push named:
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter_nsd/flutter_nsd.dart';
import 'package:australremote/screens/screen5.dart';
import 'package:australremote/screens/screen6.dart';
void main() {
runApp(const Screen1());
}
class Screen1 extends StatefulWidget {
const Screen1({Key? key}) : super(key: key);
#override
State createState() => _Screen1State();
}
class _Screen1State extends State<Screen1> {
final flutterNsd = FlutterNsd();
final services = <NsdServiceInfo>[];
bool initialStart = true;
bool _scanning = false;
//List<String> _deviceIPAddresses = [];
_Screen1State();
#override
void initState() {
super.initState();
// Try one restart if initial start fails, which happens on hot-restart of
// the flutter app.
flutterNsd.stream.listen(
(NsdServiceInfo service) {
setState(() {
services.add(service);
});
},
onError: (e) async {
if (e is NsdError) {
if (e.errorCode == NsdErrorCode.startDiscoveryFailed &&
initialStart) {
await stopDiscovery();
} else if (e.errorCode == NsdErrorCode.discoveryStopped &&
initialStart) {
initialStart = false;
await startDiscovery();
}
}
},
);
startDiscovery();
}
Future<void> startDiscovery() async {
if (_scanning) return;
setState(() {
services.clear();
_scanning = true;
});
await flutterNsd.discoverServices('_http._tcp.');
/*List<InternetAddress> addresses = await services.resolve();
setState(() {
_deviceIPAddresses.add(addresses[0].address);
});*/
}
Future<void> stopDiscovery() async {
if (!_scanning) return;
setState(() {
services.clear();
_scanning = false;
});
flutterNsd.stopDiscovery();
}
#override
Widget build(BuildContext context) {
//return MaterialApp(
return Scaffold(
appBar: AppBar(
title: const Text('Your devices',style: TextStyle(color: Colors.black87),),
titleSpacing: 00.0,
centerTitle: true,
toolbarHeight: 60.2,
toolbarOpacity: 0.6,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(25),
bottomLeft: Radius.circular(25)),
),
elevation: 0.00,
backgroundColor: Colors.transparent,
),
body:
Column(
children: <Widget>[
Expanded(
child: _buildMainWidget(context),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
SafeArea(
child: IconButton(
iconSize: 32.0,
icon: const Icon(Icons.add),
tooltip: 'Add a new device',
onPressed: () {
// Navigate to the second screen using a named route.
Navigator.pushNamed(context, '/second');
},
),
),
],
),
],
),
);
// );
}
Widget _buildMainWidget(BuildContext context) {
if (services.isEmpty && _scanning) {
return const Center(
child: CircularProgressIndicator(),
);
} else if (services.isEmpty && !_scanning) {
return const SizedBox.shrink();
} else {
print(services);
return ListView.builder(
itemBuilder: (context, index) => ListTile(
leading: Icon(
Icons.speaker,
color: Colors.grey[500],
),
title: Text(services[index].name ?? 'Invalid service name',textAlign: TextAlign.left,style: TextStyle(color: Colors.black,fontWeight: FontWeight.w500,
fontSize: 20,)
),
subtitle: Text(services[index].hostname ?? 'Invalid service name',textAlign: TextAlign.left,style: TextStyle(color: Colors.grey,fontWeight: FontWeight.w500,
fontSize: 10,)
),
trailing: Icon(
Icons.arrow_forward_ios,
),
onTap: ()
{
Navigator.of(context).pushNamed(
'/sixth',
arguments:services,
);
},
),
itemCount: services.length,
);
}
}
}
Second screen:
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:australremote/screens/screen1.dart';
import 'package:convert/convert.dart';
void main() {
runApp(MyHomePage6());
}
class MyHomePage6 extends StatefulWidget {
const MyHomePage6({Key? key}) : super(key: key);
#override
_MyHomePage6State createState() => _MyHomePage6State();
}
class _MyHomePage6State extends State<MyHomePage6> {
// final String args = "";
#override
Widget build(BuildContext context) {
//final services args = ModalRoute.of(context)?.settings?.arguments;
//String hostname = args[0];
final args = ModalRoute.of(context)?.settings?.arguments as List<String>;
final String services = '';
final int index;
final String hostname ='';
final String service = '';
return Scaffold(
appBar: AppBar(
title: const Text('Connect your DAC to your network',
style: TextStyle(color: Colors.black87)),
titleSpacing: 00.0,
centerTitle: true,
toolbarHeight: 60.2,
toolbarOpacity: 0.6,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
bottomRight: Radius.circular(25),
bottomLeft: Radius.circular(25)),
),
elevation: 0.00,
backgroundColor: Colors.transparent,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(service),
//Text("Your name is :$name"),
/*SizedBox(height: 20),
Text("HEX value: $_hexValue"),
SizedBox(height: 20),
Text("API response: $_apiResponse"),
SizedBox(height: 20),
Text("SSID Hex: $_ssidHexValue"),*/
SizedBox(height: 20),
TextButton(
onPressed:() async{
// Send an API request with the HEX value
var response = await http.get(Uri.parse(
"http://10.10.10.254/httpapi.asp?command=getStatusEx"),);
if (response.statusCode == 200) {
print(response);
// Navigator.pushNamed(context, '/');
} else {
print("There is something wrong here");
}
},
child: Text("Next", style: TextStyle(color: Colors.grey)),
),
],
),
),
);
}
}
I tried to define the getters, but I'm not quite sure I can extract the content from the arguments I've sent from the previous screen.Also, I'm receiving the following error:
type 'List' is not a subtype of type 'List' in type cast
Problem:
At first screen you defined services with generic NsdServiceInfo like:
final services = <NsdServiceInfo>[];
but on second screen , at modalRoute you used String as generic like:
final args = ModalRoute.of(context)?.settings?.arguments as List<String>;
Solution:
so make String to NsdServiceInfo in route arg defining variable like:
final args = ModalRoute.of(context)?.settings?.arguments as List<NsdServiceInfo>;

Error: Could not find the correct Provider<RecipeProvider> above this AddRecipe Widget

`I want to access the provider in the AddRecipe page in order to save a new recipe and notify the listeners, in my case the list.builder in UserRecipes to rebuild.
Sorry for the big amount of code, I added everything that I thought may be useful. I just can't figure it out, have been trying for hours.
This is the error that I get:
ProviderNotFoundException (Error: Could not find the correct Provider<RecipeProvider> above this AddRecipe Widget
These are the files:
Wrapper.dart:
class Wrapper extends StatelessWidget {
const Wrapper({super.key});
#override
Widget build(BuildContext context) {
final user = Provider.of<User?>(context);
if (user == null) {
return const AuthPage();
} else {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => RecipeProvider(uid: user.uid))
],
child: const HomePage()
);
}
}
}
Home.dart:
class HomePage extends StatefulWidget {
const HomePage({super.key});
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final AuthService _auth = AuthService();
// The 4 main application screens
static const List<Destination> allDestinations = <Destination>[
Destination(0, 'Home', Icons.home, Colors.teal),
Destination(1, 'Meals', Icons.dinner_dining, Colors.orange),
Destination(2, 'Recipes', Icons.restaurant_menu_sharp, Colors.amber),
Destination(3, 'Profile', Icons.person, Colors.blue),
];
int currentPageIndex = 0;
final screens = [
Center(child: Text('Home'),),
Center(child: Text('Meals'),),
RecipesPage(),
Center(child: Text('My profile'),),
];
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(title: allDestinations[currentPageIndex].title, backButton: false, signOutButton: true),
bottomNavigationBar: NavigationBar(
onDestinationSelected: (int index) {
setState(() {
currentPageIndex = index;
});
},
selectedIndex: currentPageIndex,
destinations: allDestinations.map((Destination destination) {
return NavigationDestination(
icon: Icon(destination.icon, color: destination.color),
label: destination.title
);
}).toList(),
),
body: screens[currentPageIndex]
);
}
}
class Destination {
const Destination(this.index, this.title, this.icon, this.color);
final int index;
final String title;
final IconData icon;
final MaterialColor color;
}
Recipes.dart:
const List<Widget> recipes = <Widget>[
Text('My recipes'),
Text('Other recipes')
];
class RecipesPage extends StatefulWidget {
const RecipesPage({super.key});
#override
State<RecipesPage> createState() => _RecipesPageState();
}
class _RecipesPageState extends State<RecipesPage> {
final List<bool> _selectedRecipes = <bool>[true, false];
final _searchContoller = TextEditingController();
#override
void dispose() {
_searchContoller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
var provider = context.watch<RecipeProvider>();
return Scaffold(
body: SafeArea(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
SizedBox(height: 20),
ToggleButtons(
direction: Axis.horizontal,
onPressed: (int index) {
setState(() {
for (int i = 0; i < _selectedRecipes.length; i++) {
_selectedRecipes[i] = i == index;
}
});
},
borderRadius: const BorderRadius.all(Radius.circular(12)),
selectedBorderColor: Colors.blue[700],
selectedColor: Colors.white,
fillColor: Colors.blue[200],
color: Colors.blue[400],
constraints: const BoxConstraints(
minHeight: 40.0,
minWidth: 165.0,
),
isSelected: _selectedRecipes,
children: recipes
),
SizedBox(height: 10),
// Search textfield
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: TextField(
controller: _searchContoller,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.white),
borderRadius: BorderRadius.circular(12),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(color: Colors.blue),
borderRadius: BorderRadius.circular(12),
),
hintText: 'Search',
fillColor: Colors.grey[250],
filled: true
),
),
),
SizedBox(height: 20),
Expanded(
child: _getRecipePage(),
),
],
)
),
),
floatingActionButton: Consumer<RecipeProvider>(
builder: (context, value, child) {
return _getFAB();
},
)
);
}
Widget _getFAB() {
if (_selectedRecipes[1]) {
return Container();
} else {
return FloatingActionButton(
child: Icon(Icons.add, size: 35),
onPressed: () => {
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => AddRecipe()
)
),
},
);
}
}
Widget _getRecipePage() {
if (_selectedRecipes[0]) {
return UserRecipesWidget(search: _searchContoller.text.trim());
} else {
return OtherRecipesWidget();
}
}
}
User_recipes.dart:
class UserRecipesWidget extends StatefulWidget {
UserRecipesWidget({super.key, required this.search});
String search;
#override
State<UserRecipesWidget> createState() => _UserRecipesWidgetState();
}
class _UserRecipesWidgetState extends State<UserRecipesWidget> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
var provider = context.watch<RecipeProvider>();
return FutureBuilder(
future: provider.getUserRecipesFuture,
builder: (BuildContext ctx, AsyncSnapshot asyncSnapshot) {
if (asyncSnapshot.connectionState == ConnectionState.done) {
if (asyncSnapshot.hasError) {
return const Center(child: Text('Could not retreive recipes!'));
}
return ListView.builder(
itemCount: provider.recipes.length,
itemBuilder: (BuildContext ctx, int index) {
return GestureDetector(
onTap: () => {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => RecipePage(recipe: provider.recipes[index])
)
),
},
child: RecipeCard(recipe: provider.recipes[index]),
);
}
);
} else {
return Center(child: CircularProgressIndicator());
}
}
);
}
}
RecipeProvider:
class RecipeProvider extends ChangeNotifier {
late RecipeService _recipeService;
List<RecipeModel> recipes = [];
late Future getUserRecipesFuture;
final String uid;
RecipeProvider({ required this.uid }) {
_recipeService = RecipeService(uid: uid);
getUserRecipesFuture = _getUserRecipesFuture();
}
Future _getUserRecipesFuture() async {
recipes = await _recipeService.getUserRecipes();
addDummyRecipe();
}
addDummyRecipe() {
recipes.add(RecipeModel(uid: "test", userId: "test", recipeName: "Pork"));
recipes.add(RecipeModel(uid: "test1", userId: "test1", recipeName: "Pizza"));
recipes.add(RecipeModel(uid: "test2", userId: "test2", recipeName: "Burger"));
notifyListeners();
}
}
And the main one that gives the error, add_recipe.dart:
class AddRecipe extends StatefulWidget {
const AddRecipe({super.key});
#override
State<AddRecipe> createState() => _AddRecipeState();
}
class _AddRecipeState extends State<AddRecipe> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: CustomAppBar(title: 'Add recipe', backButton: true),
body: SafeArea(
child: Center(
child: Column(
children: [
Text('Add recipe'),
SizedBox(height: 50),
// Add recipe button
Padding(
padding: const EdgeInsets.symmetric(horizontal: 30.0),
child: GestureDetector(
onTap: () async {
context.read<RecipeProvider>().addDummyRecipe();
},
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: Text(
'Save recipe',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
),
),
),
),
]
)
)
),
);
}
}
`
the exception happens because you provided the RecipeProvider only for the HomePage(). means when you push a new route, it doesn't have that the RecipeProvider instance.
if you can't provide the RecipeProvider Golobally because it needs the uid, then you must provide it each time you push a new route
change your RecipePage Navigator to this
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ChangeNotifierProvider.value(
value: context.read<RecipeProvider>(),
child: RecipePage(recipe: provider.recipes[index]),
),
),
),
and your AddRecipe
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => ChangeNotifierProvider.value(
value: context.read<RecipeProvider>(),
child: AddRecipe(),
),
),
),

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 can i fix flutter app stuck in loading screen?

I'm working on a project(as a student) about writing a dictionary application. And now my code can run without an error but my app is stuck in a loading screen loop. Please help me.
ps. My English isn’t so great but I’ll try, please forgive me if I make a few mistakes
There is a problem with this area.
_search() async {
if (_controller.text == null || _controller.text.length == 0) {
_streamController.add(null);
return;
}
_streamController.add("waiting");
Uri uri = Uri.parse(_url + _controller.text.trim());
http.Response response = await http.get(uri,
headers: {"Authorization": "Token " + _token});
_streamController.add(json.decode(response.body));
}
And here is all of my code.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
//const MyApp({ Key? key }) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "FlutterDemo",
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _url = "https://owlbot.info";
String _token = "My token";
TextEditingController _controller = TextEditingController();
late StreamController _streamController;
late Stream _stream;
_search() async {
if (_controller.text == null || _controller.text.length == 0) {
_streamController.add(null);
return;
}
_streamController.add("waiting");
Uri uri = Uri.parse(_url + _controller.text.trim());
http.Response response = await http.get(uri,
headers: {"Authorization": "Token " + _token});
_streamController.add(json.decode(response.body));
}
#override
void initState() {
super.initState();
_streamController = StreamController();
_stream = _streamController.stream;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Flictionary"),
bottom: PreferredSize(
preferredSize: Size.fromHeight(48.0),
child: Row(
children: <Widget>[
Expanded(
child: Container(
margin: const EdgeInsets.only(left: 12.0, bottom: 8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24.0),
),
child: TextFormField(
onChanged: (String text) {},
controller: _controller,
decoration: InputDecoration(
hintText: "Search for a word",
contentPadding: const EdgeInsets.only(left: 24.0),
border: InputBorder.none,
),
),
),
),
IconButton(
icon: Icon(
Icons.search,
color: Colors.white,
),
onPressed: () {
_search();
},
),
],
),
),
),
body: Container(
margin: const EdgeInsets.all(8.0),
child: StreamBuilder(
stream: _stream,
builder: (BuildContext ctx, AsyncSnapshot snapshot){
if(snapshot.data == null){
return Center(
child: Text("Enter a search word"),
);
}
if(snapshot.data == "waiting"){
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data["definitions"].length,
itemBuilder: (context, int index){
return ListBody(
children: <Widget> [
Container(
color: Colors.grey[300],
child: ListTile(
leading: snapshot.data["definitions"][index]["image_url"] == null
? null:
CircleAvatar(
backgroundImage: NetworkImage(snapshot.data["definitions"][index]["image_url"]),
),
title: Text(_controller.text.trim() + "(" + snapshot.data["definitions"][index]["type"] + ")"),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(snapshot.data["definitions"][index]["definitions"]),
),
],
);
},);
},
),
),
);
}
}```
import 'dart:convert';
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
//const MyApp({ Key? key }) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: "FlutterDemo",
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final String _url = "https://owlbot.info/api/v4/dictionary/";
final String _token = "c7a0e47311736683d87aa05498b81862dee4f0d4";
final TextEditingController _controller = TextEditingController();
late StreamController _streamController;
late Stream _stream;
_search() async {
if (_controller.text.isEmpty) {
_streamController.add(null);
return;
}
_streamController.add("waiting");
Uri uri = Uri.parse(_url + _controller.text.trim());
http.Response response = await http.get(uri,
headers: {"Authorization": "Token " + _token});
var jsoon = json.decode(response.body);
print(jsoon["definitions"]);
_streamController.add(json.decode(response.body)["definitions"]);
}
#override
void initState() {
super.initState();
_streamController = StreamController();
_stream = _streamController.stream;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Dictionary"),
bottom: PreferredSize(
preferredSize: const Size.fromHeight(48.0),
child: Row(
children: <Widget>[
Expanded(
child: Container(
margin: const EdgeInsets.only(left: 12.0, bottom: 8.0),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(24.0),
),
child: TextFormField(
onChanged: (String text) {},
controller: _controller,
decoration: const InputDecoration(
hintText: "Search for a word",
contentPadding: EdgeInsets.only(left: 24.0),
border: InputBorder.none,
),
),
),
),
IconButton(
icon: const Icon(
Icons.search,
color: Colors.white,
),
onPressed: _search,
),
],
),
),
),
body: Container(
margin: const EdgeInsets.all(8.0),
child: StreamBuilder(
stream: _stream,
builder: (BuildContext ctx, AsyncSnapshot snapshot){
if(snapshot.data == null){
return const Center(
child: Text("Enter a search word"),
);
}
if(snapshot.data == "waiting"){
return const Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (context, int index){
print(snapshot.data.length);
return ListBody(
children: <Widget> [
Container(
color: Colors.grey[300],
child: ListTile(
leading: snapshot.data[index]["image_url"] == null
? null:
CircleAvatar(
backgroundImage: NetworkImage(snapshot.data[index]["image_url"]),
),
title: Text(_controller.text.trim() + "(" + snapshot.data[index]["definition"] + ")"),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Text(snapshot.data[index]["definition"]),
),
],
);
},);
},
),
),
);
}
}
You were modeling your data wrong and API used was not upto standards presented at the documentation
https://owlbot.info/api-reference#tag/English-Dictionary
Refer the link above...
This is working...
async should be imported for stream controller...
body is not required for onPressed you can directly point to the function.
unless you want to add more funtionality.