This commit is contained in:
ayub 2025-09-06 22:08:01 +07:00
commit fa6e959714
38 changed files with 1125 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.DS_Store

79
CHANGELOG.md Normal file
View File

@ -0,0 +1,79 @@
# CHANGELOG
## 2.0.2
* File not sharing issue fixed addresses issues -> #17, #16
## 2.0.1
* Is installed Fix for android 11 and above
## 2.0.0
* Upgraded to NULL safety
## 1.1.1
* Read me examples updated
## 1.1.0
* Plugin respects Business whatsapp
* **Breaking Change**
* fileShare function takes list of paths instead of single path.
* Multiple images, files can be shared
## 1.0.9
* Home page modified
## 1.0.8
* ```isInstalled()``` method for checking whatsapp installed in device or not
## 1.0.7
* Documented and optimized for whatsapp share
## 1.0.6
* Whatsapp share
## 1.0.5
* Fix sdk environment
## 1.0.4
* Update to android v2 embedding
## 1.0.3
* Fix share image top view controller dismissal bug on iOS 13+
* Update to use the new platforms definition
## 1.0.2+1
* Fix Android bug (FileUriExposedException) on new versions with FileProvider implementation (see the readme to configure)
## 1.0.1+1
* Remove unused codes and improve description
## 1.0.1
* Fix: Get the top most ViewController(IOS embedded support)
## 1.0.0
* **BREAKING CHANGE**: Add support to AndroidX
* **BREAKING CHANGE**: Separate in two methods share (to share messages and links) and shareFile (to share files)
* Add ChooserTitle (Just for Android)
* Add Text
* Add Docs
## 0.0.5
* Move documents_picker plugin to example project
## 0.0.1
* Add file share.

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Amit Patil
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

121
README.md Normal file
View File

@ -0,0 +1,121 @@
# [Whatsapp Share Plugin](https://pub.dev/packages/whatsapp_share)
[![pub package](https://img.shields.io/pub/v/whatsapp_share.svg)](https://pub.dartlang.org/packages/flutter_share)
A Flutter plugin for Android providing a simple way to share a message, link or local files to specific WhatsApp contact.
## Features:
* Share messages or link urls to specific contact.
* Share local files to specific contact.
## Installation
First, add this to your package's pubspec.yaml file:
```
dependencies:
whatsapp_share: ^1.1.1
```
Now in your Dart code, you can use:
```
import 'package:whatsapp_share/whatsapp_share.dart';
```
## Installation (Platform Specific)
### iOS
Add if not exists one row to the `ios/podfile` after target runner:
```
...
target 'Runner' do
use_frameworks!
...
```
### Android
If you pretends to use the file share, you need to configure the file provider, this will give access to the files turning possible to share with other applications.
Add to `AndroidManifest.xml`:
```
<application>
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
```
Obs: You can change the android:name if you have an extension of file provider.
Add `res/xml/provider_paths.xml`:
```
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
```
If you want to learn more about file provider you can access:
- https://developer.android.com/reference/android/support/v4/content/FileProvider
## How to use?
Here is an snippets app displaying the two whatsapp share methods .
### Whatsapp installed in this device ?
```Dart
Future<void> isInstalled() async {
final val = await WhatsappShare.isInstalled(
package: Package.businessWhatsapp
);
print('Whatsapp Business is installed: $val');
}
```
<small>If whatsapp is not installed, please do not call ```WhatsappShare.share()``` and ```WhatsappShare.shareFile()```
</small>
### Share text, links
```Dart
Future<void> share() async {
await WhatsappShare.share(
text: 'Whatsapp share text',
linkUrl: 'https://flutter.dev/',
phone: '911234567890',
);
}
```
### Share images, files
```_image1.path``` contains path of the file which is shared to the whatsapp.
```Dart
Future<void> shareFile() async {
await WhatsappShare.shareFile(
phone: '911234567890',
filePath: [_image1.path, _image2.path],
);
}
```

41
android/build.gradle Normal file
View File

@ -0,0 +1,41 @@
group 'com.example.whatsapp_share'
version '1.0-SNAPSHOT'
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.0'
}
}
rootProject.allprojects {
repositories {
google()
jcenter()
}
}
apply plugin: 'com.android.library'
android {
namespace 'com.example.whatsapp_share'
compileSdkVersion 33
defaultConfig {
minSdkVersion 16
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
}
}
dependencies {
api 'commons-io:commons-io:2.6'
implementation 'androidx.core:core:1.3.2'
}

View File

@ -0,0 +1,4 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true

1
android/settings.gradle Normal file
View File

@ -0,0 +1 @@
rootProject.name = 'whatsapp_share'

View File

@ -0,0 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.whatsapp_share">
<queries>
<!-- Explicit apps you know in advance about: -->
<package android:name="com.whatsapp"/>
<package android:name="com.whatsapp.w4b"/>
</queries>
</manifest>

View File

@ -0,0 +1,163 @@
package com.example.whatsapp_share;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import java.io.File;
import java.util.ArrayList;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
/** WhatsappShare */
public class WhatsappShare implements FlutterPlugin, MethodCallHandler, ActivityAware {
private Context context;
private MethodChannel methodChannel;
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
this.context = binding.getApplicationContext();
setupChannel(binding.getBinaryMessenger());
}
private void setupChannel(BinaryMessenger messenger) {
methodChannel = new MethodChannel(messenger, "whatsapp_share");
methodChannel.setMethodCallHandler(this);
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
methodChannel.setMethodCallHandler(null);
methodChannel = null;
}
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
// optional: use if you need access to activity
}
@Override
public void onDetachedFromActivity() {}
@Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {}
@Override
public void onDetachedFromActivityForConfigChanges() {}
@Override
public void onMethodCall(MethodCall call, Result result) {
switch (call.method) {
case "shareFile":
shareFile(call, result);
break;
case "share":
share(call, result);
break;
case "isInstalled":
isInstalled(call, result);
break;
default:
result.notImplemented();
break;
}
}
private boolean isPackageInstalled(String packageName, PackageManager packageManager) {
try {
packageManager.getPackageInfo(packageName, 0);
return true;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
private void isInstalled(MethodCall call, Result result) {
String packageName = call.argument("package");
if (packageName == null || packageName.isEmpty()) {
result.error("FlutterShare", "Package name cannot be null or empty", null);
return;
}
boolean installed = isPackageInstalled(packageName, context.getPackageManager());
result.success(installed);
}
private void share(MethodCall call, Result result) {
try {
String title = call.argument("title");
String text = call.argument("text");
String linkUrl = call.argument("linkUrl");
String phone = call.argument("phone");
String packageName = call.argument("package");
if (title == null || phone == null || packageName == null) {
result.error("FlutterShare", "Required arguments missing", null);
return;
}
ArrayList<String> parts = new ArrayList<>();
if (!TextUtils.isEmpty(text)) parts.add(text);
if (!TextUtils.isEmpty(linkUrl)) parts.add(linkUrl);
String fullText = TextUtils.join("\n\n", parts);
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.setPackage(packageName);
intent.putExtra("jid", phone + "@s.whatsapp.net");
intent.putExtra(Intent.EXTRA_SUBJECT, title);
intent.putExtra(Intent.EXTRA_TEXT, fullText);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
result.success(true);
} catch (Exception e) {
result.error("FlutterShare", e.toString(), null);
}
}
private void shareFile(MethodCall call, Result result) {
try {
ArrayList<String> filePaths = call.argument("filePath");
String phone = call.argument("phone");
String packageName = call.argument("package");
if (filePaths == null || filePaths.isEmpty() || phone == null || packageName == null) {
result.error("FlutterShare", "Required arguments missing", null);
return;
}
ArrayList<Uri> files = new ArrayList<>();
for (String path : filePaths) {
File file = new File(path);
Uri uri = FileProvider.getUriForFile(context,
context.getPackageName() + ".provider", file);
files.add(uri);
}
Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);
intent.setType("*/*");
intent.setPackage(packageName);
intent.putExtra("jid", phone + "@s.whatsapp.net");
intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, files);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
result.success(true);
} catch (Exception e) {
result.error("FlutterShare", e.toString(), null);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

16
example/README.md Normal file
View File

@ -0,0 +1,16 @@
# example
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

View File

@ -0,0 +1,71 @@
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.example"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion flutter.minSdkVersion
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}

View File

@ -0,0 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -0,0 +1,43 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<application
android:label="example"
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>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
<!-- 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>

View File

@ -0,0 +1,6 @@
package com.example.example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
<cache-path name="cache_files" path="."/>
<files-path name="files" path="."/>
</paths>

View File

@ -0,0 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.example">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -0,0 +1,31 @@
buildscript {
ext.kotlin_version = '1.9.10'
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:7.1.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true
android.enableJetifier=true

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip

View File

@ -0,0 +1,11 @@
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"

121
example/lib/main.dart Normal file
View File

@ -0,0 +1,121 @@
import 'dart:async';
import 'dart:developer';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:whatsapp_share/whatsapp_share.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:screenshot/screenshot.dart';
void main() => runApp(MyApp());
// ignore: must_be_immutable
class MyApp extends StatelessWidget {
final _controller = ScreenshotController();
File? _image;
MyApp({super.key});
Future<void> share() async {
await WhatsappShare.share(
text: 'Example share text',
linkUrl: 'https://flutter.dev/',
phone: '911234567890',
);
}
Future<void> shareFile() async {
await getImage();
Directory? directory;
if (Platform.isAndroid) {
directory = await getExternalStorageDirectory();
} else {
directory = await getApplicationDocumentsDirectory();
}
debugPrint('${directory?.path} / ${_image?.path}');
await WhatsappShare.shareFile(
phone: '911234567890',
filePath: ["${_image?.path}"],
);
}
Future<void> isInstalled() async {
final val = await WhatsappShare.isInstalled(package: Package.whatsapp);
debugPrint('Whatsapp is installed: $val');
}
Future<void> shareScreenShot() async {
Directory? directory;
if (Platform.isAndroid) {
directory = await getExternalStorageDirectory();
} else {
directory = await getApplicationDocumentsDirectory();
}
final String localPath =
'${directory?.path}/${DateTime.now().millisecondsSinceEpoch}.png';
await _controller.captureAndSave(localPath);
await Future.delayed(const Duration(seconds: 1));
await WhatsappShare.shareFile(
phone: '911234567890',
filePath: [localPath],
);
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Whatsapp Share'),
),
body: Center(
child: Screenshot(
controller: _controller,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
onPressed: share,
child: const Text('Share text and link'),
),
ElevatedButton(
onPressed: shareFile,
child: const Text('Share Image'),
),
ElevatedButton(
onPressed: shareScreenShot,
child: const Text('Share screenshot'),
),
ElevatedButton(
onPressed: isInstalled,
child: const Text('is Installed'),
),
],
),
),
),
),
);
}
///Pick Image From gallery using image_picker plugin
Future getImage() async {
try {
XFile? pickedFile =
// ignore: deprecated_member_use
await ImagePicker().pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
_image = File(pickedFile.path);
} else {}
} catch (er) {
log(er.toString());
}
}
}

98
example/pubspec.yaml Normal file
View File

@ -0,0 +1,98 @@
name: example
description: A new Flutter project.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
environment:
sdk: '>=2.18.6 <3.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
screenshot: ^2.1.0
path_provider: ^2.1.1
image_picker: ^0.8.6+1
file_picker: ^5.2.5
whatsapp_share:
path: ../
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.5
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.1
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages

19
flutter_share.iml Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
</component>
</module>

30
flutter_share_android.iml Normal file
View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="android" name="Android">
<configuration>
<option name="ALLOW_USER_CONFIGURATION" value="false" />
<option name="GEN_FOLDER_RELATIVE_PATH_APT" value="/android/gen" />
<option name="GEN_FOLDER_RELATIVE_PATH_AIDL" value="/android/gen" />
<option name="MANIFEST_FILE_RELATIVE_PATH" value="/android/AndroidManifest.xml" />
<option name="RES_FOLDER_RELATIVE_PATH" value="/android/res" />
<option name="ASSETS_FOLDER_RELATIVE_PATH" value="/android/assets" />
<option name="LIBS_FOLDER_RELATIVE_PATH" value="/android/libs" />
<option name="PROGUARD_LOGS_FOLDER_RELATIVE_PATH" value="/android/proguard_logs" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/android">
<sourceFolder url="file://$MODULE_DIR$/android/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/android/gen" isTestSource="false" generated="true" />
</content>
<content url="file://$MODULE_DIR$/example/android">
<sourceFolder url="file://$MODULE_DIR$/example/android/app/src/main/java" isTestSource="false" />
</content>
<orderEntry type="jdk" jdkName="Android API 25 Platform" jdkType="Android SDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Flutter for Android" level="project" />
</component>
</module>

94
lib/whatsapp_share.dart Normal file
View File

@ -0,0 +1,94 @@
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
/// Select Whatsapp Type
enum Package { whatsapp, businessWhatsapp }
class WhatsappShare {
static const MethodChannel _channel = MethodChannel('whatsapp_share');
/// Checks whether whatsapp is installed in device or not
///
/// [Package] is optional enum parameter which is defualt to [Package.whatsapp]
/// for business whatsapp set it to [Package.businessWhatsapp], it cannot be null
///
/// return true if installed otherwise false.
static Future<bool?> isInstalled({Package package = Package.whatsapp}) async {
String _package;
_package = package.index == 0 ? "com.whatsapp" : "com.whatsapp.w4b";
final bool? success =
await _channel.invokeMethod('isInstalled', <String, dynamic>{
"package": _package,
});
return success;
}
/// Shares a message or/and link url with whatsapp.
/// - Text: Is the [text] of the message.
/// - LinkUrl: Is the [linkUrl] to include with the message.
/// - Phone: is the [phone] contact number to share with.
static Future<bool?> share({
required String phone,
String? text,
String? linkUrl,
Package package = Package.whatsapp,
}) async {
assert(phone.isNotEmpty);
String _package;
_package = package.index == 0 ? "com.whatsapp" : "com.whatsapp.w4b";
final bool? success =
await _channel.invokeMethod('share', <String, dynamic>{
'title': ' ',
'text': text,
'linkUrl': linkUrl,
'chooserTitle': ' ',
'phone': phone,
'package': _package,
});
return success;
}
/// Shares a local file with whatsapp.
/// - Text: Is the [text] of the message.
/// - FilePath: Is the List of paths which can be prefilled.
/// - Phone: is the [phone] contact number to share with.
static Future<bool?> shareFile({
required List<String> filePath,
required String phone,
@Deprecated(
"No support for text along with files, this field is ignored")
String? text,
Package package = Package.whatsapp,
}) async {
assert(filePath.isNotEmpty);
assert(phone.isNotEmpty);
if (filePath.isEmpty) {
throw FlutterError('FilePath cannot be Empty');
} else if (phone.isEmpty) {
throw FlutterError('Phone cannot be Empty');
}
String _package;
_package = package.index == 0 ? "com.whatsapp" : "com.whatsapp.w4b";
final bool? success =
await _channel.invokeMethod('shareFile', <String, dynamic>{
'title': ' ',
'text': ' ',
'filePath': filePath,
'chooserTitle': ' ',
'phone': phone,
'package': _package,
});
return success;
}
}

26
pubspec.yaml Normal file
View File

@ -0,0 +1,26 @@
name: whatsapp_share
description: Simple way to share message, links or files from your flutter app to specific contact in whatsapp
version: 2.0.2
homepage: https://github.com/amitpatil215/whatsapp_share
dependencies:
flutter:
sdk: flutter
flutter:
plugin:
platforms:
android:
package: com.example.whatsapp_share
pluginClass: WhatsappShare
dev_dependencies:
flutter_test:
sdk: flutter
environment:
sdk: '>=2.12.0 <4.0.0'
flutter: ">=1.20.0"