Login with Facebook not working as expected - flutter - flutter

I'm trying to implement login with Facebook in my app which it's "working" but not necessarily as expected, I'll write down the issues I'm having.
When I try to login, I don't get redirected to the homepage and the logs show an error this error:
W/Firestore(27484): (24.1.2) [Firestore]: Listen for Query(target=Query(users where Email==random_email#gmail.com order by name);limitType=LIMIT_TO_FIRST) failed: Status{code=PERMISSION_DENIED, description=Missing or insufficient permissions., cause=null}
E/flutter (27484): [ERROR:flutter/lib/ui/ui_dart_state.cc(198)] Unhandled Exception: [cloud_firestore/permission-denied] The caller does not have permission to execute the specified operation.
I'm redirected to the login page again and the login only works in the second try.
When I sign out it looks like it works fine because I got redirected to the login page but the logs show the same error above.
For last some times it happens that if I exit the app(by pressing back button) and then re-open I get redirected to the home page instead of the login page it doesn't happen always so not sure what is causing that. I'm displaying the user data in the home page and in this case it's empty.
I was thinking that it could be related to my rules set in firebase but I'm not really sure , I would appreciate it very much if someone could guide me or point me what I'm missing or doing incorrectly.
Below my code:
main.dart
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
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(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const Wrapper(),
routes: <String, WidgetBuilder>{
'/home': (BuildContext context) => const HomePage(),
'/login': (BuildContext context) => const LoginPage(),
},
);
}
}
wrapper.dart
class Wrapper extends StatelessWidget {
const Wrapper({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
//print(FirebaseAuth.instance.currentUser);
if(FirebaseAuth.instance.currentUser?.email == null){
return const LoginPage();
}
else{
return const HomePage();
}
}
}
login_page.dart
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
String get title => 'login';
#override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
String userEmail = "";
UserModel? _currentUser;
bool loading = false;
#override
Widget build(BuildContext context) {
return WillPopScope(
child: Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Padding(
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 10.0),
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Email',
),
),
),
const Padding(
padding:
EdgeInsets.symmetric(horizontal: 8.0, vertical: 10.0),
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(),
hintText: 'Password',
),
)),
CustomWidgets.socialButtonRect('Login with Facebook',
facebookColor, FontAwesomeIcons.facebookF, onTap: () async {
await signInWithFacebook();
}),
Padding(
padding: const EdgeInsets.all(15.0),
child:
loading ? const CircularProgressIndicator() : Container(),
),
],
),
), // This trailing comma makes auto-formatting nicer for build methods.
),
onWillPop: () async {
final shouldPop = await showDialog<bool>(
context: context,
builder: (context) {
return AlertDialog(
title: const Text('Do you want to go exit?'),
actionsAlignment: MainAxisAlignment.spaceBetween,
actions: [
TextButton(
onPressed: () {
Navigator.pop(context, true);
},
child: const Text('Yes'),
),
TextButton(
onPressed: () {
Navigator.pop(context, false);
},
child: const Text('No'),
),
],
);
},
);
return shouldPop!;
},
);
}
Future<void> signInWithFacebook() async {
final LoginResult loginResult =
await FacebookAuth.instance.login(permissions: ['email']);
// Create a credential from the access token
final OAuthCredential facebookAuthCredential =
FacebookAuthProvider.credential(loginResult.accessToken!.token);
final userData = await FacebookAuth.instance.getUserData();
FirebaseAuth.instance.signInWithCredential(facebookAuthCredential);
var snapshot = await FirebaseFirestore.instance
.collection('users')
.where('Email', isEqualTo: userData['email'])
.get();
if (snapshot.size == 0) {
await FirebaseFirestore.instance
.collection('users')
.add({'Email': userData["email"], 'Name': userData['name']});
}
//userEmail = userData["email"];
UserModel user = UserModel.fromJson(userData);
_currentUser = user;
setState(() {
loading = true;
});
Navigator.pushNamed(context, '/home');
}
}
home_page.dart
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
final FirebaseAuth auth = FirebaseAuth.instance;
bool loading = false;
String name = "";
#override
Widget build(BuildContext context) {
getName();
return Scaffold(
backgroundColor: Colors.blue,
bottomNavigationBar: const BottomBar(),
body: SafeArea(
child: Column(
children: [
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: 2,
itemBuilder: (context, index) {
return matchTile();
}),
Center(
child: ElevatedButton(
onPressed: () async {
//Navigator.pop(context,true);
//Navigator.popUntil(context, ModalRoute.withName('/login'));
Navigator.of(context).pushReplacementNamed('/login');
await signOut();
},
child: Text(name),
style: TextButton.styleFrom(
primary: Colors.redAccent,
onSurface: Colors.red,
),
),
),
],
),
),
);
}
Future<void> signOut() async {
await FacebookAuth.i.logOut();
await FirebaseAuth.instance.signOut();
}
Future<void> getName() async {
User? user = auth.currentUser;
await user?.reload();
user = auth.currentUser;
String? temp = user?.email;
var snapshot = await FirebaseFirestore.instance
.collection('users')
.where('Email', isEqualTo: temp)
.limit(1)
.get()
.then((value) => value.docs[0]);
Map<String, dynamic> data = snapshot.data();
//print('the lenght : $data.length');
setState(() {
name = data['Name'];
});
}
}
For last these are the rules I have set in firebase
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}

You probably are missing an await.
Without this await the user is not logged when accessing Firestore, as the code "just runs by".
await FirebaseAuth.instance.signInWithCredential(facebookAuthCredential);

Related

Future<void>.value();

Im having an issue while running the app. after initializing the app I couldnt properly see the screen at the same tiem there was an error shows which is **Future.value(); inside the feedback.dart. I have tried to catch any error which I have added but I couldnt find any.
I have seen many solution also tried but didnt worked out. Can I anyone please Let me know error which I showed below.
This is loginPage file:
import 'package:flutter/material.dart';
import 'package:food_app/bottombar/screenA.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
#override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
static Future<User?>loginUsingEmailPassword({required String email, required String password, required BuildContext context}) async {
FirebaseAuth auth = FirebaseAuth.instance;
User? user;
try {
UserCredential userCredential = await auth.signInWithEmailAndPassword(email: email, password: password);
user = userCredential.user;
} on FirebaseAuthException catch (e) {
if(e.code == "user not found"){
print("No User found for that email");
}
}
return user;
}
#override
Widget build(BuildContext context) {
TextEditingController emailController = TextEditingController();
TextEditingController passwordController = TextEditingController();
return Scaffold(
appBar: AppBar(
title: const Text("Hello world"),
),
body: SizedBox(
height: 400.0,
child: Column(
children: [
Padding(
padding: const EdgeInsets.all(15.0),
child: TextFormField(
controller: emailController,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter Username",
),
),
),
const SizedBox(
height: 20.0,
),
Padding(
padding: const EdgeInsets.all(15.0),
child: TextFormField(
controller: passwordController,
obscureText: true,
decoration: const InputDecoration(
border: OutlineInputBorder(),
hintText: "Enter Password",
),
),
),
const SizedBox(
height: 20.0,
),
ElevatedButton(
onPressed: () async{
User? user = await loginUsingEmailPassword(email: emailController.text, password: passwordController.text, context: context);
print(user);
if (user != null) {
Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context) => const ScreenA()));
}
},
child: const Text("Login")),
],
),
),
);
}
}
And this is the main.dart file:
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:food_app/loginpage.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Future<FirebaseApp> _initializeFirebase() async {
FirebaseApp firebaseApp = await Firebase.initializeApp();
return firebaseApp;
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder(
future: _initializeFirebase(),
builder: (context, snapshot){
if(snapshot.connectionState == ConnectionState.done) {
return const LoginPage();
}
return const Center(
child: CircularProgressIndicator(),
);
},
),
);
}
}
And my error is :
context.findRenderObject()!.sendSemanticsEvent(const TapSemanticEvent());
switch (_platform(context)) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
return SystemSound.play(SystemSoundType.click);
case TargetPlatform.iOS:
case TargetPlatform.linux:
case TargetPlatform.macOS:
case TargetPlatform.windows:
return Future<void>.value();
}
}

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 persist a List<String> toDo = ['eat', 'sleep', 'code'] using Flutter's SharedPreferences please?

I am making a Tasks app where users type in their tasks for the day and they can check off a task once complete. To Store tasks, i am starting with an empty [ ] that gets populated with user's input. How do i save that data using shared preferences package and display each list item in a Listview, Here's my code :
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:tooodo/views/task_lists_containers.dart';
class TasksScreen extends StatefulWidget {
const TasksScreen({Key? key}) : super(key: key);
#override
State<TasksScreen> createState() => _TasksScreenState();
}
class _TasksScreenState extends State<TasksScreen> {
// hard-coded a list of words. How do i get our to-do items from a database.
// the database will contain all user input.
List<String> toDoItems = [
'play',
'eat',
];
final TextEditingController _taskTextFieldController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: const Color.fromARGB(255, 7, 152, 70),
title: const Text('Tasks'),
centerTitle: true,
),
body: Container(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
// ignore: prefer_const_literals_to_create_immutables
child: ListView.builder(
itemCount: toDoItems.length,
itemBuilder: (context, index) {
return TaskContainer(
child: toDoItems[index],
);
})),
// floating action bar and onpressed function
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Scaffold
return AlertDialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(15))),
content: TextField(
controller: _taskTextFieldController,
textInputAction: TextInputAction.done,
decoration: InputDecoration(hintText: 'Task'),
),
actions: <Widget>[
TextButton(
child: Text("Save"),
onPressed: () {
toDoItems.add(_taskTextFieldController.text);
Navigator.of(context).pop();
_taskTextFieldController.clear();
setState(() {});
},
),
],
);
},
);
},
enableFeedback: true,
child: const Icon(Icons.add),
),
);
}
}
I welcome any advise pertaining to the code in terms of cleanliness and common standards am not following.
I created two functions:
To load from shared preferences:
List<String>? _toDoList;
Future<void> _loadToDoItems() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_toDoList = prefs.getStringList('toDoList') ?? [];
});
}
And to save to shared preferences:
_saveToDoItems() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setStringList('toDoList', _toDoList != null ? _toDoList! : []);
}
We call _loadToDoItems() in initState and _saveToDoItems() within the TextButton():
TextButton(
child: Text("Save"),
onPressed: () {
// add the task to the list and save to shared preferences
setState(() {
_toDoList?.add(_taskTextFieldController.text);
_taskTextFieldController.clear();
_saveToDoItems();
});
Navigator.of(context).pop();
_taskTextFieldController.clear();
setState(() {});
},
),
Here is a complete runnable example (note: You'll have to fully stop your current project before using this snippet):
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.dark().copyWith(
scaffoldBackgroundColor: darkBlue,
),
debugShowCheckedModeBanner: false,
home: Scaffold(
body: Center(
child: TasksScreen(),
),
),
);
}
}
class TasksScreen extends StatefulWidget {
const TasksScreen({Key? key}) : super(key: key);
#override
State<TasksScreen> createState() => _TasksScreenState();
}
class _TasksScreenState extends State<TasksScreen> {
List<String>? _toDoList;
Future<void> _loadToDoItems() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
_toDoList = prefs.getStringList('toDoList') ?? [];
});
}
_saveToDoItems() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setStringList('toDoList', _toDoList != null ? _toDoList! : []);
}
final TextEditingController _taskTextFieldController =
TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: const Color.fromARGB(255, 7, 152, 70),
title: const Text('Tasks'),
centerTitle: true,
),
body: Container(
padding: EdgeInsets.symmetric(horizontal: 10.0, vertical: 10.0),
// ignore: prefer_const_literals_to_create_immutables
child: ListView.builder(
itemCount: _toDoList?.length,
itemBuilder: (context, index) {
return Container(
child: Text(_toDoList![index]),
);
})),
// floating action bar and onpressed function
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Scaffold
return AlertDialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(15))),
content: TextField(
controller: _taskTextFieldController,
textInputAction: TextInputAction.done,
decoration: InputDecoration(hintText: 'Task'),
),
actions: <Widget>[
TextButton(
child: Text("Save"),
onPressed: () {
// add the task to the list and save to shared preferences
setState(() {
_toDoList?.add(_taskTextFieldController.text);
_taskTextFieldController.clear();
_saveToDoItems();
});
Navigator.of(context).pop();
_taskTextFieldController.clear();
setState(() {});
},
),
],
);
},
);
},
enableFeedback: true,
child: const Icon(Icons.add),
),
);
}
// load list from shared preferences
#override
void initState() {
_loadToDoItems();
super.initState();
}
}
Result:
You can follow this snippet, I am using getStringList and setStringList.
class TasksScreen extends StatefulWidget {
const TasksScreen({Key? key}) : super(key: key);
#override
State<TasksScreen> createState() => _TasksScreenState();
}
class _TasksScreenState extends State<TasksScreen> {
final TextEditingController _taskTextFieldController =
TextEditingController();
static const String listKey = "ITEMS";
Future<List<String>?> getItems() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getStringList(listKey) ?? [];
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: const Color.fromARGB(255, 7, 152, 70),
title: const Text('Tasks'),
centerTitle: true,
),
body: FutureBuilder<List<String>?>(
future: getItems(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: snapshot.data?.length,
itemBuilder: (context, index) {
return Text(snapshot.data![index]);
},
);
}),
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
floatingActionButton: FloatingActionButton(
onPressed: () async {
await showDialog(
context: context,
builder: (BuildContext context) {
// return object of type Scaffold
return AlertDialog(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(15))),
content: TextField(
controller: _taskTextFieldController,
textInputAction: TextInputAction.done,
decoration: InputDecoration(hintText: 'Task'),
),
actions: <Widget>[
TextButton(
child: Text("Save"),
onPressed: () async {
final prefs = await SharedPreferences.getInstance();
final oldItems = prefs.getStringList(listKey) ?? [];
await prefs.setStringList(listKey,
[...oldItems, _taskTextFieldController.text]);
Navigator.of(context).pop();
},
),
],
);
},
);
setState(() {}); //to rebuild the future builder , better way of doing it getting data from dialog and then update database and ui
},
enableFeedback: true,
child: const Icon(Icons.add),
),
);
}
}

NavigatorPush is not working on my Flutter App

I try to build simple login with laravel but then got stuck. After login success I can't redirect to another page with Navigator.push. I think I've followed the tutorial right.
this is login.dart
class LoginScreen extends StatefulWidget {
static const routeName = '/login-screen';
const LoginScreen({Key? key}) : super(key: key);
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
TextEditingController txtUsername = new TextEditingController();
TextEditingController txtPassword = new TextEditingController();
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size; //provide total height and width
return Scaffold(
body: Background(
child1: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Positioned(
top: 0,
left: 0,
child: Image.asset('assets/images/wedding.png', width: 250),
),
SizedBox(height: size.height * 0.01),
roundedInputField(
hintText: 'Email',
controller: txtUsername,
onChanged: (value) {},
),
PasswordField(
hintText: 'Password',
controller: txtPassword,
onChanged: (value) {},
),
Button(
text: 'LOGIN',
press: () {
this.doLogin();
},
)
],
),
),
),
);
}
void showToast(msg) => Fluttertoast.showToast(msg: msg);
Future doLogin() async {
WidgetsBinding.instance.focusManager.primaryFocus?.unfocus();
if(txtUsername.text.isEmpty || txtPassword.text.isEmpty) {
showToast('email/password kosong');
}else {
showDialog(
context: context,
builder: (context) {
return Center(
child: CircularProgressIndicator(),
);
});
final response = await http.post(
Uri.parse('http://10.0.2.2/flutter/api/login'),
body: {'email': txtUsername.text, 'password': txtPassword.text},
headers: {'Accept': 'application/json'}
);
final responseData = json.decode(response.body);
if (response.statusCode == 200) {
showToast('berhasil login');
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => const NavbarScreen(),
));
// Navigator.of(context).push(
// MaterialPageRoute(builder: (_){
// return NavbarScreen();
// },
// ),
// );
//print(responseData);
} else {
showToast('gagal login');
}
Navigator.of(context).pop(); //end loading
}
}
}
This is the login logic in login.dart
if (response.statusCode == 200) {
showToast('berhasil login');
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => const NavbarScreen(),
));
//print(responseData);
} else {
showToast('gagal login');
}
This is 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: 'Breeze',
theme: ThemeData(
primaryColor: kPrimaryColor,
scaffoldBackgroundColor: Colors.white,
),
//home: DashboardScreen(),
initialRoute: '/',
routes: {
'/': (ctx) => LoginScreen(),
LoginScreen.routeName: (ctx) => LoginScreen(),
NavbarScreen.routeName: (ctx) => NavbarScreen(),
CheckinScreen.routeName: (ctx) => CheckinScreen(),
CheckoutScreen.routeName: (ctx) => CheckoutScreen(),
},
);
}
}
#Damara Jati P Kindly make the following changes Step 1-3
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class LoginScreen extends StatefulWidget {
static const routeName = '/login-screen';
const LoginScreen({Key? key}) : super(key: key);
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
TextEditingController txtUsername = new TextEditingController();
TextEditingController txtPassword = new TextEditingController();
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size; //provide total height and width
return Scaffold(
body: Background(
child1: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Positioned(
top: 0,
left: 0,
child: Image.asset('assets/images/wedding.png', width: 250),
),
SizedBox(height: size.height * 0.01),
roundedInputField(
hintText: 'Email',
controller: txtUsername,
onChanged: (value) {},
),
PasswordField(
hintText: 'Password',
controller: txtPassword,
onChanged: (value) {},
),
Button(
text: 'LOGIN',
press: () {
// Steps 1
this.doLogin(context);
},
)
],
),
),
),
);
}
void showToast(msg) => Fluttertoast.showToast(msg: msg);
// Steps 2
Future doLogin(BuildContext context) async {
WidgetsBinding.instance.focusManager.primaryFocus?.unfocus();
if (txtUsername.text.isEmpty || txtPassword.text.isEmpty) {
showToast('email/password kosong');
} else {
showDialog(
context: context,
builder: (context) {
return Center(
child: CircularProgressIndicator(),
);
});
final response = await http.post(
Uri.parse('http://10.0.2.2/flutter/api/login'),
body: {'email': txtUsername.text, 'password': txtPassword.text},
headers: {'Accept': 'application/json'});
final responseData = json.decode(response.body);
if (response.statusCode == 200) {
showToast('berhasil login');
// Steps 3
Navigator.push(
context, MaterialPageRoute(builder: (context) => NavbarScreen()));
} else {
showToast('gagal login');
}
}
}
}
try using named route navigator. I show how to route with or without parameters. The generator class contains all the routing definitions in one place
class MyApp extends StatelessWidget{
return MaterialApp(
...
onGenerateRoute: RouteGenerator.handleRoute,
...
}
Navigator.pushNamed(context, RouteGenerator.homePage);
Navigator.pushNamed(
context,
RouteGenerator.page2Page,
arguments: myView)
.then((completion) {
});
class RouteGenerator {
static const String homePage = "/home";
static const String page1Page = "/page1";
static const String page2Page = "/page2";
RouteGenerator._();
static Route<dynamic> handleRoute(RouteSettings routeSettings) {
Widget childWidget;
switch (routeSettings.name) {
case homePage:
{
childWidget = HomePageWidget(title: 'Home');
}
break;
case page1Page:
{
childWidget = Page1Widget();
}
break;
case page2Page:
{
final args = routeSettings.arguments as MyView;
childWidget = Page2Widget(args);
}
break;
default:
throw FormatException("Route Not Found");
}
return MaterialPageRoute(builder: (context) => childWidget);
}
}
Firstly, you are using two different routename for LoginScreen. While this will be the home use
static const routeName = '/';
Now for the method try passing context for safety doLogin(context)
showDialog, push and Fluttertoast.showToast are future methods, provide await before theses.
Future<void> showToast(msg) async => await Fluttertoast.showToast(msg: msg);
showDialog brings another context that is needed to be close to move further. Hopping you are just depending on barrierDismissible: true. else create button or logic to close the dialog.
Future<void> doLogin(BuildContext context) async {
await showDialog(
context: context,
barrierDismissible: true,
builder: (context) {
return Center(
child: Column(
children: [
Center(
child: CircularProgressIndicator(),
),
ElevatedButton(
onPressed: Navigator.of(context).pop,
child: Text("Close the dialog"))
],
),
);
},
);
await showToast('berhasil login');
await Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) => TS(), // place your screen widget
));
}

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