-
Notifications
You must be signed in to change notification settings - Fork 137
/
flutter_twitter_login.dart
231 lines (203 loc) · 7.39 KB
/
flutter_twitter_login.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
/// A Flutter plugin for authenticating users by using the native Twitter
/// login SDKs on Android & iOS.
class TwitterLogin {
static const MethodChannel channel =
const MethodChannel('com.roughike/flutter_twitter_login');
/// Creates a new Twitter login instance, with the specified key and secret.
///
/// The [consumerKey] and [consumerSecret] can be obtained from the Twitter
/// apps site at https://apps.twitter.com/, in the "Keys and Access Tokens"
/// tab.
TwitterLogin({
@required this.consumerKey,
@required this.consumerSecret,
})
: assert(consumerKey != null && consumerKey.isNotEmpty,
'Consumer key may not be null or empty.'),
assert(consumerSecret != null && consumerSecret.isNotEmpty,
'Consumer secret may not be null or empty.'),
_keys = {
'consumerKey': consumerKey,
'consumerSecret': consumerSecret,
};
final String consumerKey;
final String consumerSecret;
final Map<String, String> _keys;
/// Returns whether the user is currently logged in or not.
///
/// Convenience method for checking if the [currentSession] is not null.
Future<bool> get isSessionActive async => await currentSession != null;
/// Retrieves the currently active session, if any.
///
/// A common use case for this is logging the user automatically in if they
/// have already logged in before and the session is still active.
///
/// For example:
///
/// ```dart
/// final TwitterSession session = await twitterLogin.currentSession;
///
/// if (session != null) {
/// _fetchTweets(session);
/// } else {
/// _showLoginRequiredUI();
/// }
/// ```
///
/// If the user is not logged in, this returns null.
Future<TwitterSession> get currentSession async {
final Map<dynamic, dynamic> session =
await channel.invokeMethod('getCurrentSession', _keys);
if (session == null) {
return null;
}
return new TwitterSession.fromMap(session.cast<String, dynamic>());
}
/// Logs the user in.
///
/// If the user has a native Twitter client installed, this will present a
/// native login screen. Otherwise a WebView is used.
///
/// The "Callback URL" field must be configured to a valid address in your
/// app's "Settings" tab. When using the Twitter login only on mobile devices,
/// an example of a valid callback url would be http://127.0.0.1:4000.
///
/// Use [TwitterLoginResult.status] for determining if the login was successful
/// or not. For example:
///
/// ```dart
/// var twitterLogin = new TwitterLogin(
/// consumerKey: '<your consumer key>',
/// consumerSecret: '<your consumer secret>',
/// );
///
/// final TwitterLoginResult result = await twitterLogin.authorize();
///
/// switch (result.status) {
/// case TwitterLoginStatus.loggedIn:
/// var session = result.session;
/// _sendTokenAndSecretToServer(session.token, session.secret);
/// break;
/// case TwitterLoginStatus.cancelledByUser:
/// _showCancelMessage();
/// break;
/// case TwitterLoginStatus.error:
/// _showErrorMessage(result.error);
/// break;
/// }
/// ```
///
/// See the [TwitterLoginResult] class for more documentation.
Future<TwitterLoginResult> authorize() async {
final Map<dynamic, dynamic> result =
await channel.invokeMethod('authorize', _keys);
return new TwitterLoginResult._(result.cast<String, dynamic>());
}
/// Logs the currently logged in user out.
Future<void> logOut() async => channel.invokeMethod('logOut', _keys);
}
/// The result when a Twitter login flow has completed.
///
/// To handle this result, first check what the [status] is. If the status
/// equals [TwitterLoginStatus.loggedIn], the login was successful. In this
/// case, the [session] contains all relevant information about the
/// currently logged in user.
class TwitterLoginResult {
/// The status after a Twitter login flow has completed.
///
/// This affects whether the [session] or [error] are available or not.
/// If the user cancelled the login flow, both [session] and [errorMessage]
/// are null.
final TwitterLoginStatus status;
/// Only available when the [status] equals [TwitterLoginStatus.loggedIn],
/// otherwise null.
final TwitterSession session;
/// Only available when the [status] equals [TwitterLoginStatus.error]
/// otherwise null.
final String errorMessage;
TwitterLoginResult._(Map<String, dynamic> map)
: status = _parseStatus(map['status'], map['errorMessage']),
session = map['session'] != null
? new TwitterSession.fromMap(
map['session'].cast<String, dynamic>(),
)
: null,
errorMessage = map['errorMessage'];
static TwitterLoginStatus _parseStatus(String status, String errorMessage) {
switch (status) {
case 'loggedIn':
return TwitterLoginStatus.loggedIn;
case 'error':
// Kind of a hack, but the only way of determining this.
if (errorMessage.contains('canceled') ||
errorMessage.contains('cancelled')) {
return TwitterLoginStatus.cancelledByUser;
}
return TwitterLoginStatus.error;
}
throw new StateError('Invalid status: $status');
}
}
/// The status after a Twitter login flow has completed.
enum TwitterLoginStatus {
/// The login was successful and the user is now logged in.
loggedIn,
/// The user cancelled the login flow, usually by backing out of the dialog.
///
/// This might be unrealiable; see the [_parseStatus] method in TwitterLoginResult.
cancelledByUser,
/// The login flow completed, but for some reason resulted in an error. The
/// user couldn't log in.
error,
}
/// The information about a Twitter user session.
///
/// Includes the token and secret, along with the user's id and name. Both
/// the [token] and [secret] are needed for making authenticated Twitter API
/// calls.
class TwitterSession {
final String secret;
final String token;
/// The user's unique identifier, usually a long series of numbers.
final String userId;
/// The user's Twitter handle.
///
/// For example, if you can visit your Twitter profile by typing the URL
/// http://twitter.com/hello, your Twitter handle (or username) is "hello".
final String username;
/// Constructs a new access token instance from a [Map].
///
/// This is used mostly internally by this library.
TwitterSession.fromMap(Map<String, dynamic> map)
: secret = map['secret'],
token = map['token'],
userId = map['userId'],
username = map['username'];
/// Transforms this access token to a [Map].
///
/// This could be useful for encoding this access token as JSON and then
/// sending it to a server.
Map<String, dynamic> toMap() {
return {
'secret': secret,
'token': token,
'userId': userId,
'username': username,
};
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is TwitterSession &&
runtimeType == other.runtimeType &&
secret == other.secret &&
token == other.token &&
userId == other.userId &&
username == other.username;
@override
int get hashCode =>
secret.hashCode ^ token.hashCode ^ userId.hashCode ^ username.hashCode;
}