The Most Famous and Powerful Dart/Flutter Library for Twitter API v2.0 π¦
| English | ζ₯ζ¬θͺ | FranΓ§ais | TiαΊΏng Viα»t | বাΰ¦ΰ¦²ΰ¦Ύ | EspaΓ±ol | Deutsch | PortuguΓͺs |
- 1. Guide π
- 1.1. Features π
- 1.2. Getting Started β‘
- 1.3. Supported Endpoints π
- 1.4. Tips π
- 1.4.1. Method Names
- 1.4.2. Generate App-Only Bearer Token
- 1.4.3. Null Parameter at Request
- 1.4.4. Expand Object Fields with
expansions
- 1.4.5. Expand Object Fields with
fields
- 1.4.6. JSON Serialization and Deserialization
- 1.4.7. OAuth 2.0 Authorization Code Flow with PKCE
- 1.4.8. Change the Timeout Duration
- 1.4.9. Automatic Retry
- 1.4.10. Meaning of the Returned Boolean
- 1.4.11. Thrown Exceptions
- 1.4.12. Upload Media
- 1.4.13. Check the Progress of Media Upload
- 1.4.14. Generate Filtering Rules Safely and Easily
- 1.5. Contribution π
- 1.6. Contributors β¨
- 1.7. Support β€οΈ
- 1.8. License π
- 1.9. More Information π§
This library provides the easiest way to use Twitter API v2.0 in Dart and Flutter apps.
Show some β€οΈ and star the repo to support the project.
We also provide twitter_oauth2_pkce for easy OAuth 2.0 PKCE authentication when using the Twitter API!
Also, this library is also posted on the Official Twitter page π¦
β
The wrapper library for Twitter API v2.0.
β
Easily integrates with the Dart & Flutter apps.
β
Provides response objects with a guaranteed safe types.
β
Supports all endpoints.
β
Support all request parameters and response fields.
β
Supports high-performance streaming endpoints.
β
Supports expansions and fields features.
β
Well documented and well tested.
β
Supports the powerful automatic retry.
β
Supports for large media uploads (image, gif, video).
β
Supports safe and powerful paging feature.
With Dart:
dart pub add twitter_api_v2
Or With Flutter:
flutter pub add twitter_api_v2
import 'package:twitter_api_v2/twitter_api_v2';
import 'dart:async';
import 'dart:io';
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
//! You need to get keys and tokens at https://developer.twitter.com
final twitter = v2.TwitterApi(
//! Authentication with OAuth2.0 is the default.
//!
//! Note that to use endpoints that require certain user permissions,
//! such as Tweets and Likes, you need a token issued by OAuth2.0 PKCE.
//!
//! The easiest way to achieve authentication with OAuth 2.0 PKCE is
//! to use [twitter_oauth2_pkce](https://pub.dev/packages/twitter_oauth2_pkce)!
bearerToken: 'YOUR_TOKEN_HERE',
//! Or perhaps you would prefer to use the good old OAuth1.0a method
//! over the OAuth2.0 PKCE method. Then you can use the following code
//! to set the OAuth1.0a tokens.
//!
//! However, note that some endpoints cannot be used for OAuth 1.0a method
//! authentication.
oauthTokens: v2.OAuthTokens(
consumerKey: 'YOUR_CONSUMER_KEY_HERE',
consumerSecret: 'YOUR_CONSUMER_SECRET_HERE',
accessToken: 'YOUR_ACCESS_TOKEN_HERE',
accessTokenSecret: 'YOUR_ACCESS_TOKEN_SECRET_HERE',
),
//! Automatic retry is available when a TimeoutException occurs when
//! communicating with the API.
retryConfig: v2.RetryConfig.ofExponentialBackOffAndJitter(
maxAttempts: 5,
onExecute: (event) => print(
'Retry after ${event.intervalInSeconds} seconds... '
'[${event.retryCount} times]',
),
),
//! The default timeout is 10 seconds.
timeout: Duration(seconds: 20),
);
try {
//! Get the authenticated user's profile.
final me = await twitter.users.lookupMe();
//! Get the tweets associated with the search query.
final tweets = await twitter.tweets.searchRecent(
query: '#ElonMusk',
maxResults: 20,
// You can expand the search result.
expansions: [
v2.TweetExpansion.authorId,
v2.TweetExpansion.inReplyToUserId,
],
tweetFields: [
v2.TweetField.conversationId,
v2.TweetField.publicMetrics,
v2.TweetField.editControls,
],
userFields: [
v2.UserField.location,
v2.UserField.verified,
v2.UserField.entities,
v2.UserField.publicMetrics,
],
//! Safe paging is easy to implement.
paging: (event) {
print(event.response);
if (event.count == 3) {
return v2.ForwardPaginationControl.stop();
}
return v2.ForwardPaginationControl.next();
},
);
//! You can serialize & deserialize JSON from response object
//! and model object.
final tweetJson = tweets.data.first.toJson();
final tweet = v2.TweetData.fromJson(tweetJson);
print(tweet);
await twitter.tweets.createLike(
userId: me.data.id,
tweetId: tweets.data.first.id,
);
//! You can upload media such as image, gif and video.
final uploadedMedia = await twitter.media.uploadMedia(
file: File.fromUri(Uri.file('FILE_PATH')),
altText: 'This is alt text.',
//! You can check the upload progress.
onProgress: (event) {
switch (event.state) {
case v2.UploadState.preparing:
print('Upload is preparing...');
break;
case v2.UploadState.inProgress:
print('${event.progress}% completed...');
break;
case v2.UploadState.completed:
print('Upload has completed!');
break;
}
},
onFailed: (error) => print('Upload failed due to "${error.message}"'),
);
//! You can easily post a tweet with the uploaded media.
await twitter.tweets.createTweet(
text: 'Tweet with uploaded media',
media: v2.TweetMediaParam(
mediaIds: [uploadedMedia.data.id],
),
);
//! High-performance Volume Stream endpoint is available.
final sampleStream = await twitter.tweets.connectSampleStream();
await for (final response in sampleStream.stream.handleError(print)) {
print(response);
}
//! Also high-performance Filtered Stream endpoint is available.
await twitter.tweets.createFilteringRules(
rules: [
v2.FilteringRuleParam(value: '#ElonMusk'),
//! You can easily build filtering rule using by "FilteringRule" object.
v2.FilteringRuleParam(
//! => #Tesla has:media
value: v2.FilteringRule.of()
.matchHashtag('Tesla')
.and()
.matchTweetContainsMedia()
.build(),
),
v2.FilteringRuleParam(
//! => (#SpaceX has:media) OR (#SpaceX has:hashtags) sample:50
value: v2.FilteringRule.ofSample(percent: 50)
.group(
v2.FilteringRule.of()
.matchHashtag('SpaceX')
.and()
.matchTweetContainsMedia(),
)
.or()
.group(
v2.FilteringRule.of()
.matchHashtag('SpaceX')
.and()
.matchTweetContainsHashtags(),
)
.build(),
),
],
);
final filteredStream = await twitter.tweets.connectFilteredStream();
await for (final response in filteredStream.stream.handleError(print)) {
print(response.data);
print(response.matchingRules);
}
} on TimeoutException catch (e) {
print(e);
} on v2.UnauthorizedException catch (e) {
print(e);
} on v2.RateLimitExceededException catch (e) {
print(e);
} on v2.DataNotFoundException catch (e) {
print(e);
} on v2.TwitterUploadException catch (e) {
print(e);
} on v2.TwitterException catch (e) {
print(e.response.headers);
print(e.body);
print(e);
}}
Endpoint | Method Name |
---|---|
POST /2/tweets | createTweet |
DELETE /2/tweets/:id | destroyTweet |
Endpoint | Method Name |
---|---|
POST /2/users/:id/retweets | createRetweet |
DELETE /2/users/:id/retweets/:source_tweet_id | destroyRetweet |
GET /2/tweets/:id/retweeted_by | lookupRetweetedUsers |
Endpoint | Method Name |
---|---|
GET /2/tweets/:id/quote_tweets | lookupQuoteTweets |
Endpoint | Method Name |
---|---|
GET /2/tweets/search/all | searchAll |
GET /2/tweets/search/recent | searchRecent |
Endpoint | Method Name |
---|---|
GET /2/tweets | lookupByIds |
GET /2/tweets/:id | lookupById |
Endpoint | Method Name |
---|---|
GET /2/tweets/counts/all | countAll |
GET /2/tweets/counts/recent | countRecent |
Endpoint | Method Name |
---|---|
POST /2/users/:id/bookmarks | createBookmark |
DELETE /2/users/:id/bookmarks/:tweet_id | destroyBookmark |
GET /2/users/:id/bookmarks | lookupBookmarks |
Endpoint | Method Name |
---|---|
GET /2/users/:id/mentions | lookupMentions |
GET /2/users/:id/tweets | lookupTweets |
GET /2/users/:id/timelines/reverse_chronological | lookupHomeTimeline |
Endpoint | Method Name |
---|---|
PUT /2/tweets/:id/hidden | createHiddenReply |
PUT /2/tweets/:id/hidden | destroyHiddenReply |
Endpoint | Method Name |
---|---|
GET /2/tweets/sample/stream | connectSampleStream |
GET /2/tweets/sample10/stream | connectSample10Stream |
Endpoint | Method Name |
---|---|
POST /2/tweets/search/stream/rules | createFilteringRules |
GET /2/tweets/search/stream/rules | lookupFilteringRules |
GET /2/tweets/search/stream | connectFilteredStream |
Endpoint | Method Name |
---|---|
GET /2/users | lookupByIds |
GET /2/users/:id | lookupById |
GET /2/users/by | lookupByNames |
GET /2/users/by/username/:username | lookupByName |
GET /2/users/me | lookupMe |
Endpoint | Method Name |
---|---|
POST /2/users/:id/muting | createMute |
DELETE /2/users/:source_user_id/muting/:target_user_id | destroyMute |
GET /2/users/:id/muting | lookupMutingUsers |
Endpoint | Method Name |
---|---|
POST /2/users/:id/blocking | createBlock |
DELETE /2/users/:source_user_id/blocking/:target_user_id | destroyBlock |
GET /2/users/:id/blocking | lookupBlockingUsers |
Endpoint | Method Name |
---|---|
GET /2/spaces/search | search |
Endpoint | Method Name |
---|---|
GET /2/spaces | lookupByIds |
GET /2/spaces/:id | lookupById |
GET /2/spaces/:id/buyers | lookupBuyers |
GET /2/spaces/:id/tweets | lookupTweets |
GET /2/spaces/by/creator_ids | lookupByCreatorIds |
Endpoint | Method Name |
---|---|
GET /2/lists/:id | lookupById |
GET /2/users/:id/owned_lists | lookupOwnedBy |
Endpoint | Method Name |
---|---|
POST /2/users/:id/pinned_lists | createPinnedList |
DELETE /2/users/:id/pinned_lists/:list_id | destroyPinnedList |
GET /2/users/:id/pinned_lists | lookupPinnedLists |
Endpoint | Method Name |
---|---|
GET /2/lists/:id/tweets | lookupTweets |
Endpoint | Method Name |
---|---|
POST /2/lists | createPublicList |
POST /2/lists | createPrivateList |
DELETE /2/lists/:id | destroyList |
PUT /2/lists/:id | updateListAsPublic |
PUT /2/lists/:id | updateListAsPrivate |
Note
Twitter API v1.1 endpoint is used because Twitter Official does not yet release the Media endpoint for Twitter API v2.0. Therefore, this service may be changed in the future.
Endpoint | Method Name |
---|---|
GET /2/dm_events | lookupEvents |
GET /2/dm_conversations/with/:participant_id/dm_events | lookupEventsWith |
GET /2/dm_conversations/:dm_conversation_id/dm_events | lookupEventsIn |
Note
Twitter API v1.1 endpoint is used because Twitter Official does not yet release the Geo endpoint for Twitter API v2.0. Therefore, this service may be changed in the future.
Endpoint | Method Name |
---|---|
GET /1.1/geo/id/:placeId.json | lookupById |
Endpoint | Method Name |
---|---|
GET /1.1/geo/search.json | searchLocations |
Note
Twitter API v1.1 endpoint is used because Twitter Official does not yet release the Trends endpoint for Twitter API v2.0. Therefore, this service may be changed in the future.
Endpoint | Method Name |
---|---|
GET /1.1/trends/available.json | searchAvailableLocations |
GET /1.1/trends/closest.json | searchClosestLocations |
Endpoint | Method Name |
---|---|
GET /1.1/trends/place.json | lookupTrends |
Endpoint | Method Name |
---|---|
POST /2/compliance/jobs | createJob |
GET /2/compliance/jobs | lookupJobs |
GET /2/compliance/jobs/:id | lookupJob |
twitter_api_v2 uses the following standard prefixes depending on endpoint characteristics. So it's very easy to find the method corresponding to the endpoint you want to use!
Prefix | Description |
---|---|
lookup | This prefix is attached to endpoints that reference tweets, users, etc. |
search | This prefix is attached to endpoints that perform extensive searches. |
connect | This prefix is attached to endpoints with high-performance streaming. |
count | This prefix is attached to the endpoint that counts a particular item. |
create | This prefix is attached to the endpoint performing the create state such as Tweet and Follow . |
destroy | This prefix is attached to the endpoint performing the destroy state such as Tweet and Follow . |
update | This prefix is attached to the endpoint performing the update state. |
upload | This prefix is attached to the endpoint performing the media uploading. |
twitter_api_v2 provides utility to generate/find your app-only bearer token.
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
final bearerToken = await v2.OAuthUtils.generateAppOnlyBearerToken(
consumerKey: 'YOUR_CONSUMER_KEY',
consumerSecret: 'YOUR_CONSUMER_SECRET',
);
print(bearerToken);
}
In this library, parameters that are not required at request time, i.e., optional parameters, are defined as nullable. However, developers do not need to be aware of the null parameter when sending requests when using this library.
It means the parameters specified with a null value are safely removed and ignored before the request is sent.
For example, arguments specified with null are ignored in the following request.
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
final twitter = v2.TwitterApi(bearerToken: 'YOUR_TOKEN_HERE');
await twitter.tweets.createTweet(
text: 'Hello, World!',
// These parameters are ignored at request because they are null.
mediaIds: null,
expansions: null,
);
}
For example, there may be a situation where data contains only an ID, and you want to retrieve the data object associated with that ID as well. In such cases, the Twitter API v2.0
specification called expansions
is useful, and this library supports that specification.
Basically it can be used in endpoints that perform GET communication such as lookup
and search
processing. Some fields may also be included in the includes
property of TwitterResponse.
You can use expansions
like below:
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
final twitter = v2.TwitterApi(bearerToken: 'YOUR_TOKEN_HERE');
try {
final tweets = await twitter.tweets.searchRecent(
query: '#ElonMusk',
// Specify fields you need!
expansions: [
v2.TweetExpansion.authorId,
v2.TweetExpansion.inReplyToUserId,
],
);
print(tweets);
} on v2.TwitterException catch (e) {
print(e);
}
}
You can see more details about expansions
from Official Documentation.
Twitter API v2.0
supports a very interesting specification, allowing users to control the amount of data contained in the response object for each endpoint depending on the situation. It's called fields
, and this library supports this specification.
Basically it can be used in endpoints that perform GET communication such as lookup
and search
processing. Some fields may also be included in the includes
field of TwitterResponse.
You can use fields
like below:
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
final twitter = v2.TwitterApi(bearerToken: 'YOUR_TOKEN_HERE');
try {
final tweets = await twitter.tweets.searchRecent(
query: '#ElonMusk',
maxResults: 20,
expansions: v2.TweetExpansion.values,
tweetFields: [
v2.TweetField.conversationId,
v2.TweetField.publicMetrics,
],
userFields: [
v2.UserField.location,
v2.UserField.publicMetrics,
],
);
print(tweets);
} on v2.TwitterException catch (e) {
print(e);
}
}
Note
Some fields must be combined withexpansions
.
You can see more details about fields
from Official Documentation.
All Twitter API responses obtained using twitter_api_v2 are returned stored in a safe type object. However, there may be cases where the raw JSON returned from the Twitter API is needed when creating applications in combination with other libraries.
In that case, you can use the toJson
method provided by the TwitterResponse
or other model objects to convert it to the raw JSON format returned by the Twitter API.
The JSON converted by this toJson
method is completely equivalent to the JSON returned by the Twitter API and can be deserialized by using the factory constructor fromJson
provided with each model object.
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
final twitter = v2.TwitterApi(bearerToken: 'YOUR_BEARER_TOKEN');
try {
final response = await twitter.tweets.searchRecent(
query: '#ElonMusk',
maxResults: 10,
expansions: v2.TweetExpansion.values,
);
//! You can get raw JSON from this response.
print(response.toJson());
//! Or you can get raw JSON from specific model object.
print(response.data.first.toJson());
} on v2.TwitterException catch (e) {
print(e);
}
}
You could also write like:
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
final twitter = v2.TwitterApi(bearerToken: 'YOUR_BEARER_TOKEN');
try {
final response = await twitter.tweets.searchRecent(
query: '#ElonMusk',
maxResults: 10,
expansions: v2.TweetExpansion.values,
);
//! Serialize & Deserialize
final tweetJson = response.data.first.toJson();
final tweet = v2.TweetData.fromJson(tweetJson);
print(tweet);
} on v2.TwitterException catch (e) {
print(e);
}
}
Twitter API v2.0 supports authentication methods with OAuth 2.0 PKCE, and it allows users of apps using Twitter API v2.0 to request authorization for the minimum necessary scope of operation.
Since OAuth2.0 PKCE authentication requires going through a browser, twitter_api_v2 does not provide this specification for compatibility with CLI applications. Instead, we provide twitter_oauth2_pkce, a library for Flutter apps.
The twitter_oauth2_pkce is 100% compatible with twitter_api_v2 and can be used. You can see more details from links below.
Also, please refer to the next simple sample Flutter application that combines twitter_api_v2 and twitter_oauth2_pkce.
The library specifies a default timeout of 10 seconds for all API communications.
However, there may be times when you wish to specify an arbitrary timeout duration. If there is such a demand, an arbitrary timeout duration can be specified as follows.
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() {
final twitter = v2.TwitterApi(
bearerToken: 'YOUR_TOKEN_HERE',
//! The default timeout is 10 seconds.
timeout: Duration(seconds: 5),
);
}
Due to the nature of this library's communication with external systems, timeouts may occur due to inevitable communication failures or temporary crashes of the server to which requests are sent.
When such timeouts occur, an effective countermeasure in many cases is to send the request again after a certain interval. And twitter_api_v2 provides an automatic retry feature as a solution to this problem.
There are 3 retry methods provided by twitter_api_v2.
Retry Strategy | Constructor | Description |
---|---|---|
Regular Intervals | RetryConfig.ofRegularIntervals | Retry at regular intervals. |
Exponential Backoff | RetryConfig.ofExponentialBackOff | The retry interval is increased exponentially according to the number of retries. |
Exponential Backoff and Jitter | RetryConfig.ofExponentialBackOffAndJitter | A random number called Jitter is added to increase the retry interval exponentially according to the number of retries. |
Also, errors subject to retry are as follows.
- When the status code of the response returned from Twitter is
500
or503
. - When the network is temporarily lost and a
SocketException
is thrown. - When communication times out temporarily and a
TimeoutException
is thrown
It would be easy to imagine retries at regular intervals. For example, if a timeout occurs and the request is assumed to be retried 3 times, waiting for 5 seconds and then sending the request again, it can be defined as follows.
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
final twitter = v2.TwitterApi(
bearerToken: 'YOUR_TOKEN_HERE',
//! Add these lines.
retryConfig: v2.RetryConfig.ofRegularIntervals(
maxAttempts: 3,
intervalInSeconds: 5,
),
);
}
Although retries can be effective by simply performing them at regular intervals as in the above example, sending a large number of requests at regular intervals when the server to which the request is being sent is experiencing a failure is something that should be avoided. Even if the network or server is already down, the retry process can further aggravate the situation by adding to the load.
The solution to these problems is to increase the interval exponentially for each retry. This is an algorithm called Exponential Backoff
and twitter_api_v2 supports a specification that allows easy use of this algorithm.
The Exponential Backoff algorithm can be applied on retries by defining RetryConfig as follows.
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
final twitter = v2.TwitterApi(
bearerToken: 'YOUR_TOKEN_HERE',
//! Add these lines.
retryConfig: v2.RetryConfig.ofExponentialBackOff(
maxAttempts: 3,
),
);
}
In the above implementation, the interval increases exponentially for each retry count. It can be expressed by next formula.
2 ^ retryCount
Although the algorithm introduced earlier that exponentially increases the retry interval is already powerful, some may believe that it is not yet sufficient to distribute the sensation of retries. It's more distributed than equally spaced retries, but retries still occur at static intervals.
This problem can be solved by adding a random number called Jitter, and this method is called the Exponential Backoff and Jitter algorithm. By adding a random number to the exponentially increasing retry interval, the retry interval can be distributed more flexibly.
Similar to the previous example, twitter_api_v2 can be implemented as follows.
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
final twitter = v2.TwitterApi(
bearerToken: 'YOUR_TOKEN_HERE',
//! Add these lines.
retryConfig: v2.RetryConfig.ofExponentialBackOffAndJitter(
maxAttempts: 3,
),
);
}
In the above implementation, the interval increases exponentially for each retry count with jitter. It can be expressed by next formula.
(2 ^ retryCount) + jitter(Random Number between 0 ~ 3)
It would be useful to output logging on retries and a popup notifying the user that a retry has been executed. So twitter_api_v2 provides callbacks that can perform arbitrary processing when retries are executed.
It can be implemented as follows.
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
final twitter = v2.TwitterApi(
bearerToken: 'YOUR_TOKEN_HERE',
retryConfig: v2.RetryConfig.ofRegularIntervals(
maxAttempts: 3,
intervalInSeconds: 5,
//! Add this line.
onExecute: (event) => print('Retrying... ${event.retryCount} times.'),
),
);
}
The RetryEvent passed to the callback contains information on retries.
A boolean value is returned from the endpoint when the communication is primarily POST, DELETE, or PUT.
For example, twitter_api_v2 provides the createRetweet method for creating retweets. This method returns true if the request sent in generating the retweet was accepted, and false if an error occurred for whatever reason.
If true is returned at this time, the reason is as follows.
- The target tweet exists and the target tweet was successfully retweeted.
Conversely, if false is returned, the reasons may be as follows.
- The tweet to be retweeted did not exist.
- The tweet subject to retweet was deleted by the author.
- Miscellaneous errors.
Note that this specification differs from the official Twitter API v2.0. The official Twitter API v2.0 consistently returns a flag indicating the state of the content after API communication when using endpoints that change the state of such content.
However, as mentioned earlier in twitter_api_v2, for example if you use the createRetweet method, it will return a flag indicating whether the process was successful or not. This principle applies not only to the createRetweet method, but to all methods that return flags as a result of processing.
twitter_api_v2 provides a convenient exception object for easy handling of exceptional responses and errors returned from Twitter API v2.0.
Exception | Description |
---|---|
TwitterException | The most basic exception object. For example, it can be used to search for tweets that have already been deleted, etc. |
TwitterUploadException | Thrown when an exception occurs during media upload. |
UnauthorizedException | Thrown when authentication fails with the specified access token. |
RateLimitExceededException | Thrown when the request rate limit is exceeded. |
DataNotFoundException | Thrown when response has no body or data field in body string. |
Also, all of the above exceptions thrown from the twitter_api_v2 process extend TwitterException. This means that you can take all exceptions as TwitterException or handle them as certain exception types, depending on the situation.
However note that, if you receive an individual type exception, be sure to define the process so that the individual exception type is cached before TwitterException. Otherwise, certain type exceptions will also be caught as TwitterException.
Therefore, if you need to catch a specific type of exception in addition to TwitterException, be sure to catch TwitterException in the bottom catch clause as in the following example.
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
final twitter = v2.TwitterApi(bearerToken: 'YOUR_TOKEN_HERE');
try {
final tweets = await twitter.tweets.searchRecent(
query: '#ElonMusk',
maxResults: 20,
);
print(tweets);
} on v2.UnauthorizedException catch (e) {
print(e);
} on v2.RateLimitExceededException catch (e) {
print(e);
} on v2.DataNotFoundException catch (e) {
print(e);
} on v2.TwitterUploadException catch (e) {
print(e);
} on v2.TwitterException catch (e) {
print(e);
}
}
Uploading media on Twitter and sharing it with various people is a very interesting activity. Also, from a business perspective, accompanying tweets with media will attract even more interest from people.
twitter_api_v2 provides strong support for this very high demand specification. All you have to do is prepare the media file to be uploaded and pass it to the methods, and specifically the following methods are available.
Method Name | Description |
---|---|
uploadImage | Low capacity images can be uploaded, such as PNG and GIF. |
uploadMedia | In addition to images, you can upload large media such as video. (Preferable) |
Both methods are very easy to use.
All the difficult uploading process is capsuled, and as noted earlier, all you have to do is simply pass the prepared media file to the method. For example, if you want to upload a large video to Twitter you can implement the following.
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
final twitter = v2.TwitterApi(
bearerToken: 'NO_NEED_BEARER_TOKEN_BECAUSE_IT_IS_V1.1_ENDPOINT',
//! You need to set OAuth 1.0a tokens,
//! because this is the v1.1 endpoint.
oauthTokens: v2.OAuthTokens(
consumerKey: 'YOUR_CONSUMER_KEY_HERE',
consumerSecret: 'YOUR_CONSUMER_SECRET_HERE',
accessToken: 'YOUR_ACCESS_TOKEN_HERE',
accessTokenSecret: 'YOUR_ACCESS_TOKEN_SECRET_HERE',
),
);
try {
final uploadedResponse = await twitter.media.uploadMedia(
file: File.fromUri(
Uri.file('FILE_PATH'),
),
);
print(uploadedResponse);
} on v2.TwitterUploadException catch (e) {
print(e);
}
}
This upload process works very safely, but note that TwitterUploadException will be thrown if media in a format not supported by Twitter is specified.
Note
Also note that the v1.1 endpoint is used to achieve this specification in twitter_api_v2. This is because the official Twitter API v2.0 does not yet support media uploads. While I'm trying to keep the implementation as non-disruptive as possible in the future, there may be disruptive changes when media uploads are supported by the official Twitter API v2.0.
Uploading small images to Twitter does not take long, but uploading large videos takes longer to complete. At that time, it would be very useful if you could show users how far along we are in the uploading process.
twitter_api_v2 supports this specification and can be easily implemented as follows.
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
Future<void> main() async {
final twitter = v2.TwitterApi(
bearerToken: 'NO_NEED_BEARER_TOKEN_BECAUSE_IT_IS_V1.1',
//! You need to set OAuth 1.0a tokens,
//! because this is the v1.1 endpoint.
oauthTokens: v2.OAuthTokens(
consumerKey: 'YOUR_CONSUMER_KEY_HERE',
consumerSecret: 'YOUR_CONSUMER_SECRET_HERE',
accessToken: 'YOUR_ACCESS_TOKEN_HERE',
accessTokenSecret: 'YOUR_ACCESS_TOKEN_SECRET_HERE',
),
);
try {
final uploadedResponse = await twitter.media.uploadMedia(
file: File.fromUri(
Uri.file('FILE_PATH'),
),
//! Add this callback function.
onProgress: (event) {
switch (event.state) {
case v2.UploadState.preparing:
print('Upload is preparing...');
break;
case v2.UploadState.inProgress:
print('${event.progress}% completed...');
break;
case v2.UploadState.completed:
print('Upload has completed!');
break;
}
},
);
print(uploadedResponse);
} on v2.TwitterUploadException catch (e) {
print(e);
}
}
You can add processing when there is upload progress by specifying an onProgress
callback for uploadMedia
, as shown above.
The argument passed to this callback function is an UploadEvent
object, which holds the status and progress rate of the upload at the time the callback function is called.
Importantly, there are 3 upload statuses, which transition from top to bottom during the upload process
Status | Description |
---|---|
preparing | This indicates the start of the media upload process, up to the point where the media to be uploaded is sent to Twitter. The progress rate is always 0 at this point. |
inProgress | This indicates that the media upload process is in progress on Twitter's server. This library polls Twitter's server at regular intervals to obtain the progress rate of media uploads. |
completed | This indicates that the media upload has been successfully completed. |
And the trigger that calls the onProgress
callback is as follows. But if the media upload completes immediately and no polling is required, the inProgress
status will not occur.
- When the upload status becomes
preparing
(Always called once at the start of processing) - When the upload status becomes
inProgress
(Per polling, and it's not called if polling is not required) - When the upload status becomes
completed
(Always called once at the end of processing)
Note that media uploads may also fail for reasons such as broken media. In such cases, TwitterUploadException is always thrown.
Some endpoints in Twitter API v2.0 supports a number of operators for advanced searches, not just keywords
and hashtags
.
For example, if you want to extract tweets with the hashtag "#ElonMusk" and the language of the tweeted content is English, you would create the following rule:
#ElonMusk lang:en
Generating these filtering rules is simple at first glance, but the number of supported operators is so large that it's quite a painstaking task to look at the references for each rule you create. Also, there is a syntax specific to this filtering rule, and typos may occur when entering the rules manually.
So, twitter_api_v2 provides FilteringRule object that allows this filtering rule to be constructed safely and easily.
The previous filtering rule example would look like following with FilteringRule object.
import 'package:twitter_api_v2/twitter_api_v2.dart' as v2;
void main() {
final rule = v2.FilteringRule.of()
.matchHashtag('ElonMusk')
.and()
.matchLanguage(v2.Language.english);
// -> #ElonMusk lang:en
print(rule.build());
}
Okay, the specifications of this feature are difficult to explain in this README
alone. For this reason, I have created a document for the FilteringRule object, which you should also review.
If you would like to contribute to twitter_api_v2, please create an issue or create a Pull Request.
There are many ways to contribute to the OSS. For example, the following subjects can be considered:
- There are request parameters or response fields that are not implemented.
- Documentation is outdated or incomplete.
- Have a better way or idea to achieve the functionality.
- etc...
You can see more details from resources below:
Or you can create a discussion if you like.
Feel free to join this development, diverse opinions make software better!
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!
The simplest way to show us your support is by giving the project a star at GitHub and Pub.dev.
You can also support this project by becoming a sponsor on GitHub:
You can also show on your repository that your app is made with twitter_api_v2 by using one of the following badges:
[![Powered by twitter_api_v2](https://img.shields.io/badge/Powered%20by-twitter_api_v2-00acee.svg)](https://github.com/twitter-dart/twitter-api-v2)
[![Powered by twitter_api_v2](https://img.shields.io/badge/Powered%20by-twitter_api_v2-00acee.svg?style=flat-square)](https://github.com/twitter-dart/twitter-api-v2)
[![Powered by twitter_api_v2](https://img.shields.io/badge/Powered%20by-twitter_api_v2-00acee.svg?style=for-the-badge)](https://github.com/twitter-dart/twitter-api-v2)
All resources of twitter_api_v2 is provided under the BSD-3
license.
Note
License notices in the source are strictly validated based on.github/header-checker-lint.yml
. Please check header-checker-lint.yml for the permitted standards.
twitter_api_v2 was designed and implemented by Kato Shinya (@myConsciousness).