how to close the application on clicking cancel in local auth and also when maximum tries exceeds in flutter - flutter

I'm new in flutter. I wanted to create an application with local biometrics I have used local auth and i need to have help with
close the application on the click of cancel button in local_auth,
close the application when maximum tries are done.
pause the background untill authentication complete
my code is
import 'dart:async';
import 'package:LogInSignIn.dart';
import 'package:flutter/material.dart';
import 'package:cashhub/homescreen.dart';
import 'package:local_auth/local_auth.dart';
import 'package:flutter/services.dart';
void main() {
setupLocator();
runApp(new MaterialApp(
debugShowCheckedModeBanner: false,
home: new SplashScreen(),
routes: <String, WidgetBuilder>{
'/HomeScreen': (BuildContext context) => new LogInSignIn(),
},
));
}
class SplashScreen extends StatefulWidget {
#override
_SplashScreenState createState() => new _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen> {
//final LocalAuthenticationService _localAuth = locator<LocalAuthenticationService>();
final LocalAuthentication auth = LocalAuthentication();
bool _canCheckBiometrics;
List<BiometricType> _availableBiometrics;
String _authorized = 'Not Authorized';
bool _isAuthenticating = false;
startTime() async {
var _duration = new Duration(seconds: 4);
return new Timer(_duration, navigationPage);
}
Future<void> _authenticate() async {
bool authenticated = false;
try {
setState(() {
_isAuthenticating = true;
_authorized = 'Authenticating';
});
authenticated = await auth.authenticateWithBiometrics(
localizedReason: 'Scan your fingerprint to authenticate',
useErrorDialogs: true,
stickyAuth: true);
setState(() {
_isAuthenticating = false;
_authorized = 'Authenticating';
});
} on PlatformException catch (e) {
print(e);
}
if (!mounted) return;
final String message = authenticated ? 'Authorized' : 'Not Authorized';
// if( message == "Not Authorized"){
// SystemNavigator.pop();
// }
setState(() {
_authorized = message;
});
}
void navigationPage() {
Navigator.of(context).pushReplacementNamed('/HomeScreen');
}
#override
void initState() {
_authenticate();
//autho();
super.initState();
startTime();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Image.asset('assets/splashlogo.png',
),
),
);
}
}
anyone please help me with this 3 queries..

you can close the app with cancel click like this
setState(() {
if (isAuthorized) {
_authorizedOrNot = "Authorized";
} else {
_authorizedOrNot = "Not Authorized";
exit(0);
}
});
just so you know exit(0) need to impot dart:io

Related

Flutter GetX dependency Injection

I'm new to GetX flutter state management. I'm using two controllers, one for Login and other for Home data(fetching some restaurants data through API call). I'm having trouble in bindings. I'm using bindings in my app following GetX docs. But I'm unable to use it properly and getting error. Following is the code -:
main.dart
void main() async {
await GetStorage.init('My Storage');
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 GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flunkey Task',
getPages: [
GetPage(
name: '/',
page: () => LandingPage(),
binding: BindingsBuilder(() {
Get.lazyPut<LoginController>(() => LoginController());
})),
GetPage(
name: '/login',
page: () => LoginScreen(),
binding: BindingsBuilder(() {
Get.lazyPut<LoginController>(() => LoginController());
})),
GetPage(
name: '/home',
page: () => HomeScreen(),
binding: BindingsBuilder(() {
Get.lazyPut<HomeController>(() => HomeController());
}),
)
],
initialRoute: '/',
);
}
}
class LandingPage extends StatelessWidget {
LandingPage({Key? key}) : super(key: key);
final _controller = Get.find<LoginController>();
#override
Widget build(BuildContext context) {
SizeConfig().init(context);
return Obx(() =>
_controller.isLoggedIn.value == true ? HomeScreen() : LoginScreen());
}
}
loginController.dart
class LoginController extends GetxController {
final box = GetStorage('My Storage');
var isLoggedIn = false.obs;
final formKey = GlobalKey<FormState>();
final usernameTED = TextEditingController();
final passwordTED = TextEditingController();
#override
void onInit() {
isLoggedIn(loginStatus);
super.onInit();
}
#override
void onClose() {
usernameTED.dispose();
passwordTED.dispose();
super.onClose();
}
String? checkUsername(String username) {
if (username.isEmpty || username.length < 3 || username.length > 11) {
return 'Username must have 3-11 characters';
}
return null;
}
String? checkPassword(String password) {
if (password.isEmpty || password.length < 3 || password.length > 11) {
return 'Password must have 3-11 characters';
}
return null;
}
Future<void> login() async {
if (!formKey.currentState!.validate()) {
return;
}
if ((usernameTED.text.trim() == 'flunkey' &&
passwordTED.text.trim() == 'password123') ||
(usernameTED.text.trim() == 'user' &&
passwordTED.text.trim() == 'password123')) {
formKey.currentState!.save();
await changeLoginStatus(true);
await saveUserName(usernameTED.text);
usernameTED.clear();
passwordTED.clear();
} else {
Get.snackbar('Login Error', 'User does not exists',
backgroundColor: Colors.red[400]);
}
}
void signOut() async {
await changeLoginStatus(false);
}
Future<void> changeLoginStatus(bool status) async {
await box.write('status', status);
isLoggedIn(status);
}
Future<void> saveUserName(String name) async {
await box.write('name', name);
}
bool get loginStatus => box.read('status') ?? false;
String get currentUserName => box.read('name') ?? '';
}
homeController.dart
class HomeController extends GetxController {
final _isLoading = false.obs;
final _restaurantData = <restau.Datum>[].obs;
#override
void onInit() {
getData();
super.onInit();
}
bool get isLoading => _isLoading.value;
List<restau.Datum> get getRestaurants => _restaurantData;
Future<void> getData() async {
try {
_isLoading(true);
var apiData = await RestaurantDataApiCall.getRestaurantData();
_restaurantData.value = apiData!.data.data;
_isLoading(false);
} catch (e, s) {
print(e);
print(s);
}
}
}
Following is the error I'm getting.
I'm using Get.find() on Login Screen and Get.find() on Home screen as following,
Please guide me how to properly use Bindings in GetX.
I don't like to bind the controllers on route.
I create a MainBind.dart and put inside this class all getx controllers.
class MainBinding implements Bindings {
#override
Future<void> dependencies() async{
Get.lazyPut<AppController>(() => AppController(), fenix: true);
}
}
And in my Main.dart :
void main() async{
WidgetsFlutterBinding.ensureInitialized();
MainBinding mainBinding = MainBinding();
await mainBinding.dependencies();
runApp(const MyApp());
}
In this way I'm sure that Controllers are binded.
But you can try use Put insted lazyPut too..
You can use StatefulWidget with state class which will contain your controller.
E.g.
StateClass bla, bla {
late final yourController = Get.put<YourController>();
#override
dispose() {
Get.delete<YourController>();
}
}
That's it!

how to await for network connectivity status in flutter

I have used connectivity_plus and internet_connection_checker packages to check the internet connectivity.
The problem occured is , the app works perfectly fine as expected when the app start's with internet on state. But when the app is opened with internet off, the dialog isn't shown !!
I assume this is happening because the build method is called before the stream of internet is listened.
Code :
class _HomePageState extends State<HomePage> {
late StreamSubscription subscription;
bool isDeviceConnected = false;
bool isAlertSet = false;
#override
void initState() {
getConnectivity();
super.initState();
}
getConnectivity() {
subscription = Connectivity().onConnectivityChanged.listen(
(ConnectivityResult result) async {
isDeviceConnected = await InternetConnectionChecker().hasConnection;
if (!isDeviceConnected && isAlertSet == false) {
showDialogBox();
setState(() {
isAlertSet = true;
});
}
},
);
}
#override
void dispose() {
subscription.cancel();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
...
);
}
showDialogBox() => showDialog(/* no internet dialog */)
Extending the question: Is it assured that this works for all the pages ?
if yes, how ?
if not , how to overcome this?
First of all you need to listen for internet connectivity in your app first screen which is probably app.dart
GlobalKey<NavigatorState> navigatorKey = GlobalKey();
final noInternet = NoInternetDialog();
class TestApp extends StatefulWidget {
#override
State<TestApp> createState() => _TestAppState();
}
class _TestAppState extends State<TestApp> {
#override
void initState() {
super.initState();
checkInternetConnectivity();
}
#override
Widget build(BuildContext context) {
return MaterialApp(...);
}
Future<void> checkInternetConnectivity() async {
Connectivity().onConnectivityChanged.getInternetStatus().listen((event)
{
if (event == InternetConnectionStatus.disconnected) {
if (!noInternet.isShowing) {
noInternet.showNoInternet();
}
}
});
}
}
Make the screen stateful in which you are calling MaterialApp and in initState of that class check for your internet connection, like above
You are saying how can I show dialog when internet connection changes for that you have to create a Generic class or extension which you can on connectivity change. You have to pass context to that dialogue using NavigatorKey
class NoInternetDialog {
bool _isShowing = false;
NoInternetDialog();
void dismiss() {
navigatorKey.currentState?.pop();
}
bool get isShowing => _isShowing;
set setIsShowing(bool value) {
_isShowing = value;
}
Future showNoInternet() {
return showDialog(
context: navigatorKey.currentState!.overlay!.context,
barrierDismissible: true,
barrierColor: Colors.white.withOpacity(0),
builder: (ctx) {
setIsShowing = true;
return AlertDialog(
elevation: 0,
backgroundColor: Colors.transparent,
insetPadding: EdgeInsets.all(3.0.h),
content: Container(...),
);
},
);
}
}
Use checkConnectivity to check current status. Only changes are exposed to the stream.
final connectivityResult = await Connectivity().checkConnectivity();

How can I use condition and go to different page on flutter using Getx without click

I am trying to use firebase data to route different pages using Getx. First I have a splash screen and want to automatically go to different pages according to conditions. If the user has already login, it will redirect the Home page, if not the route to the login page. But I can't use initState() on the Stateless widget as I using Getx, I don't want a Stateful widget.
class SplashPage extends StatelessWidget {
RxBool isloading = true.obs;
#override
Widget build(BuildContext context) {
String Uid = "";
return isloading.value
? SpinKitThreeInOut(
color: Colors.red,
)
: Obx(() {
return Get.find<AuthController>().user != null
? homeMethod()
: login();
});
}
Widget homeMethod() {
return Home(AuthController.instance.user.toString());
isloading.value = false;
}
}
But I ain't able to override isloading.value = false;
My Getx Auth Controller:
class AuthController extends GetxController {
static AuthController instance = Get.find();
FirebaseAuth auth = FirebaseAuth.instance;
Rxn<User> _firebaseUser = Rxn<User>();
String? get user => _firebaseUser.value?.uid;
#override
void onReady() {
// TODO: implement onReady
super.onReady();
_firebaseUser.value = auth.currentUser;
_firebaseUser.bindStream(auth.userChanges());
ever(_firebaseUser, _initialScreen);
}
/* #override
void onInit() {
_firebaseUser.bindStream(_auth.authStateChanges());
}*/
_initialScreen(User? user) {
if (user == null) {
Get.offAll(login());
} else {
String userId = user.uid;
Get.offAll(Home(userId));
}
}
Future<User?> LogInAccounts(String Email, String Password) async {
FirebaseAuth auth = FirebaseAuth.instance;
try {
User? user = (await auth.signInWithEmailAndPassword(
email: Email, password: Password))
.user;
if (user != null) {
Fluttertoast.showToast(msg: "Account Create Sucessfully");
return user;
} else {
Fluttertoast.showToast(msg: "Account Create Failed!");
return user;
}
} catch (e) {
return null;
}
}
}
Updated Answer
You can use bindStream and do it that way, but instead of trying to turn your User object into a stream this can be done with a simple RxBool. Firebase already provides a function to listen to auth state changes.
class AuthController extends GetxController {
RxBool loggedIn = false.obs;
#override
void onInit() {
super.onInit();
_subscribe();
}
void _subscribe() {
FirebaseAuth.instance.authStateChanges().listen((User? user) {
if (user == null) {
loggedIn(false);
log('User is currently signed out');
} else {
loggedIn(true);
log('User is signed in');
}
});
}
}
Then you can add another couple methods to your GetX class.
void initNaviationListener() {
/// inital startup naviation
_navigateBasedOnLogin();
/// future navigation based on auth state changes
ever(loggedIn, (value) {
_navigateBasedOnLogin();
});
}
void _navigateBasedOnLogin() {
if (loggedIn.value == false) {
Get.offAndToNamed(LoginPage.id);
} else {
Get.offAndToNamed(HomePage.id);
}
}
Then you can call initNaviationListener in the onReady of GetMaterialApp
GetMaterialApp(
/// onReady is called after GetMaterialApp is fully initialized
onReady: () => Get.find<AuthController>().initNaviationListener(),
theme: ThemeData.dark(),
initialRoute: LoginPage.id,
getPages: [
GetPage(
name: SplashPage.id,
page: () => SplashPage(),
),
GetPage(
name: HomePage.id,
page: () => HomePage(),
),
GetPage(
name: LoginPage.id,
page: () => LoginPage(),
),
],
)
That will navigate on app start to the corresponding screen and also respond to any future changes in auth status.
Original Answer
You don't have to navigate from the SplashPage you can do it from the controller.
Let's say your GetMaterialApp looks like this. This takes you to SplashPage first.
GetMaterialApp(
initialRoute: SplashPage.id,
getPages: [
GetPage(
name: SplashPage.id,
page: () => SplashPage(),
),
GetPage(
name: HomePage.id,
page: () => HomePage(),
),
GetPage(
name: LoginPage.id,
page: () => LoginPage(),
),
],
)
Then check logged in status and navigate to the corresponding screen from your AuthController.
class AuthController extends GetxController {
#override
void onInit() {
super.onInit();
_navigateBasedOnLogin();
}
Future<void> _navigateBasedOnLogin() async {
final loggedIn = await _isLoggedIn();
if (loggedIn) {
Get.offAndToNamed(HomePage.id); // offAndToNamed will remove the SplashScreen from the navigation stack
} else {
Get.offAndToNamed(LoginPage.id);
}
}
Future<bool> _isLoggedIn() async {
/// run your code to check logged in status and return true or false
}
}
Then just init the AuthController in your main.
void main() async {
Get.put(AuthController());
runApp(MyApp());
}
With this setup, your SplashScreen can be a generic loading screen with zero logic.
You can handle initialRoute of GetMaterialApp using isLogin flag
class _MyAppState extends State<MyApp> {
bool isLogin = false;
#override
void initState() {
isLogin = isAlreadyLogin();// Your function to check is user logged in.
super.initState();
}
#override
Widget build(BuildContext context) {
return GetMaterialApp(
debugShowCheckedModeBanner: false,
title: 'Rider App',
translationsKeys: AppTranslation.translationsKeys,
locale: Get.find<CacheManager>().getLocale(),
getPages: AppPages.pages,
initialRoute: isLogin ? Routes.homeScreen : Routes.loginScreen,
initialBinding: InitialBinding(),
);
}
class Routes {
static const homeScreen = '/home-screen';
static const loginScreen = '/login-screen';
}
class AuthController extends GetxController {
late Rx<User?> firebaseUser;
#override
void onReady() async {
super.onReady();
firebaseUser = Rx<User?>(FirebaseAuth.instance.currentUser);
firebaseUser.bindStream(firebaseAuth.instance.userChanges());
ever(firebaseUser, _setInitialScreen);
}
_setInitialScreen(user) async{
if (user != null) {
Get.offAllNamed(Routes.home);
} else {
Get.offAllNamed(Routes.login);
}
}
}

shared preference + firebase auth not working properly

Firebase Auth is working properly, I'm able to log in, Sign in but I want to preserve the state of the application. For state persistence, I'm using Share preferences.
I'm using sharedpreferences. I made a class and defined some keys and methods to fetch and set the data in the keys at the time of login and use it later in the app.
Please help me out with an easy approch how to do this.
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class CurrentUser {
static SharedPreferences? mypreferences;
static const emailKey = 'emailKey';
static const passwordKey = 'paswordKey';
static const loginStatusKey = 'loginStatusKey';
static Future init() async {
mypreferences = await SharedPreferences.getInstance();
}
static Future setUserEmail(String? emailValue) async {
return await mypreferences?.setString(emailKey, emailValue!);
}
static Future setUserPassword(String? passwordValue) async {
return await mypreferences?.setString(passwordKey, passwordValue!);
}
static Future setLoginStatus(bool status) async {
return await mypreferences?.setBool(loginStatusKey, status);
}
static String? getUserEmail() {
return mypreferences?.getString(emailKey);
}
static String? getUserPassword() {
return mypreferences?.getString(passwordKey);
}
static bool? getUserLoginStatus(){
if(loginStatusKey==null){
mypreferences?.setBool(loginStatusKey, false);
}
return mypreferences?.getBool(loginStatusKey);
}
}```
// storing the value during login //
``` myLogIn() async {
try {
UserCredential myuser= await FirebaseAuth.instance
.signInWithEmailAndPassword(email: email, password: password);
// print(myuser);
// print(myuser.user?.email);
CurrentUser.setUserEmail(myuser.user?.email);
CurrentUser.setUserPassword(password);
CurrentUser.setLoginStatus(true);
Navigator.pushReplacementNamed(context, MyRoutes.homeRoute);
} on FirebaseAuthException catch (e) {
if (e.code == 'user-not-found') {
print('user is not registered');
ScaffoldMessenger.of(context).showSnackBar(
MySnackBar.showcustomSnackbar('user is not registered'));
} else if (e.code == 'wrong-password') {
print('wrong password');
ScaffoldMessenger.of(context)
.showSnackBar(MySnackBar.showcustomSnackbar('wrong password'));
}
}
}```
// using the store login status in main file to show login page and home page accordingly.
```import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
// import 'firebase_options.dart';
import './pages/login.dart';
import './pages/signup.dart';
import './pages/homepage.dart';
import './utils/my_theme.dart';
import './utils/my_routes.dart';
import './pages/forgot_password.dart';
import './utils/my_user.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
CurrentUser.init();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// const MyApp({ Key? key }) : super(key: key);
final Future<FirebaseApp> initializeMyFirebaseApp = Firebase.initializeApp();
bool? isLogin = CurrentUser.getUserLoginStatus();
#override
void initState() {
// TODO: implement initState
isLogin = CurrentUser.getUserLoginStatus();
super.initState();
}
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: initializeMyFirebaseApp,
builder: (context, snaphot) {
//Error checking
if (snaphot.hasError) {
print("Something went wrong!");
}
//If snapshot state is in waiting or so
if (snaphot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
//else we will show the normal login page.
return MaterialApp(
theme: MyTheme.lightTheme(context),
//isUserLoggedIn !=null ? isUserLoggedIn ? Home() : SignUp() : SignIn(),
home:isLogin!=null ?
isLogin! ? HomePage() : Login() : Login(),
// Login(),
routes: {
// "/":(context) => Login(),
MyRoutes.loginRoute: (context) => Login(),
MyRoutes.signupRoute: (context) => SignUp(),
MyRoutes.homeRoute: (context) => HomePage(),
MyRoutes.forgotpasswordRoute: (context) => ForgotPassword(),
},
);
});
}
}
```

How to show message to the snackBar when user does not have internet in flutter app?

if internet connection is off i want to show "No internet connection" message in SnackBar.
if internet connection is on it will show UI.
This is my code:
import 'dart:io';
import 'package:flutter/material.dart';
import 'main.dart';
class InternetCheck extends StatefulWidget {
#override
_InternetState createState() => _InternetState();
}
class _InternetState extends State<InternetCheck> {
bool _isInternet = true;
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
checkInternet() async {
try {
final response = await InternetAddress.lookup('example.com'); // google
if (response.isNotEmpty && response[0].rawAddress.isNotEmpty) {
_isInternet = true; // internet
setState(() {});
}
} on SocketException catch (_) {
_isInternet = false; // no internet
setState(() {
_showSnackBar();
});
}
return _isInternet;
}
#override
void initState() {
checkInternet();
super.initState();
}
void _showSnackBar() {
SnackBar snackBar = SnackBar(content: Text('No Connection'));
_scaffoldKey.currentState.showSnackBar(snackBar);
}
#override
Widget build(BuildContext context) {
// checkInternet();
return MaterialApp(
home: Scaffold(
key: _scaffoldKey,
body: _isInternet == true ? Text('internet') : Text('no Internet'),
),
);
}
}
using my code when app run first time with internet off it show SnackBar but when i switch internet on and again switch internet off then it will not showing snakbar.
Check out the connectivity package. It allows you to listen for network state changes :
#override
initState() {
super.initState();
subscription = Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
if(connectivityResult == ConnectivityResult.none)
_showSnackbar();
})
}
// Be sure to cancel subscription after you are done
#override
dispose() {
super.dispose();
subscription.cancel();
}