-
-
Notifications
You must be signed in to change notification settings - Fork 207
/
Copy pathsupabase.dart
187 lines (172 loc) · 5.73 KB
/
supabase.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
import 'dart:async';
import 'package:async/async.dart';
import 'package:flutter/foundation.dart';
import 'package:http/http.dart';
import 'package:supabase/supabase.dart';
import 'package:supabase_flutter/src/constants.dart';
import 'package:supabase_flutter/src/flutter_go_true_client_options.dart';
import 'package:supabase_flutter/src/local_storage.dart';
import 'package:supabase_flutter/src/supabase_auth.dart';
/// Supabase instance.
///
/// It must be initialized before used, otherwise an error is thrown.
///
/// ```dart
/// await Supabase.initialize(...)
/// ```
///
/// Use it:
///
/// ```dart
/// final instance = Supabase.instance;
/// ```
///
/// See also:
///
/// * [SupabaseAuth]
class Supabase {
/// Gets the current supabase instance.
///
/// An [AssertionError] is thrown if supabase isn't initialized yet.
/// Call [Supabase.initialize] to initialize it.
static Supabase get instance {
assert(
_instance._initialized,
'You must initialize the supabase instance before calling Supabase.instance',
);
return _instance;
}
/// Initialize the current supabase instance
///
/// This must be called only once. If called more than once, an
/// [AssertionError] is thrown
///
/// [url] and [anonKey] can be found on your Supabase dashboard.
///
/// You can access none public schema by passing different [schema].
///
/// Default headers can be overridden by specifying [headers].
///
/// Pass [localStorage] to override the default local storage option used to
/// persist auth.
///
/// Custom http client can be used by passing [httpClient] parameter.
///
/// [storageRetryAttempts] specifies how many retry attempts there should be
/// to upload a file to Supabase storage when failed due to network
/// interruption.
///
/// Set [authFlowType] to [AuthFlowType.implicit] to use the old implicit flow for authentication
/// involving deep links.
///
/// PKCE flow uses shared preferences for storing the code verifier by default.
/// Pass a custom storage to [pkceAsyncStorage] to override the behavior.
///
/// If [debug] is set to `true`, debug logs will be printed in debug console.
static Future<Supabase> initialize({
required String url,
required String anonKey,
Map<String, String>? headers,
Client? httpClient,
RealtimeClientOptions realtimeClientOptions = const RealtimeClientOptions(),
PostgrestClientOptions postgrestOptions = const PostgrestClientOptions(),
StorageClientOptions storageOptions = const StorageClientOptions(),
FlutterAuthClientOptions authOptions = const FlutterAuthClientOptions(),
Future<String> Function()? accessToken,
bool? debug,
}) async {
assert(
!_instance._initialized,
'This instance is already initialized',
);
if (authOptions.pkceAsyncStorage == null) {
authOptions = authOptions.copyWith(
pkceAsyncStorage: SharedPreferencesGotrueAsyncStorage(),
);
}
if (authOptions.localStorage == null) {
authOptions = authOptions.copyWith(
localStorage: SharedPreferencesLocalStorage(
persistSessionKey:
"sb-${Uri.parse(url).host.split(".").first}-auth-token",
),
);
}
_instance._init(
url,
anonKey,
httpClient: httpClient,
customHeaders: headers,
realtimeClientOptions: realtimeClientOptions,
authOptions: authOptions,
postgrestOptions: postgrestOptions,
storageOptions: storageOptions,
accessToken: accessToken,
);
_instance._debugEnable = debug ?? kDebugMode;
_instance.log('***** Supabase init completed $_instance');
_instance._supabaseAuth = SupabaseAuth();
await _instance._supabaseAuth.initialize(options: authOptions);
// Wrap `recoverSession()` in a `CancelableOperation` so that it can be canceled in dispose
// if still in progress
_instance._restoreSessionCancellableOperation =
CancelableOperation.fromFuture(
_instance._supabaseAuth.recoverSession(),
);
return _instance;
}
Supabase._();
static final Supabase _instance = Supabase._();
bool _initialized = false;
/// The supabase client for this instance
///
/// Throws an error if [Supabase.initialize] was not called.
late SupabaseClient client;
late SupabaseAuth _supabaseAuth;
bool _debugEnable = false;
/// Wraps the `recoverSession()` call so that it can be terminated when `dispose()` is called
late CancelableOperation _restoreSessionCancellableOperation;
/// Dispose the instance to free up resources.
Future<void> dispose() async {
await _restoreSessionCancellableOperation.cancel();
client.dispose();
_instance._supabaseAuth.dispose();
_initialized = false;
}
void _init(
String supabaseUrl,
String supabaseAnonKey, {
Client? httpClient,
Map<String, String>? customHeaders,
required RealtimeClientOptions realtimeClientOptions,
required PostgrestClientOptions postgrestOptions,
required StorageClientOptions storageOptions,
required AuthClientOptions authOptions,
required Future<String> Function()? accessToken,
}) {
final headers = {
...Constants.defaultHeaders,
if (customHeaders != null) ...customHeaders
};
client = SupabaseClient(
supabaseUrl,
supabaseAnonKey,
httpClient: httpClient,
headers: headers,
realtimeClientOptions: realtimeClientOptions,
postgrestOptions: postgrestOptions,
storageOptions: storageOptions,
authOptions: authOptions,
accessToken: accessToken,
);
_initialized = true;
}
void log(String msg, [StackTrace? stackTrace]) {
if (_debugEnable) {
debugPrint(msg);
if (stackTrace != null) {
debugPrintStack(stackTrace: stackTrace);
}
}
}
}