Sign in with token with google_sign_in - flutter

I'm trying to implement a "silent" login for my application, currently, after the application restarts, the user is signed out, and needs to signin again, but I do store the token.
How can I sign in the user automatically using the token (or am I doing something wrong?) when the application starts?
current code:
import 'package:get/get.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:syncornot/controllers/cache_manager.dart';
class GoogleAuthController extends GetxController with CacheManager {
var _googleSignIn = GoogleSignIn(scopes: ['email']);
var googleAcc = Rx<GoogleSignInAccount?>(null);
var isSignedIn = false.obs;
var googleToken;
#override
void onInit() {
checkLoginStatus();
super.onInit();
}
void signInWithGoogle() async {
try {
googleAcc.value = await _googleSignIn.signIn();
isSignedIn.value = true;
googleToken = await googleAcc.value?.authentication;
update(); // <-- without this the isSignedin value is not updated.
saveToken(googleToken.accessToken);
} catch (e) {
Get.snackbar(
'Error occured!',
e.toString(),
snackPosition: SnackPosition.BOTTOM,
);
}
}
void login() async {
isSignedIn.value = true;
await saveToken(googleToken);
}
void silentLogin() async {
googleAcc.value = await _googleSignIn.signIn();
isSignedIn.value = true;
await saveToken(googleToken);
}
void logOut() {
isSignedIn.value = false;
saveToken(googleToken);
}
void checkLoginStatus() {
final token = getToken();
if (token != null) {
silentLogin();
isSignedIn.value = true;
}
}
}
and the CacheManager:
import 'package:get_storage/get_storage.dart';
mixin CacheManager {
Future<bool> saveToken(String? token) async {
final box = GetStorage();
await box.write(CacheManagerKey.TOKEN.toString(), token);
return true;
}
String? getToken() {
final box = GetStorage();
return box.read(CacheManagerKey.TOKEN.toString());
}
Future<void> removeToken() async {
final box = GetStorage();
await box.remove(CacheManagerKey.TOKEN.toString());
}
}
enum CacheManagerKey { TOKEN }
I want some silent signing by using the credentials or something..
Thanks

Related

This expression has a type of 'void' so its value can't be used - Flutter

import 'package:demo_app/services/api.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class AuthProvider extends ChangeNotifier{
bool isAuthenticated = false;
late String token;
late ApiService apiService;
AuthProvider() {
init();
}
Future<void> init() async {
token = await getToken();
if (token.isNotEmpty) {
isAuthenticated = true;
}
apiService = ApiService(token);
notifyListeners();
}
Future<void> register(String name, String email, String password, String passwordConfirm, String deviceName) async{
token = await apiService.register(name, email, password, passwordConfirm, deviceName);
isAuthenticated = true;
setToken();
notifyListeners();
}
Future<void> logIn(String email, String password, String deviceName) async{
token = await apiService.login(email, password, deviceName);
isAuthenticated = true;
setToken();
notifyListeners();
}
Future<void> logOut() async{
token = '';
isAuthenticated = false;
setToken();
notifyListeners();
}
Future<void> setToken() async{
final pref = await SharedPreferences.getInstance();
pref.setString('token', token);
}
Future<void> getToken() async{
final pref = await SharedPreferences.getInstance();
pref.getString('token') ?? '';
}
}
token = await getToken();
gives this error
This expression has a type of 'void' so its value can't be used.
Try checking to see if you're using the correct API; there might be a function or call that returns void you didn't expect. Also check type parameters and variables which might also be void.
Any clue on solving this issue?
Try the following code:
Future<void> init() async {
token = await getToken();
if (token.isNotEmpty) {
isAuthenticated = true;
}
apiService = ApiService(token);
notifyListeners();
}
Future<String> getToken() async {
final pref = await SharedPreferences.getInstance();
final token = pref.getString("token") ?? "";
return token;
}

How to download a file on flutter?

How to download a .pdf file from an URL with POST Request having headers in Flutter?
i have a different pdf files for each user and i want the link to be different depend on the user.
You can use flutter_downloader plugin for downloading your files.
dependencies:
flutter_downloader: ^1.8.4
And also you can use this codebase for that:
import 'dart:io';
import 'dart:ui';
import 'package:fimber/fimber.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
class DownloadingService {
static const downloadingPortName = 'downloading';
static Future<void> createDownloadTask(String url) async {
final _storagePermission = await _permissionGranted();
Fimber.d('Current storage permission: $_storagePermission');
if (!_storagePermission) {
final _status = await Permission.storage.request();
if (!_status.isGranted) {
Fimber.d('Permission wasnt granted. Cancelling downloading');
return;
}
}
final _path = await _getPath();
Fimber.d('Downloading path $_path');
if (_path == null) {
Fimber.d('Got empty path. Cannot start downloading');
return;
}
final taskId = await FlutterDownloader.enqueue(
url: url,
savedDir: _path,
showNotification: true,
// show download progress in status bar (for Android)
openFileFromNotification: true,
// click on notification to open downloaded file (for Android)
saveInPublicStorage: true);
await Future.delayed(const Duration(seconds: 1));
if (taskId != null) {
await FlutterDownloader.open(taskId: taskId);
}
}
static Future<bool> _permissionGranted() async {
return await Permission.storage.isGranted;
}
static Future<String?> _getPath() async {
if (Platform.isAndroid) {
final _externalDir = await getExternalStorageDirectory();
return _externalDir?.path;
}
return (await getApplicationDocumentsDirectory()).absolute.path;
}
static downloadingCallBack(id, status, progress) {
final _sendPort = IsolateNameServer.lookupPortByName(downloadingPortName);
if (_sendPort != null) {
_sendPort.send([id, status, progress]);
} else {
Fimber.e('SendPort is null. Cannot find isolate $downloadingPortName');
}
}
}
And in another page you can use this class to download your file:
final _receivePort = ReceivePort();
#override
void initState() {
super.initState();
IsolateNameServer.registerPortWithName(
_receivePort.sendPort, DownloadingService.downloadingPortName);
FlutterDownloader.registerCallback(DownloadingService.downloadingCallBack);
_receivePort.listen((message) {
Fimber.d('Got message from port: $message');
});
}
#override
void dispose() {
_receivePort.close();
super.dispose();
}
void _downloadFile() async {
try {
await DownloadingService.createDownloadTask(url.toString());
} catch (e) {
print("error")
}
}

displaying only the current user data

I protected data_service with current user to only display the current user's habits.
data_service.dart:
class DataService {...
late final Database db;
Users? _user;
late final StreamData<Map<int, Habit>> habits;
Future<void> init() async {
db = await HabitsDb.connectToDb();
habits = StreamData(initialValue: await _getAllHabits(), broadcast: true);
}
String get userEmail => AuthService.firebase().currentUser!.email;
Future<Map<int, Habit>> _getAllHabits() async {
getOrCreateUser(email: userEmail); //issue
final habits = await _getAllHabitsFromDb();
final map = Map<int, Habit>();
final currentUser = _user;
print(currentUser);
for (final habit in habits) {
if (currentUser != null) {
print(currentUser.id);
print(habit.userId);
if (habit.userId == currentUser.id) {
map[habit.id] = habit;
}
}
//map[habit.userId] = currentUser?.id;
}
return map;
}
Future<List<Habit>> _getAllHabitsFromDb() async {
final habitsMap = await HabitsDb.getAllHabits(db);
final habitsList = habitsMap.map((e) => Habit.fromDb(e)).toList();
return habitsList;
}
Future<Users> getOrCreateUser({
required String email,
bool setAsCurrentUser = true,
}) async {
try {
//we found the user
final user = await getUser(email: email);
if (setAsCurrentUser) {
_user = user;
}
print(_user?.email);
return user;
} on CouldNotFindUser {
//we didn't find the user
final createdUser = await createUser(email: email);
if (setAsCurrentUser) {
_user = createdUser;
}
return createdUser;
} catch (e) {
rethrow;
}
}
...}
in main class:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
final dataService = DataService();
await dataService.init();
GetIt.I.registerSingleton(dataService);
... }
StreamData class:
class StreamData<T> {
List<Habit> _notes = [];
User? _user;
late final StreamController<T> _controller;
Stream<T> get stream => _controller.stream;
late T _value;
T get value => _value;
StreamData({required T initialValue, bool broadcast = true}) {
if (broadcast) {
_controller = StreamController<T>.broadcast();
} else {
_controller = StreamController<T>();
}
_value = initialValue;
}
the problem is that the line getOrCreateUser(email: userEmail); is only called once and it does not work when I switch user and I need to Hot Restart to fix it. I think using Futurebuilder will fix it. but if yes, how do I use it when there is a need to call dataService.init at the beginning of the main?
Since your getOrCreateUser function is declared as async, you'll want to use await when you call it in _getAllHabits:
await getOrCreateUser(email: userEmail)
This ensures the getOrCreateUser code has completed before the rest of the code in _getAllHabits (that depends on the result of getOrCreateUser) executes.

An eexception occurs when using flutter_downloader package

I'm trying to use flutter_downloader package to download some files (images/pdf). There is a listView with ListTiles each containing a button to start downloading when clicked but this error occurs when scrolling the list view.
[ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: 'package:flutter_downloader/src/downloader.dart': Failed assertion: line 30 pos 12: '!_initialized': FlutterDownloader.initialize() must be called only once!
//my code is like this:
import 'dart:io';
import 'dart:isolate';
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
class DownloadFile extends StatefulWidget {
DownloadFile({this.downloadUrl});
final String downloadUrl;
#override
_DownloadFileState createState() => _DownloadFileState();
}
class _DownloadFileState extends State<DownloadFile> {
String downloadId;
String _localPath;
ReceivePort _port = ReceivePort();
#override
void initState(){
super.initState();
_init();
}
Future<void> _init() async {
await FlutterDownloader.initialize();
IsolateNameServer.registerPortWithName(
_port.sendPort, 'downloader_send_port');
_port.listen((dynamic data) {
String id = data[0];
DownloadTaskStatus status = data[1];
int progress = data[2];
print("status: $status");
print("progress: $progress");
print("id == downloadId: ${id == downloadId}");
});
FlutterDownloader.registerCallback(downloadCallback);
_localPath = (await _findLocalPath()) + '/Download';
final savedDir = Directory(_localPath);
bool hasExisted = await savedDir.exists();
if (!hasExisted) {
savedDir.create();
}
}
static void downloadCallback(String id, DownloadTaskStatus status, int progress) {
print(
'Background Isolate Callback: task ($id) is in status ($status) and process ($progress)');
final SendPort send =
IsolateNameServer.lookupPortByName('downloader_send_port');
send.send([id, status, progress]);
}
Future<String> _findLocalPath() async {
final directory = await getExternalStorageDirectory();
return directory.path;
}
Future<bool> _checkPermission() async {
if (Theme.of(context).platform == TargetPlatform.android) {
PermissionStatus permission = await PermissionHandler()
.checkPermissionStatus(PermissionGroup.storage);
if (permission != PermissionStatus.granted) {
Map<PermissionGroup, PermissionStatus> permissions =
await PermissionHandler()
.requestPermissions([PermissionGroup.storage]);
if (permissions[PermissionGroup.storage] == PermissionStatus.granted) {
return true;
}
} else {
return true;
}
} else {
return true;
}
return false;
}
//----------------------------------------------------------------
#override
void dispose() {
super.dispose();
}
//---------------------------------------------------------------
#override
Widget build(BuildContext context) {
return FlatButton(
onPressed: () async {
if (await _checkPermission()) {
final taskId = await FlutterDownloader.enqueue(
url: widget.downloadUrl,
savedDir: _localPath,
showNotification:
true, // show download progress in status bar (for Android)
openFileFromNotification:
true, // click on notification to open downloaded file (for Android)
);
downloadId = taskId;
}
},
child: Text('Downloa File',style: TextStyle(color: Colors.teal),)
);
}
}
According to the Usage section in the flutter_downloader package and the error you are getting, you must call the FlutterDownloader.initialize not more than once.
You can do that in the main method of your application, just like so:
WidgetsFlutterBinding.ensureInitialized();
await FlutterDownloader.initialize();

How to correctly save the value in sharedPreferences? - Flutter

Where am I going wrong?
I have login with google to get the token and send it to graphgl, this token is saved (it was meant to be) in sharedpreferences, but it is not saving, I have the following action (mobx).
#action
Future loginWithGoogle() async {
user = await _authRepository.getGoogleLogin();
final idToken = await user.getIdToken();
print('Bearer ${idToken.token}');
sharedPreferenceService.setToken('Bearer ${idToken.token}');
}
Services shared.
class SharedPreferenceService {
SharedPreferences _prefs;
Future<bool> getSharedPreferencesInstance() async {
_prefs = await SharedPreferences.getInstance().catchError((e) {
print("shared prefrences error : $e");
return false;
});
return true;
}
Future setToken(String token) async {
await _prefs.setString('token', token);
}
Future clearToken() async {
await _prefs.clear();
}
Future<String> get token async => _prefs.getString('token');
}
SharedPreferenceService sharedPreferenceService = SharedPreferenceService();
Action login in view.
#action
Future loginWithGoogle() async {
try {
loading = true;
await auth.loginWithGoogle();
Modular.to.pushReplacementNamed('/index');
} catch (e) {
loading = false;
}
}
The login happens normal but it accuses error when it goes to index, informing that it received null the getString("token").
I/flutter ( 3198): ClientException: Unhandled Failure NoSuchMethodError: The method 'getString' was called on null.
I/flutter ( 3198): Receiver: null
I/flutter ( 3198): Tried calling: getString("token")
This token string is not being saved.
Sorry for bad english
Just copied your code and made some changes just check:
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
SharedPreferenceService sharedPreferenceService = SharedPreferenceService();
#override
void initState() {
super.initState();
loginWithGoogle();
getSharedValues();
}
getSharedValues() async{
bool value = await sharedPreferenceService.getSharedPreferencesInstance();
if(value)
print(await sharedPreferenceService.token);
}
loginWithGoogle() async {
// this is the where you get your bearer, but time being I have taken sample bearer
String token =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJZb3VuaXNaYXJnYXIiLCJlbWFpbCI6InlvdW5pc0BiYXh0dXJlLmNvbSIsImp0aSI6IjlhNjc2OTVlLTBiZmEtNDdmMy04ZTVlLWVhYWMzY2VmNmRlOSIsIklkIjoiMSIsIkVtYWlsIjoieW91bmlzQGJheHR1cmUuY29tIiwiZXhwIjoxNTgzODQ2ODU0LCJpc3MiOiJQYWNpZmljIFByaW50aW5nIiwiYXVkIjoiUGFjaWZpYyBQcmludGluZyJ9.CKxBwAB7YeOKJRmoCg4_JAhJKHP2qXb7KJXPysqmbAs';
bool value = await sharedPreferenceService.getSharedPreferencesInstance();
if (value == true) {
sharedPreferenceService.setToken('Bearer $token');
}
}
#override
Widget build(BuildContext context) {
return MaterialApp(home: Scaffold(body: Center(child: Text('sample'))));
}
}
class SharedPreferenceService {
SharedPreferences _prefs;
Future<bool> getSharedPreferencesInstance() async {
_prefs = await SharedPreferences.getInstance().catchError((e) {
print("shared prefrences error : $e");
return false;
});
return true;
}
Future setToken(String token) async {
await _prefs.setString('token', token);
}
Future clearToken() async {
await _prefs.clear();
}
Future<String> get token async => _prefs.getString('token');
}
Thank you very much, I made the correction in the action.
#action
Future loginWithGoogle() async {
user = await _authRepository.getGoogleLogin();
final idToken = await user.getIdToken();
print('Bearer ${idToken.token}');
bool value = await sharedPreferenceService.getSharedPreferencesInstance();
if (value == true) {
sharedPreferenceService.setToken('Bearer ${idToken.token}');
}
}