I have an iOS app already on Store, Now planning to replace it with Flutter with new version release.
I want to get access native app's UserDefaults data in Flutter code.
Using the way suggested in Flutter docs I m getting null value. What I have tried is:
In my pubspec.yaml file :
dependencies:
shared_preferences: ^0.5.12+4
Im my home.dart class file I'm importing header :
import 'package:shared_preferences/shared_preferences.dart';
And this is how I m trying to access data stored in iOS app from native app, I m using same bundle ID (Package ID) in flutter project to overwrite the app and it is successfully overwriting.
#override
void initState() {
super.initState();
getFromLocalMemory('user').then((value) =>
userInfo = value
);
}
Future<String> getFromLocalMemory(String key) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String user = prefs.getString(key);
return user;
}
It always returns null. While 'user' key has data when getting from iOS native app.
We can use existing plugin native_shared_preferences. It allows us to access native SharedPreferences on Android and UserDefaults on iOS.
Sample code to read a value by key "SomeKey":
final someKey = "SomeKey";
var prefs = await NativeSharedPreferences.getInstance();
if (prefs.containsKey(someKey)) {
print("Value for SomeKey: ${prefs.get(someKey)}");
}
In that case, I suggest you use platform channel method
platform channels
iOS doesn't seem to allow flutter framework to access NSUserDefaults, when it didn't create it from the beginning,..
I hope it will work for you.
Related
I have the current workflow for my authentication
User signs in via google OAuth2
User is then given a server_auth_code which they send to my backend authentication
The auth code is validated on the back end and users is sent a JWT
I had this all working in raw Java with the Android SDK, but Flutter seemed a lot nicer. But now when using the google_sign_in plugin on android, I am unable to retrieve the serverAuthCore anymore, this plugin just wants to return null the entire time.
I Am using the client ID that's specified for Android, however, I tested the WebApplication that's auto-generated by google too but that's the same issue (Null serverAutHCode)
This is the code that I am currently using:
/// Provides the `GoogleSignIn` class
import 'package:google_sign_in/google_sign_in.dart';
class GoogleLoginPage extends StatefulWidget {
final String name = "Logging in with google.";
late GoogleSignIn _googleSignIn;
GoogleLoginPage() : super() {
_googleSignIn = GoogleSignIn(
scopes: ['https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile'],
serverClientId: "XX-XXX.apps.googleusercontent.com"
);
}
Future<void> fetchAuthToken() async {
try {
var response = await _googleSignIn.signIn();
log(response?.serverAuthCode ?? "No server auth code");
_googleSignIn.signOut();
} catch (error) {
print("ERR");
print(error);
}
}
#override
State<GoogleLoginPage> createState() => GoogleLoginState();
}
The output of this code is: [log] No server auth code
The question:
Am I doing something wrong? As mentioned this works 100% on my java project using the google play services SDK so I know it's nothing to do with my google console configurations.
Okay so I figured out the issues:
It appears that by default the google login plugin for flutter comes on an older version (If I remember correctly it was 20.0.5)
I Simply changed the version to the latest version:
'com.google.android.gms:play-services-auth:20.2.0'
You can do this by editing the project's build.gradle (In IntelliJ you open build.gradle and click "Open for editing in the android studio" in the top right, from there you need to find the gradle file for google_sign_in, and change the import there, remember to click sync in the top right of android studio before you close out of it)
And I began to receive my serverAuthCode as normal, cheers!
I've built an app using Flutter and SQFlite to persist data. I have a database client file, in which there are the key functions to get it to create and initiate the database etc.
To create the database:
/// Creating the database values
static final DatabaseClient instance = DatabaseClient._init();
static Database? _database;
DatabaseClient._init();
To call the database:
/// Calling the database
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDB('database.db');
return _database!;
}
To initiate or open the database:
/// Future function to open the database
Future<Database> _initDB(String filePath) async {
final path = await getDatabasesPath();
final dbPath = join(path, filePath);
return await openDatabase(dbPath,
version: 2, onCreate: _create, onUpgrade: _onUpgrade);
}
I've also created a function to read all the entries within the database, to show with a ListView type widget:
/// WB
Future<List<WBCalcResult>> readAllNotesWB() async {
final db = await instance.database;
final orderBy =
'${WBCalcResultFields.wbDate} DESC ,${WBCalcResultFields.wbTime} DESC';
final result = await db.query(wbCalcResults, orderBy: orderBy);
return result.map((json) => WBCalcResult.fromJson(json)).toList();
}
Then, the function that gets this data from the database in the screen that displays the ListView widget looks like this:
/// Function to get the info from the database
Future<void> refreshWBCalcResults() async {
setState(() => isLoading = true);
this.wbCalcResults = await DatabaseClient.instance.readAllNotesWB();
setState(() {});
setState(() => isLoading = false);
}
This all works perfectly on both the Android Emulator and iOS simulator.
However, it does not seem to work at all on a physical Android device (it probably doesn't work on a physical iOS device either, although I don't have one to test it with). When I say 'it doesn't work' essentially all I am looking at is a blank screen, where the ListView should be. I can't see an error, or the app freezing or anything like that.
I read it could be to do with resources being shrunk in the release version of a Flutter app messing up the database path finder, so I updated my build.gradle file to prevent resources being shrunk:
buildTypes {
release {
signingConfig signingConfigs.release
shrinkResources false
}
}
Sadly, none of this seems to be working. My app's SQFlite still will not work in the release versions on physical devices, whilst it still does in debug simulator versions. It did, however, in the first release version that I released for testing.
I am using the latest versions of all the relevant packages, including path_provider (2.0.8)
Can anyone suggest why this might be happening to the release version of my app? What can I do to sort it? Thanks.
Answer that helped me: https://stackoverflow.com/a/54267755/2866570
I ran flutter with verbose logging on: flutter run -v -d .... It showed exactly what was causing the issue.
In my case I have changed the default package of my application in the manifest file but haven't changed the directory names of the MainActivity.kt file.
Error
[ ] E/AndroidRuntime(12376): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{io.answer.hello/io.answer.hello.MainActivity}:
java.lang.ClassNotFoundException: Didn't find class "io.answer.hello.MainActivity"
on path: DexPathList[[zip file
My git diff
diff --git a/android/app/src/main/kotlin/com/example/my_app/MainActivity.kt b/android/app/src/main/kotlin/io/answer/hello/MainActivity.kt
similarity index 75%
rename from android/app/src/main/kotlin/com/example/my_app/MainActivity.kt
rename to android/app/src/main/kotlin/io/answer/hello/MainActivity.kt
index a310df9..8920150 100644
--- a/android/app/src/main/kotlin/com/example/my_app/MainActivity.kt
+++ b/android/app/src/main/kotlin/io/answer/hello/MainActivity.kt
## -1,4 +1,4 ##
-package com.example.my_app
+package io.answer.hello
My AndroidManifest.xml starts with:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="io.answer.hello">
Note that I have changed the package name in this answer to a fake one.
I am making an app with token based system. So users can buy tokens and using them they can make some actions.
Each user gets 10 tokens for free (as a trial version) after creating an account using email and password.
I want to prevent that user gets another 10 tokens by getting a new account each time and I was wondering if there is something like unique device ID for both Android and iOS devices?
Can I use this for that purpose? https://pub.dartlang.org/packages/device_id#-example-tab-
Use device_info_plus plugin.
In your pubspec.yaml file add this
dependencies:
device_info_plus: any
Create a method:
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
// ...
Future<String?> _getId() async {
var deviceInfo = DeviceInfoPlugin();
if (Platform.isIOS) { // import 'dart:io'
var iosDeviceInfo = await deviceInfo.iosInfo;
return iosDeviceInfo.identifierForVendor; // Unique ID on iOS
} else {
var androidDeviceInfo = await deviceInfo.androidInfo;
return androidDeviceInfo.androidId; // Unique ID on Android
}
}
Use it like:
String? deviceId = await _getId();
or
_getId().then((id) {
String? deviceId = id;
});
Yes thats the plugin you need. If you really want to
I want to prevent that user gets another 10 tokens by getting a new account each time
You must as well check for rooted phone (it can change id)
You may want to consider using this GET IMEI
I have implemented the Akavache to work with my Xamarin.Android app. The saving and retrieval of keys work fine. I wanted to achieve a single sign on experience over two of my apps. I wanted to know if there is a way I can share the keys saved from one of my apps to the other app.
Some code I use for Key saving and retrieval.
BlobCache.ApplicationName = "IIMSuite";
await BlobCache.Secure.InsertObject("Token", authResult.Token);
await BlobCache.Secure.InsertObject("SavedUserEmail", userName);
await BlobCache.Secure.InsertObject("SavedUserName", userEmail);
try
{
userName = await BlobCache.Secure.GetObject<string>("SavedUserName");
userEmail = await BlobCache.Secure.GetObject<string>("SavedUserEmail");
token = await BlobCache.Secure.GetObject<string>("Token");
}
catch(KeyNotFoundException ex){}
I tried implementing the same code in the other app by keeping the BlobCache.ApplicationName consistent but that does not seem to work and does not retrieve any values.
Any help is appreciated.
This library allows you to get unique device id / Mac address of Android devices, which doesn't change after reinstallation.
Expo.Constants.deviceId changes after every reinstallation (even if the app version number is the same).
Is there a way to get an unique id for Android that doesn't change after reinstallation (at least for if it's the same version), without ejecting?
For Expo IOS theres currently very limited options, Since Apple forbids getting private device info. We will need to create our own unique identifier below.
Solution:
My solution is a combination of uuid and Expo SecureStore works for IOS and Android.
import * as SecureStore from 'expo-secure-store';
import 'react-native-get-random-values';
import { v4 as uuidv4 } from 'uuid';
let uuid = uuidv4();
await SecureStore.setItemAsync('secure_deviceid', JSON.stringify(uuid));
let fetchUUID = await SecureStore.getItemAsync('secure_deviceid');
console.log(fetchUUID)
This solution will work even if app gets reinstalled, or if user switches devices and copy's all data to new device.
(Expo.Constants.deviceId is deprecated and will be removed in Expo SDK 44).
Full Example:
To check if you already stored the uuid in SecureStore
import * as SecureStore from 'expo-secure-store';
import 'react-native-get-random-values';
import { v4 as uuidv4 } from 'uuid';
let uuid = uuidv4();
let fetchUUID = await SecureStore.getItemAsync('secure_deviceid');
//if user has already signed up prior
if (fetchUUID) {
uuid = fetchUUID
}
await SecureStore.setItemAsync('secure_deviceid', JSON.stringify(uuid));
console.log(uuid)
Use Application.androidId from expo-application. Id will not change after reinstall or update. The value may change if a factory reset is performed on the device or if an APK signing key changes.
https://docs.expo.dev/versions/latest/sdk/application/#applicationandroidid
Example:
import * as Application from 'expo-application';
import { Platform } from 'expo-modules-core';
import * as SecureStore from 'expo-secure-store';
import Constants from 'expo-constants';
const getDeviceId = async () => {
if (Platform.OS === 'android') {
return Application.androidId;
} else {
let deviceId = await SecureStore.getItemAsync('deviceId');
if (!deviceId) {
deviceId = Constants.deviceId; //or generate uuid
await SecureStore.setItemAsync('deviceId', deviceId);
}
return deviceId;
}
}
Guess you can use facebook module for this purpose.
https://docs.expo.io/versions/latest/sdk/facebook-ads/#currentdevicehash
Not sure what happens under hood - but looks like it unique between app reinstal, device restart etc.
Just use getuniqueid() method from react-native-device-info. Works on iOS and android to uniquely identify a device .
To get the device UniqueId in expo project,
npm install react-native-device-info
expo run:ios or expo run:android
Please note: expo start will throw an error