Unable to authenticate using Flutter local_auth plugin - android

I am using the Flutter local_auth plugin, biometrics works fine but Pincode/pattern doesn't provide authentication. I found if I remove my fingerprints from my mobile then Pincode and pattern authentication works but I need to input 2 times. The library seems correct but couldn't get a proper hold on the reason for this strange behaviour. Can anyone suggest?
Moreover, can we use a custom UI for the authentication, like how it is in WhatsApp?
local_auth_api.dart:
import 'package:flutter/services.dart';
import 'package:local_auth/local_auth.dart';
import 'package:local_auth/auth_strings.dart';
class LocalAuthApi {
static final _auth = LocalAuthentication();
static const iosStrings = IOSAuthMessages(
cancelButton: 'cancel',
goToSettingsButton: 'settings',
goToSettingsDescription: 'Please set up your Touch ID.',
lockOut: 'Please reenable your Touch ID');
static const androidStrings = AndroidAuthMessages(
cancelButton: 'cancel',
goToSettingsButton: 'settings',
goToSettingsDescription: 'Please set up your Touch ID.',
signInTitle: 'User Authorization Required',
);
static Future<bool> hasBiometrics() async {
try {
return await _auth.canCheckBiometrics;
} on PlatformException catch (e) {
return false;
}
}
static Future<List<BiometricType>> getBiometrics() async {
try {
return await _auth.getAvailableBiometrics();
} on PlatformException catch (e) {
return <BiometricType>[];
}
}
static Future<bool> authenticate() async {
try {
return await _auth.authenticate(
localizedReason: 'Scan your Fingerprint to Authenticate',
useErrorDialogs: true,
sensitiveTransaction: true,
stickyAuth: true,
iOSAuthStrings: iosStrings,
androidAuthStrings: androidStrings
);
} on PlatformException catch (e) {
print(e);
return false;
}
}
}
lock_screen.dart
import 'package:xyz/resources/constants.dart';
import 'package:xyz/src/services/local_auth_api.dart';
import 'package:flutter/material.dart';
class LockScreen extends StatefulWidget{
const LockScreen({Key? key}) : super(key: key);
#override
_LockScreenState createState() => _LockScreenState();
}
class _LockScreenState extends State<LockScreen>{
#override
void initState() {
super.initState();
authenticate();
}
authenticate() async {
bool isAuthenticated = false;
while(true){
isAuthenticated = await LocalAuthApi.authenticate();
if (isAuthenticated) {
print(isAuthenticated);
break;
}
}
print("unlocked");
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
double screenHeight = size.height;
double screenWidth = size.width;
return Scaffold(
backgroundColor: primaryColor,
body: Center(
)
);
}
}
Response:
E/BiometricFragment(19568): Not launching prompt. Client activity was null.

Related

Should I pass the google sign-in variables to other class?

I made a log-in page and made a button for google log-in like this :
Future<String> login_google() async{
FirebaseAuth auth = FirebaseAuth.instance;
GoogleSignIn googleSignIn = GoogleSignIn();
GoogleSignInAccount? account = await googleSignIn.signIn();
GoogleSignInAuthentication authentication = await account!.authentication;
AuthCredential credential = GoogleAuthProvider.credential(
idToken: authentication.idToken,
accessToken: authentication.accessToken);
final authResult = await auth.signInWithCredential(credential);
final user = authResult.user;
print (user?.uid);
print (user?.email);
print('google log-in completed');
return Future.value(user?.uid);
}
...
class _login_pageState extends State<login_page> {
#override
Widget build(BuildContext context) {
// String uid_r = await login_google();
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
toolbarHeight: 1.0,
),
body:Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image( image: AssetImage('asset/logo.png'),),
ElevatedButton(onPressed: (){
login_kakao();
Navigator.of(context).push(MaterialPageRoute(builder: (context) =>main_bone(uid:'')));
}, child: Text('Kakao Log-In')),
ElevatedButton(onPressed: (){
// login_kakao();
final uid = login_google().then(
(value){
Navigator.of(context).push(MaterialPageRoute(builder: (context) =>main_bone(uid: value))); => The button for LOG-IN .
}
);
}, child: Text('Google Log-In'))
],
),
);
}
}
Here I succeeded in log-in and passed only uid thinking that only uid is required. And My firestore rules for accessing is following :
match /post/{document=**}
{
allow read;
allow write:if
auth.uid != null;
}
On the next page, None of the data in collection(post) were shown, which means the log in has disabled on the next class .
Should I pass the whole variables for authentication to next class, for example, auth, credential ?
its called inherited widget, there is a lot method to make your entire app authenticated, not just one class. make a stream builder, listen firebase_auth.authStateChanges()
or you can use a package ( state management ) :
like
flutter_bloc
or
i prefer goRouter
check this video, its chris itself explain how to use it, its even better; work at flutter web too
take look at mine for your reference :
import 'dart:ui';
import 'package:cms_prototype/auth/auth.dart';
import 'package:cms_prototype/home/home.dart';
import 'package:cms_prototype/login/login.dart';
import 'package:cms_prototype/theme.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: const FirebaseOptions(
apiKey: "********",
authDomain: "",
appId: "",
measurementId: "",
storageBucket: "**.com",
messagingSenderId: "**",
projectId: "**"));
final auth = AuthRepo();
await auth.user.first;
final router = GoRouter(
urlPathStrategy: UrlPathStrategy.path,
redirect: (GoRouterState state){
if(auth.currentUser.isEmpty && state.location != '/login'){
return '/login';
}
if(auth.currentUser.isNotEmpty && state.location == '/login'){
return '/';
}
return null;
},
refreshListenable: GoRouterRefreshStream(auth.user),
routes: [
GoRoute(
name: 'home',
path: '/',
pageBuilder: (context, state) => MaterialPage(
key: state.pageKey,
child: const HomePage())),
GoRoute(
name: 'login',
path: '/login',
pageBuilder: (context, state) => MaterialPage(
key: state.pageKey,
child: LoginPage(authRepo: auth,))),
],
errorPageBuilder: (context, state) => const MaterialPage(
child: Scaffold(
body: Center(
child: Text("404 Error"),
),
)));
runApp(MyApp(router: router,authRepo: auth,));
}
class MyApp extends StatelessWidget {
final GoRouter router;
final AuthRepo authRepo;
const MyApp({Key? key, required this.router, required this.authRepo}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp.router(
routeInformationProvider: router.routeInformationProvider,
routeInformationParser: router.routeInformationParser,
routerDelegate: router.routerDelegate,
debugShowCheckedModeBanner: false,
scrollBehavior: const MaterialScrollBehavior().copyWith(
dragDevices: {
PointerDeviceKind.mouse,
PointerDeviceKind.touch,
PointerDeviceKind.stylus,
PointerDeviceKind.unknown
},
),
title: 'CMS Prototype',
theme: appTheme,
);
}
}
auth repo class : ---->
class AuthRepo {
final AuthCache _cache;
final firebase_auth.FirebaseAuth _firebaseAuth;
final GoogleSignIn _googleSignIn;
AuthRepo({AuthCache? cache, firebase_auth.FirebaseAuth? firebaseAuth, GoogleSignIn? googleSignIn})
: _cache = cache?? AuthCache(),
_firebaseAuth = firebaseAuth ?? firebase_auth.FirebaseAuth.instance,
_googleSignIn = googleSignIn ?? GoogleSignIn.standard();
Stream<User> get user {
return _firebaseAuth.authStateChanges().map((fUser){
final user = fUser == null ? User.empty : fUser.toUser;
_cache.write(key: user.id, value: user);
return user;
});
}
User get currentUser {
final key = _firebaseAuth.currentUser != null? _firebaseAuth.currentUser!.uid : '';
return _cache.read(key: key) ?? User.empty;
}
Future<void> signUp({required String email, required String password}) async {
try {
await _firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
} on firebase_auth.FirebaseAuthException catch (e) {
throw SignUpWithEmailAndPasswordFailure.fromCode(e.code);
} catch (_) {
throw const SignUpWithEmailAndPasswordFailure();
}
}
//login with google (GOOGLE SIGN IN)
Future<void> loginWithGoogle() async {
try {
late final firebase_auth.AuthCredential credential;
if(kIsWeb){
final googleProvider = firebase_auth.GoogleAuthProvider();
final userCredential = await _firebaseAuth.signInWithPopup(googleProvider);
credential = userCredential.credential!;
}else{
final googleUser = await _googleSignIn.signIn();
final googleAuth = await googleUser!.authentication;
credential = firebase_auth.GoogleAuthProvider.credential(
accessToken: googleAuth.accessToken,
idToken: googleAuth.idToken
);
}
await _firebaseAuth.signInWithCredential(credential);
}
on firebase_auth.FirebaseAuthException catch (e){
throw LogInWithGoogleFailure.fromCode(e.code);
}
catch (e){
throw const LogInWithGoogleFailure();
}
}
//email dan password
Future<LogInWithEmailAndPasswordFailure?> loginWithEmailAndPassword ({required String email, required String password}) async{
try{
await _firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password
);
}
on firebase_auth.FirebaseAuthException catch (e){
//return e.code;
return LogInWithEmailAndPasswordFailure.fromCode(e.code);
}
catch (_){
//return 'masalah jaringan terdeteksi';
return const LogInWithEmailAndPasswordFailure();
}
return null;
}
Future<void> logOut() async {
try {
await Future.wait([
_firebaseAuth.signOut(),
_googleSignIn.signOut(),
]);
} catch (_) {
throw LogOutFailure();
}
}
}
You should be able to access all the data for the current user using FirebaseAuth.instance.currentUser. Use that to access the current user and pass any data to backend using that user's credentials and token.
final user = FirebaseAuth.instance.currentUser;

FlutterError (setState() called after dispose(): _welcomeScreen_1State#a1185(lifecycle state: defunct, not mounted) Error

I have an application. I added an introductory screen to this application that only appears at the entrance. This screen will appear when the user first opens the application. The next time they open, the promotional screen will not open again.
I've done it with Secure Storage and GetX whether the user has passed the promotion or not.
main.dart:
import 'package:flutter/material.dart';
import 'package:teen_browser/pages/home.dart';
import 'package:teen_browser/welcome_screens/welcomeMain.dart';
import 'package:get/get.dart';
import 'features/controllers/pagination-controller/pagination_controller.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
var skipStatus;
void main() {
runApp(mainApp());
}
class mainApp extends StatefulWidget {
mainApp({Key? key}) : super(key: key);
#override
State<mainApp> createState() => _mainAppState();
}
class _mainAppState extends State<mainApp> {
#override
void initState() {
super.initState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(fontFamily: 'Montserrat Regular'),
home: passedTheWelcomeScreen(),
);
}
}
class passedTheWelcomeScreen extends StatefulWidget {
passedTheWelcomeScreen({Key? key}) : super(key: key);
#override
State<passedTheWelcomeScreen> createState() => _passedTheWelcomeScreenState();
}
class _passedTheWelcomeScreenState extends State<passedTheWelcomeScreen> {
final PaginationController _paginationController = Get.put(PaginationController());
#override
Widget build(BuildContext context) {
return GetX<PaginationController>(
init: _paginationController,
initState: (initController) {
initController.controller!.CheckSkipStatus();
},
builder: (controller) {
if (controller.isSkipped.value == null) {
return CircularProgressIndicator();
} else if (controller.isSkipped.value == true){
return HomeApp();
} else {
return welcomeMain();
}
},
);
}
}
pagination_controller.dart:
import 'package:get/get.dart';
import 'package:teen_browser/functions/secure_storage.dart';
class PaginationController extends GetxController {
RxnBool isSkipped = RxnBool(false);
void CheckSkipStatus() async {
final resp = await UserSecureStorage.isSkip();
isSkipped.value = resp;
}
}
secure_storage.dart:
import 'dart:developer';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class UserSecureStorage {
static const _storage = FlutterSecureStorage();
static Future setField(String key, value) async {
await _storage.write(key: key, value: value);
}
static Future<String?> getField(key) async {
return await _storage.read(key: key);
}
static Future deleteField(String key) async {
return await _storage.delete(key: key);
}
static Future deleteAll() async {
return await _storage.deleteAll();
}
static Future<bool> isSkip() async {
inspect(await getField("isSkipped"));
var value = await getField('isSkipped');
if (value != null) return true;
inspect(value);
return false;
}
}
On the last promotion page, I stated that the user passed the definition as follows:
await UserSecureStorage.setField("isSkipped", "true");
controller.isSkipped.value = true;
If the user has not passed the promotion before, there is no problem. All promotional screens are coming and error etc. does not give. But after passing all the demo screens, that is, when the value of isSkipped is true, it gives an error.
The error I got:
FlutterError (setState() called after dispose(): _welcomeScreen_1State#7c0ca(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build). This error can occur when code calls setState() from a timer or an animation callback.
The preferred solution is to cancel the timer or stop listening to the animation in the dispose() callback. Another solution is to check the "mounted" property of this object before calling setState() to ensure the object is still in the tree.
This error might indicate a memory leak if setState() is being called because another object is retaining a reference to this State object after it has been removed from the tree. To avoid memory leaks, consider breaking the reference to this object during dispose().)
I think the error is caused by the initState on the first demo page, which also shows the first demo page, welcomeMain1.dart, on the console.
welcomeScreen1.dart initState codes:
void initState() {
super.initState();
print(_clickGoButtonErrorMessages[Random().nextInt(_clickGoButtonErrorMessages.length)]);
if (controller.isSkipped.value == false) {
Timer(const Duration(milliseconds: 3200), () {
setState(() {
_helloText = 0;
});
});
Timer(const Duration(milliseconds: 3300), () {
AssetsAudioPlayer.newPlayer().open(
Audio("assets/audios/StopBroDontPassSound.mp3"),
autoStart: true,
showNotification: true,
);
});
Timer(const Duration(milliseconds: 3400), () {
setState(() {
_helloTextState = true;
});
});
Timer(const Duration(milliseconds: 3500), () {
setState(() {
_helloTextState = false;
});
});
Timer(const Duration(milliseconds: 3900), () {
setState(() {
_helloText = 1;
});
});
Timer(const Duration(milliseconds: 4200), () {
setState(() {
_helloTextState = true;
});
});
Timer(const Duration(milliseconds: 5700), () {
setState(() {
_contentText = true;
});
});
Timer(const Duration(milliseconds: 7000), () {
setState(() {
_letsGoButton = true;
});
});
Timer(const Duration(milliseconds: 7300), () {
setState(() {
_skipButton = true;
});
});
Timer(const Duration(milliseconds: 9000), () {
setState(() {
_clickGoButton = true;
_clickSkipButton = true;
});
});
}
else {
inspect("isSkipped true");
}
}
Once the user introduction has passed, it will always be redirected to the homepage, ie HomeApp. If the value of isSkipped is false, it means that the user first opened the application. This time, it directs you to the promotional screens.
My guess is that the application is trying to run the initState codes in welcomeMain1.dart while redirecting to HomeApp() and throws an error when it doesn't.
I hope I was able to explain my problem.When the user promotion passes, the value of isSkipped is true, ie "user promotion passed". I tested it by printing it to the console. How can I solve this problem?
Thanks for help.
There could be a race condition so that isSkipped is false long enough for the timers to start, then it switches to true and the widget is rebuilt but the timers are still running.
You could add this before setState in your timer callbacks:
Timer(const Duration(milliseconds: 3200), () {
if (!mounted) {
return;
}
setState(() {
_helloText = 0;
});
});
You could also save all timers as individual properties and cancel them in the widget's dispose method:
class MyWidget extends StatelessWidget
{
late Timer _timer1;
late Timer _timer2;
void initState() {
super.initState();
if (controller.isSkipped.value == false) {
_timer1 = Timer(const Duration(milliseconds: 3200), () {
setState(() {
_helloText = 0;
});
});
_timer2 = ...
}
// ...
}
#override
void dispose() {
_timer2.cancel();
_timer1.cancel();
super.dispose();
}
}
(The above is dry-coded, sorry for any errors. Storing the timers in an array is left as an exercise to the reader.)

Issue while using background_locator plugin while tracking background location in flutter

I'm trying to track location in the background using flutter and to do so I'm using the background_locator plugin. It has been implemented in such a way that there are certain static callback functions that were registered. I've declared a class variable of File type to save the log in the background. The global variable is built at the very beginning of the class.
Issue: While invoking the callback method, the global variable built is becoming null. So though I could see the location log in my console, I couldn't write it to the file as the object is null.
Tries:
I've tried with the exact example provided in their documentation.
I've declared it as non static property and tried to access with the class object.
Tried it out declaring it as static property as well.
Tried building file object with the same path every time needed but it is throwing following issue.
No implementation found for method getApplicationDocumentsDirectory on channel plugins.flutter.io/path_provider
Here is my complete source code for reference.
import 'dart:async';
import 'dart:ffi';
import 'dart:io';
import 'dart:isolate';
import 'dart:math';
import 'dart:ui';
import 'package:background_locator/background_locator.dart';
import 'package:background_locator/location_dto.dart';
import 'package:background_locator/settings/android_settings.dart';
import 'package:background_locator/settings/ios_settings.dart';
import 'package:background_locator/settings/locator_settings.dart';
import 'package:flutter/material.dart';
import 'package:location_permissions/location_permissions.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart' as ph;
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
ReceivePort port = ReceivePort();
String logStr = '';
bool isRunning = false;
LocationDto? lastLocation;
bool permissionsGranted = false;
static const String isolateName = 'LocatorIsolate';
static int _count = -1;
static File? finalFile;
void requestPermission() async {
var storageStatus = await ph.Permission.storage.status;
if (!storageStatus.isGranted) {
await ph.Permission.storage.request();
}
if (storageStatus.isGranted) {
permissionsGranted = true;
setPrerequisites();
}
setState(() {});
}
static Future<void> init(Map<dynamic, dynamic> params) async {
//TODO change logs
print("***********Init callback handler");
if (params.containsKey('countInit')) {
dynamic tmpCount = params['countInit'];
if (tmpCount is double) {
_count = tmpCount.toInt();
} else if (tmpCount is String) {
_count = int.parse(tmpCount);
} else if (tmpCount is int) {
_count = tmpCount;
} else {
_count = -2;
}
} else {
_count = 0;
}
print("$_count");
await setLogLabel("start");
final SendPort? send = IsolateNameServer.lookupPortByName(isolateName);
send?.send(null);
}
static Future<void> disposeLocationService() async {
await setLogLabel("end");
final SendPort? send = IsolateNameServer.lookupPortByName(isolateName);
send?.send(null);
}
static Future<void> callback(LocationDto locationDto) async {
await setLogPosition(_count, locationDto);
final SendPort? send = IsolateNameServer.lookupPortByName(isolateName);
send?.send(locationDto);
_count++;
}
static Future<void> setLogLabel(String label) async {
final date = DateTime.now();
await _MyAppState().writeToLogFile(
'------------\n$label: ${formatDateLog(date)}\n------------\n');
}
static Future<void> setLogPosition(int count, LocationDto data) async {
final date = DateTime.now();
await _MyAppState().writeToLogFile(
'$count : ${formatDateLog(date)} --> ${formatLog(data)} --- isMocked: ${data.isMocked}\n');
}
static double dp(double val, int places) {
num mod = pow(10.0, places);
return ((val * mod).round().toDouble() / mod);
}
static String formatDateLog(DateTime date) {
return date.hour.toString() +
":" +
date.minute.toString() +
":" +
date.second.toString();
}
static String formatLog(LocationDto locationDto) {
return dp(locationDto.latitude, 4).toString() +
" " +
dp(locationDto.longitude, 4).toString();
}
#override
void initState() {
super.initState();
if (permissionsGranted) {
setPrerequisites();
} else {
requestPermission();
}
}
void setPrerequisites() async {
finalFile = await _getTempLogFile();
if (IsolateNameServer.lookupPortByName(isolateName) != null) {
IsolateNameServer.removePortNameMapping(isolateName);
}
IsolateNameServer.registerPortWithName(port.sendPort, isolateName);
port.listen(
(dynamic data) async {
await updateUI(data);
},
);
initPlatformState();
setState(() {});
}
Future<void> updateUI(LocationDto data) async {
final log = await readLogFile();
await _updateNotificationText(data);
setState(() {
if (data != null) {
lastLocation = data;
}
logStr = log;
});
}
Future<void> _updateNotificationText(LocationDto data) async {
if (data == null) {
return;
}
await BackgroundLocator.updateNotificationText(
title: "new location received",
msg: "${DateTime.now()}",
bigMsg: "${data.latitude}, ${data.longitude}");
}
Future<void> initPlatformState() async {
print('Initializing...');
await BackgroundLocator.initialize();
logStr = await readLogFile();
print('Initialization done');
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
});
print('Running ${isRunning.toString()}');
}
#override
Widget build(BuildContext context) {
final start = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: const Text('Start'),
onPressed: () {
_onStart();
},
),
);
final stop = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Stop'),
onPressed: () {
onStop();
},
),
);
final clear = SizedBox(
width: double.maxFinite,
child: ElevatedButton(
child: Text('Clear Log'),
onPressed: () {
clearLogFile();
setState(() {
logStr = '';
});
},
),
);
String msgStatus = "-";
if (isRunning != null) {
if (isRunning) {
msgStatus = 'Is running';
} else {
msgStatus = 'Is not running';
}
}
final status = Text("Status: $msgStatus");
final log = Text(
logStr,
);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Flutter background Locator'),
),
body: Container(
width: double.maxFinite,
padding: const EdgeInsets.all(22),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[start, stop, clear, status, log],
),
),
),
),
);
}
void onStop() async {
await BackgroundLocator.unRegisterLocationUpdate();
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
});
}
void _onStart() async {
if (await _checkLocationPermission()) {
await _startLocator();
final _isRunning = await BackgroundLocator.isServiceRunning();
setState(() {
isRunning = _isRunning;
lastLocation = null;
});
} else {
// show error
}
}
static Future<void> initCallback(Map<dynamic, dynamic> params) async {
await init(params);
}
static Future<void> disposeCallback() async {
await disposeLocationService();
}
Future<void> locationServicecallback(LocationDto locationDto) async {
await callback(locationDto);
}
static Future<void> notificationCallback() async {
print('***notificationCallback');
}
Future<void> writeToLogFile(String log) async {
await finalFile!.writeAsString(log, mode: FileMode.append);
}
Future<String> readLogFile() async {
return finalFile!.readAsString();
}
static Future<File?> _getTempLogFile() async {
File file =
File('${(await getApplicationDocumentsDirectory()).path}/log.txt');
if (file.existsSync()) {
return file;
} else {
file = await file.create(recursive: true);
}
return file;
}
Future<void> clearLogFile() async {
await finalFile!.writeAsString('');
}
Future<bool> _checkLocationPermission() async {
final access = await LocationPermissions().checkPermissionStatus();
switch (access) {
case PermissionStatus.unknown:
case PermissionStatus.denied:
case PermissionStatus.restricted:
final permission = await LocationPermissions().requestPermissions(
permissionLevel: LocationPermissionLevel.locationAlways,
);
if (permission == PermissionStatus.granted) {
return true;
} else {
return false;
}
case PermissionStatus.granted:
return true;
default:
return false;
}
}
Future<void> _startLocator() async {
Map<String, dynamic> data = {'countInit': 1};
return await BackgroundLocator.registerLocationUpdate(
callback,
initCallback: initCallback,
initDataCallback: data,
disposeCallback: disposeCallback,
iosSettings: const IOSSettings(
accuracy: LocationAccuracy.NAVIGATION, distanceFilter: 0),
autoStop: false,
androidSettings: const AndroidSettings(
accuracy: LocationAccuracy.NAVIGATION,
interval: 5,
distanceFilter: 0,
client: LocationClient.google,
androidNotificationSettings: AndroidNotificationSettings(
notificationChannelName: 'Location tracking',
notificationTitle: 'Start Location Tracking',
notificationMsg: 'Track location in background',
notificationBigMsg:
'Background location is on to keep the app up-tp-date with your location. This is required for main features to work properly when the app is not running.',
notificationIconColor: Colors.grey,
notificationTapCallback: notificationCallback,
),
),
);
}
}
Any help/suggestion would be highly appreciated. Thank you!
The callback function not getting called was an issue I faced inthe version 1.6.12.
I fixed the problem by
forking the background_locator repo on github.
cloning the repo to my computer
opened the location_dto.dart file and went to fromJson function.
added json[Keys.ARG_PROVIDER] ?? '' instead
commited and pushed to my forked repository
in pubspec.yaml, I updated my dependency to point to my forked repository as follows:
background_locator:
git:
url: git#github.com:frankvollebregt/background_locator.git
Please follow these two github issues if you find any problem:
https://github.com/rekabhq/background_locator/issues/320
https://github.com/rekabhq/background_locator/issues/301
background_locator dosen't work on latest flutter sdk versions
for me it's worked when I do this steps
Flutter sdk version should be :3.0.1
In pubspec.yaml file change sdk: ">=2.8.0 <3.0.0"
Don't migrate your code to null safety
in gradle-wrapper.properties change gradle version to gradle-6.5
android/build gradle change ext.kotlin_version to '1.4.31'
android/app/build gradle change compileSdkVersion to 31, minSdkVersion to 19 and targetSdkVersion to 30
This is not a problem with the background locator plugin. When the plugin/library is not registered with Flutter Engine, the 'No implementation' error occurs.
You have been attempting to access the path provider methods from within a Background Isolate. Normally, the path provider plugin will be registered with main isolate.
If you want to use it in your background isolate, you must manually register it with the engine.
Follow the steps below and add these two functions to the Init function in location_service_repositary.dart
if (Platform.isAndroid) PathProviderAndroid.registerWith();
if (Platform.isIOS) PathProviderIOS.registerWith();
Have a good day.

Sometimes Provider doesn't trigger after anonymous sign-up in flutter

Hello fellow flutter developers,
I have a bug that's been making my life pretty complicated while developing my own app using Flutter. It goes like this:
User opens app
if they're not signed in, they're redirected to USP page.
If they click next, they're redirected to the sign up page.
Sign-up is provided by Firebase and it's anonymous
If sign-up is successful, a Provider should be triggered and a new page is loaded
The bug is that sometimes the user is sent back to the USP page (meaning their user_id is null) despite no exception between thrown during sign-up. If I force the navigation to the signed-in page, then the user doesn't have an user_id and that's an issue for me.
Any one experienced and fixed the same issue? Below you can see how I built my code, maybe this can help?
This is my main file:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await SignIn.initializeFirebase();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
getSignInUser() {
return SignIn().user;
}
Widget getMaterialApp() {
return MaterialApp(
title: 'app_title',
home: HomePagePicker(),
onGenerateRoute: RouteGenerator.generateRoute,
debugShowCheckedModeBanner: false,
);
}
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<MyUser?>.value(value: SignIn().user, initialData: null),
],
child: getMaterialApp(),
);
}
}
class HomePagePicker extends StatefulWidget {
#override
_HomePagePickerState createState() => _HomePagePickerState();
}
class _HomePagePickerState extends State<HomePagePicker> {
#override
Widget build(BuildContext context) {
MyUser? myUser = Provider.of<MyUser?>(context);
if (myUser == null) return IntroScreen(); // this shows the USPs
else {
// this takes you to the signed-in part of the app
return AnotherScreen();
}
}
}
The IntroScreen is a very simple screen with a few USPs and a button to open the registration page. It goes something like
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'login.dart';
class IntroScreen extends StatelessWidget {
static const routeName = '/introScreen';
#override
Widget build(BuildContext context) {
analytics.setScreenName("introScreen");
return Scaffold(
body: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(statusBarColor: ThemeConfig.darkPrimary),
child: Column(...), // show the USPs
floatingActionButton: getFloatingButton(context)
);
}
Widget getFloatingButton(BuildContext buildContext) {
return FloatingActionButton(
backgroundColor: ThemeConfig.primary,
foregroundColor: Colors.white,
child: Icon(Icons.arrow_forward),
onPressed: () {
navigateToScreen(MyLogin.routeName, buildContext, null);
},
);
}
// this is in another file normally but putting it here for completeness
navigateToScreen(String routeName, BuildContext context, Object? arguments) {
Navigator.pushNamed(
context,
routeName,
arguments: arguments
);
}
The important bit in the registration page is this
Future<void> finalizeRegistration(String userName, String userToken) async {
await usersCollection.add({'userName': userName, "userToken": userToken});
}
Future<void> registerUser(String userName) {
return SignIn()
.anonymousSignIn(userName)
.timeout(Duration(seconds: 2))
.then((userToken) {
finalizeRegistration(userName, userToken)
.then((value) => Navigator.pop(context));
})
.catchError((error) {
registrationErrorDialog();
});
}
The SignIn class is the following
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:house_party/models/MyUser.dart';
class SignIn {
final FirebaseAuth _auth = FirebaseAuth.instance;
static Future<FirebaseApp> initializeFirebase() async {
FirebaseApp firebaseApp = await Firebase.initializeApp();
return firebaseApp;
}
Stream<MyUser?> get user {
return _auth
.authStateChanges()
.asyncMap(getUser);
}
Future<String> anonymousSignIn(String userName) async {
var authResult = await _auth.signInAnonymously();
return authResult.user!.uid;
}
Future<MyUser?> getUser(User? user) async {
if (user == null) {
return Future.value(null);
}
return FirebaseFirestore.instance
.collection('users')
.where('userToken', isEqualTo: user.uid)
.get()
.then((res) {
if (res.docs.isNotEmpty) {
return MyUser.fromFireStore(res.docs.first.data());
} else {
return null;
}
});
}
}
Finally, I'm using these versions of firebase
firebase_core: ^1.0.0
cloud_firestore: ^1.0.0
firebase_dynamic_links: ^2.0.0
firebase_auth: 1.1.2
firebase_analytics: ^8.1.1
I hope the problem statement is clear enough!
Thanks in advance!
I fixed this problem (or at least I'm not able to reproduce it anymore) by upgrading the firebase libraries as follows
firebase_core: ^1.4.0
cloud_firestore: ^2.4.0
firebase_dynamic_links: ^2.0.7
firebase_auth: 3.0.1
firebase_analytics: ^8.2.0

How do i use asset image in flutter for setting wallpaper in android

I am unable to find the solution of setting asset image as wallpaper in android, while i am doing everything right as documented in official flutter document ion, in below image Set As Wallpaper button uses Method channel and use native code in java activity but could not set this image as wallpaper from java activity. Please guide.
This image loaded from local assets folder in flutter
You can use package https://pub.dev/packages/wallpaper_manager
You can set wallpaper in Home screen or Lock screen
wall paper can from a File or Asset
code snippet
Future<void> setWallpaperFromAsset() async {
setState(() {
_wallpaperAsset = "Loading";
});
String result;
String assetPath = "assets/tmp1.jpg";
// Platform messages may fail, so we use a try/catch PlatformException.
try {
result = await WallpaperManager.setWallpaperFromAsset(
assetPath, WallpaperManager.HOME_SCREEN);
} on PlatformException {
result = 'Failed to get wallpaper.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_wallpaperAsset = result;
});
}
working demo
full code
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:wallpaper_manager/wallpaper_manager.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
String _wallpaperFile = 'Unknown';
String _wallpaperAsset = 'Unknown';
#override
void initState() {
super.initState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
try {
platformVersion = await WallpaperManager.platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> setWallpaperFromFile() async {
setState(() {
_wallpaperFile = "Loading";
});
String result;
var file = await DefaultCacheManager().getSingleFile(
'https://images.unsplash.com/photo-1542435503-956c469947f6');
// Platform messages may fail, so we use a try/catch PlatformException.
try {
result = await WallpaperManager.setWallpaperFromFile(
file.path, WallpaperManager.HOME_SCREEN);
} on PlatformException {
result = 'Failed to get wallpaper.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_wallpaperFile = result;
});
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> setWallpaperFromAsset() async {
setState(() {
_wallpaperAsset = "Loading";
});
String result;
String assetPath = "assets/tmp1.jpg";
// Platform messages may fail, so we use a try/catch PlatformException.
try {
result = await WallpaperManager.setWallpaperFromAsset(
assetPath, WallpaperManager.HOME_SCREEN);
} on PlatformException {
result = 'Failed to get wallpaper.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_wallpaperAsset = result;
});
}
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Column(
children: <Widget>[
RaisedButton(
child: Text("Platform Version"),
onPressed: initPlatformState,
),
Center(
child: Text('Running on: $_platformVersion\n'),
),
RaisedButton(
child: Text("Set wallpaper from file"),
onPressed: setWallpaperFromFile,
),
Center(
child: Text('Wallpaper status: $_wallpaperFile\n'),
),
RaisedButton(
child: Text("Set wallpaper from asset"),
onPressed: setWallpaperFromAsset,
),
Center(
child: Text('Wallpaper status: $_wallpaperAsset\n'),
),
],
)),
);
}
}

Categories

Resources