update flutter_sms to the latest version

This commit is contained in:
2026-01-14 00:23:07 -08:00
parent 33258c580c
commit 6250a11afd
38 changed files with 1192 additions and 480 deletions
+119 -5
View File
@@ -49,6 +49,12 @@
"packageUri": "lib/", "packageUri": "lib/",
"languageVersion": "3.4" "languageVersion": "3.4"
}, },
{
"name": "clock",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/clock-1.1.2",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{ {
"name": "code_builder", "name": "code_builder",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/code_builder-4.11.1", "rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/code_builder-4.11.1",
@@ -79,6 +85,12 @@
"packageUri": "lib/", "packageUri": "lib/",
"languageVersion": "3.9" "languageVersion": "3.9"
}, },
{
"name": "fake_async",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/fake_async-1.3.3",
"packageUri": "lib/",
"languageVersion": "3.3"
},
{ {
"name": "file", "name": "file",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/file-7.0.1", "rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/file-7.0.1",
@@ -103,6 +115,18 @@
"packageUri": "lib/", "packageUri": "lib/",
"languageVersion": "3.5" "languageVersion": "3.5"
}, },
{
"name": "flutter_test",
"rootUri": "file:///opt/homebrew/Caskroom/flutter/2.10.4/flutter/packages/flutter_test",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "flutter_web_plugins",
"rootUri": "file:///opt/homebrew/Caskroom/flutter/2.10.4/flutter/packages/flutter_web_plugins",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{ {
"name": "glob", "name": "glob",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/glob-2.1.3", "rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/glob-2.1.3",
@@ -115,6 +139,24 @@
"packageUri": "lib/", "packageUri": "lib/",
"languageVersion": "3.4" "languageVersion": "3.4"
}, },
{
"name": "leak_tracker",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/leak_tracker-11.0.2",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "leak_tracker_flutter_testing",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/leak_tracker_flutter_testing-3.0.10",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{
"name": "leak_tracker_testing",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/leak_tracker_testing-3.0.2",
"packageUri": "lib/",
"languageVersion": "3.2"
},
{ {
"name": "lints", "name": "lints",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/lints-5.1.1", "rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/lints-5.1.1",
@@ -123,9 +165,9 @@
}, },
{ {
"name": "matcher", "name": "matcher",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/matcher-0.12.18", "rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/matcher-0.12.17",
"packageUri": "lib/", "packageUri": "lib/",
"languageVersion": "3.7" "languageVersion": "3.4"
}, },
{ {
"name": "material_color_utilities", "name": "material_color_utilities",
@@ -157,6 +199,12 @@
"packageUri": "lib/", "packageUri": "lib/",
"languageVersion": "3.9" "languageVersion": "3.9"
}, },
{
"name": "plugin_platform_interface",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/plugin_platform_interface-2.1.8",
"packageUri": "lib/",
"languageVersion": "3.0"
},
{ {
"name": "pub_semver", "name": "pub_semver",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/pub_semver-2.2.0", "rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/pub_semver-2.2.0",
@@ -201,9 +249,9 @@
}, },
{ {
"name": "test_api", "name": "test_api",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/test_api-0.7.9", "rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/test_api-0.7.7",
"packageUri": "lib/", "packageUri": "lib/",
"languageVersion": "3.7" "languageVersion": "3.5"
}, },
{ {
"name": "typed_data", "name": "typed_data",
@@ -211,18 +259,78 @@
"packageUri": "lib/", "packageUri": "lib/",
"languageVersion": "3.5" "languageVersion": "3.5"
}, },
{
"name": "url_launcher",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/url_launcher-6.3.2",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "url_launcher_android",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/url_launcher_android-6.3.28",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "url_launcher_ios",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/url_launcher_ios-6.3.6",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "url_launcher_linux",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/url_launcher_linux-3.2.2",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{
"name": "url_launcher_macos",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/url_launcher_macos-3.2.5",
"packageUri": "lib/",
"languageVersion": "3.9"
},
{
"name": "url_launcher_platform_interface",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/url_launcher_platform_interface-2.3.2",
"packageUri": "lib/",
"languageVersion": "3.1"
},
{
"name": "url_launcher_web",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/url_launcher_web-2.4.1",
"packageUri": "lib/",
"languageVersion": "3.6"
},
{
"name": "url_launcher_windows",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/url_launcher_windows-3.1.5",
"packageUri": "lib/",
"languageVersion": "3.8"
},
{ {
"name": "vector_math", "name": "vector_math",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/vector_math-2.2.0", "rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/vector_math-2.2.0",
"packageUri": "lib/", "packageUri": "lib/",
"languageVersion": "3.1" "languageVersion": "3.1"
}, },
{
"name": "vm_service",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/vm_service-15.0.2",
"packageUri": "lib/",
"languageVersion": "3.5"
},
{ {
"name": "watcher", "name": "watcher",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/watcher-1.2.1", "rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/watcher-1.2.1",
"packageUri": "lib/", "packageUri": "lib/",
"languageVersion": "3.4" "languageVersion": "3.4"
}, },
{
"name": "web",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/web-1.1.1",
"packageUri": "lib/",
"languageVersion": "3.4"
},
{ {
"name": "yaml", "name": "yaml",
"rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/yaml-3.1.3", "rootUri": "file:///Users/rodydavis/.pub-cache/hosted/pub.dev/yaml-3.1.3",
@@ -239,7 +347,13 @@
"name": "flutter_vibrate", "name": "flutter_vibrate",
"rootUri": "../packages/flutter_vibrate", "rootUri": "../packages/flutter_vibrate",
"packageUri": "lib/", "packageUri": "lib/",
"languageVersion": "3.10" "languageVersion": "3.5"
},
{
"name": "flutter_sms",
"rootUri": "../packages/flutter_sms",
"packageUri": "lib/",
"languageVersion": "3.5"
} }
], ],
"generator": "pub", "generator": "pub",
+226 -53
View File
@@ -1,5 +1,6 @@
{ {
"roots": [ "roots": [
"flutter_sms",
"flutter_vibrate", "flutter_vibrate",
"packages_dart_rodydavis" "packages_dart_rodydavis"
], ],
@@ -21,6 +22,20 @@
"pigeon" "pigeon"
] ]
}, },
{
"name": "flutter_sms",
"version": "3.0.0",
"dependencies": [
"flutter",
"flutter_web_plugins",
"url_launcher",
"web"
],
"devDependencies": [
"flutter_test",
"pigeon"
]
},
{ {
"name": "pigeon", "name": "pigeon",
"version": "26.1.5", "version": "26.1.5",
@@ -38,10 +53,47 @@
] ]
}, },
{ {
"name": "flutter_lints", "name": "flutter_test",
"version": "5.0.0", "version": "0.0.0",
"dependencies": [ "dependencies": [
"lints" "clock",
"collection",
"fake_async",
"flutter",
"leak_tracker_flutter_testing",
"matcher",
"meta",
"path",
"stack_trace",
"stream_channel",
"test_api",
"vector_math"
]
},
{
"name": "web",
"version": "1.1.1",
"dependencies": []
},
{
"name": "url_launcher",
"version": "6.3.2",
"dependencies": [
"flutter",
"url_launcher_android",
"url_launcher_ios",
"url_launcher_linux",
"url_launcher_macos",
"url_launcher_platform_interface",
"url_launcher_web",
"url_launcher_windows"
]
},
{
"name": "flutter_web_plugins",
"version": "0.0.0",
"dependencies": [
"flutter"
] ]
}, },
{ {
@@ -56,6 +108,13 @@
"vector_math" "vector_math"
] ]
}, },
{
"name": "flutter_lints",
"version": "5.0.0",
"dependencies": [
"lints"
]
},
{ {
"name": "yaml", "name": "yaml",
"version": "3.1.3", "version": "3.1.3",
@@ -143,20 +202,137 @@
] ]
}, },
{ {
"name": "lints", "name": "stream_channel",
"version": "5.1.1", "version": "2.1.4",
"dependencies": [] "dependencies": [
"async"
]
}, },
{ {
"name": "sky_engine", "name": "leak_tracker_flutter_testing",
"version": "0.0.0", "version": "3.0.10",
"dependencies": [] "dependencies": [
"flutter",
"leak_tracker",
"leak_tracker_testing",
"matcher",
"meta"
]
}, },
{ {
"name": "vector_math", "name": "vector_math",
"version": "2.2.0", "version": "2.2.0",
"dependencies": [] "dependencies": []
}, },
{
"name": "stack_trace",
"version": "1.12.1",
"dependencies": [
"path"
]
},
{
"name": "clock",
"version": "1.1.2",
"dependencies": []
},
{
"name": "fake_async",
"version": "1.3.3",
"dependencies": [
"clock",
"collection"
]
},
{
"name": "matcher",
"version": "0.12.17",
"dependencies": [
"async",
"meta",
"stack_trace",
"term_glyph",
"test_api"
]
},
{
"name": "test_api",
"version": "0.7.7",
"dependencies": [
"async",
"boolean_selector",
"collection",
"meta",
"source_span",
"stack_trace",
"stream_channel",
"string_scanner",
"term_glyph"
]
},
{
"name": "url_launcher_windows",
"version": "3.1.5",
"dependencies": [
"flutter",
"url_launcher_platform_interface"
]
},
{
"name": "url_launcher_web",
"version": "2.4.1",
"dependencies": [
"flutter",
"flutter_web_plugins",
"url_launcher_platform_interface",
"web"
]
},
{
"name": "url_launcher_platform_interface",
"version": "2.3.2",
"dependencies": [
"flutter",
"plugin_platform_interface"
]
},
{
"name": "url_launcher_macos",
"version": "3.2.5",
"dependencies": [
"flutter",
"url_launcher_platform_interface"
]
},
{
"name": "url_launcher_linux",
"version": "3.2.2",
"dependencies": [
"flutter",
"url_launcher_platform_interface"
]
},
{
"name": "url_launcher_ios",
"version": "6.3.6",
"dependencies": [
"flutter",
"url_launcher_platform_interface"
]
},
{
"name": "url_launcher_android",
"version": "6.3.28",
"dependencies": [
"flutter",
"url_launcher_platform_interface"
]
},
{
"name": "sky_engine",
"version": "0.0.0",
"dependencies": []
},
{ {
"name": "material_color_utilities", "name": "material_color_utilities",
"version": "0.11.1", "version": "0.11.1",
@@ -169,6 +345,11 @@
"version": "1.4.0", "version": "1.4.0",
"dependencies": [] "dependencies": []
}, },
{
"name": "lints",
"version": "5.1.1",
"dependencies": []
},
{ {
"name": "string_scanner", "name": "string_scanner",
"version": "1.4.1", "version": "1.4.1",
@@ -192,17 +373,6 @@
"path" "path"
] ]
}, },
{
"name": "matcher",
"version": "0.12.18",
"dependencies": [
"async",
"meta",
"stack_trace",
"term_glyph",
"test_api"
]
},
{ {
"name": "built_value", "name": "built_value",
"version": "8.12.2", "version": "8.12.2",
@@ -258,38 +428,51 @@
"meta" "meta"
] ]
}, },
{
"name": "async",
"version": "2.13.0",
"dependencies": [
"collection",
"meta"
]
},
{
"name": "leak_tracker_testing",
"version": "3.0.2",
"dependencies": [
"leak_tracker",
"matcher",
"meta"
]
},
{
"name": "leak_tracker",
"version": "11.0.2",
"dependencies": [
"clock",
"collection",
"meta",
"path",
"vm_service"
]
},
{ {
"name": "term_glyph", "name": "term_glyph",
"version": "1.2.2", "version": "1.2.2",
"dependencies": [] "dependencies": []
}, },
{ {
"name": "test_api", "name": "boolean_selector",
"version": "0.7.9", "version": "2.1.2",
"dependencies": [ "dependencies": [
"async",
"boolean_selector",
"collection",
"meta",
"source_span", "source_span",
"stack_trace", "string_scanner"
"stream_channel",
"string_scanner",
"term_glyph"
] ]
}, },
{ {
"name": "stack_trace", "name": "plugin_platform_interface",
"version": "1.12.1", "version": "2.1.8",
"dependencies": [ "dependencies": [
"path"
]
},
{
"name": "async",
"version": "2.13.0",
"dependencies": [
"collection",
"meta" "meta"
] ]
}, },
@@ -314,19 +497,9 @@
] ]
}, },
{ {
"name": "stream_channel", "name": "vm_service",
"version": "2.1.4", "version": "15.0.2",
"dependencies": [ "dependencies": []
"async"
]
},
{
"name": "boolean_selector",
"version": "2.1.2",
"dependencies": [
"source_span",
"string_scanner"
]
} }
], ],
"configVersion": 1 "configVersion": 1
-30
View File
@@ -1,30 +0,0 @@
name: github pages
on:
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Setup Flutter
uses: subosito/flutter-action@v1
with:
channel: 'dev'
- name: Install
run: |
flutter config --enable-web
flutter pub get
- name: Build
run: cd example && flutter build web
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
deploy_key: ${{ secrets.ACTIONS_DEPLOY_KEY }}
publish_dir: ./example/build/web
+9
View File
@@ -1,3 +1,12 @@
# 3.0.0
* **Breaking Change**: Migrated to Pigeon for type-safe platform channels.
* **Breaking Change**: Removed `plugin_platform_interface` dependency.
* **Breaking Change**: Removed `sendDirect` functionality and `SEND_SMS` permission requirement.
* Updated Dart SDK constraint to `^3.0.0`.
* Refactored package to use conditional imports for Web and Native support.
* Modernized example app with Material 3.
# 2.3.3 # 2.3.3
* Adding ability to send SMS directly on Android (#57). * Adding ability to send SMS directly on Android (#57).
+43 -65
View File
@@ -1,95 +1,73 @@
[![Flutter Community: flutter_sms](https://fluttercommunity.dev/_github/header/flutter_sms)](https://github.com/fluttercommunity/community)
[![Buy Me A Coffee](https://img.shields.io/badge/Donate-Buy%20Me%20A%20Coffee-yellow.svg)](https://www.buymeacoffee.com/rodydavis)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=WSH3GVC49GNNJ)
![github pages](https://github.com/fluttercommunity/flutter_sms/workflows/github%20pages/badge.svg)
[![GitHub stars](https://img.shields.io/github/stars/fluttercommunity/flutter_sms?color=blue)](https://github.com/fluttercommunity/flutter_sms)
[![flutter_sms](https://img.shields.io/pub/v/flutter_sms.svg)](https://pub.dev/packages/flutter_sms)
# flutter_sms # flutter_sms
![alt-text-1](https://github.com/fluttercommunity/flutter_sms/blob/master/screenshots/ios_blank.PNG) [![pub package](https://img.shields.io/pub/v/flutter_sms.svg)](https://pub.dev/packages/flutter_sms)
Online Demo: https://fluttercommunity.github.io/flutter_sms/ Flutter Plugin for sending SMS and MMS on Android and iOS. If you send to more than one person, it will send as MMS. On iOS, if the number is an iPhone and iMessage is enabled, it will send as an iMessage.
## Description ## Features
Flutter Plugin for sending SMS and MMS on Android and iOS. If you send to more than one person it will send as MMS. On the iOS if the number is an iPhone and iMessage is enabled it will send as an iMessage. - Send SMS/MMS to one or multiple recipients.
- Check if the device is capable of sending SMS.
## How To Use - Catch errors when sending fails.
You can send multiple ways: ## Usage
1. Message and No People ### Install
2. People and No Message
3. Message and People
This will populate the correct fields. Add `flutter_sms` as a dependency in your `pubspec.yaml` file.
```yaml
dependencies:
flutter_sms: ^3.0.0
```
## Example ### Import
Make sure to Install and Import the Package. ```dart
``` dart
import 'package:flutter_sms/flutter_sms.dart'; import 'package:flutter_sms/flutter_sms.dart';
``` ```
Create a function for sending messages. ### Example
``` dart ```dart
void _sendSMS(String message, List<String> recipents) async { void _sendSMS() async {
String _result = await sendSMS(message: message, recipients: recipents) List<String> recipients = ["1234567890", "5556787676"];
.catchError((onError) { String message = "This is a test message!";
print(onError);
}); try {
print(_result); String result = await sendSMS(message: message, recipients: recipients);
print(result);
} catch (error) {
print(error);
}
} }
``` ```
You can quickly send the message with this function. ### Check Capability
``` dart You can check if the current device is capable of sending SMS.
String message = "This is a test message!";
List<String> recipents = ["1234567890", "5556787676"];
_sendSMS(message, recipents); ```dart
bool canSend = await canSendSMS();
if (!canSend) {
print("Device cannot send SMS.");
}
``` ```
### Launch SMS URL
## Sending Direct Launch the SMS URL scheme directly.
**WARNING, there is a narrow category of apps that can get into the play store ```dart
using this feature. Using it is only advisable if you fit into this category or await launchSms(message: "This is a test message!", number: "1234567890");
you intent to distribute through a third party platform**
On Android, you can skip the additional dialog with the sendDirect parameter.
``` dart
String message = "This is a test message!";
List<String> recipents = ["1234567890", "5556787676"];
String _result = await sendSMS(message: message, recipients: recipents, sendDirect: true)
.catchError((onError) {
print(onError);
});
print(_result);
``` ```
NOTE: This also requires the SEND_SMS permission to be added to the AndroidManifest.xml ### Launch SMS URL (Multiple Recipients)
Launch the SMS URL scheme with multiple recipients.
```dart
await launchSmsMulti(message: "This is a test message!", numbers: ["1234567890", "5556787676"]);
``` ```
<uses-permission android:name="android.permission.SEND_SMS"/>
<application
...
```
## Screenshots
iOS SMS | Android MMS
:-------------------------:|:-------------------------:
![alt-text-1](https://github.com/fluttercommunity/flutter_sms/blob/master/screenshots/ios_sms.PNG) | ![alt-text-2](https://github.com/fluttercommunity/flutter_sms/blob/master/screenshots/android_mms.png)
You can find other [screenshots here](https://github.com/fluttercommunity/flutter_sms/tree/master/screenshots).
@@ -25,6 +25,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
android { android {
namespace "com.example.flutter_sms"
compileSdkVersion 34 compileSdkVersion 34
sourceSets { sourceSets {
@@ -37,6 +38,13 @@ android {
lintOptions { lintOptions {
disable 'InvalidPackage' disable 'InvalidPackage'
} }
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
} }
dependencies { dependencies {
@@ -2,28 +2,21 @@ package com.example.flutter_sms
import android.annotation.TargetApi import android.annotation.TargetApi
import android.app.Activity import android.app.Activity
import android.app.PendingIntent
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.telephony.SmsManager
import android.util.Log import android.util.Log
import androidx.annotation.NonNull import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.BinaryMessenger 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
import io.flutter.plugin.common.PluginRegistry.Registrar
class FlutterSmsPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
private lateinit var mChannel: MethodChannel class FlutterSmsPlugin: FlutterPlugin, SmsHostApi, ActivityAware {
private var activity: Activity? = null var activity: Activity? = null
private val REQUEST_CODE_SEND_SMS = 205 private val REQUEST_CODE_SEND_SMS = 205
override fun onAttachedToActivity(binding: ActivityPluginBinding) { override fun onAttachedToActivity(binding: ActivityPluginBinding) {
@@ -43,97 +36,60 @@ class FlutterSmsPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
} }
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
setupCallbackChannels(flutterPluginBinding.binaryMessenger) Log.d("FlutterSmsPlugin", "onAttachedToEngine")
SmsHostApi.setUp(flutterPluginBinding.binaryMessenger, this)
} }
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
teardown() SmsHostApi.setUp(binding.binaryMessenger, null)
}
private fun setupCallbackChannels(messenger: BinaryMessenger) {
mChannel = MethodChannel(messenger, "flutter_sms")
mChannel.setMethodCallHandler(this)
}
private fun teardown() {
mChannel.setMethodCallHandler(null)
} }
// V1 embedding entry point. This is deprecated and will be removed in a future Flutter // V1 embedding entry point. This is deprecated and will be removed in a future Flutter
// release but we leave it here in case someone's app does not utilize the V2 embedding yet. // release but we leave it here in case someone's app does not utilize the V2 embedding yet.
companion object {
@JvmStatic
fun registerWith(registrar: Registrar) {
val inst = FlutterSmsPlugin()
inst.activity = registrar.activity()
inst.setupCallbackChannels(registrar.messenger())
}
}
override fun onMethodCall(call: MethodCall, result: Result) {
when (call.method) { override fun sendSms(message: String, recipients: List<String>, callback: (Result<String>) -> Unit) {
"sendSMS" -> { if (!checkCanSendSms()) {
if (!canSendSMS()) { callback(Result.failure(FlutterError("device_not_capable", "The current device is not capable of sending text messages.", "A device may be unable to send messages if it does not support messaging or if it is not currently configured to send messages. This only applies to the ability to send text messages via iMessage, SMS, and MMS.")))
result.error( return
"device_not_capable",
"The current device is not capable of sending text messages.",
"A device may be unable to send messages if it does not support messaging or if it is not currently configured to send messages. This only applies to the ability to send text messages via iMessage, SMS, and MMS.")
return
}
val message = call.argument<String?>("message") ?: ""
val recipients = call.argument<String?>("recipients") ?: ""
val sendDirect = call.argument<Boolean?>("sendDirect") ?: false
sendSMS(result, recipients, message!!, sendDirect)
}
"canSendSMS" -> result.success(canSendSMS())
else -> result.notImplemented()
} }
val phones = recipients.joinToString(";")
callback(Result.success(sendSMSDialog(phones, message)))
} }
@TargetApi(Build.VERSION_CODES.ECLAIR) @TargetApi(Build.VERSION_CODES.ECLAIR)
private fun canSendSMS(): Boolean { override fun canSendSms(callback: (Result<Boolean>) -> Unit) {
if (!activity!!.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) callback(Result.success(checkCanSendSms()))
return false }
@TargetApi(Build.VERSION_CODES.ECLAIR)
private fun checkCanSendSms(): Boolean {
if (activity == null) {
Log.d("FlutterSmsPlugin", "Activity is null")
return false
}
if (!activity!!.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
Log.d("FlutterSmsPlugin", "No TELEPHONY feature")
return false
}
val intent = Intent(Intent.ACTION_SENDTO) val intent = Intent(Intent.ACTION_SENDTO)
intent.data = Uri.parse("smsto:") intent.data = Uri.parse("smsto:123456")
val activityInfo = intent.resolveActivityInfo(activity!!.packageManager, intent.flags.toInt()) val activityInfo = intent.resolveActivityInfo(activity!!.packageManager, 0)
return !(activityInfo == null || !activityInfo.exported) if (activityInfo == null || !activityInfo.exported) {
Log.d("FlutterSmsPlugin", "No activity to handle smsto intent or not exported")
return false
}
return true
} }
private fun sendSMS(result: Result, phones: String, message: String, sendDirect: Boolean) { private fun sendSMSDialog(phones: String, message: String): String {
if (sendDirect) {
sendSMSDirect(result, phones, message);
}
else {
sendSMSDialog(result, phones, message);
}
}
private fun sendSMSDirect(result: Result, phones: String, message: String) {
// SmsManager is android.telephony
val sentIntent = PendingIntent.getBroadcast(activity, 0, Intent("SMS_SENT_ACTION"), PendingIntent.FLAG_IMMUTABLE)
val mSmsManager = SmsManager.getDefault()
val numbers = phones.split(";")
for (num in numbers) {
Log.d("Flutter SMS", "msg.length() : " + message.toByteArray().size)
if (message.toByteArray().size > 80) {
val partMessage = mSmsManager.divideMessage(message)
mSmsManager.sendMultipartTextMessage(num, null, partMessage, null, null)
} else {
mSmsManager.sendTextMessage(num, null, message, sentIntent, null)
}
}
result.success("SMS Sent!")
}
private fun sendSMSDialog(result: Result, phones: String, message: String) {
val intent = Intent(Intent.ACTION_SENDTO) val intent = Intent(Intent.ACTION_SENDTO)
intent.data = Uri.parse("smsto:$phones") intent.data = Uri.parse("smsto:$phones")
intent.putExtra("sms_body", message) intent.putExtra("sms_body", message)
intent.putExtra(Intent.EXTRA_TEXT, message) intent.putExtra(Intent.EXTRA_TEXT, message)
activity?.startActivityForResult(intent, REQUEST_CODE_SEND_SMS) activity?.startActivityForResult(intent, REQUEST_CODE_SEND_SMS)
result.success("SMS Sent!") return "SMS Sent!"
} }
} }
@@ -0,0 +1,115 @@
// Autogenerated from Pigeon (v26.1.5), do not edit directly.
// See also: https://pub.dev/packages/pigeon
@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass")
package com.example.flutter_sms
import android.util.Log
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMethodCodec
import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
private object MessagesPigeonUtils {
fun wrapResult(result: Any?): List<Any?> {
return listOf(result)
}
fun wrapError(exception: Throwable): List<Any?> {
return if (exception is FlutterError) {
listOf(
exception.code,
exception.message,
exception.details
)
} else {
listOf(
exception.javaClass.simpleName,
exception.toString(),
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception)
)
}
}
}
/**
* Error class for passing custom error details to Flutter via a thrown PlatformException.
* @property code The error code.
* @property message The error message.
* @property details The error details. Must be a datatype supported by the api codec.
*/
class FlutterError (
val code: String,
override val message: String? = null,
val details: Any? = null
) : Throwable()
private open class MessagesPigeonCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return super.readValueOfType(type, buffer)
}
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
super.writeValue(stream, value)
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface SmsHostApi {
fun sendSms(message: String, recipients: List<String>, callback: (Result<String>) -> Unit)
fun canSendSms(callback: (Result<Boolean>) -> Unit)
companion object {
/** The codec used by SmsHostApi. */
val codec: MessageCodec<Any?> by lazy {
MessagesPigeonCodec()
}
/** Sets up an instance of `SmsHostApi` to handle messages through the `binaryMessenger`. */
@JvmOverloads
fun setUp(binaryMessenger: BinaryMessenger, api: SmsHostApi?, messageChannelSuffix: String = "") {
val separatedMessageChannelSuffix = if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_sms.SmsHostApi.sendSms$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val messageArg = args[0] as String
val recipientsArg = args[1] as List<String>
api.sendSms(messageArg, recipientsArg) { result: Result<String> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(MessagesPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(MessagesPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.flutter_sms.SmsHostApi.canSendSms$separatedMessageChannelSuffix", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
api.canSendSms{ result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(MessagesPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(MessagesPigeonUtils.wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.SEND_SMS"/>
<application <application
android:label="flutter_sms_example" android:label="flutter_sms_example"
android:name="${applicationName}" android:name="${applicationName}"
@@ -42,5 +42,9 @@
<action android:name="android.intent.action.PROCESS_TEXT"/> <action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/> <data android:mimeType="text/plain"/>
</intent> </intent>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="smsto" />
</intent>
</queries> </queries>
</manifest> </manifest>
+5 -18
View File
@@ -65,7 +65,8 @@ class SmsViewModel extends ChangeNotifier {
_setLoading(true); _setLoading(true);
try { try {
_canSendSms = await canSendSMS(); _canSendSms = await canSendSMS();
} catch (e) { } catch (e, t) {
debugPrint('Error checking capability: $e\n$t');
_status = 'Error checking capability: $e'; _status = 'Error checking capability: $e';
} finally { } finally {
_setLoading(false); _setLoading(false);
@@ -90,7 +91,7 @@ class SmsViewModel extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
Future<void> send({bool sendDirect = false}) async { Future<void> send() async {
if (_recipients.isEmpty || _message.isEmpty) { if (_recipients.isEmpty || _message.isEmpty) {
_status = 'Please add at least one recipient and a message.'; _status = 'Please add at least one recipient and a message.';
notifyListeners(); notifyListeners();
@@ -105,10 +106,10 @@ class SmsViewModel extends ChangeNotifier {
final result = await sendSMS( final result = await sendSMS(
message: _message, message: _message,
recipients: _recipients, recipients: _recipients,
sendDirect: sendDirect,
); );
_status = result; _status = result;
} catch (e) { } catch (e, t) {
debugPrint('Error sending SMS: $e\n$t');
_status = e.toString(); _status = e.toString();
} finally { } finally {
_setLoading(false); _setLoading(false);
@@ -270,20 +271,6 @@ class _SmsHomePageState extends State<SmsHomePage> {
padding: const EdgeInsets.symmetric(vertical: 16), padding: const EdgeInsets.symmetric(vertical: 16),
), ),
), ),
if (Theme.of(context).platform ==
TargetPlatform.android) ...[
const SizedBox(height: 12),
OutlinedButton.icon(
onPressed: _viewModel.isLoading
? null
: () => _viewModel.send(sendDirect: true),
icon: const Icon(Icons.flash_on),
label: const Text('Send Direct (Android)'),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
),
),
],
], ],
), ),
), ),
@@ -0,0 +1,135 @@
// Autogenerated from Pigeon (v26.1.5), do not edit directly.
// See also: https://pub.dev/packages/pigeon
import Foundation
#if os(iOS)
import Flutter
#elseif os(macOS)
import FlutterMacOS
#else
#error("Unsupported platform.")
#endif
/// Error class for passing custom error details to Dart side.
final class PigeonError: Error {
let code: String
let message: String?
let details: Sendable?
init(code: String, message: String?, details: Sendable?) {
self.code = code
self.message = message
self.details = details
}
var localizedDescription: String {
return
"PigeonError(code: \(code), message: \(message ?? "<nil>"), details: \(details ?? "<nil>")"
}
}
private func wrapResult(_ result: Any?) -> [Any?] {
return [result]
}
private func wrapError(_ error: Any) -> [Any?] {
if let pigeonError = error as? PigeonError {
return [
pigeonError.code,
pigeonError.message,
pigeonError.details,
]
}
if let flutterError = error as? FlutterError {
return [
flutterError.code,
flutterError.message,
flutterError.details,
]
}
return [
"\(error)",
"\(type(of: error))",
"Stacktrace: \(Thread.callStackSymbols)",
]
}
private func isNullish(_ value: Any?) -> Bool {
return value is NSNull || value == nil
}
private func nilOrValue<T>(_ value: Any?) -> T? {
if value is NSNull { return nil }
return value as! T?
}
private class MessagesPigeonCodecReader: FlutterStandardReader {
}
private class MessagesPigeonCodecWriter: FlutterStandardWriter {
}
private class MessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter {
override func reader(with data: Data) -> FlutterStandardReader {
return MessagesPigeonCodecReader(data: data)
}
override func writer(with data: NSMutableData) -> FlutterStandardWriter {
return MessagesPigeonCodecWriter(data: data)
}
}
class MessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
static let shared = MessagesPigeonCodec(readerWriter: MessagesPigeonCodecReaderWriter())
}
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
protocol SmsHostApi {
func sendSms(message: String, recipients: [String], completion: @escaping (Result<String, Error>) -> Void)
func canSendSms(completion: @escaping (Result<Bool, Error>) -> Void)
}
/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
class SmsHostApiSetup {
static var codec: FlutterStandardMessageCodec { MessagesPigeonCodec.shared }
/// Sets up an instance of `SmsHostApi` to handle messages through the `binaryMessenger`.
static func setUp(binaryMessenger: FlutterBinaryMessenger, api: SmsHostApi?, messageChannelSuffix: String = "") {
let channelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""
let sendSmsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.flutter_sms.SmsHostApi.sendSms\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
sendSmsChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let messageArg = args[0] as! String
let recipientsArg = args[1] as! [String]
api.sendSms(message: messageArg, recipients: recipientsArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
sendSmsChannel.setMessageHandler(nil)
}
let canSendSmsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.flutter_sms.SmsHostApi.canSendSms\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
if let api = api {
canSendSmsChannel.setMessageHandler { _, reply in
api.canSendSms { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
reply(wrapError(error))
}
}
}
} else {
canSendSmsChannel.setMessageHandler(nil)
}
}
}
@@ -2,60 +2,50 @@ import Flutter
import UIKit import UIKit
import MessageUI import MessageUI
public class SwiftFlutterSmsPlugin: NSObject, FlutterPlugin, UINavigationControllerDelegate, MFMessageComposeViewControllerDelegate { public class SwiftFlutterSmsPlugin: NSObject, FlutterPlugin, SmsHostApi, UINavigationControllerDelegate, MFMessageComposeViewControllerDelegate {
var result: FlutterResult? var result: ((Result<String, Error>) -> Void)?
var _arguments = [String: Any]() var _arguments = [String: Any]()
public static func register(with registrar: FlutterPluginRegistrar) { public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "flutter_sms", binaryMessenger: registrar.messenger())
let instance = SwiftFlutterSmsPlugin() let instance = SwiftFlutterSmsPlugin()
registrar.addMethodCallDelegate(instance, channel: channel) SmsHostApiSetup.setUp(binaryMessenger: registrar.messenger(), api: instance)
} }
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { public func sendSms(message: String, recipients: [String], completion: @escaping (Result<String, Error>) -> Void) {
switch call.method { #if targetEnvironment(simulator)
case "sendSMS": completion(.failure(FlutterError(
_arguments = call.arguments as! [String : Any]; code: "message_not_sent",
#if targetEnvironment(simulator) message: "Cannot send message on this device!",
result(FlutterError( details: "Cannot send SMS and MMS on a Simulator. Test on a real device."
code: "message_not_sent", )))
message: "Cannot send message on this device!", #else
details: "Cannot send SMS and MMS on a Simulator. Test on a real device." if (MFMessageComposeViewController.canSendText()) {
) self.result = completion
) let controller = MFMessageComposeViewController()
#else controller.body = message
if (MFMessageComposeViewController.canSendText()) { controller.recipients = recipients
self.result = result controller.messageComposeDelegate = self
let controller = MFMessageComposeViewController() UIApplication.shared.keyWindow?.rootViewController?.present(controller, animated: true, completion: nil)
controller.body = _arguments["message"] as? String } else {
controller.recipients = _arguments["recipients"] as? [String] completion(.failure(FlutterError(
controller.messageComposeDelegate = self code: "device_not_capable",
UIApplication.shared.keyWindow?.rootViewController?.present(controller, animated: true, completion: nil) message: "The current device is not capable of sending text messages.",
} else { details: "A device may be unable to send messages if it does not support messaging or if it is not currently configured to send messages. This only applies to the ability to send text messages via iMessage, SMS, and MMS."
result(FlutterError( )))
code: "device_not_capable", }
message: "The current device is not capable of sending text messages.", #endif
details: "A device may be unable to send messages if it does not support messaging or if it is not currently configured to send messages. This only applies to the ability to send text messages via iMessage, SMS, and MMS." }
)
)
}
#endif
case "canSendSMS": public func canSendSms() -> Bool {
#if targetEnvironment(simulator) #if targetEnvironment(simulator)
result(false) return false
#else #else
if (MFMessageComposeViewController.canSendText()) { if (MFMessageComposeViewController.canSendText()) {
result(true) return true
} else { } else {
result(false) return false
} }
#endif #endif
default:
result(FlutterMethodNotImplemented)
break
}
} }
public func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) { public func messageComposeViewController(_ controller: MFMessageComposeViewController, didFinishWith result: MessageComposeResult) {
@@ -65,7 +55,7 @@ public class SwiftFlutterSmsPlugin: NSObject, FlutterPlugin, UINavigationControl
MessageComposeResult.failed: "failed", MessageComposeResult.failed: "failed",
] ]
if let callback = self.result { if let callback = self.result {
callback(map[result]) callback(.success(map[result] ?? "unknown"))
} }
UIApplication.shared.keyWindow?.rootViewController?.dismiss(animated: true, completion: nil) UIApplication.shared.keyWindow?.rootViewController?.dismiss(animated: true, completion: nil)
} }
@@ -0,0 +1,32 @@
#
# Generated file, do not edit.
#
import lldb
def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict):
"""Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages."""
base = frame.register["x0"].GetValueAsAddress()
page_len = frame.register["x1"].GetValueAsUnsigned()
# Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the
# first page to see if handled it correctly. This makes diagnosing
# misconfiguration (e.g. missing breakpoint) easier.
data = bytearray(page_len)
data[0:8] = b'IHELPED!'
error = lldb.SBError()
frame.GetThread().GetProcess().WriteMemory(base, data, error)
if not error.Success():
print(f'Failed to write into {base}[+{page_len}]', error)
return
def __lldb_init_module(debugger: lldb.SBDebugger, _):
target = debugger.GetDummyTarget()
# Caveat: must use BreakpointCreateByRegEx here and not
# BreakpointCreateByName. For some reasons callback function does not
# get carried over from dummy target for the later.
bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$")
bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__))
bp.SetAutoContinue(True)
print("-- LLDB integration loaded --")
@@ -0,0 +1,5 @@
#
# Generated file, do not edit.
#
command script import --relative-to-command-file flutter_lldb_helper.py
+19 -13
View File
@@ -1,32 +1,38 @@
import 'dart:async'; import 'dart:async';
import 'src/flutter_sms_platform.dart'; import 'src/flutter_sms_io.dart'
if (dart.library.js_interop) 'src/flutter_sms_web.dart';
final FlutterSms _flutterSms = FlutterSms();
/// Open SMS Dialog on iOS/Android/Web /// Open SMS Dialog on iOS/Android/Web
Future<String> sendSMS({ Future<String> sendSMS({
required String message, required String message,
required List<String> recipients, required List<String> recipients,
bool sendDirect = false, }) {
}) => return _flutterSms.sendSMS(
FlutterSmsPlatform.instance.sendSMS( message: message,
message: message, recipients: recipients,
recipients: recipients, );
sendDirect: sendDirect, }
);
/// Launch SMS Url Scheme on all platforms /// Launch SMS Url Scheme on all platforms
Future<bool> launchSms({ Future<bool> launchSms({
String? message, String? message,
String? number, String? number,
}) => }) {
FlutterSmsPlatform.instance.launchSms(number, message); return _flutterSms.launchSms(number, message);
}
/// Launch SMS Url Scheme on all platforms /// Launch SMS Url Scheme on all platforms
Future<bool> launchSmsMulti({ Future<bool> launchSmsMulti({
required String message, required String message,
required List<String> numbers, required List<String> numbers,
}) => }) {
FlutterSmsPlatform.instance.launchSmsMulti(numbers, message); return _flutterSms.launchSmsMulti(numbers, message);
}
/// Check if you can send SMS on this platform /// Check if you can send SMS on this platform
Future<bool> canSendSMS() => FlutterSmsPlatform.instance.canSendSMS(); Future<bool> canSendSMS() {
return _flutterSms.canSendSMS();
}
@@ -1,27 +0,0 @@
import 'dart:async';
import 'package:flutter_web_plugins/flutter_web_plugins.dart';
import 'src/flutter_sms_platform.dart';
class FlutterSmsPlugin extends FlutterSmsPlatform {
static void registerWith(Registrar registrar) {
// WidgetsFlutterBinding.ensureInitialized();
FlutterSmsPlatform.instance = FlutterSmsPlugin();
}
@override
Future<String> sendSMS({
required String message,
required List<String> recipients,
bool sendDirect = false,
}) async {
bool _messageSent =
await FlutterSmsPlatform.instance.launchSmsMulti(recipients, message);
if (_messageSent) return 'Message Sent!';
return 'Error Sending Message!';
}
@override
Future<bool> canSendSMS() => Future.value(true);
}
@@ -0,0 +1,14 @@
abstract class FlutterSmsPlatform {
const FlutterSmsPlatform();
Future<String> sendSMS({
required String message,
required List<String> recipients,
});
Future<bool> canSendSMS();
Future<bool> launchSmsMulti(List<String> numbers, [String? body]);
Future<bool> launchSms(String? number, [String? body]);
}
@@ -0,0 +1,49 @@
import 'dart:async';
import 'package:flutter_sms/src/messages.g.dart';
import 'package:url_launcher/url_launcher.dart';
import 'flutter_sms_interface.dart';
import 'user_agent/io.dart';
class FlutterSms extends FlutterSmsPlatform {
final SmsHostApi _api = SmsHostApi();
///
///
Future<String> sendSMS({
required String message,
required List<String> recipients,
}) {
return _api.sendSms(message, recipients);
}
Future<bool> canSendSMS() {
return _api.canSendSms();
}
Future<bool> launchSmsMulti(List<String> numbers, [String? body]) {
if (numbers.length == 1) {
return launchSms(numbers.first, body);
}
String _phones = numbers.join(';');
if (body != null) {
final _body = Uri.encodeComponent(body);
return launchUrl(
Uri.parse('sms:/open?addresses=$_phones${separator}body=$_body'));
}
return launchUrl(Uri.parse('sms:/open?addresses=$_phones'));
}
Future<bool> launchSms(String? number, [String? body]) {
// ignore: parameter_assignments
number ??= '';
if (body != null) {
final _body = Uri.encodeComponent(body);
return launchUrl(Uri.parse('sms:/$number${separator}body=$_body'));
}
return launchUrl(Uri.parse('sms:/$number'));
}
String get separator => isCupertino() ? '&' : '?';
}
@@ -1,89 +0,0 @@
import 'dart:async';
import 'dart:io' show Platform;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'package:url_launcher/url_launcher.dart';
import 'user_agent/io.dart' if (dart.library.html) 'user_agent/web.dart';
const MethodChannel _channel = MethodChannel('flutter_sms');
class FlutterSmsPlatform extends PlatformInterface {
/// Constructs a FlutterSmsPlatform.
FlutterSmsPlatform() : super(token: _token);
static final Object _token = Object();
static FlutterSmsPlatform _instance = FlutterSmsPlatform();
/// The default instance of [FlutterSmsPlatform] to use.
///
/// Defaults to [MethodChannelFlutterSmsPlatform].
static FlutterSmsPlatform get instance => _instance;
/// Platform-specific plugins should set this with their own platform-specific
/// class that extends [FlutterSmsPlatform] when they register themselves.
// TODO(amirh): Extract common platform interface logic.
// https://github.com/flutter/flutter/issues/43368
static set instance(FlutterSmsPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
///
///
Future<String> sendSMS({
required String message,
required List<String> recipients,
bool sendDirect = false,
}) {
final mapData = <dynamic, dynamic>{};
mapData['message'] = message;
if (!kIsWeb && Platform.isIOS) {
mapData['recipients'] = recipients;
return _channel
.invokeMethod<String>('sendSMS', mapData)
.then((value) => value ?? 'Error sending sms');
} else {
String _phones = recipients.join(';');
mapData['recipients'] = _phones;
mapData['sendDirect'] = sendDirect;
return _channel
.invokeMethod<String>('sendSMS', mapData)
.then((value) => value ?? 'Error sending sms');
}
}
Future<bool> canSendSMS() {
return _channel
.invokeMethod<bool>('canSendSMS')
.then((value) => value ?? false);
}
Future<bool> launchSmsMulti(List<String> numbers, [String? body]) {
if (numbers.length == 1) {
return launchSms(numbers.first, body);
}
String _phones = numbers.join(';');
if (body != null) {
final _body = Uri.encodeComponent(body);
return launchUrl(
Uri.parse('sms:/open?addresses=$_phones${separator}body=$_body'));
}
return launchUrl(Uri.parse('sms:/open?addresses=$_phones'));
}
Future<bool> launchSms(String? number, [String? body]) {
// ignore: parameter_assignments
number ??= '';
if (body != null) {
final _body = Uri.encodeComponent(body);
return launchUrl(Uri.parse('sms:/$number${separator}body=$_body'));
}
return launchUrl(Uri.parse('sms:/$number'));
}
String get separator => isCupertino() ? '&' : '?';
}
@@ -0,0 +1,48 @@
import 'dart:async';
import 'package:url_launcher/url_launcher.dart';
import 'flutter_sms_interface.dart';
import 'user_agent/web.dart';
class FlutterSms extends FlutterSmsPlatform {
@override
Future<String> sendSMS({
required String message,
required List<String> recipients,
}) async {
bool messageSent = await launchSmsMulti(recipients, message);
if (messageSent) return 'Message Sent!';
return 'Error Sending Message!';
}
@override
Future<bool> canSendSMS() => Future.value(true);
@override
Future<bool> launchSmsMulti(List<String> numbers, [String? body]) {
if (numbers.length == 1) {
return launchSms(numbers.first, body);
}
String phones = numbers.join(';');
if (body != null) {
final encodedBody = Uri.encodeComponent(body);
return launchUrl(
Uri.parse('sms:/open?addresses=$phones$separator}body=$encodedBody'));
}
return launchUrl(Uri.parse('sms:/open?addresses=$phones'));
}
@override
Future<bool> launchSms(String? number, [String? body]) {
// ignore: parameter_assignments
number ??= '';
if (body != null) {
final encodedBody = Uri.encodeComponent(body);
return launchUrl(Uri.parse('sms:/$number$separator}body=$encodedBody'));
}
return launchUrl(Uri.parse('sms:/$number'));
}
String get separator => isCupertino() ? '&' : '?';
}
@@ -0,0 +1,106 @@
// Autogenerated from Pigeon (v26.1.5), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, omit_obvious_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
import 'package:flutter/services.dart';
PlatformException _createConnectionError(String channelName) {
return PlatformException(
code: 'channel-error',
message: 'Unable to establish connection on channel: "$channelName".',
);
}
class _PigeonCodec extends StandardMessageCodec {
const _PigeonCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
if (value is int) {
buffer.putUint8(4);
buffer.putInt64(value);
} else {
super.writeValue(buffer, value);
}
}
@override
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
default:
return super.readValueOfType(type, buffer);
}
}
}
class SmsHostApi {
/// Constructor for [SmsHostApi]. The [binaryMessenger] named argument is
/// available for dependency injection. If it is left null, the default
/// BinaryMessenger will be used which routes to the host platform.
SmsHostApi({BinaryMessenger? binaryMessenger, String messageChannelSuffix = ''})
: pigeonVar_binaryMessenger = binaryMessenger,
pigeonVar_messageChannelSuffix = messageChannelSuffix.isNotEmpty ? '.$messageChannelSuffix' : '';
final BinaryMessenger? pigeonVar_binaryMessenger;
static const MessageCodec<Object?> pigeonChannelCodec = _PigeonCodec();
final String pigeonVar_messageChannelSuffix;
Future<String> sendSms(String message, List<String> recipients) async {
final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_sms.SmsHostApi.sendSms$pigeonVar_messageChannelSuffix';
final pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(<Object?>[message, recipients]);
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as String?)!;
}
}
Future<bool> canSendSms() async {
final pigeonVar_channelName = 'dev.flutter.pigeon.flutter_sms.SmsHostApi.canSendSms$pigeonVar_messageChannelSuffix';
final pigeonVar_channel = BasicMessageChannel<Object?>(
pigeonVar_channelName,
pigeonChannelCodec,
binaryMessenger: pigeonVar_binaryMessenger,
);
final Future<Object?> pigeonVar_sendFuture = pigeonVar_channel.send(null);
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
if (pigeonVar_replyList == null) {
throw _createConnectionError(pigeonVar_channelName);
} else if (pigeonVar_replyList.length > 1) {
throw PlatformException(
code: pigeonVar_replyList[0]! as String,
message: pigeonVar_replyList[1] as String?,
details: pigeonVar_replyList[2],
);
} else if (pigeonVar_replyList[0] == null) {
throw PlatformException(
code: 'null-error',
message: 'Host platform returned null value for non-null return value.',
);
} else {
return (pigeonVar_replyList[0] as bool?)!;
}
}
}
@@ -16,5 +16,11 @@ bool isCupertino() {
return true; return true;
} }
} }
final userAgent = web.window.navigator.userAgent.toLowerCase();
if (userAgent.contains('iphone') ||
userAgent.contains('ipad') ||
userAgent.contains('ipod')) {
return true;
}
return false; return false;
} }
@@ -0,0 +1,16 @@
import 'package:pigeon/pigeon.dart';
@ConfigurePigeon(PigeonOptions(
dartOut: 'lib/src/messages.g.dart',
kotlinOut: 'android/src/main/kotlin/com/example/flutter_sms/Messages.g.kt',
kotlinOptions: KotlinOptions(package: 'com.example.flutter_sms'),
swiftOut: 'ios/Classes/Messages.g.swift',
))
@HostApi()
abstract class SmsHostApi {
@async
String sendSms(String message, List<String> recipients);
@async
bool canSendSms();
}
+10 -11
View File
@@ -1,26 +1,27 @@
name: flutter_sms name: flutter_sms
description: A Flutter plugin to Send SMS and MMS on iOS and Android. If iMessage is enabled it will send as iMessage on iOS. This plugin must be tested on a real device on iOS. description: A Flutter plugin to send SMS and MMS on iOS and Android.
version: 2.3.3 version: 3.0.0
homepage: https://github.com/rodydavis/plugins homepage: https://github.com/rodydavis/packages.dart
repository: https://github.com/fluttercommunity/flutter_sms repository: https://github.com/rodydavis/packages.dart
maintainer: Rody Davis (@rodydavis) maintainer: Rody Davis (@rodydavis)
resolution: workspace
environment: environment:
sdk: ">=3.0.0 <4.0.0" sdk: ^3.5.0
flutter: ">=3.0.0" flutter: ^3.38.5
dependencies: dependencies:
flutter: flutter:
sdk: flutter sdk: flutter
flutter_web_plugins: flutter_web_plugins:
sdk: flutter sdk: flutter
plugin_platform_interface: ^2.1.0
url_launcher: ^6.1.10 url_launcher: ^6.1.10
web: ^1.1.1 web: ^1.1.1
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
pigeon: ^26.1.5
flutter: flutter:
plugin: plugin:
@@ -29,7 +30,5 @@ flutter:
package: com.example.flutter_sms package: com.example.flutter_sms
pluginClass: FlutterSmsPlugin pluginClass: FlutterSmsPlugin
ios: ios:
pluginClass: FlutterSmsPlugin pluginClass: SwiftFlutterSmsPlugin
web:
pluginClass: FlutterSmsPlugin
fileName: flutter_sms_web.dart
Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

-30
View File
@@ -1,30 +0,0 @@
name: github pages
on:
push:
branches:
- master
jobs:
deploy:
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- name: Setup Flutter
uses: subosito/flutter-action@v1
with:
channel: 'dev'
- name: Install
run: |
flutter config --enable-web
flutter pub get
- name: Build
run: cd example && flutter build web
- name: Deploy
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./example/build/web
+1 -1
View File
@@ -2,7 +2,7 @@ name: flutter_vibrate
description: A Haptic Feedback and Vibration plugin. description: A Haptic Feedback and Vibration plugin.
version: 1.4.0 version: 1.4.0
environment: environment:
sdk: ^3.10.4 sdk: ^3.5.0
flutter: ^3.38.5 flutter: ^3.38.5
maintainer: Rody Davis (@rodydavis) maintainer: Rody Davis (@rodydavis)
homepage: https://github.com/rodydavis/packages.dart homepage: https://github.com/rodydavis/packages.dart
+144 -6
View File
@@ -65,6 +65,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
clock:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
source: hosted
version: "1.1.2"
code_builder: code_builder:
dependency: transitive dependency: transitive
description: description:
@@ -105,6 +113,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
file: file:
dependency: transitive dependency: transitive
description: description:
@@ -134,6 +150,16 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "5.0.0" version: "5.0.0"
flutter_test:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
glob: glob:
dependency: transitive dependency: transitive
description: description:
@@ -150,6 +176,30 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.2" version: "2.3.2"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@@ -162,10 +212,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6" sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.18" version: "0.12.17"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
@@ -206,6 +256,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "26.1.5" version: "26.1.5"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
pub_semver: pub_semver:
dependency: transitive dependency: transitive
description: description:
@@ -263,10 +321,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636" sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.9" version: "0.7.7"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@@ -275,6 +333,70 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.4.0" version: "1.4.0"
url_launcher:
dependency: transitive
description:
name: url_launcher
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
url: "https://pub.dev"
source: hosted
version: "6.3.2"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611"
url: "https://pub.dev"
source: hosted
version: "6.3.28"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: cfde38aa257dae62ffe79c87fab20165dfdf6988c1d31b58ebf59b9106062aad
url: "https://pub.dev"
source: hosted
version: "6.3.6"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
url: "https://pub.dev"
source: hosted
version: "3.2.2"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
url: "https://pub.dev"
source: hosted
version: "3.2.5"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
url: "https://pub.dev"
source: hosted
version: "3.1.5"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@@ -283,6 +405,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.0" version: "2.2.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
url: "https://pub.dev"
source: hosted
version: "15.0.2"
watcher: watcher:
dependency: transitive dependency: transitive
description: description:
@@ -291,6 +421,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.2.1" version: "1.2.1"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
yaml: yaml:
dependency: transitive dependency: transitive
description: description:
@@ -300,5 +438,5 @@ packages:
source: hosted source: hosted
version: "3.1.3" version: "3.1.3"
sdks: sdks:
dart: ">=3.10.4 <4.0.0" dart: ">=3.9.0 <4.0.0"
flutter: ">=3.38.5 <4.0.0" flutter: ">=3.38.5"
+1 -1
View File
@@ -6,4 +6,4 @@ environment:
workspace: workspace:
- packages/flutter_vibrate - packages/flutter_vibrate
- packages/flutter_sms