Open Method Channel in Flutter for Intent requests in Android Smartphones.

 For creating Intent and Open Method Channel for read it.


first we need to change some in AndroidManifest.xml file. You can found it inside your flutter project in demo_project\android\app\src\main\AndroidManifest.xml 

AndroidManifest.xml

add extra lines in AndroidManifest.xml


AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.demo_project">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

<queries>
    <intent>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:mimeType="application/pdf" />
    </intent>
</queries>


    <application
        android:label="demo_project"
        android:name="${applicationName}"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop"
            android:theme="@style/LaunchTheme"
            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
            android:hardwareAccelerated="true"
            android:windowSoftInputMode="adjustResize">
            <!-- Specifies an Android theme to apply to this Activity as soon as
                 the Android process has started. This theme is visible to the user
                 while the Flutter UI initializes. After that, this theme continues
                 to determine the Window background behind the Flutter UI. -->
            <meta-data
              android:name="io.flutter.embedding.android.NormalTheme"
              android:resource="@style/NormalTheme"
              />
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="application/pdf" />
            </intent-filter>
        </activity>
        <!-- Don't delete the meta-data below.
             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
        <meta-data
            android:name="flutterEmbedding"
            android:value="2" />
    </application>
</manifest>



In Above code you see these extra lines you should add in your Android Manifest files. so Your android system can understand that which types of your app and which types of files your app handle. Such as my app is a pdf reader so i add this line you can used your line according to your app.

android:mimeType="application/pdf"


AndroidManifest.xml:
<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:mimeType="application/pdf" />
</intent-filter>


Second

Second go to your MainActivity.kt file inside your project folder inside android folder
demo_project\android\app\src\main\kotlin\com\example\your_project_name\MainActivity.kt file.

demo_project\android\app\src\main\kotlin\com\example\your_project_name\MainActivity.kt

Write this code inside MainActivity.kt

MainActivity.kt:
package com.example.demo_project

import android.content.Intent
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.embedding.android.FlutterActivity

class FileIntentHandler(private val activity: FlutterActivity) : MethodChannel.MethodCallHandler {

override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
if (call.method == "getFileIntentData") {
val intent : Intent? = activity.intent
val filePath: String? = intent?.data?.path
result.success(filePath)
} else {
result.notImplemented()
}
}
}

class MainActivity : FlutterActivity() {
private val channelName = "com.example.demo_project/openPDF" // Replace with your package name

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)

// Initialize the platform channel and set the method call handler
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channelName)
.setMethodCallHandler(FileIntentHandler(this)::onMethodCall)
}
}


Remember : change package name to your actual package name.

 Now you have to download some dependencies or i say some file for gradle.build file. which you don't worry. we do it in android stdio.
but first you have to remove all the dependency which is stored in pubsec.yaml file. you can do it by following command in vs code terminal or which ever terminal you used.

flutter clean

if you do not clean dependencies next step never complete. it show some error so you have to clean your dependencies.

when you cleaned all dependency you see error in dart file don't worry about that we can fixed it later.



after that open android folder in android stdio which automaticaaly download all necessary dependency in gradle.build file.
For that you right click on
android folder and choose Open in Android Stdio option


This open your Folder in android stdio which download all dependies you need in gradle. build file.

In the corner of android stdio you can see progress of download.
Download progress

when download is completed you see loading... into actual files.
Download Complete


Now back into you VS code Editor and download your all dependency by one single click.

flutter pub get

Flutter pub get

It download all your dependencies according to your pubsec.yaml dependencies section have.

Now we are left only dart file. So we have distribute screen according to intent or some open our app directily. We can do this first we have to install  flutter_plugin_android_lifecycle  plugin to work in Method Channel. you can add this flutter pub add  flutter_plugin_android_lifecycle. 

Now we have free to work for Method channel. You do not need import this depedency in your dart file.

first import some basic things in flutter .dart file.


main.dart:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'dart:async';

bool _isIntent = false;
String? _pdfPath;

_isIntent is false we prefer that user open our app directly. If intend occurs we will handle it in later. _pdfPath is used to stored our path.


main.dart:

const platform = MethodChannel('com.example.demo_project/openPDF');

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await _requestPermissionStatus();

  // Define the platform channel name
  const channelName = 'com.example.demo_project/openPDF';

  // Initialize the platform channel
  const MethodChannel channel = MethodChannel(channelName);

  // Set up the handler
  channel.setMethodCallHandler((call) async {
    if (call.method == 'getFileIntentData') {
      // Handle the platform method call here
      final String? filePath = await getFileIntentData();
      if (filePath != null) {
        _pdfPath = filePath.toString();
        _isIntent = true;
      }
      print('Received PDF file path: $filePath');
    }
  });

  runApp(MyApp());
}


'com.example.demo_project/openPDF' change it to your channel name in MainActivity.kt.

WidgetsFlutterBinding.ensureInitialized();

this line is necessary. don't forget it.


MainActivity.kt:
private val channelName = "com.example.demo_project/openPDF" // Replace with your package name

and also change it with your call method 'getFileIntentData'

in MainActivity.kt


MainActivity.kt:
 if (call.method == "getFileIntentData") {

Remember : Your channelName and call.method both are same in

main.dart and MainActivity.dart.

Now we create getFileIntentData( ) function which return path of our file.


main.dart:
Future<String?> getFileIntentData() async {
  try {
    final String? filePath = await platform.invokeMethod('getFileIntentData');
    return filePath;
  } on PlatformException catch (e) {
    print('Error: $e');
    return null;
  }
}

Above code channel.setMethodCallHandler it Checks whether intent call or not. If intent called this function works. It means it handle Intent.

For used intent in our code we used Future Widget so that we can differentiate route to homeScreen or other Screen.


main.dart:
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String?>(
      future: getFileIntentData(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          // Return a loading indicator or splash screen while waiting for intent data.
          return const MaterialApp(
            home: CircularProgressIndicator(), // Replace with your loading UI.
          );
        } else if (snapshot.hasError) {
          // Handle error case.
          return MaterialApp(
            home:
                Text('Error: ${snapshot.error}'), // Replace with your error UI.
          );
        } else if (snapshot.hasData) {
          // If intent data is available, navigate to PDFScreen.
          return MaterialApp(
            home: PDFScreen(
                path: snapshot.data!
                    .replaceAll("external_files", "storage/emulated/0")),
          );
        } else {
          // Default case: show HomeScreen.
          return const MaterialApp(
            home: HomeScreen(),
          );
        }
      },
    );
  }
}

Remember : This code path such as external_files so we replace it by this "storage/emulated/ 0". It works fine.

In the End

I upload my all code which contain permission and other things. Just check it.


main.dart:
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'dart:io';
import 'dart:async';
import 'package:demo_project/pdf_reader.dart';

import 'package:flutter/services.dart';
import 'package:demo_project/screen/pdf_open.dart';

BuildContext? appContext;
bool _isIntent = false;
String? _pdfPath;

const platform = MethodChannel('com.example.demo_project/openPDF');

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await _requestPermissionStatus();

  // Define the platform channel name
  const channelName = 'com.example.demo_project/openPDF';

  // Initialize the platform channel
  const MethodChannel channel = MethodChannel(channelName);

  // Set up the handler
  channel.setMethodCallHandler((call) async {
    if (call.method == 'getFileIntentData') {
      // Handle the platform method call here
      final String? filePath = await getFileIntentData();
      if (filePath != null) {
        _pdfPath = filePath.toString();
        _isIntent = true;
      }
      print('Received PDF file path: $filePath');
    }
  });

  runApp(MyApp());
}

Future<String?> getFileIntentData() async {
  try {
    final String? filePath = await platform.invokeMethod('getFileIntentData');
    return filePath;
  } on PlatformException catch (e) {
    print('Error: $e');
    return null;
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return FutureBuilder<String?>(
      future: getFileIntentData(),
      builder: (context, snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          // Return a loading indicator or splash screen while waiting for intent data.
          return const MaterialApp(
            home: CircularProgressIndicator(), // Replace with your loading UI.
          );
        } else if (snapshot.hasError) {
          // Handle error case.
          return MaterialApp(
            home:
                Text('Error: ${snapshot.error}'), // Replace with your error UI.
          );
        } else if (snapshot.hasData) {
          // If intent data is available, navigate to PDFScreen.
          return MaterialApp(
            home: PDFScreen(
                path: snapshot.data!
                    .replaceAll("external_files", "storage/emulated/0")),
          );
        } else {
          // Default case: show HomeScreen.
          return const MaterialApp(
            home: HomeScreen(),
          );
        }
      },
    );
  }
}

Future<bool> _requestPermissionStatus() async {
  // final status = await Permission.storage.request();
  // if (!status.isGranted) {
  //   await Permission.storage.request();
  // }
  PermissionStatus status = await Permission.manageExternalStorage.request();
  // if (status.isGranted) {
  //   print("Granted Permission");
  // }
  return status == PermissionStatus.granted;
}



Comments