Sending SMS Messages with Dart and Twilio Programmable SMS
Time to read: 9 minutes
According to GitHub Octoverse Dart was the fastest growing language of 2019.
Dart is a client-optimized language for fast apps on any platform. Developed by Google, Dart is used to build mobile, desktop, backend, and web applications. Dart code is easy to write and doesn't require lots of hassle with memory management and build systems.
Since version 2.6, announced November 2019, Dart comes with dart2native, a compiler with the ability to produce platform-specific, standalone executables. This enables developers to create tools for the command line on macOS, Windows, or Linux. The entirely self-contained executables can run instantly on machines that don’t need to have the Dart SDK installed.
Twilio’s Programmable SMS API makes sending and receiving SMS easy. You can send messages globally using Twilio phone numbers that handle carrier complexities for you. By wrapping the API in a package, you and your fellow Dart developers can get to the important work even faster.
What are you going to learn
Creating Dart packages is both easy and fun, and this post will show you how. You learn how to build a Dart library package and use it in a different package. You’ll learn how to call Twilio’s Programmable SMS API from Dart code. And you’ll learn how to convert your Dart package into an executable command-line application which takes arguments.
What are you going to build
Your own Dart package named my_twilio. You will then use my_twilio in a command line application to send text messages with Twilio’s API.
Prerequisites
- Dart SDK (version 2.6 or latter to be able to compile with dart2native)
- Visual Studio Code (or your preferred IDE or text editor)
- Dart Code plugin for Visual Studio Code (or for your editor, if there is one)
- Twilio trial account (Use this link to get a $10 credit on your account when you sign up.)
- Twilio phone number
- A tour of the Dart language (recommended reading)
Package layout conventions
A library package is a package that other packages can depend on. When you build a package there are some style conventions to follow. The ones most relevant to this project are:
- name libraries, packages, directories, and source files using lowercase_with_underscores
- name functions, variables, and parameters using lowerCamelCase
- name classes and types using UpperCamelCase
- place
dart:
imports at the top before other imports - place external
package:
imports before relative imports
Package layout conventions relevant to this project are:
- every package has a pubspec.yaml file in the root directory of the package
- libraries public to other packages should go inside the lib folder
- implementation code is placed under lib/src and is considered private
- if you include an example of how to use your library it should go into the example directory at the root of the package
The my_twilio package structure
my_twilio
├── example
│ └── send_sms.dart
├── lib
│ ├── src
│ │ ├── constants.dart
│ │ ├── messages.dart
│ │ └── utils.dart
│ └── my_twilio.dart
└── pubspec.yaml
Setting up the package structure
In the location of your choice, create a new folder called my_twilio.
In the my_twilio folder, create a text file named pubspec.yaml and two folders named lib and example.
In the example folder, create a text file named send_sms.dart.
Inside lib create a text file named my_twilio.dart and a folder named src.
Inside the src folder create three text files named messages.dart, constants.dart, and utils.dart.
Creating the source code
Insert the following code in pubspec.yaml file:
The pubspec.yaml file contains some metadata about the package:
- every package needs a name
- every package has a version
- a package might only work with certain versions of the Dart SDK and you can specify those versions using an SDK constraint
- the dependencies section lists each package that your package needs in order to work
Insert the following Dart code in the my_twilio.dart file in the lib directory:
The code in the my_twilio.dart file is the library exposed by your my_twilio package, it can be imported in other packages or Dart programs. It starts with an import of the Messages
class defined in ./src/messages.dart. It defines a MyTwilio
class with two fields, _accountSid
and _authToken
, a MyTwilio()
constructor and a getter, messages
which returns a Messages
class. As you’ll see below, the Messages
class defines a method named create()
which does the job of calling Twilio’s API to send the text message.
Insert the following code in the constants.dart file in the lib directory:
To keep the code clean and organized, constants are kept in constants.dart. For now, a constant of type String
is defined to hold the value of Twilio’s SMS API base URL. If needed, you can add more constants here.
Insert the following code in the utils.dart file in the lib directory:
The code in utils.dart defines a function named toAuthCredentials()
which returns your Twilio credentials as a base64 encoded string ready to be used in the authorization header of an HTTP request to Twilio’s API. Encoding is done with the help of the dart:convert
library, which is part of Dart’s core libraries.
Insert the following Dart code in the messages.dart file in the lib folder:
The messages.dart code imports the code from constants.dart, utils.dart, and the external library package http from pub.dev (the primary public repository for Dart packages). http contains a set of high-level functions and classes that make it easy to consume HTTP resources.
The code in messages.dart defines a Messages
class which contains two fields, _accountSid
and _authToken
, a constructor Messages()
, and a create()
method. When an instance of Messages
is created, values for your Twilio credentials are stored in _accountSid
and _authToken
. When create()
is invoked an authenticated HTTP POST request is made to Twilio’s API to send the text message.
With the help of the http package, an http.Client()
instance is created and client.post()
is invoked. This is an asynchronous HTTP request and the async/await keywords are used to instruct the program that it has to wait for the response from Twilio.
Installing dependencies
The pub tool is the package manager for the Dart programming language. The pub get
command gets all the dependencies listed in the pubspec.yaml file in the current working directory, as well as their transitive dependencies. A transitive dependency is a dependency that your package indirectly uses because one of its dependencies requires it.
my_twilio depends on the http package hosted at pub.dev. You’ve let pub know about this in the dependencies section in pubspec.yaml:
Ellipsis (“...
”) indicates a section of code redacted for brevity or emphasis.
Open a console window in the my_twilio directory and run the following command-line instruction:
The pub utility will download and save the http package and its dependencies.
Testing the package with an example
Copy the following code to the send_sms.dart file in the example folder:
The send_sms.dart code imports the my_twilio library, creates an instance of the MyTwilio
class, then calls client.messages.create()
to send a text message. The argument to create()
is a collection of key/value pairs for 'body': the text of the message; 'from', a valid Twilio number; and 'to', the phone number where the message should be received.
Credentials for access to Twilio’s API are first taken from environment variables with an attempt like this:
If environment variables are not defined, _accountSid
will hold a value of null
.
With _accountSid ??= 'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX';
_accountSid
is tested for null
, and if it is null
the value from the right side of the ??= operator is assigned to it. If you’ve defined environment variables for your Twilio credentials this line won’t have any effect.
Replace _accountSid
and _authToken
with your Twilio user secret values—or go with the better option and set TWILIO_ACCOUNT_SID
and TWILIO_AUTH_TOKEN
environment variables.
Replace the from
and to
phone numbers with your Twilio phone number and the mobile phone number you used to register for your Twilio account.
Optionally, replace the value for body
with your own message.
To run the example app from the command line, use the Dart VM by running the dart command, as follows.
Open a console window in the my_twilio folder and execute the following command-line instruction:
If the program executes successfully you should see command line output similar to the following:
Shortly thereafter you should receive an SMS message on your mobile device. It will be from your Twilio phone number and the message will be the value supplied for body
above.
Compiling a standalone executable with dart2native
The dart2native tool is a compiler which you can use to compile a Dart program to machine code. You can make an executable version of the my_twilio package in a few steps.
Open a console window in the my_twilio folder.
On Windows, execute the following command-line instruction:
On macOS, execute:
The compiler will create send_sms.exe on Windows or send_sms on macOS) inside the example folder.
You can run this executable on any computer which has the same operating system as the one it was created on. At the moment, cross-compilation is not supported. Other than that, the executable contains everything it needs to run. (The Dart SDK is not needed.)
Using the my_twilio package in another Dart project
In the same folder where you created the my_twilio folder, create a new folder named my_dart_project.
In the my_dart_project folder, create a text file named pubspec.yaml and two folders named bin and lib.
In the bin folder, create a text file named main.dart. This will be the entry point for your app.
Copy the following code to the pubspec.yaml file:
You can add a local package as a dependency to your project by specifying the relative path to the package in pubspec.yaml dependencies section.This tells the pub utility how the package can be located.
Open a console window in the my_dart_project folder and run the following command-line instruction:
Once the pub utility finishes running you can import my_twilio in your Dart code.
Insert the following Dart code into the main.dart file:
Replace _accountSid
and _authToken
with your Twilio user secret values—or go with the better option and set TWILIO_ACCOUNT_SID
and TWILIO_AUTH_TOKEN
environment variables.
Replace the from
and to
phone numbers with your Twilio phone number and the mobile phone number you used to register for your Twilio account.
Optionally, replace the value for body
with your own message.
Testing the my_dart_project executable
Open a console window in the my_dart_project folder and execute the following command-line instruction:
You should see console output similar to that produced by the my_twilio package and you should receive an SMS message on your mobile phone.
Creating a standalone executable that takes arguments
You’re going to modify main.dart to create an sms.exe, on Windows, or a sms, on macOS, command-line executable and pass in --from
, --to
, and --body
arguments so it can be run like the following example:
Begin by importing the hosted package args, which has a library, arg_parser.dart, you’ll use to parse the list of arguments and return the results.
Add the args package as a dependency in the pubspec.yaml file by modifying the dependencies
section so it looks like the following code block. Note that the indentation shown is required.
In a console window in the my_dart_project directory, run pub get
to get the args package.
At the top of main.dart file, import args, modify the import dart:io
statement to expose the exit
function and define a global variable named argResults
with a type of ArgResults
, as shown in the code block below.
You will use the exit()
function to terminate the program if no valid arguments are passed.
You will use the argResults
variable to store a collection of key/value pairs containing the program’s arguments.
Modify the main()
method signature to accept command-line arguments, which will be passed in as a list of strings.
At the top of the main()
function, add a call to a function parseCommandLineArguments()
, as shown below:
Define the parseCommandLineArguments()
function by adding the following Dart code to the main.dart file above the main()
function:
The function begins by creating a parser
object, an instance of the ArgParser
class exposed by the args package. Using addOption()
method calls, the expected command-line options are added to the parser. Then the program's arguments are parsed with a call to parser.parse(arguments)
and saved to argResults
. If the program is called with invalid arguments or without arguments, an ArgParserException
is thrown and usage instructions are printed with the help of parser.usage
, which contains the help text.
The last thing to change in the main()
function code is to replace hard-coded values of the body
, from
, and to
parameters in the client.messages.create()
method with values taken from the argResults
collection.
Replace the existing client.messages.create()
function with the following code:
If you need to verify your code changes are correct you can check them against the main.dart file in the dart-twilio-sms companion repository on GitHub.
Testing the my_dart_project package
In the my_dart_project directory, open a console window and run the following command-line instruction:
You should see console output similar to the output shown above for the my_twilio package. You should also receive an SMS message on the mobile device specified by the --to
argument.
Creating a standalone executable from the my_dart_project package
On Windows, open a console window in the my_dart_project folder and execute the following command-line instruction:
On macOS, execute the following command:
Testing the my_dart_project executable with arguments
On Windows test the compiled executable by opening a console window in the bin folder and running the program with the command-line arguments, replacing the values shown below with your Twilio phone number and your registered mobile phone number:
On macOS run:
You should see console output similar to the following and you should receive an SMS message on your mobile device.
Summary
By building a simple Dart library package and then using it to create a standalone command-line program to send text messages you learned how to communicate with Twilio’s Messaging API from Dart code. You could add more functionality to my_twilio to interact with other Twilio APIs such as Programmable Voice.
Additional resources
Dart Docs: Creating packages
Dart Docs: Write command-line apps
pub.dev http (library for making HTTP requests)
pub.dev args (library to parses command-line arguments)
Learn more by reading other Dart projects source code at GitHub firebase-dart, dart-sass.
Alex Baban is a Romanian-born Canadian web and mobile developer and is a Twilio Champion. Alex has been working on web sites in various capacities for over 15 years. In addition to software development, Alex has a background in electronics, with a Bachelor of Science in Electrical Engineering from the Technical University of Cluj-Napoca, Romania. Alex can be reached on Twitter @alexbaban.
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.