Page updated Nov 3, 2023

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 to CognitoAuthSession 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 type AuthResult 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, and CognitoAuthSession.identityId are deprecated and will be removed in a future release. Use CognitoAuthSession.credentialsResult, CognitoAuthSession.userPoolTokensResult, CognitoAuthSession.userSubResult, and CognitoAuthSession.identityIdResult instead.
1// v0
2try {
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// v1
2final 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 the
16 // 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// v0
2final result = await Amplify.Auth.signUp(/* ... */);
3switch (result.nextStep?.signUpStep) {
4 case 'CONFIRM_SIGN_UP':
5 // prompt user to confirm sign in using nextStep.codeDeliveryDetails
6 break;
7 case 'DONE':
8 // sign up is complete, user can be taken to an authenticated state.
9 break;
10}
1// v1
2final result = await Amplify.Auth.signUp(/* ... */);
3switch (result.nextStep.signUpStep) {
4 case AuthSignUpStep.confirmSignUp:
5 // prompt user to confirm sign in using nextStep.codeDeliveryDetails
6 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 a type that is an enum. See the example below for how to migrate.
1// v0
2final 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// v1
2final 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 of pluginOptions. The classes will be removed in a future release. See below for an example of how to migrate for Auth.signIn().
1// v0
2final result = await Amplify.Auth.signIn(
3 username: username,
4 password: password,
5 options: const CognitoSignInOptions(
6 clientMetadata: {'data': 'value'},
7 ),
8);
1// v1
2final 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 <activity
10 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 <activity
10 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- <activity
19- 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>

For iOS, v0 required the following changes be made to your Info.plist.

1<plist version="1.0">
2
3 <dict>
4 <!-- YOUR OTHER PLIST ENTRIES HERE -->
5
6 <key>CFBundleURLTypes</key>
7 <array>
8 <dict>
9 <key>CFBundleURLSchemes</key>
10 <array>
11 <string>myapp</string>
12 </array>
13 </dict>
14 </array>
15
16 <!-- ... -->
17 </dict>

In v1, these changes are no longer required and can be safely removed without any effect on social sign-in (Hosted UI).

1- <key>CFBundleURLTypes</key>
2- <array>
3- <dict>
4- <key>CFBundleURLSchemes</key>
5- <array>
6- <string>myapp</string>
7- </array>
8- </dict>
9- </array>

Web and Desktop platforms were not supported in v0.

Follow the instructions here to configure Web and Desktop platforms for social sign-in (Hosted UI) in v1.

Analytics

  • AnalyticsProperties renamed to CustomProperties
  • AnalyticsUserProfile renamed to UserProfile
  • AnalyticsUserProfileLocation renamed to UserProfileLocation
  • In v0, the autoFlushEventsInterval in amplifyconfiguration.dart was read as seconds on iOS and milliseconds on Android. v1 fixes this behavior by reading autoFlushEventsInterval 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 an AWSPinpointUserProfile to identifyUser.
  • Analytics cached event data is now stored differently. Existing cached analytics events will not be migrated.
  • There are now typed AnalyticsExceptions for specific exception cases.

API

  • REST API methods have breaking changes. See REST API docs for more details.
  • RestException has been replaced with HttpStatusException.
  • 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() and ModelMutations.deleteById() have a breaking change where the ID is no longer a String but a ModelIdentifier that supports custom primary keys. See GraphQL docs for examples.
    • ModelSubscriptions helpers now take a where clause so users can get server-side subscription filters without a custom request.
  • 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 use ApiException 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 for uploadFile:
1// v0
2final result = await Amplify.Storage.uploadFile(
3 local: exampleFile,
4 key: 'ExampleKey',
5);
6print('Uploaded file key: ${result.key}')
7
8// v1
9final 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.