Issue
I need my Flutter app to be associated with the file type .foo
. So that if a user opens their local file manager for example, and clicks on the file bar.foo
android prompts them to open the file with my flutter app.
So far I understand, that this is basically an incoming Android intent which has to be registered with a so called intent-filter
as described here: Creating app which opens a custom file extension.
But further, I do not understand how to handle it in Kotlin.
Therefore the next logical thing would be to find out how incoming intents work in Flutter. The documentation however doesn't help me, as it is explained only in Java and not Kotlin. I have no experience with Java at all and would like to stick to Kotlin anyway, because I have other platform specific code written in Kotlin.
In this post Deep Shah seems to have the same problem, but doesn't share the solution: Support custom file extension in a flutter app ( Open file with extension .abc in flutter ).
This post by Shanks died quietly: Open custom file extension with Flutter App.
I hope to find answers or pointers to relevant resources.
Solution
I found a solution for android. This post should bundle all important steps into one. The things starting with YOUR_
are dependant on you. Change them accordingly.
First, you have to register the intent-filter
in the AndroidManifest.xml
. This will tell android to list your app as a possible option.
<!-- TODO: CHANGE ICON AND LABEL HERE -->
<intent-filter android:icon="YOUR_ICON_LOCATION"
android:label="YOUR_APP_LABEL"
android:priority="1">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="content" />
<data android:scheme="http" />
<data android:scheme="file" />
<data android:mimeType="*/*" />
<!-- TODO: CHANGE FILE EXTENSION -->
<data android:pathPattern=".*\\.YOUR_FILE_EXTENSION" />
</intent-filter>
Next, set up your Android Method Channel. For this, in Kotlin it is done like so:
import io.flutter.embedding.android.FlutterActivity
import android.content.Intent
import android.os.Bundle
import androidx.annotation.NonNull
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugins.GeneratedPluginRegistrant
class MainActivity : FlutterActivity() {
// TODO: CHANGE METHOD CHANNEL NAME
private val CHANNEL = "YOUR_CHANNEL_NAME"
var openPath: String? = null
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine)
val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
channel.setMethodCallHandler { call, result ->
when (call.method) {
"getOpenFileUrl" -> {
result.success(openPath)
}
else -> result.notImplemented()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handleOpenFileUrl(intent)
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handleOpenFileUrl(intent)
}
private fun handleOpenFileUrl(intent: Intent?) {
val path = intent?.data?.path
if (path != null) {
openPath = path
}
}
}
Finally, set up a handler on the dart side of things. The following code has to be in a drawn Widget. I placed it into my MainView
widget, as it is always drawn on app startup and is relatively high up in the Widget tree. (For me it is the first Widget outside the MaterialApp
Widget)
class MainView extends StatefulWidget {
const MainView({Key key}) : super(key: key);
@override
_MainViewState createState() => _MainViewState();
}
class _MainViewState extends State<MainView> with WidgetsBindingObserver {
// TODO: CHANGE CHANNEL NAME
static const platform = const MethodChannel("YOUR_CHANNEL_NAME");
@override
void initState() {
super.initState();
getOpenFileUrl();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
super.dispose();
WidgetsBinding.instance.removeObserver(this);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
getOpenFileUrl();
}
}
@override
Widget build(BuildContext context) {
// TODO: Implement Build Method
}
void getOpenFileUrl() async {
dynamic url = await platform.invokeMethod("getOpenFileUrl");
if (url != null) {
setState(() {
// TODO: DO SOMETING WITH THE FILE URL
});
}
}
}
After completely killing and restarting both my app and the external app (in my case the file manager) it worked.
Answered By - David Penkowoj
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.