Read shared preferences in Flutter app that were previously stored in a native app
Since there was no satisfying answer to my question that works for both of the major operating systems and taking into account the distribution over multiple resource files, I have written a platform channel solution myself.
On Android (Kotlin) I let the caller provide a "file" argument because it's possible to have the data spread over multiple resource files (like I described in my question).
package my.test
import android.content.*
import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
class MainActivity: FlutterActivity() {
private val CHANNEL = "testChannel"
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
call, result ->
when (call.method) {
"getStringValue" -> {
val key: String? = call.argument<String>("key");
val file: String? = call.argument<String>("file");
when {
key == null -> {
result.error("KEY_MISSING", "Argument 'key' is not provided.", null)
}
file == null -> {
result.error("FILE_MISSING", "Argument 'file' is not provided.", null)
}
else -> {
val value: String? = getStringValue(file, key)
result.success(value)
}
}
}
else -> {
result.notImplemented()
}
}
}
}
private fun getStringValue(file: String, key: String): String? {
return context.getSharedPreferences(
file,
Context.MODE_PRIVATE
).getString(key, null);
}
}
On iOS (Swift) this is not necessary as I'm working with UserDefaults
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let platformChannel = FlutterMethodChannel(
name: "testChannel",
binaryMessenger: controller.binaryMessenger
)
platformChannel.setMethodCallHandler({
[weak self] (call: FlutterMethodCall, result: FlutterResult) -> Void in
guard call.method == "getStringValue" else {
result(FlutterMethodNotImplemented)
return
}
if let args: Dictionary<String, Any> = call.arguments as? Dictionary<String, Any>,
let number: String = args["key"] as? String, {
self?.getStringValue(key: key, result: result)
return
} else {
result(
FlutterError.init(
code: "KEY_MISSING",
message: "Argument 'key' is not provided.",
details: nil
)
)
}
})
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
private func getStringValue(key: String, result: FlutterResult) -> String? {
let fetchedValue: String? = UserDefaults.object(forKey: key) as? String
result(fetchedValue)
}
I'd wish for the SharedPreferences
package to add the possibility of omitting the flutter prefix, enabling the developer to migrate content seamlessly from native apps.
I also wrote a blog post that explains the problem and the solution in a little bit more details: https://www.flutterclutter.dev/flutter/tutorials/read-shared-preferences-from-native-apps/2021/9753/
How to access iOS UserDefaults stored data in Flutter SharedPrefrences
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.
how to check if SharedPreferences exists for this application in flutter
You need to check if the key exists yet by using prefs.containsKey('username')
.
How to use shared preferences to keep user logged in flutter?
You can navigate to the Login
page if the user details are saved in the storage else to the Home
page with the below code
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences prefs = await SharedPreferences.getInstance();
var email = prefs.getString('email');
print(email);
runApp(MaterialApp(home: email == null ? Login() : Home()));
}
Save the required user details after the successful login
class Login extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: RaisedButton(
onPressed: () async {
//after the login REST api call && response code ==200
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('email', 'useremail@gmail.com');
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (BuildContext ctx) => Home()));
},
child: Text('Login'),
),
),
);
}
}
clear the details on logout
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Center(
child: RaisedButton(
onPressed: () async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.remove('email');
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (BuildContext ctx) => Login()));
},
child: Text('Logout'),
),
),
);
}
}
Hope it helps!
Related Topics
Swift Firebase Storage How to Retrieve Image with Unknow Name(Nsuuid)
Fetch Coredata with One to Many Relationship in Swift
Simple Observable Struct with Rxswift
Expression Pattern of Type 'String' Cannot Match Values of Type 'Nsstoryboardsegue.Identifier
Instance Member Cannot Be Used on Type | Closures
Drag a Cgrect Using Uipangesturerecognizer
Inline Kvo of a Property in Another View Controller
Swift Spritekit Arc for Dummies
Crop/Mask Circular Image Node in Sprite Kit Gives Jagged Edges
Retrieving an Array from Firebase
Initializing Property via Closure
How to Masking the Last Number in Swift
Decode Dictionary with Random Initial Key
Why Do We Need to Set Delegate to Self? Why Isn't It Defaulted by the Compiler
Can You Enforce a Typealias in Swift
Swift Callkit Sometimes Can't Activate Loudspeaker After Received Call (Only Incoming Call)
How Set Custom Fonts for iOS13 Context Menu Actions
How to Display Current Time (Realtime) in iOS 14 Home Widget