Upgrade guide
With the release of v1 Amplify Flutter now supports Web and Desktop for Auth, API, Analytics, and Storage use cases. Developers can build cross-platform Flutter apps with Amplify that target iOS, Android, Web, and Desktop (macOS, Windows, Linux) using a single codebase.
We have re-written our libraries in Dart. In some places, we have made breaking changes to improve ergonomics or enable features that had been missing from the v0 implementations.
Prerequisites
- A Flutter application targeting Flutter SDK >= 3.3.0 with Amplify libraries integrated
Amplify requires a minimum target platform for iOS (13.0), Android (API level 24), and macOS (10.15). Additional setup is required for some target platforms. Please see the platform setup guide for more details on platform-specific setup.
Platform Setup
The requirements for individual platforms have been modified in some cases. If you haven't done so already, go through the project setup guide to make sure each platform has been configured correctly.
Auth
fetchAuthSession
has several changes. See the example below for how to migrate.Amplify.Auth.getPlugin()
has been added to return plugin-specific subtypes. Casting toCognitoAuthSession
is no longer needed when using this method.- The
getAWSCredentials
flag was previously needed to retrieve AWS credentials. This flag has been deprecated and is no longer needed. AWS credentials will always be included in the response. fetchAuthSession
will no longer throw exceptions. Instead, individual getters of typeAuthResult
will throw if the underlying operation fails.- Why? The previous behavior did not allow for partial successes in offline scenarios when user pool tokens were still valid, but AWS credentials were expired. See amplify-flutter#760 for more details.
CognitoAuthSession.credentials
,CognitoAuthSession.userPoolTokens
,CognitoAuthSession.userSub
, andCognitoAuthSession.identityId
are deprecated and will be removed in a future release. UseCognitoAuthSession.credentialsResult
,CognitoAuthSession.userPoolTokensResult
,CognitoAuthSession.userSubResult
, andCognitoAuthSession.identityIdResult
instead.
1// v02try {3 final session = await Amplify.Auth.fetchAuthSession(4 options: CognitoFetchAuthSessionOptions(getAWSCredentials: true),5 ) as CognitoAuthSession;6
7 final accessToken = session.userPoolTokens?.accessToken;8 safePrint('accessToken: $accessToken');9
10 final identityId = session.identityId;11 safePrint('identityId: $identityId');12} on AuthException catch (e) {13 // fetchAuthSession will fail if any of the underlying operations fail.14 safePrint('The operation failed: ${e.message}');15}
1// v12final session = await Amplify.Auth.getPlugin(3 AmplifyAuthCognito.pluginKey,4).fetchAuthSession();5
6// AuthResult.value will throw if the underlying operation failed.7try {8 final accessToken = session.userPoolTokensResult.value.accessToken.toJson();9 safePrint('accessToken: $accessToken');10} on SignedOutException {11 // the user is not signed in.12} on SessionExpiredException {13 // the users session has expired.14} on NetworkException {15 // the access and/or id token is expired but cannot be refreshed because the16 // user is offline.17}18
19// `AuthResult.valueOrNull` will return the identityId if present or null,20// but will never throw even if the underlying operation failed.21final identityId = session.identityIdResult.valueOrNull;22safePrint('identityId: $identityId');
- The next step returned from auth operations are now enums. See the example below for how to migrate for
Auth.signUp()
.
1// v02final result = await Amplify.Auth.signUp(/* ... */);3switch (result.nextStep?.signUpStep) {4 case 'CONFIRM_SIGN_UP':5 // prompt user to confirm sign in using nextStep.codeDeliveryDetails6 break;7 case 'DONE':8 // sign up is complete, user can be taken to an authenticated state.9 break;10}
1// v12final result = await Amplify.Auth.signUp(/* ... */);3switch (result.nextStep.signUpStep) {4 case AuthSignUpStep.confirmSignUp:5 // prompt user to confirm sign in using nextStep.codeDeliveryDetails6 break;7 case AuthSignUpStep.done:8 // sign up is complete, user can be taken to an authenticated state.9 break;10}
- Auth hub events now have a specific subtype (
AuthHubEvent
) and contain atype
that is an enum. See the example below for how to migrate.
1// v02final hubSubscription = Amplify.Hub.listen([HubChannel.Auth], (hubEvent) {3 switch(hubEvent.eventName) {4 case 'SIGNED_IN':5 safePrint('User is signed in.');6 break;7 case 'SIGNED_OUT':8 safePrint('User is signed out.');9 break;10 case 'SESSION_EXPIRED':11 safePrint('The session has expired.');12 break;13 case 'USER_DELETED':14 safePrint('The user has been deleted.');15 break;16 }17});
1// v12final subscription = Amplify.Hub.listen(HubChannel.Auth, (AuthHubEvent event) {3 switch (event.type) {4 case AuthHubEventType.signedIn:5 safePrint('User is signed in.');6 break;7 case AuthHubEventType.signedOut:8 safePrint('User is signed out.');9 break;10 case AuthHubEventType.sessionExpired:11 safePrint('The session has expired.');12 break;13 case AuthHubEventType.userDeleted:14 safePrint('The user has been deleted.');15 break;16 }17});
- Cognito-specific API options such as
CognitoSignInOptions
are deprecated in favor ofpluginOptions
. The classes will be removed in a future release. See below for an example of how to migrate forAuth.signIn()
.
1// v02final result = await Amplify.Auth.signIn(3 username: username,4 password: password,5 options: const CognitoSignInOptions(6 clientMetadata: {'data': 'value'},7 ),8);
1// v12final result = await Amplify.Auth.signIn(3 username: username,4 password: password,5 options: const SignInOptions(6 pluginOptions: CognitoSignInPluginOptions(7 clientMetadata: {'data': 'value'},8 ),9 ),10);
Social Sign-In (Hosted UI)
Configuration for social sign-in (Hosted UI) varies slightly in v1 compared to v0.
For Android, v0 required the following changes be made to your AndroidManifest.xml
.
1<queries>2 <intent>3 <action android:name=4 "android.support.customtabs.action.CustomTabsService" />5 </intent>6</queries>7<application>8 ...9 <activity10 android:name="com.amplifyframework.auth.cognito.activities.HostedUIRedirectActivity"11 android:exported="true">12 <intent-filter>13 <action android:name="android.intent.action.VIEW" />14 <category android:name="android.intent.category.DEFAULT" />15 <category android:name="android.intent.category.BROWSABLE" />16 <data android:scheme="myapp" />17 </intent-filter>18 </activity>19 ...20</application>
Start by removing this code, then update the AndroidManifest.xml
as follows, replacing myapp
with your custom URI scheme you configured in your backend.
1+<queries>2+ <intent>3+ <action android:name=4+ "android.support.customtabs.action.CustomTabsService" />5+ </intent>6+</queries>7 <application>8 ...9 <activity10 android:name=".MainActivity" android:exported="true">11+ <intent-filter>12+ <action android:name="android.intent.action.VIEW" />13+ <category android:name="android.intent.category.DEFAULT" />14+ <category android:name="android.intent.category.BROWSABLE" />15+ <data android:scheme="myapp" />16+ </intent-filter>17 </activity>18- <activity19- android:name="com.amplifyframework.auth.cognito.activities.HostedUIRedirectActivity"20- android:exported="true">21- <intent-filter>22- <action android:name="android.intent.action.VIEW" />23- <category android:name="android.intent.category.DEFAULT" />24- <category android:name="android.intent.category.BROWSABLE" />25- <data android:scheme="myapp" />26- </intent-filter>27- </activity>28 ...29 </application>
Analytics
AnalyticsProperties
renamed toCustomProperties
AnalyticsUserProfile
renamed toUserProfile
AnalyticsUserProfileLocation
renamed toUserProfileLocation
- In v0, the
autoFlushEventsInterval
inamplifyconfiguration.dart
was read as seconds on iOS and milliseconds on Android. v1 fixes this behavior by readingautoFlushEventsInterval
as seconds across all platforms. - In v1, auto-flushing of events occurs at 30 second intervals. In v0, the default auto flush iOS was 60 seconds and Android was 30 seconds.
- Automatic session reporting behavior has been aligned in v1. For all platforms, backgrounding and foregrounding the app will stop and start a new session. Previously on iOS, the session would pause and then resume.
- You can now save to the
UserAttributes
field of a Pinpoint Endpoint by passing anAWSPinpointUserProfile
toidentifyUser
. - Analytics cached event data is now stored differently. Existing cached analytics events will not be migrated.
- There are now typed
AnalyticsException
s for specific exception cases.
API
- REST API methods have breaking changes. See REST API docs for more details.
RestException
has been replaced withHttpStatusException
.- GraphQL model helpers have a few changes:
- Updated codegen models are required. Please upgrade to the latest version of the Amplify CLI and run
amplify codegen models
. ModelQueries.get()
andModelMutations.deleteById()
have a breaking change where the ID is no longer aString
but aModelIdentifier
that supports custom primary keys. See GraphQL docs for examples.ModelSubscriptions
helpers now take awhere
clause so users can get server-side subscription filters without a custom request.
- Updated codegen models are required. Please upgrade to the latest version of the Amplify CLI and run
- GraphQL subscriptions will attempt to automatically reconnect when the user's device loses and recovers internet access. Updates in connectivity status are available through new hub events on the channel
HubChannel.Api
. - Server-side GraphQL errors on iOS no longer throw an exception, but instead return a
GraphQLResponse
with the errors in the.errors
field (as has always been the case in Android). This applies to all platforms but is only a change for iOS. Note this is only true for errors that come from the AppSync server as part of the server-side GraphQL response. The client will still throw exceptions for cases when a valid response could not be successfully returned. - Exceptions have been made more specific although they still extend abstract class
ApiException
so catch clauses that useApiException
will still be valid.
Storage
- All Storage S3 plugin APIs now return an operation object rather than the result object. The operation object provides more control over the in-flight request, such as cancellation, pause, and resume capabilities (varies by API). The result
Future
can be retrieved via the.result
property of the operation object. Here is an example foruploadFile
:
1// v02final result = await Amplify.Storage.uploadFile(3 local: exampleFile,4 key: 'ExampleKey',5);6print('Uploaded file key: ${result.key}')7
8// v19final result = await Amplify.Storage.uploadFile(10 localFile: exampleFile,11 key: 'ExampleKey',12).result;13print('Uploaded file key: ${result.uploadedItem.key}');
See storage docs for more detailed examples of other storage methods.