diff --git a/cspell.json b/cspell.json index 74ec0b47c6d..df9de6167e6 100644 --- a/cspell.json +++ b/cspell.json @@ -1126,6 +1126,7 @@ "resolver's", "resourcename", "Resources.S3Bucket.Properties.BucketName", + "respectprimarykeyattributesonconnectionfield", "RESTAPI", "RESTENDPOINT", "resubscription", diff --git a/redirects.json b/redirects.json index fe5e4654c75..9d74a7b3294 100644 --- a/redirects.json +++ b/redirects.json @@ -10178,5 +10178,10 @@ "source": "/gen1/react/reference/cli-commands/", "target": "/gen1/react/tools/cli/commands/", "status": "301" - } + }, + { + "source": "/gen1/flutter/build-a-backend/storage/move/", + "target": "/gen1/flutter/prev/build-a-backend/storage/move/", + "status": "301" + } ] diff --git a/src/components/Breadcrumbs/index.tsx b/src/components/Breadcrumbs/index.tsx index 3af5bfcce09..8b7d0f337d3 100644 --- a/src/components/Breadcrumbs/index.tsx +++ b/src/components/Breadcrumbs/index.tsx @@ -21,7 +21,7 @@ const overrides = { '/gen1/javascript/prev': 'V5', '/gen1/swift/prev': 'V1', '/gen1/android/prev': 'V1', - '/gen1/flutter/prev': 'V0', + '/gen1/flutter/prev': 'V1', '/gen1/react/prev': 'V5', '/gen1/react-native/prev': 'V5', '/gen1/angular/prev': 'V5', diff --git a/src/constants/feature-lists-data.ts b/src/constants/feature-lists-data.ts index f05d3579495..978eaee6a46 100644 --- a/src/constants/feature-lists-data.ts +++ b/src/constants/feature-lists-data.ts @@ -266,12 +266,6 @@ const featureListData = { 'Upload and download files to and from cloud storage with advanced controls like pausing and resuming upload operations.', linkText: 'Upload and Download files', link: 'build-a-backend/storage/upload/' - }, - { - content: - 'Manage content through APIs for listing, accessing, and manipulating files. Set file permission levels, configure automatic events and triggers, and more.', - linkText: 'Advanced file operations and access control', - link: 'build-a-backend/storage/configure-access/' } ], heading: 'Storage' diff --git a/src/data/platforms.ts b/src/data/platforms.ts index 43c15c08edb..9d73bfcb718 100644 --- a/src/data/platforms.ts +++ b/src/data/platforms.ts @@ -49,8 +49,8 @@ export const PLATFORM_VERSIONS = { current: 'v2' }, flutter: { - prev: 'v0', - current: 'v1' + prev: 'v1', + current: 'v2' }, javascript: { prev: 'v5', diff --git a/src/directory/directory.mjs b/src/directory/directory.mjs index d0b275b9e50..bb42f929cb0 100644 --- a/src/directory/directory.mjs +++ b/src/directory/directory.mjs @@ -1284,9 +1284,6 @@ export const directory = { { path: 'src/pages/gen1/[platform]/build-a-backend/storage/copy/index.mdx' }, - { - path: 'src/pages/gen1/[platform]/build-a-backend/storage/move/index.mdx' - }, { path: 'src/pages/gen1/[platform]/build-a-backend/storage/remove/index.mdx' }, @@ -2362,6 +2359,9 @@ export const directory = { { path: 'src/pages/gen1/[platform]/prev/build-a-backend/storage/copy/index.mdx' }, + { + path: 'src/pages/gen1/[platform]/prev/build-a-backend/storage/move/index.mdx' + }, { path: 'src/pages/gen1/[platform]/prev/build-a-backend/storage/remove/index.mdx' }, diff --git a/src/fragments/lib-v1/analytics/flutter/getting-started/10_preReq.mdx b/src/fragments/lib-v1/analytics/flutter/getting-started/10_preReq.mdx index ac8c9802dab..16c98fc9109 100644 --- a/src/fragments/lib-v1/analytics/flutter/getting-started/10_preReq.mdx +++ b/src/fragments/lib-v1/analytics/flutter/getting-started/10_preReq.mdx @@ -1,5 +1,3 @@ -- [Install and configure Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) -- A Flutter application targeting Flutter SDK >= 2.10.0 (stable version) with Amplify libraries integrated - - An iOS configuration targeting at least iOS 11.0 - - An Android configuration targeting at least Android API level 21 (Android 5.0) or above - - For a full example please follow the [project setup walkthrough](/gen1/[platform]/prev/start/project-setup/create-application/) +* [Install and configure Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) + +Amplify Flutter 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](/gen1/[platform]/prev/start/project-setup/platform-setup/) guide for more details on platform specific setup. diff --git a/src/fragments/lib-v1/analytics/flutter/getting-started/20_installLib.mdx b/src/fragments/lib-v1/analytics/flutter/getting-started/20_installLib.mdx index 0e680af9d42..f0406dcf953 100644 --- a/src/fragments/lib-v1/analytics/flutter/getting-started/20_installLib.mdx +++ b/src/fragments/lib-v1/analytics/flutter/getting-started/20_installLib.mdx @@ -5,14 +5,8 @@ In your Flutter project directory, open **pubspec.yaml**. Add Analytics by adding these libraries into your dependencies block: ```yaml -environment: - sdk: '>=2.15.0 <3.0.0' - dependencies: - # Should already be added during Project Setup walkthrough - amplify_flutter: ^0.6.0 - - # Add these lines in `dependencies` if you have not added it earlier during the Project Setup - amplify_auth_cognito: ^0.6.0 - amplify_analytics_pinpoint: ^0.6.0 + amplify_analytics_pinpoint: ^1.0.0 + amplify_auth_cognito: ^1.0.0 + amplify_flutter: ^1.0.0 ``` diff --git a/src/fragments/lib-v1/analytics/flutter/getting-started/30_initAnalytics.mdx b/src/fragments/lib-v1/analytics/flutter/getting-started/30_initAnalytics.mdx index 717a7d8182c..81521c53412 100644 --- a/src/fragments/lib-v1/analytics/flutter/getting-started/30_initAnalytics.mdx +++ b/src/fragments/lib-v1/analytics/flutter/getting-started/30_initAnalytics.mdx @@ -16,6 +16,12 @@ Future _configureAmplify() async { } ``` + + +When running your app on macOS you will need to enable keychain sharing in Xcode, as described in the [Project setup guide](/gen1/[platform]/prev/start/project-setup/platform-setup/#enable-keychain). + + + Make sure that the `amplifyconfiguration.dart` file generated in the project setup is included and sent to `Amplify.configure`: ```dart diff --git a/src/fragments/lib-v1/analytics/flutter/getting-started/40_record.mdx b/src/fragments/lib-v1/analytics/flutter/getting-started/40_record.mdx index 90d1c775b38..454da0f318b 100644 --- a/src/fragments/lib-v1/analytics/flutter/getting-started/40_record.mdx +++ b/src/fragments/lib-v1/analytics/flutter/getting-started/40_record.mdx @@ -6,7 +6,7 @@ To record an event, create an `AnalyticsEvent` and call `Amplify.Analytics.recor Future trackEventsWithProperties() async { final event = AnalyticsEvent('test'); - event.properties + event.customProperties ..addBoolProperty('boolKey', true) ..addDoubleProperty('doubleKey', 10) ..addIntProperty('intKey', 10) diff --git a/src/fragments/lib-v1/analytics/flutter/identifyuser.mdx b/src/fragments/lib-v1/analytics/flutter/identifyuser.mdx index 338718a2f7a..88dba5d80da 100644 --- a/src/fragments/lib-v1/analytics/flutter/identifyuser.mdx +++ b/src/fragments/lib-v1/analytics/flutter/identifyuser.mdx @@ -2,7 +2,7 @@ This call sends information that you have specified about a user to Amazon Pinpo You can get the current user's ID from the Amplify Auth category as shown per the Auth category documentation. Be sure to have it ready before you set it as shown below (Check out the [Authentication Getting Started](/gen1/[platform]/prev/build-a-backend/auth/set-up-auth/) guide for detailed explanation). -If you have asked for location access and received permission, you can also provide that in `AnalyticsUserProfileLocation`. +If you have asked for location access and received permission, you can also provide that in `UserProfileLocation` ```dart Future addAnalyticsWithLocation({ @@ -12,23 +12,21 @@ Future addAnalyticsWithLocation({ required String phoneNumber, required int age, }) async { - final location = AnalyticsUserProfileLocation() - ..latitude = 47.606209 - ..longitude = -122.332069 - ..postalCode = '98122' - ..city = 'Seattle' - ..region = 'WA' - ..country = 'USA'; - - final properties = AnalyticsProperties() - ..addStringProperty('phoneNumber', phoneNumber) - ..addIntProperty('age', age); - - final userProfile = AnalyticsUserProfile() - ..name = name - ..email = email - ..location = location - ..properties = properties; + final userProfile = UserProfile( + name: name, + email: email, + location: const UserProfileLocation( + latitude: 47.606209, + longitude: -122.332069, + postalCode: '98122', + city: 'Seattle', + region: 'WA', + country: 'USA', + ), + customProperties: CustomProperties() + ..addStringProperty('phoneNumber', phoneNumber) + ..addIntProperty('age', age), + ); await Amplify.Analytics.identifyUser( userId: userId, @@ -36,3 +34,11 @@ Future addAnalyticsWithLocation({ ); } ``` + +Sending user information allows you to associate a user to their user profile and activities or actions in your app. The user's actions and attributes can also tracked across devices and platforms by using the same `userId`. + +Some scenarios for identifying a user and their associated app activities are: +* When a user completes app sign up +* When a user completes sign in process +* When a user launches your app +* When a user modifies or updates their user profile diff --git a/src/fragments/lib-v1/analytics/flutter/record.mdx b/src/fragments/lib-v1/analytics/flutter/record.mdx index 3245e07bb2b..a454009f2c9 100644 --- a/src/fragments/lib-v1/analytics/flutter/record.mdx +++ b/src/fragments/lib-v1/analytics/flutter/record.mdx @@ -6,13 +6,13 @@ The Amplify analytics plugin also makes it easy to record custom events within t Future recordCustomEvent() async { final event = AnalyticsEvent('PasswordReset'); - event.properties + event.customProperties ..addStringProperty('Channel', 'SMS') ..addBoolProperty('Successful', true); // You can also add the properties one by one like the following - event.properties.addIntProperty('ProcessDuration', 792); - event.properties.addDoubleProperty('doubleKey', 120.3); + event.customProperties.addIntProperty('ProcessDuration', 792); + event.customProperties.addDoubleProperty('doubleKey', 120.3); await Amplify.Analytics.recordEvent(event: event); } @@ -28,6 +28,33 @@ However, it can take upwards of 30 minutes for the event to display in the Filte ## Flush events +Events have default configuration to flush out to the network every 30 seconds. If you would like to change this, update `amplifyconfiguration.dart` with the value in milliseconds you would like for `autoFlushEventsInterval`. This configuration will flush events every 10 seconds: + +```json +{ + "UserAgent": "aws-amplify-cli/2.0", + "Version": "1.0", + "analytics": { + "plugins": { + "awsPinpointAnalyticsPlugin": { + "pinpointAnalytics": { + "appId": "AppID", + "region": "Region" + }, + "pinpointTargeting": { + "region": "Region" + }, + "autoFlushEventsInterval": 10 + } + } + } +} +``` + +> **Note** +> +> Setting `autoFlushEventsInterval` to 0 will **disable** the automatic flush of events and you will be responsible for submitting them. + To manually flush events, call: ```dart @@ -40,7 +67,7 @@ You can register global properties which will be sent along with all invocations ```dart Future registerGlobalProperties() async { - final properties = AnalyticsProperties() + final properties = CustomProperties() ..addStringProperty('AppStyle', 'DarkMode'); await Amplify.Analytics.registerGlobalProperties( globalProperties: properties, @@ -57,3 +84,11 @@ Future unregisterGlobalProperties() async { ); } ``` + +Furthermore, you can remove all global properties by calling `unregisterGlobalProperties` without `propertyNames`: + +```dart +Future unregisterAllGlobalProperties() async { + await Amplify.Analytics.unregisterGlobalProperties(); +} +``` diff --git a/src/fragments/lib-v1/auth/android/signin/20_confirmSignUp.mdx b/src/fragments/lib-v1/auth/android/signin/20_confirmSignUp.mdx index be6a688b9e2..5a1a8195bbe 100644 --- a/src/fragments/lib-v1/auth/android/signin/20_confirmSignUp.mdx +++ b/src/fragments/lib-v1/auth/android/signin/20_confirmSignUp.mdx @@ -57,3 +57,9 @@ RxAmplify.Auth.confirmSignUp("username", "the code you received via email") + +You will know the sign up flow is complete if you see the following in your console window: + +```console +Confirm signUp succeeded +``` diff --git a/src/fragments/lib-v1/auth/android/signin/30_signIn.mdx b/src/fragments/lib-v1/auth/android/signin/30_signIn.mdx index 7437e3856a8..4d127003641 100644 --- a/src/fragments/lib-v1/auth/android/signin/30_signIn.mdx +++ b/src/fragments/lib-v1/auth/android/signin/30_signIn.mdx @@ -55,3 +55,9 @@ RxAmplify.Auth.signIn("username", "password") + +You will know the sign in flow is complete if you see the following in your console window: + +```console +Sign in succeeded +``` diff --git a/src/fragments/lib-v1/auth/common/device_features/common.mdx b/src/fragments/lib-v1/auth/common/device_features/common.mdx index 576d81a3510..1832be445a6 100644 --- a/src/fragments/lib-v1/auth/common/device_features/common.mdx +++ b/src/fragments/lib-v1/auth/common/device_features/common.mdx @@ -101,4 +101,4 @@ import flutter13 from '/src/fragments/lib-v1/auth/flutter/device_features/40_tra When using the federated OAuth flow with Cognito User Pools, the [device tracking and remembering](https://aws.amazon.com/blogs/mobile/tracking-and-remembering-devices-using-amazon-cognito-your-user-pools/) features are currently not available within the library. - + \ No newline at end of file diff --git a/src/fragments/lib-v1/auth/common/mfa/flows.mdx b/src/fragments/lib-v1/auth/common/mfa/flows.mdx index 3ef933a4821..238e0f8e284 100644 --- a/src/fragments/lib-v1/auth/common/mfa/flows.mdx +++ b/src/fragments/lib-v1/auth/common/mfa/flows.mdx @@ -117,6 +117,27 @@ async function handleSignUp(username, password, phone_number, email) { + +```dart +Future signUpWithPhoneVerification( + String username, + String password, +) async { + await Amplify.Auth.signUp( + username: username, + password: password, + options: SignUpOptions( + userAttributes: { + // ... if required + AuthUserAttributeKey.email: 'test@example.com', + AuthUserAttributeKey.phoneNumber: '+18885551234', + }, + ), + ); +} +``` + + By default, you have to verify a user account after they sign up using the `confirmSignUp` API, which will send a one-time password to the user's phone number or email, depending on your Amazon Cognito configuration. @@ -159,6 +180,20 @@ async function handleSignUpConfirmation(username, confirmationCode) { + +```dart +Future confirmSignUpPhoneVerification( + String username, + String otpCode, +) async { + await Amplify.Auth.confirmSignUp( + username: username, + confirmationCode: otpCode, + ); +} +``` + + ### Handling SMS MFA challenge during Sign In After a user signs in, if they have MFA enabled for their account, a challenge will be returned that you would need to call the `confirmSignIn` API where the user provides their confirmation code sent to their phone number. @@ -200,6 +235,20 @@ async function handleSignIn(username, password) { + +```dart +Future signInWithPhoneVerification( + String username, + String password, +) async { + await Amplify.Auth.signIn( + username: username, + password: password, + ); +} +``` + + If MFA is **ON** or enabled for the user, you must call `confirmSignIn` with the OTP sent to their phone. @@ -240,6 +289,16 @@ async function handleSignInConfirmation(otpCode) { + +```dart +Future confirmSignInPhoneVerification(String otpCode) async { + await Amplify.Auth.confirmSignIn( + confirmationValue: otpCode, + ); +} +``` + + After a user has been signed in, call `updateMFAPreference` to record the MFA type as enabled for the user and optionally set it as preferred so that subsequent logins default to using this MFA type. @@ -258,6 +317,18 @@ async function handleUpdateMFAPreference() { + +```dart +Future updateMfaPreferences() async { + final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); + + await cognitoPlugin.updateMfaPreference( + sms: MfaPreference.enabled, // or .preferred + ); +} +``` + + ## Multi-factor authentication with TOTP You can use Time-based One-Time Password (TOTP) for multi-factor authentication (MFA) in your web or mobile applications. The Amplify Auth category includes support for TOTP setup and verification using authenticator apps, offering an integrated solution and enhanced security for your users. These apps, such as Google Authenticator, Microsoft Authenticator, have the TOTP algorithm built-in and work by using a shared secret key and the current time to generate short-lived, six digit passwords. @@ -350,6 +421,33 @@ function handleSignInNextSteps(output) { + +```dart +Future signInUser(String username, String password) async { + try { + final result = await Amplify.Auth.signIn( + username: username, + password: password, + ); + return _handleSignInResult(result); + } on AuthException catch (e) { + safePrint('Error signing in: ${e.message}'); + } +} + +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + // ··· + case AuthSignInStep.continueSignInWithTotpSetup: + final totpSetupDetails = result.nextStep.totpSetupDetails!; + final setupUri = totpSetupDetails.getSetupUri(appName: 'MyApp'); + safePrint('Open URI to complete setup: $setupUri'); + // ··· + } +} +``` + + The TOTP code can be obtained from the user via a text field or any other means. Once the user provides the TOTP code, call `confirmSignIn` with the TOTP code as the `challengeResponse` parameter. @@ -390,9 +488,25 @@ async function handleSignInConfirmation(totpCode) { + +```dart +Future confirmTotpUser(String totpCode) async { + try { + final result = await Amplify.Auth.confirmSignIn( + confirmationValue: totpCode, + ); + return _handleSignInResult(result); + } on AuthException catch (e) { + safePrint('Error confirming TOTP code: ${e.message}'); + } +} +``` + + + + After a user has been signed in, call `updateMFAPreference` to record the MFA type as enabled for the user and optionally set it as preferred so that subsequent logins default to using this MFA type. - ```ts import { updateMFAPreference } from 'aws-amplify/auth'; @@ -440,6 +554,20 @@ async function handleTOTPSetup() { + +```dart +Future setUpTotp() async { + try { + final totpSetupDetails = await Amplify.Auth.setUpTotp(); + final setupUri = totpSetupDetails.getSetupUri(appName: 'MyApp'); + safePrint('Open URI to complete setup: $setupUri'); + } on AuthException catch (e) { + safePrint('An error occurred setting up TOTP: $e'); + } +} +``` + + Once the Authenticator app is set up, the user must generate a TOTP code and provide it to the library. Pass the code to `verifyTOTPSetup` to complete the TOTP setup process. @@ -480,10 +608,22 @@ async function handleTOTPVerification(totpCode) { -After TOTP setup is complete, call `updateMFAPreference` to record the MFA type as enabled for the user and optionally set it as preferred so that subsequent logins default to using this MFA type. + +```dart +Future verifyTotpSetup(String totpCode) async { + try { + await Amplify.Auth.verifyTotpSetup(totpCode); + } on AuthException catch (e) { + safePrint('An error occurred verifying TOTP: $e'); + } +} +``` + +After TOTP setup is complete, call `updateMFAPreference` to record the MFA type as enabled for the user and optionally set it as preferred so that subsequent logins default to using this MFA type. + ```ts import { updateMFAPreference } from 'aws-amplify/auth'; @@ -532,6 +672,18 @@ async function handleFetchMFAPreference() { + +```dart +Future getCurrentMfaPreference() async { + final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); + + final currentPreference = await cognitoPlugin.fetchMfaPreference(); + safePrint('Enabled MFA types for user: ${currentPreference.enabled}'); + safePrint('Preferred MFA type for user: ${currentPreference.preferred}'); +} +``` + + ### Update the current user's MFA preferences Invoke the following API to update the MFA preference for the current user. @@ -558,7 +710,31 @@ async function handleUpdateMFAPreference() { + +```dart +Future updateMfaPreferences() async { + final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); + + await cognitoPlugin.updateMfaPreference( + sms: MfaPreference.enabled, + totp: MfaPreference.preferred, + ); +} +``` + + + If multiple MFA methods are enabled for the user, the `signIn` API will return `CONTINUE_SIGN_IN_WITH_MFA_SELECTION` as the next step in the auth flow. During this scenario, the user should be prompted to select the MFA method they want to use to sign in and their preference should be passed to `confirmSignIn`. + + + +If multiple MFA methods are enabled for the user, the signIn API will return continueSignInWithMFASelection as the next step in the auth flow. During this scenario, the user should be prompted to select the MFA method they want to use to sign in and their preference should be passed to confirmSignIn. + +The MFA types which are currently supported by Amplify Auth are: + +- `MfaType.sms` +- `MfaType.totp` + @@ -645,3 +821,33 @@ async function handleMFASelection(mfaType) { + + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + // ··· + case AuthSignInStep.continueSignInWithMfaSelection: + final allowedMfaTypes = result.nextStep.allowedMfaTypes!; + final selection = await _promptUserPreference(allowedMfaTypes); + return _handleMfaSelection(selection); + // ··· + } +} + +Future _promptUserPreference(Set allowedTypes) async { + // ··· +} + +Future _handleMfaSelection(MfaType selection) async { + try { + final result = await Amplify.Auth.confirmSignIn( + confirmationValue: selection.confirmationValue, + ); + return _handleSignInResult(result); + } on AuthException catch (e) { + safePrint('Error resending code: ${e.message}'); + } +} +``` + diff --git a/src/fragments/lib-v1/auth/flutter/access_credentials/10_fetchAuthSession.mdx b/src/fragments/lib-v1/auth/flutter/access_credentials/10_fetchAuthSession.mdx index a776ffd5363..b660900491c 100644 --- a/src/fragments/lib-v1/auth/flutter/access_credentials/10_fetchAuthSession.mdx +++ b/src/fragments/lib-v1/auth/flutter/access_credentials/10_fetchAuthSession.mdx @@ -1,33 +1,12 @@ ```dart -Future fetchAuthSession() async { +Future fetchCognitoAuthSession() async { try { - final result = await Amplify.Auth.fetchAuthSession( - options: CognitoSessionOptions(getAWSCredentials: true), - ); - String identityId = (result as CognitoAuthSession).identityId!; - safePrint('identityId: $identityId'); + final cognitoPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); + final result = await cognitoPlugin.fetchAuthSession(); + final identityId = result.identityIdResult.value; + safePrint("Current user's identity ID: $identityId"); } on AuthException catch (e) { - safePrint(e.message); - } -} -``` - -If the `getAWSCredentials` option is true, the result will contain AWS credentials and tokens. If it is set to false, the result will contain a simple `isSignedIn` flag. - -### Setting a timeout for fetchAuthSession - -On spotty networks, the `fetchAuthSession` call can take upwards of a minute to either complete or fail due to internal retries. If this is too long, consider adding a custom timeout using the `timeout` function as shown in the below example. - -```dart -Future fetchAuthSessionWithTimeout() async { - try { - final result = await Amplify.Auth.fetchAuthSession().timeout( - const Duration(seconds: 5), - ); - final identityId = (result as CognitoAuthSession).identityId!; - safePrint('identityId: $identityId'); - } on Exception catch (e) { - safePrint('Something went wrong while fetching the session: $e'); + safePrint('Error retrieving auth session: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/advanced/advanced.mdx b/src/fragments/lib-v1/auth/flutter/advanced/advanced.mdx new file mode 100644 index 00000000000..0b6c999d76f --- /dev/null +++ b/src/fragments/lib-v1/auth/flutter/advanced/advanced.mdx @@ -0,0 +1,81 @@ +## Identity Pool Federation + +With identity federation, you don't need to create custom sign-in code or manage your own user identities. Instead, users of your app can sign in using a well-known external identity +provider (IdP), such as Login with Amazon, Facebook, Google, or any other OpenID Connect (OIDC)-compatible IdP. They can receive an authentication token, and then exchange that token for +temporary security credentials in AWS that map to an IAM role with permissions to use the resources in your AWS account. Using an IdP helps you keep your AWS account secure because you +don't have to embed and distribute long-term security credentials with your application. + +Imagine that you are creating a mobile app that accesses AWS resources, such as a game that runs on a mobile device and stores player and score information using Amazon S3 and DynamoDB. + +When you write such an app, you make requests to AWS services that must be signed with an AWS access key. However, we strongly recommend that you do not embed or distribute long-term +AWS credentials with apps that a user downloads to a device, even in an encrypted store. Instead, build your app so that it requests temporary AWS security credentials dynamically when +needed using identity federation. The supplied temporary credentials map to an AWS role that has only the permissions needed to perform the tasks required by the mobile app. + +You can use `federateToIdentityPool` to get AWS credentials directly from Cognito Federated Identities and not use User Pool federation. If you logged in with `Auth.signIn` you **cannot** +call `federateToIdentityPool` as Amplify will perform this federation automatically for you in the background. In general, you should only call `Auth.federatedSignIn()` when using OAuth flows. + +You can use the escape hatch API `federateToIdentityPool` with a valid token from other social providers. + +```dart +final cognitoPlugin = + Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); +const googleIdToken = 'idToken'; +final session = await cognitoPlugin.federateToIdentityPool( + token: googleIdToken, + provider: AuthProvider.google, +); +``` + + + +Note that when federated, APIs such as `Auth.getCurrentUser` will throw an error as the user is not authenticated with User Pools. + + + +### Retrieve Session + +After federated login, you can retrieve the session using the `Auth.fetchAuthSession` API. + +### Token Refresh + + + +Automatic authentication token refresh is NOT supported when federated. + + + +By default, Amplify will **NOT** automatically refresh the tokens from the federated providers. You will need to handle the token refresh logic and provide the new token to the `federateToIdentityPool` API. + +### Clear Session + +You can clear the federated session using the `clearFederationToIdentityPool` API. + +```dart +final cognitoPlugin = + Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); +await cognitoPlugin.clearFederationToIdentityPool(); +``` + + + +`clearFederationToIdentityPool` will only clear the session from the local cache; the developer needs to handle signing out from the federated identity provider. + + + +### Provide Custom Identity ID + +You can provide a custom identity ID to the `federateToIdentityPool` API. This is useful when you want to use the same identity ID across multiple sessions. + +```dart +final cognitoPlugin = + Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); +const googleIdToken = 'idToken'; +const identityId = 'us-west-2:b4cd4809-7ab1-42e1-b044-07dab9eaa768'; +final session = await cognitoPlugin.federateToIdentityPool( + token: googleIdToken, + provider: AuthProvider.google, + options: FederateToIdentityPoolOptions( + developerProvidedIdentityId: identityId, + ), +); +``` diff --git a/src/fragments/lib-v1/auth/flutter/delete_user/10_delete_user.mdx b/src/fragments/lib-v1/auth/flutter/delete_user/10_delete_user.mdx index fae1d39af5f..85a9fbe9838 100644 --- a/src/fragments/lib-v1/auth/flutter/delete_user/10_delete_user.mdx +++ b/src/fragments/lib-v1/auth/flutter/delete_user/10_delete_user.mdx @@ -2,9 +2,9 @@ Future deleteUser() async { try { await Amplify.Auth.deleteUser(); - print('Delete user succeeded'); - } on Exception catch (e) { - print('Delete user failed with error: $e'); + safePrint('Delete user succeeded'); + } on AuthException catch (e) { + safePrint('Delete user failed with error: $e'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/device_features/10_rememberDevice.mdx b/src/fragments/lib-v1/auth/flutter/device_features/10_rememberDevice.mdx index 43bacb50aa7..bdae2b1fea9 100644 --- a/src/fragments/lib-v1/auth/flutter/device_features/10_rememberDevice.mdx +++ b/src/fragments/lib-v1/auth/flutter/device_features/10_rememberDevice.mdx @@ -2,9 +2,9 @@ Future rememberCurrentDevice() async { try { await Amplify.Auth.rememberDevice(); - print('Remember device succeeded'); - } on Exception catch (e) { - print('Remember device failed with error: $e'); + safePrint('Remember device succeeded'); + } on AuthException catch (e) { + safePrint('Remember device failed with error: $e'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/device_features/20_forgetDevice.mdx b/src/fragments/lib-v1/auth/flutter/device_features/20_forgetDevice.mdx index cdff5fa7419..253fb2c1ed4 100644 --- a/src/fragments/lib-v1/auth/flutter/device_features/20_forgetDevice.mdx +++ b/src/fragments/lib-v1/auth/flutter/device_features/20_forgetDevice.mdx @@ -6,9 +6,9 @@ Future forgetCurrentDevice() async { try { await Amplify.Auth.forgetDevice(); - print('Forget device succeeded'); - } on Exception catch (e) { - print('Forget device failed with error: $e'); + safePrint('Forget device succeeded'); + } on AuthException catch (e) { + safePrint('Forget device failed with error: $e'); } } ``` @@ -17,13 +17,13 @@ Future forgetCurrentDevice() async { ```dart -Future forgetSpecificDevice() async { +// A device that was fetched via Amplify.Auth.fetchDevices() +Future forgetSpecificDevice(AuthDevice registeredDevice) async { try { - const device = CognitoDevice(id: 'us-west-2_38284cea-6c7f-4a8c-bcfa-ac8946a0d1eb'); - await Amplify.Auth.forgetDevice(device); - print('Forget device succeeded'); - } on Exception catch (e) { - print('Forget device failed with error: $e'); + await Amplify.Auth.forgetDevice(registeredDevice); + safePrint('Forget device succeeded'); + } on AuthException catch (e) { + safePrint('Forget device failed with error: $e'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/device_features/30_fetchDevice.mdx b/src/fragments/lib-v1/auth/flutter/device_features/30_fetchDevice.mdx index 2e6bdeabc04..4e8454369f0 100644 --- a/src/fragments/lib-v1/auth/flutter/device_features/30_fetchDevice.mdx +++ b/src/fragments/lib-v1/auth/flutter/device_features/30_fetchDevice.mdx @@ -3,10 +3,10 @@ Future fetchAllDevices() async { try { final devices = await Amplify.Auth.fetchDevices(); for (final device in devices) { - print('Device: $device'); + safePrint('Device: $device'); } - } on Exception catch (e) { - print('Fetch devices failed with error: $e'); + } on AuthException catch (e) { + safePrint('Fetch devices failed with error: $e'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/existing_resources/10_existingResources.mdx b/src/fragments/lib-v1/auth/flutter/existing_resources/10_existingResources.mdx index 382e1c4f8d9..8314a534d1d 100644 --- a/src/fragments/lib-v1/auth/flutter/existing_resources/10_existingResources.mdx +++ b/src/fragments/lib-v1/auth/flutter/existing_resources/10_existingResources.mdx @@ -40,7 +40,7 @@ const amplifyconfig = ''' { "profile", "aws.cognito.signin.user.admin" ] - }, + } } } } diff --git a/src/fragments/lib-v1/auth/flutter/getting_started/10_preReq.mdx b/src/fragments/lib-v1/auth/flutter/getting_started/10_preReq.mdx index 6b9fe0c90c9..c961d8b8d05 100644 --- a/src/fragments/lib-v1/auth/flutter/getting_started/10_preReq.mdx +++ b/src/fragments/lib-v1/auth/flutter/getting_started/10_preReq.mdx @@ -1,8 +1 @@ -A Flutter application targeting Flutter SDK >= 2.10.0 with Amplify libraries integrated. - -The following are also required, depending on which platforms you are targeting: - - * An iOS configuration targeting at least iOS 11.0 - * An Android configuration targeting at least Android API level 21 (Android 5.0) or above - -For a full example please follow the [project setup walkthrough](/gen1/[platform]/prev/start/project-setup/create-application/) +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](/gen1/[platform]/prev/start/project-setup/platform-setup/) guide for more details on platform specific setup. diff --git a/src/fragments/lib-v1/auth/flutter/getting_started/20_installLib.mdx b/src/fragments/lib-v1/auth/flutter/getting_started/20_installLib.mdx index 99f37ef9869..b631e11c889 100644 --- a/src/fragments/lib-v1/auth/flutter/getting_started/20_installLib.mdx +++ b/src/fragments/lib-v1/auth/flutter/getting_started/20_installLib.mdx @@ -1,5 +1,10 @@ Add the following dependency to your **app**'s `pubspec.yaml` along with others you added above in **Prerequisites**: -import flutter0 from "/src/fragments/lib-v1/project-setup/flutter/create-application/60_dependencies.mdx"; +```yaml +dependencies: + flutter: + sdk: flutter - \ No newline at end of file + amplify_flutter: ^1.0.0 + amplify_auth_cognito: ^1.0.0 +``` diff --git a/src/fragments/lib-v1/auth/flutter/getting_started/30_initAuth.mdx b/src/fragments/lib-v1/auth/flutter/getting_started/30_initAuth.mdx index be239633988..2107fbafe41 100644 --- a/src/fragments/lib-v1/auth/flutter/getting_started/30_initAuth.mdx +++ b/src/fragments/lib-v1/auth/flutter/getting_started/30_initAuth.mdx @@ -14,7 +14,7 @@ void main() { } class MyApp extends StatefulWidget { - const MyApp({Key? key}) : super(key: key); + const MyApp({super.key}); @override State createState() => _MyAppState(); diff --git a/src/fragments/lib-v1/auth/flutter/hub_events/10_listen_events.mdx b/src/fragments/lib-v1/auth/flutter/hub_events/10_listen_events.mdx index 905211e0aa4..8362777570c 100644 --- a/src/fragments/lib-v1/auth/flutter/hub_events/10_listen_events.mdx +++ b/src/fragments/lib-v1/auth/flutter/hub_events/10_listen_events.mdx @@ -1,20 +1,17 @@ ```dart -// Do not forget to import the following for StreamSubscription -import 'dart:async'; - -StreamSubscription hubSubscription = Amplify.Hub.listen([HubChannel.Auth], (hubEvent) { - switch(hubEvent.eventName) { - case 'SIGNED_IN': - print('USER IS SIGNED IN'); +final subscription = Amplify.Hub.listen(HubChannel.Auth, (AuthHubEvent event) { + switch (event.type) { + case AuthHubEventType.signedIn: + safePrint('User is signed in.'); break; - case 'SIGNED_OUT': - print('USER IS SIGNED OUT'); + case AuthHubEventType.signedOut: + safePrint('User is signed out.'); break; - case 'SESSION_EXPIRED': - print('SESSION HAS EXPIRED'); + case AuthHubEventType.sessionExpired: + safePrint('The session has expired.'); break; - case 'USER_DELETED': - print('USER HAS BEEN DELETED'); + case AuthHubEventType.userDeleted: + safePrint('The user has been deleted.'); break; } }); diff --git a/src/fragments/lib-v1/auth/flutter/managing_credentials/10_managing_credentials.mdx b/src/fragments/lib-v1/auth/flutter/managing_credentials/10_managing_credentials.mdx index c9cc9af28bf..48045383969 100644 --- a/src/fragments/lib-v1/auth/flutter/managing_credentials/10_managing_credentials.mdx +++ b/src/fragments/lib-v1/auth/flutter/managing_credentials/10_managing_credentials.mdx @@ -1 +1,57 @@ -Amplify Flutter uses the underlying storage mechanisms used by amplify-android (EncryptedSharedPreferences) and amplify-ios (Keychain), and does not offer customization. +Amplify Flutter securely manages credentials and user identity information. You do not need to store, refresh, or delete credentials yourself. Amplify Flutter stores auth data on the device using platform capabilities such as [Keychain Services](https://developer.apple.com/documentation/security/keychain_services/) on iOS and macOS and [EncryptedSharedPreferences](https://developer.android.com/reference/androidx/security/crypto/EncryptedSharedPreferences) on Android. + + + +Amplify will refresh the [Access Token](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-access-token.html) and [ID Token](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.html) as long as the [Refresh Token](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-refresh-token.html) is valid. Once the Refresh token expires, the user will need to reauthenticate to obtain a new one. + + + +Some platform specific option can be customized with the out of the box options. In the example below, credentials will be stored in-memory on Web instead of the default behavior of using browser storage. + +```dart +await Amplify.addPlugin( + AmplifyAuthCognito( + secureStorageFactory: AmplifySecureStorage.factoryFrom( + webOptions: WebSecureStorageOptions( + persistenceOption: WebPersistenceOption.inMemory, + ), + ), + ), +); +``` + +If you would like further customization, you can provide your own factory for creating `SecureStorageInterface` instances to `AmplifyAuthCognito`. The example below shows the use of a custom implementation that stores data in-memory on all platforms. + +```dart +await Amplify.addPlugin( + AmplifyAuthCognito(secureStorageFactory: InMemoryStorage.new), +); +``` + +```dart +class InMemoryStorage implements SecureStorageInterface { + InMemoryStorage(this.scope); + + /// The scope of the item being stored. + /// + /// This can be used as a namespace for stored items. + final AmplifySecureStorageScope scope; + + static final Map _data = {}; + + @override + void write({required String key, required String value}) { + _data['${scope.name}.$key'] = value; + } + + @override + String? read({required String key}) { + return _data['${scope.name}.$key']; + } + + @override + void delete({required String key}) { + _data.remove('${scope.name}.$key'); + } +} +``` diff --git a/src/fragments/lib-v1/auth/flutter/password_management/10_reset_password.mdx b/src/fragments/lib-v1/auth/flutter/password_management/10_reset_password.mdx index a08f59ec500..79d9cff5ef1 100644 --- a/src/fragments/lib-v1/auth/flutter/password_management/10_reset_password.mdx +++ b/src/fragments/lib-v1/auth/flutter/password_management/10_reset_password.mdx @@ -1,19 +1,26 @@ ```dart -// Create this value on the class level to use as a state -bool isPasswordReset = false; - -... - -Future resetPassword() async { +Future resetPassword(String username) async { try { final result = await Amplify.Auth.resetPassword( - username: 'myusername', + username: username, ); - setState(() { - isPasswordReset = result.isPasswordReset; - }); - } on AmplifyException catch (e) { - safePrint(e); + await _handleResetPasswordResult(result); + } on AuthException catch (e) { + safePrint('Error resetting password: ${e.message}'); } } ``` + +```dart +Future _handleResetPasswordResult(ResetPasswordResult result) async { + switch (result.nextStep.updateStep) { + case AuthResetPasswordStep.confirmResetPasswordWithCode: + final codeDeliveryDetails = result.nextStep.codeDeliveryDetails!; + _handleCodeDelivery(codeDeliveryDetails); + break; + case AuthResetPasswordStep.done: + safePrint('Successfully reset password'); + break; + } +} +``` \ No newline at end of file diff --git a/src/fragments/lib-v1/auth/flutter/password_management/20_confirm_reset_password.mdx b/src/fragments/lib-v1/auth/flutter/password_management/20_confirm_reset_password.mdx index 2beed0fbf3e..c3731cd3831 100644 --- a/src/fragments/lib-v1/auth/flutter/password_management/20_confirm_reset_password.mdx +++ b/src/fragments/lib-v1/auth/flutter/password_management/20_confirm_reset_password.mdx @@ -1,13 +1,18 @@ ```dart -Future confirmResetPassword() async { +Future confirmResetPassword({ + required String username, + required String newPassword, + required String confirmationCode, +}) async { try { - await Amplify.Auth.confirmResetPassword( - username: 'myusername', - newPassword: 'mynewpassword', - confirmationCode: '123456' + final result = await Amplify.Auth.confirmResetPassword( + username: username, + newPassword: newPassword, + confirmationCode: confirmationCode, ); - } on AmplifyException catch (e) { - print(e); + safePrint('Password reset complete: ${result.isPasswordReset}'); + } on AuthException catch (e) { + safePrint('Error resetting password: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/password_management/30_change_password.mdx b/src/fragments/lib-v1/auth/flutter/password_management/30_change_password.mdx index 0aae7f1d7ba..8546d7ad6e1 100644 --- a/src/fragments/lib-v1/auth/flutter/password_management/30_change_password.mdx +++ b/src/fragments/lib-v1/auth/flutter/password_management/30_change_password.mdx @@ -1,12 +1,15 @@ ```dart -Future updatePassword() async { +Future updatePassword({ + required String oldPassword, + required String newPassword, +}) async { try { await Amplify.Auth.updatePassword( - newPassword: 'mynewpassword', - oldPassword: 'myoldpassword' + oldPassword: oldPassword, + newPassword: newPassword, ); - } on AmplifyException catch (e) { - print(e); + } on AuthException catch (e) { + safePrint('Error updating password: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/signin/10_signUp.mdx b/src/fragments/lib-v1/auth/flutter/signin/10_signUp.mdx index ef09c6ca96f..da7834a291e 100644 --- a/src/fragments/lib-v1/auth/flutter/signin/10_signUp.mdx +++ b/src/fragments/lib-v1/auth/flutter/signin/10_signUp.mdx @@ -1,27 +1,49 @@ ```dart -// Create a boolean for checking the sign up status -bool isSignUpComplete = false; - -... - -Future signUpUser() async { +/// Signs a user up with a username, password, and email. The required +/// attributes may be different depending on your app's configuration. +Future signUpUser({ + required String username, + required String password, + required String email, + String? phoneNumber, +}) async { try { - final userAttributes = { - CognitoUserAttributeKey.email: 'email@domain.com', - CognitoUserAttributeKey.phoneNumber: '+15559101234', + final userAttributes = { + AuthUserAttributeKey.email: email, + if (phoneNumber != null) AuthUserAttributeKey.phoneNumber: phoneNumber, // additional attributes as needed }; final result = await Amplify.Auth.signUp( - username: 'myusername', - password: 'mysupersecurepassword', - options: CognitoSignUpOptions(userAttributes: userAttributes), + username: username, + password: password, + options: SignUpOptions( + userAttributes: userAttributes, + ), ); - setState(() { - isSignUpComplete = result.isSignUpComplete; - }); + await _handleSignUpResult(result); } on AuthException catch (e) { - safePrint(e.message); + safePrint('Error signing up user: ${e.message}'); } } ``` +```dart +Future _handleSignUpResult(SignUpResult result) async { + switch (result.nextStep.signUpStep) { + case AuthSignUpStep.confirmSignUp: + final codeDeliveryDetails = result.nextStep.codeDeliveryDetails!; + _handleCodeDelivery(codeDeliveryDetails); + break; + case AuthSignUpStep.done: + safePrint('Sign up is complete'); + break; + } +} + +void _handleCodeDelivery(AuthCodeDeliveryDetails codeDeliveryDetails) { + safePrint( + 'A confirmation code has been sent to ${codeDeliveryDetails.destination}. ' + 'Please check your ${codeDeliveryDetails.deliveryMedium.name} for the code.', + ); +} +``` \ No newline at end of file diff --git a/src/fragments/lib-v1/auth/flutter/signin/20_confirmSignUp.mdx b/src/fragments/lib-v1/auth/flutter/signin/20_confirmSignUp.mdx index 573a72529a8..6e4d4f2d37b 100644 --- a/src/fragments/lib-v1/auth/flutter/signin/20_confirmSignUp.mdx +++ b/src/fragments/lib-v1/auth/flutter/signin/20_confirmSignUp.mdx @@ -1,23 +1,18 @@ ```dart -// Use the boolean created before -bool isSignUpComplete = false; - -... - -Future confirmUser() async { +Future confirmUser({ + required String username, + required String confirmationCode, +}) async { try { final result = await Amplify.Auth.confirmSignUp( - username: 'myusername', - confirmationCode: '123456' + username: username, + confirmationCode: confirmationCode, ); - - setState(() { - isSignUpComplete = result.isSignUpComplete; - }); - + // Check if further confirmations are needed or if + // the sign up is complete. + await _handleSignUpResult(result); } on AuthException catch (e) { - safePrint(e.message); + safePrint('Error confirming user: ${e.message}'); } } ``` - diff --git a/src/fragments/lib-v1/auth/flutter/signin/30_signIn.mdx b/src/fragments/lib-v1/auth/flutter/signin/30_signIn.mdx index a8a1a9c6201..df1312d3e9a 100644 --- a/src/fragments/lib-v1/auth/flutter/signin/30_signIn.mdx +++ b/src/fragments/lib-v1/auth/flutter/signin/30_signIn.mdx @@ -1,25 +1,65 @@ -```dart -// Create a boolean for checking the sign in status -bool isSignedIn = false; + + +Please note that you will be prevented from successfully calling `signIn` if a user has already signed in and a valid session is active. You must first call `signOut` to remove the original session. + + +```dart Future signInUser(String username, String password) async { try { final result = await Amplify.Auth.signIn( username: username, password: password, ); - - setState(() { - isSignedIn = result.isSignedIn; - }); - + await _handleSignInResult(result); } on AuthException catch (e) { - safePrint(e.message); + safePrint('Error signing in: ${e.message}'); } } ``` - -Please note that you will be prevented from successfully calling `signIn` if a user has already signed in and a valid session is active. You must first call `signOut` to remove the original session. When running on the iOS platform, you will be able to call `signIn` if the session has expired, while on Android you must first call `signOut` regardless. - +Depending on your configuration and how the user signed up, one or more confirmations will be necessary. +Use the `SignInResult` returned from `Amplify.Auth.signIn` to check the next step for signing in. When +the value is `done`, the user has successfully signed in. + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + case AuthSignInStep.confirmSignInWithSmsMfaCode: + final codeDeliveryDetails = result.nextStep.codeDeliveryDetails!; + _handleCodeDelivery(codeDeliveryDetails); + break; + case AuthSignInStep.confirmSignInWithNewPassword: + safePrint('Enter a new password to continue signing in'); + break; + case AuthSignInStep.confirmSignInWithCustomChallenge: + final parameters = result.nextStep.additionalInfo; + final prompt = parameters['prompt']!; + safePrint(prompt); + break; + case AuthSignInStep.resetPassword: + final resetResult = await Amplify.Auth.resetPassword( + username: username, + ); + await _handleResetPasswordResult(resetResult); + break; + case AuthSignInStep.confirmSignUp: + // Resend the sign up code to the registered device. + final resendResult = await Amplify.Auth.resendSignUpCode( + username: username, + ); + _handleCodeDelivery(resendResult.codeDeliveryDetails); + break; + case AuthSignInStep.done: + safePrint('Sign in is complete'); + break; + } +} +void _handleCodeDelivery(AuthCodeDeliveryDetails codeDeliveryDetails) { + safePrint( + 'A confirmation code has been sent to ${codeDeliveryDetails.destination}. ' + 'Please check your ${codeDeliveryDetails.deliveryMedium.name} for the code.', + ); +} +``` diff --git a/src/fragments/lib-v1/auth/flutter/signin/40_multi_factor_signup.mdx b/src/fragments/lib-v1/auth/flutter/signin/40_multi_factor_signup.mdx index 5b440c2848f..6d6c1075c1c 100644 --- a/src/fragments/lib-v1/auth/flutter/signin/40_multi_factor_signup.mdx +++ b/src/fragments/lib-v1/auth/flutter/signin/40_multi_factor_signup.mdx @@ -1,29 +1,19 @@ ```dart -// Create a boolean for checking the sign up status -bool isSignUpComplete = false; - -... - Future setUpMFASignUp() async { try { - final userAttributes = { - CognitoUserAttributeKey.email: 'email@domain.com', + final userAttributes = { + AuthUserAttributeKey.email: 'email@domain.com', // Note: phone_number requires country code - CognitoUserAttributeKey.phoneNumber: '+15559101234', + AuthUserAttributeKey.phoneNumber: '+15559101234', }; final result = await Amplify.Auth.signUp( - username: 'myusername', - password: 'mysupersecurepassword', - options: CognitoSignUpOptions( - userAttributes: userAttributes - ) + username: 'myusername', + password: 'mysupersecurepassword', + options: SignUpOptions(userAttributes: userAttributes), ); - setState(() { - isSignUpComplete = result.isSignUpComplete; - }); + await _handleSignUpResult(result); } on AuthException catch (e) { - safePrint(e.message); + safePrint('Error signing up: ${e.message}'); } } ``` - diff --git a/src/fragments/lib-v1/auth/flutter/signin/50_multi_factor_confirm_signin.mdx b/src/fragments/lib-v1/auth/flutter/signin/50_multi_factor_confirm_signin.mdx index 52b0ff5c0bc..5758a46842f 100644 --- a/src/fragments/lib-v1/auth/flutter/signin/50_multi_factor_confirm_signin.mdx +++ b/src/fragments/lib-v1/auth/flutter/signin/50_multi_factor_confirm_signin.mdx @@ -1,19 +1,12 @@ ```dart -// Use the boolean created before -bool isSignUpComplete = false; - -... - Future confirmMFAUser() async { try { final result = await Amplify.Auth.confirmSignIn( confirmationValue: '123456', ); - setState(() { - isSignedIn = result.isSignedIn; - }); + await _handleSignInResult(result); } on AuthException catch (e) { - safePrint(e.message); + safePrint('Error confirming sign in: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/signin/60_runtime_auth_flow.mdx b/src/fragments/lib-v1/auth/flutter/signin/60_runtime_auth_flow.mdx index 2ce54c66406..efcfecbfa0b 100644 --- a/src/fragments/lib-v1/auth/flutter/signin/60_runtime_auth_flow.mdx +++ b/src/fragments/lib-v1/auth/flutter/signin/60_runtime_auth_flow.mdx @@ -1,17 +1,22 @@ -### Switching authentication flow at runtime +### Switching authentication flow at runtime -By default, the [`authenticationFlowType`](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html) value specified in your `amplifyconfiguration.dart` will be used when authenticating with Cognito. You can change the default behavior at runtime with `CognitoSignInOptions`: +By default, the [`authenticationFlowType`](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html) value specified in your `amplifyconfiguration.dart` will be used when authenticating with Cognito. You can change the default behavior at runtime with `CognitoSignInPluginOptions`: ```dart -Future signInUser(String username, String password) async { +Future signInCustom(String username, String password) async { try { final result = await Amplify.Auth.signIn( username: username, password: password, - options: CognitoSignInOptions(authFlowType: AuthenticationFlowType.customAuth), + options: const SignInOptions( + pluginOptions: CognitoSignInPluginOptions( + authFlowType: AuthenticationFlowType.customAuthWithSrp, + ), + ), ); + await _handleSignInResult(result); } on AuthException catch (e) { - safePrint(e.message); + safePrint('Error signing in: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/signin_next_steps/100_totp_setup.mdx b/src/fragments/lib-v1/auth/flutter/signin_next_steps/100_totp_setup.mdx new file mode 100644 index 00000000000..50232e21a50 --- /dev/null +++ b/src/fragments/lib-v1/auth/flutter/signin_next_steps/100_totp_setup.mdx @@ -0,0 +1,29 @@ +If the next step is `continueSignInWithTOTPSetup`, then the user must provide a TOTP code to complete the sign in process. The step returns an associated value of type `TOTPSetupDetails` which would be used for generating TOTP. `TOTPSetupDetails` provides a helper method called `getSetupURI` that can be used to generate a URI, which can be used by native password managers for TOTP association. For example. if the URI is used on Apple platforms, it will trigger the platform's native password manager to associate TOTP with the account. For more advanced use cases, `TOTPSetupDetails` also contains the `sharedSecret` that will be used to either generate a QR code or can be manually entered into an authenticator app. + +Once the authenticator app is set up, the user can generate a TOTP code and provide it to the library to complete the sign in process. + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + // ··· + case AuthSignInStep.continueSignInWithTotpSetup: + final totpSetupDetails = result.nextStep.totpSetupDetails!; + final setupUri = totpSetupDetails.getSetupUri(appName: 'MyApp'); + safePrint('Open URI to complete setup: $setupUri'); + // ··· + } +} + +// Then, pass the TOTP code to `confirmSignIn` + +Future confirmTotpUser(String totpCode) async { + try { + final result = await Amplify.Auth.confirmSignIn( + confirmationValue: totpCode, + ); + return _handleSignInResult(result); + } on AuthException catch (e) { + safePrint('Error confirming TOTP code: ${e.message}'); + } +} +``` diff --git a/src/fragments/lib-v1/auth/flutter/signin_next_steps/10_signin.mdx b/src/fragments/lib-v1/auth/flutter/signin_next_steps/10_signin.mdx new file mode 100644 index 00000000000..c8ad0b53aba --- /dev/null +++ b/src/fragments/lib-v1/auth/flutter/signin_next_steps/10_signin.mdx @@ -0,0 +1,50 @@ + +The `Amplify.Auth.signIn` API returns a `SignInResult` object which indicates whether the sign-in flow is +complete or whether additional steps are required before the user is signed in. + +To see if additional signin steps are required, inspect the sign in result's `nextStep.signInStep` property. +- If the sign-in step is `done`, the flow is complete and the user is signed in. +- If the sign-in step is not `done`, one or more additional steps are required. These are explained in detail below. + + + + +The `signInStep` property is an enum of type `AuthSignInStep`. Depending on its value, your code should take one of the actions mentioned on this page. + + + +```dart +Future signInWithCognito( + String username, + String password, +) async { + final SignInResult result = await Amplify.Auth.signIn( + username: username, + password: password, + ); + return _handleSignInResult(result); +} + +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + case AuthSignInStep.continueSignInWithMfaSelection: + // Handle select from MFA methods case + case AuthSignInStep.continueSignInWithTotpSetup: + // Handle TOTP setup case + case AuthSignInStep.confirmSignInWithTotpMfaCode: + // Handle TOTP MFA case + case AuthSignInStep.confirmSignInWithSmsMfaCode: + // Handle SMS MFA case + case AuthSignInStep.confirmSignInWithNewPassword: + // Handle new password case + case AuthSignInStep.confirmSignInWithCustomChallenge: + // Handle custom challenge case + case AuthSignInStep.resetPassword: + // Handle reset password case + case AuthSignInStep.confirmSignUp: + // Handle confirm sign up case + case AuthSignInStep.done: + safePrint('Sign in is complete'); + } +} +``` diff --git a/src/fragments/lib-v1/auth/flutter/signin_next_steps/20_confirm_sms_mfa.mdx b/src/fragments/lib-v1/auth/flutter/signin_next_steps/20_confirm_sms_mfa.mdx new file mode 100644 index 00000000000..e0c715be144 --- /dev/null +++ b/src/fragments/lib-v1/auth/flutter/signin_next_steps/20_confirm_sms_mfa.mdx @@ -0,0 +1,40 @@ +If the next step is `confirmSignInWithSmsMfaCode`, Amplify Auth has sent the user a random code over SMS and is waiting for the user to verify that code. +To handle this step, your app's UI must prompt the user to enter the code. After the user enters the code, pass the value to the `confirmSignIn` API. + + + +The result includes an `AuthCodeDeliveryDetails` member. It includes additional information about the code delivery, such as the partial phone number of +the SMS recipient, which can be used to prompt the user on where to look for the code. + + + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + case AuthSignInStep.confirmSignInWithSmsMfaCode: + final codeDeliveryDetails = result.nextStep.codeDeliveryDetails!; + _handleCodeDelivery(codeDeliveryDetails); + // ... + } +} + +void _handleCodeDelivery(AuthCodeDeliveryDetails codeDeliveryDetails) { + safePrint( + 'A confirmation code has been sent to ${codeDeliveryDetails.destination}. ' + 'Please check your ${codeDeliveryDetails.deliveryMedium.name} for the code.', + ); +} +``` + +```dart +Future confirmMfaUser(String mfaCode) async { + try { + final result = await Amplify.Auth.confirmSignIn( + confirmationValue: mfaCode, + ); + return _handleSignInResult(result); + } on AuthException catch (e) { + safePrint('Error confirming MFA code: ${e.message}'); + } +} +``` diff --git a/src/fragments/lib-v1/auth/flutter/signin_next_steps/30_confirm_custom_challenge.mdx b/src/fragments/lib-v1/auth/flutter/signin_next_steps/30_confirm_custom_challenge.mdx new file mode 100644 index 00000000000..0d8ac9517b2 --- /dev/null +++ b/src/fragments/lib-v1/auth/flutter/signin_next_steps/30_confirm_custom_challenge.mdx @@ -0,0 +1,39 @@ +If the next step is `confirmSignInWithCustomChallenge`, Amplify Auth is awaiting completion of a custom authentication challenge. The challenge is based on the Lambda trigger you configured as part of a [custom sign in flow](/gen1/[platform]/prev/build-a-backend/auth/sign-in-custom-flow/). + +For example, your custom challenge Lambda may pass a prompt to the frontend which requires the user to enter a secret code. + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + // ... + case AuthSignInStep.confirmSignInWithCustomChallenge: + final parameters = result.nextStep.additionalInfo; + final hint = parameters['hint']!; + safePrint(hint); // "Enter the secret code" + // ... + } +} +``` + +To complete this step, you should prompt the user for the custom challenge answer, and pass the answer to the `confirmSignIn` API. + +```dart +Future confirmCustomChallenge(String answer) async { + try { + final result = await Amplify.Auth.confirmSignIn( + confirmationValue: answer, + ); + return _handleSignInResult(result); + } on AuthException catch (e) { + safePrint('Error confirming custom challenge: ${e.message}'); + } +} +``` + + + +Special Handling on confirmSignIn + +If `failAuthentication=true` is returned by the Lambda, Cognito will invalidate the session of the request. This is represented by a `NotAuthorizedException` and requires restarting the sign-in flow by calling `Amplify.Auth.signIn` again. + + diff --git a/src/fragments/lib-v1/auth/flutter/signin_next_steps/40_confirm_new_password.mdx b/src/fragments/lib-v1/auth/flutter/signin_next_steps/40_confirm_new_password.mdx new file mode 100644 index 00000000000..deea8d90bc2 --- /dev/null +++ b/src/fragments/lib-v1/auth/flutter/signin_next_steps/40_confirm_new_password.mdx @@ -0,0 +1,27 @@ +If the next step is `confirmSignInWithNewPassword`, Amplify Auth requires the user choose a new password they proceeding with the sign in. + +Prompt the user for a new password and pass it to the `confirmSignIn` API. + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + // ... + case AuthSignInStep.confirmSignInWithNewPassword: + safePrint('Please enter a new password'); + // ... + } +} +``` + +```dart +Future confirmNewPassword(String newPassword) async { + try { + final result = await Amplify.Auth.confirmSignIn( + confirmationValue: newPassword, + ); + return _handleSignInResult(result); + } on AuthException catch (e) { + safePrint('Error confirming new password: ${e.message}'); + } +} +``` \ No newline at end of file diff --git a/src/fragments/lib-v1/auth/flutter/signin_next_steps/50_reset_password.mdx b/src/fragments/lib-v1/auth/flutter/signin_next_steps/50_reset_password.mdx new file mode 100644 index 00000000000..2430f319473 --- /dev/null +++ b/src/fragments/lib-v1/auth/flutter/signin_next_steps/50_reset_password.mdx @@ -0,0 +1,36 @@ +If the next step is `resetPassword`, Amplify Auth requires that the user reset their password before proceeding. +Use the `resetPassword` API to guide the user through resetting their password, then call `Amplify.Auth.signIn` +when that's complete to restart the sign-in flow. + +See the [reset password](/gen1/[platform]/prev/build-a-backend/auth/manage-passwords/) docs for more information. + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + // ... + case AuthSignInStep.resetPassword: + final resetResult = await Amplify.Auth.resetPassword( + username: username, + ); + await _handleResetPasswordResult(resetResult); + // ... + } +} + +Future _handleResetPasswordResult(ResetPasswordResult result) async { + switch (result.nextStep.updateStep) { + case AuthResetPasswordStep.confirmResetPasswordWithCode: + final codeDeliveryDetails = result.nextStep.codeDeliveryDetails!; + _handleCodeDelivery(codeDeliveryDetails); + case AuthResetPasswordStep.done: + safePrint('Successfully reset password'); + } +} + +void _handleCodeDelivery(AuthCodeDeliveryDetails codeDeliveryDetails) { + safePrint( + 'A confirmation code has been sent to ${codeDeliveryDetails.destination}. ' + 'Please check your ${codeDeliveryDetails.deliveryMedium.name} for the code.', + ); +} +``` diff --git a/src/fragments/lib-v1/auth/flutter/signin_next_steps/60_confirm_signup.mdx b/src/fragments/lib-v1/auth/flutter/signin_next_steps/60_confirm_signup.mdx new file mode 100644 index 00000000000..4d2ef6a204b --- /dev/null +++ b/src/fragments/lib-v1/auth/flutter/signin_next_steps/60_confirm_signup.mdx @@ -0,0 +1,53 @@ +If the next step is `resetPassword`, Amplify Auth requires that the user confirm their email or phone number before proceeding. +Use the `resendSignUpCode` API to send a new sign up code to the registered email or phone number, followed by `confirmSignUp` +to complete the sign up. + +See the [confirm sign up](/gen1/[platform]/prev/build-a-backend/auth/enable-sign-in/#register-a-user) docs for more information. + + + +The result includes an `AuthCodeDeliveryDetails` member. It includes additional information about the code delivery, such as the partial phone number of +the SMS recipient, which can be used to prompt the user on where to look for the code. + + + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + // ... + case AuthSignInStep.confirmSignUp: + // Resend the sign up code to the registered device. + final resendResult = await Amplify.Auth.resendSignUpCode( + username: username, + ); + _handleCodeDelivery(resendResult.codeDeliveryDetails); + // ... + } +} + +void _handleCodeDelivery(AuthCodeDeliveryDetails codeDeliveryDetails) { + safePrint( + 'A confirmation code has been sent to ${codeDeliveryDetails.destination}. ' + 'Please check your ${codeDeliveryDetails.deliveryMedium.name} for the code.', + ); +} +``` + +```dart +Future confirmSignUp({ + required String username, + required String confirmationCode, +}) async { + try { + await Amplify.Auth.confirmSignUp( + username: username, + confirmationCode: confirmationCode, + ); + } on AuthException catch (e) { + safePrint('Error confirming sign up: ${e.message}'); + } +} +``` + +Once the sign up is confirmed, call `Amplify.Auth.signIn` again to restart the sign-in flow. + diff --git a/src/fragments/lib-v1/auth/flutter/signin_next_steps/70_done.mdx b/src/fragments/lib-v1/auth/flutter/signin_next_steps/70_done.mdx new file mode 100644 index 00000000000..4456ec4a727 --- /dev/null +++ b/src/fragments/lib-v1/auth/flutter/signin_next_steps/70_done.mdx @@ -0,0 +1,13 @@ +The sign-in flow is complete when the next step is `done`, which means the user is successfully authenticated. +As a convenience, the `SignInResult` also provides the `isSignedIn` property, which will be true if the next step is `done`. + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + // ... + case AuthSignInStep.done: + // Could also check that `result.isSignedIn` is `true` + safePrint('Sign in is complete'); + } +} +``` diff --git a/src/fragments/lib-v1/auth/flutter/signin_next_steps/80_totp.mdx b/src/fragments/lib-v1/auth/flutter/signin_next_steps/80_totp.mdx new file mode 100644 index 00000000000..7ecf9e396a8 --- /dev/null +++ b/src/fragments/lib-v1/auth/flutter/signin_next_steps/80_totp.mdx @@ -0,0 +1,27 @@ +If the next step is `confirmSignInWithTOTPCode`, you should prompt the user to enter the TOTP code from their associated authenticator app during set up. The code is a six-digit number that changes every 30 seconds. The user must enter the code before the 30-second window expires. + +After the user enters the code, your implementation must pass the value to Amplify Auth `confirmSignIn` API. + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + // ··· + case AuthSignInStep.confirmSignInWithTotpMfaCode: + safePrint('Enter a one-time code from your registered authenticator app'); + // ··· + } +} + +// Then, pass the TOTP code to `confirmSignIn` + +Future confirmTotpUser(String totpCode) async { + try { + final result = await Amplify.Auth.confirmSignIn( + confirmationValue: totpCode, + ); + return _handleSignInResult(result); + } on AuthException catch (e) { + safePrint('Error confirming TOTP code: ${e.message}'); + } +} +``` diff --git a/src/fragments/lib-v1/auth/flutter/signin_next_steps/90_mfa_selection.mdx b/src/fragments/lib-v1/auth/flutter/signin_next_steps/90_mfa_selection.mdx new file mode 100644 index 00000000000..334fea10d16 --- /dev/null +++ b/src/fragments/lib-v1/auth/flutter/signin_next_steps/90_mfa_selection.mdx @@ -0,0 +1,34 @@ +If the next step is `continueSignInWithMFASelection`, the user must select the MFA method to use. Amplify Auth currently only supports SMS and TOTP as MFA methods. After the user selects an MFA method, your implementation must pass the selected MFA method to Amplify Auth using `confirmSignIn` API. + +The MFA types which are currently supported by Amplify Auth are: + +- `MfaType.sms` +- `MfaType.totp` + +```dart +Future _handleSignInResult(SignInResult result) async { + switch (result.nextStep.signInStep) { + // ··· + case AuthSignInStep.continueSignInWithMfaSelection: + final allowedMfaTypes = result.nextStep.allowedMfaTypes!; + final selection = await _promptUserPreference(allowedMfaTypes); + return _handleMfaSelection(selection); + // ··· + } +} + +Future _promptUserPreference(Set allowedTypes) async { + // ··· +} + +Future _handleMfaSelection(MfaType selection) async { + try { + final result = await Amplify.Auth.confirmSignIn( + confirmationValue: selection.confirmationValue, + ); + return _handleSignInResult(result); + } on AuthException catch (e) { + safePrint('Error resending code: ${e.message}'); + } +} +``` diff --git a/src/fragments/lib-v1/auth/flutter/signin_web_ui/10_cli_setup.mdx b/src/fragments/lib-v1/auth/flutter/signin_web_ui/10_cli_setup.mdx index c62883075d7..d82b75a9ac0 100644 --- a/src/fragments/lib-v1/auth/flutter/signin_web_ui/10_cli_setup.mdx +++ b/src/fragments/lib-v1/auth/flutter/signin_web_ui/10_cli_setup.mdx @@ -11,14 +11,26 @@ In terminal, navigate to your project, run `amplify add auth` (or if you've alre `(default)` ? Enter your redirect signin URI: `myapp://` +? Do you want to add another redirect signin URI + `Yes` +? Enter your redirect signin URI: + `http://localhost:3000/` ? Do you want to add another redirect signin URI `No` ? Enter your redirect signout URI: `myapp://` +? Do you want to add another redirect signout URI + `Yes` +? Enter your redirect signout URI: + `http://localhost:3000/` ? Do you want to add another redirect signout URI `No` ? Select the social providers you want to configure for your user pool: `` ``` -Once finished, run `amplify push` to publish your changes. +Note that when the CLI asks for the redirect URIs that you want to use, you may need to enter multiple: + +* Desktop apps require an http localhost (ex: http://localhost:3000) +* Web requires an http localhost or https URI (ex: http://localhost:3000 or https://www.yourapp.com) +* Mobile requires a custom app URI (ex: myapp://) diff --git a/src/fragments/lib-v1/auth/flutter/signin_web_ui/20_platform_specific_setup.mdx b/src/fragments/lib-v1/auth/flutter/signin_web_ui/20_platform_specific_setup.mdx index e431bfba9fc..4d35985db64 100644 --- a/src/fragments/lib-v1/auth/flutter/signin_web_ui/20_platform_specific_setup.mdx +++ b/src/fragments/lib-v1/auth/flutter/signin_web_ui/20_platform_specific_setup.mdx @@ -4,10 +4,15 @@ Sign-in with web UI will display the sign-in UI inside a webview. After the sign ## Platform Setup -

Android

+### Web -Add the following `activity` and `queries` tag to the `AndroidManifest.xml` file in your app's `android/app/src/main` directory, -replacing `myapp` with your redirect URI prefix if necessary. Note that this differs from the amplify-flutter stable release, so you will need to make these changes even if you are transitioning an existing project. +To use Hosted UI in your Flutter web application locally, you must run the app with the `--web-port=3000` argument (with the value being whichever port you assigned to localhost host when configuring your redirect URIs). + +### Android + +Add the following `queries` element to the `AndroidManifest.xml` file in your app's `android/app/src/main` directory, as well as the following `intent-filter` to the `MainActivity` in the same file. + +Replace `myapp` with your redirect URI scheme as necessary: ```xml @@ -19,53 +24,24 @@ replacing `myapp` with your redirect URI prefix if necessary. Note that this dif ... - - - - - - + android:name=".MainActivity" android:exported="true"> + + + + + + ... ``` -In order to use the `` element cited below, you may need to upgrade the Android gradle plugin version in your `build.gradle` file to one of the versions specified below: +### macOS -| Your plugin version | Upgrade version | -| :--------------------- | ------------------: | -| 4.1.x + | N/A | -| 4.0.x | 4.0.1 | -| 3.6.x | 3.6.4 | -| 3.5.x | 3.5.4 | -| 3.4.x | 3.4.3 | -| 3.3.x | 3.3.3 | +Open XCode and enable the App Sandbox capability and then select "Incoming Connections (Server)" under "Network". +![Incoming Connections setting selected in the App Sandbox section of the runner signing and capabilities tab.](/images/project-setup/flutter/mac/xcode-entitlements.png) -

iOS

+### iOS, Windows and Linux -Add the following entry to the URL scheme in the `Info.plist` file in your app's `ios/Runner` directory. Replace `myapp` with the "redirect signin URI" you provided to the CLI: - -```xml - - - - - - - - CFBundleURLTypes - - - CFBundleURLSchemes - - myapp - - - - - - -``` +No specific platform configuration is required. diff --git a/src/fragments/lib-v1/auth/flutter/signin_web_ui/30_signin.mdx b/src/fragments/lib-v1/auth/flutter/signin_web_ui/30_signin.mdx index 52656fa5120..eaa6b732ccc 100644 --- a/src/fragments/lib-v1/auth/flutter/signin_web_ui/30_signin.mdx +++ b/src/fragments/lib-v1/auth/flutter/signin_web_ui/30_signin.mdx @@ -4,9 +4,9 @@ Sweet! You're now ready to launch sign in with web UI. Future signInWithWebUI() async { try { final result = await Amplify.Auth.signInWithWebUI(); - print('Result: $result'); + safePrint('Sign in result: $result'); } on AuthException catch (e) { - print(e.message); + safePrint('Error signing in: ${e.message}'); } } ``` @@ -16,10 +16,12 @@ You can also specify a provider with the `provider` attribute: ```dart Future signInWithWebUIProvider() async { try { - final result = await Amplify.Auth.signInWithWebUI(provider: AuthProvider.google); - print('Result: $result'); + final result = await Amplify.Auth.signInWithWebUI( + provider: AuthProvider.google, + ); + safePrint('Result: $result'); } on AuthException catch (e) { - print(e.message); + safePrint('Error signing in: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/signin_web_ui/40_private_session.mdx b/src/fragments/lib-v1/auth/flutter/signin_web_ui/40_private_session.mdx index cd58f358b47..a66d24945ee 100644 --- a/src/fragments/lib-v1/auth/flutter/signin_web_ui/40_private_session.mdx +++ b/src/fragments/lib-v1/auth/flutter/signin_web_ui/40_private_session.mdx @@ -1,15 +1,17 @@ ### Prefer private session during signIn on iOS. -Amplify Flutter uses the amplify-ios library on the iOS platform to facilitate Web UI sign in and other Auth functionality. See the [amplify-ios Web UI documentation](/gen1/[platform]/prev/build-a-backend/auth/sign-in-with-web-ui/#prefer-private-session-during-signin) for details on how amplify-ios manages the interaction between the application and the Web UI. +Amplify.Auth.signInWithWebUI uses [ASWebAuthenticationSession](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession) internally for iOS. ASWebAuthenticationSession has a property, [prefersEphemeralWebBrowserSession](https://developer.apple.com/documentation/authenticationservices/aswebauthenticationsession/3237231-prefersephemeralwebbrowsersessio) which can be used to indicate whether the session should ask the browser for a private authentication session. To set this flag to true, set `preferPrivateSession` to true using `CognitoSignInWithWebUIPluginOptions`. -As noted in the amplify-ios documentation, it is possible to use a private session when calling `Auth.signInWithWebUI`. This will bypass the permissions dialog that is displayed to the end user, although it will also prevent reuse of existing sessions from the user's browser. For example, if the user is logged into Google in their browser and try to sign in using Google in your app, they would now need to re-enter their credentials. +This will bypass the permissions dialog that is displayed to the end user during sign in and sign out. However, it will also prevent reuse of existing sessions from the user's browser. For example, if the user is logged into Google in their browser and try to sign in using Google in your app, they would now need to re-enter their credentials. ```dart Future signInWithWebUIAndPrivateSession() async { await Amplify.Auth.signInWithWebUI( - options: const CognitoSignInWithWebUIOptions( - isPreferPrivateSession: preferPrivateSession - ) + options: const SignInWithWebUIOptions( + pluginOptions: CognitoSignInWithWebUIPluginOptions( + isPreferPrivateSession: true, + ), + ), ); } ``` diff --git a/src/fragments/lib-v1/auth/flutter/signin_with_custom_flow/20_signup.mdx b/src/fragments/lib-v1/auth/flutter/signin_with_custom_flow/20_signup.mdx index 249e3ffd97c..34216c0abe2 100644 --- a/src/fragments/lib-v1/auth/flutter/signin_with_custom_flow/20_signup.mdx +++ b/src/fragments/lib-v1/auth/flutter/signin_with_custom_flow/20_signup.mdx @@ -1,28 +1,21 @@ Because authentication flows in Cognito can be switched via your configuration, it is still required that users register with a password. ```dart -// Create a boolean for checking the sign up status -bool isSignUpComplete = false; - -... - Future signUpCustomFlow() async { try { - final userAttributes = { - CognitoUserAttributeKey.email: 'email@domain.com', - CognitoUserAttributeKey.phoneNumber: '+15559101234', + final userAttributes = { + AuthUserAttributeKey.email: 'email@domain.com', + AuthUserAttributeKey.phoneNumber: '+15559101234', // additional attributes as needed }; final result = await Amplify.Auth.signUp( username: 'myusername', password: 'mysupersecurepassword', - options: CognitoSignUpOptions(userAttributes: userAttributes), + options: SignUpOptions(userAttributes: userAttributes), ); - setState(() { - isSignUpComplete = result.isSignUpComplete; - }); + safePrint('Sign up result: $result'); } on AuthException catch (e) { - safePrint(e.message); + safePrint('Error signing up: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/signin_with_custom_flow/30_signin.mdx b/src/fragments/lib-v1/auth/flutter/signin_with_custom_flow/30_signin.mdx index 3c578822af4..46a9d82aad3 100644 --- a/src/fragments/lib-v1/auth/flutter/signin_with_custom_flow/30_signin.mdx +++ b/src/fragments/lib-v1/auth/flutter/signin_with_custom_flow/30_signin.mdx @@ -1,28 +1,26 @@ ```dart -// Create a boolean for checking the sign in status and keep the status +// Create state variables for the sign in status bool isSignedIn = false; String? challengeHint; -... - Future signInCustomFlow(String username) async { try { final result = await Amplify.Auth.signIn(username: username); setState(() { isSignedIn = result.isSignedIn; // Get the publicChallengeParameters from your Create Auth Challenge Lambda - challengeHint = result.nextStep?.additionalInfo?['hint']; + challengeHint = result.nextStep.additionalInfo['hint']; }); } on AuthException catch (e) { - safePrint(e.message); + safePrint('Error signing in: ${e.message}'); } } ``` - Please note that you will be prevented from successfully calling `signIn` if a - user has already signed in and a valid session is active. You must first call - `signOut` to remove the original session. When running on the iOS platform, - you will be able to call `signIn` if the session has expired, while on Android - you must first call `signOut` regardless. + +Please note that you will be prevented from successfully calling `signIn` if a +user has already signed in and a valid session is active. You must first call +`signOut` to remove the original session. + diff --git a/src/fragments/lib-v1/auth/flutter/signin_with_custom_flow/40_custom_challenge.mdx b/src/fragments/lib-v1/auth/flutter/signin_with_custom_flow/40_custom_challenge.mdx index bc53bf7306f..4bfbd82ef9f 100644 --- a/src/fragments/lib-v1/auth/flutter/signin_with_custom_flow/40_custom_challenge.mdx +++ b/src/fragments/lib-v1/auth/flutter/signin_with_custom_flow/40_custom_challenge.mdx @@ -5,11 +5,18 @@ Future confirmSignIn(String generatedNumber) async { /// Enter the random number generated by your Create Auth Challenge trigger confirmationValue: generatedNumber, ); - print('Result: $result'); + safePrint('Sign in result: $result'); } on AuthException catch (e) { - print(e.message); + safePrint('Error signing in: ${e.message}'); } } ``` Once the user provides the correct response, they should be authenticated in your application. + + + +Special Handling on ConfirmSignIn + +During a `confirmSignIn` call, if `failAuthentication: true` is returned by the Lambda, the session of the request gets invalidated by Cognito, and a `NotAuthorizedException` is thrown. To recover, the user must initiate a new sign in by calling `Amplify.Auth.signIn`. + diff --git a/src/fragments/lib-v1/auth/flutter/signout/10_local_signout.mdx b/src/fragments/lib-v1/auth/flutter/signout/10_local_signout.mdx index 7bddd5b2a70..1a3d7aef1ce 100644 --- a/src/fragments/lib-v1/auth/flutter/signout/10_local_signout.mdx +++ b/src/fragments/lib-v1/auth/flutter/signout/10_local_signout.mdx @@ -1,9 +1,10 @@ ```dart Future signOutCurrentUser() async { - try { - await Amplify.Auth.signOut(); - } on AuthException catch (e) { - print(e.message); + final result = await Amplify.Auth.signOut(); + if (result is CognitoCompleteSignOut) { + safePrint('Sign out completed successfully'); + } else if (result is CognitoFailedSignOut) { + safePrint('Error signing user out: ${result.exception.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/signout/20_global_signout.mdx b/src/fragments/lib-v1/auth/flutter/signout/20_global_signout.mdx index 77357b292bd..477b89dce01 100644 --- a/src/fragments/lib-v1/auth/flutter/signout/20_global_signout.mdx +++ b/src/fragments/lib-v1/auth/flutter/signout/20_global_signout.mdx @@ -1,9 +1,18 @@ ```dart -Future signOutCurrentUserGlobally() async { - try { - await Amplify.Auth.signOut(options: SignOutOptions(globalSignOut: true)); - } on AmplifyException catch (e) { - print(e.message); +Future signOutGlobally() async { + final result = await Amplify.Auth.signOut( + options: const SignOutOptions(globalSignOut: true), + ); + if (result is CognitoCompleteSignOut) { + safePrint('Sign out completed successfully'); + } else if (result is CognitoPartialSignOut) { + final globalSignOutException = result.globalSignOutException!; + final accessToken = globalSignOutException.accessToken; + // Retry the global sign out using the access token, if desired + // ... + safePrint('Error signing user out: ${globalSignOutException.message}'); + } else if (result is CognitoFailedSignOut) { + safePrint('Error signing user out: ${result.exception.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/sms/confirm_sign_in.mdx b/src/fragments/lib-v1/auth/flutter/sms/confirm_sign_in.mdx index b5040e7062b..c62d34b2b00 100644 --- a/src/fragments/lib-v1/auth/flutter/sms/confirm_sign_in.mdx +++ b/src/fragments/lib-v1/auth/flutter/sms/confirm_sign_in.mdx @@ -1,7 +1,7 @@ ```dart Future confirmSignInPhoneVerification(String otpCode) async { await Amplify.Auth.confirmSignIn( - confirmationValue: otp, + confirmationValue: otpCode, ); } ``` diff --git a/src/fragments/lib-v1/auth/flutter/sms/confirm_sign_up.mdx b/src/fragments/lib-v1/auth/flutter/sms/confirm_sign_up.mdx index 87eecc30f69..f066c947b04 100644 --- a/src/fragments/lib-v1/auth/flutter/sms/confirm_sign_up.mdx +++ b/src/fragments/lib-v1/auth/flutter/sms/confirm_sign_up.mdx @@ -1,5 +1,8 @@ ```dart -Future confirmSignUpPhoneVerification(String username, String otpCode) async { +Future confirmSignUpPhoneVerification( + String username, + String otpCode, +) async { await Amplify.Auth.confirmSignUp( username: username, confirmationCode: otpCode, diff --git a/src/fragments/lib-v1/auth/flutter/sms/sign_up.mdx b/src/fragments/lib-v1/auth/flutter/sms/sign_up.mdx index 843e8915950..5b308e02076 100644 --- a/src/fragments/lib-v1/auth/flutter/sms/sign_up.mdx +++ b/src/fragments/lib-v1/auth/flutter/sms/sign_up.mdx @@ -6,11 +6,11 @@ Future signUpWithPhoneVerification( await Amplify.Auth.signUp( username: username, password: password, - options: CognitoSignUpOptions( - userAttributes: { + options: SignUpOptions( + userAttributes: { // ... if required - CognitoUserAttributeKey.email: 'test@example.com', - CognitoUserAttributeKey.phoneNumber: '+18885551234', + AuthUserAttributeKey.email: 'test@example.com', + AuthUserAttributeKey.phoneNumber: '+18885551234', }, ), ); diff --git a/src/fragments/lib-v1/auth/flutter/social_signin_web_ui/20_signin.mdx b/src/fragments/lib-v1/auth/flutter/social_signin_web_ui/20_signin.mdx index b09e78b4d07..5e3296a8a4a 100644 --- a/src/fragments/lib-v1/auth/flutter/social_signin_web_ui/20_signin.mdx +++ b/src/fragments/lib-v1/auth/flutter/social_signin_web_ui/20_signin.mdx @@ -1,10 +1,12 @@ ```dart -Future signInWithSocialWebUI() async { +Future socialSignIn() async { try { - final result = await Amplify.Auth.signInWithWebUI(provider: AuthProvider.facebook); - print('Result: $result'); - } on AmplifyException catch (e) { - print(e.message); + final result = await Amplify.Auth.signInWithWebUI( + provider: AuthProvider.google, + ); + safePrint('Sign in result: $result'); + } on AuthException catch (e) { + safePrint('Error signing in: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/user_attributes/10_fetch_attributes.mdx b/src/fragments/lib-v1/auth/flutter/user_attributes/10_fetch_attributes.mdx index 57f40dfc1b8..a51c1de93c3 100644 --- a/src/fragments/lib-v1/auth/flutter/user_attributes/10_fetch_attributes.mdx +++ b/src/fragments/lib-v1/auth/flutter/user_attributes/10_fetch_attributes.mdx @@ -3,10 +3,10 @@ Future fetchCurrentUserAttributes() async { try { final result = await Amplify.Auth.fetchUserAttributes(); for (final element in result) { - print('key: ${element.userAttributeKey}; value: ${element.value}'); + safePrint('key: ${element.userAttributeKey}; value: ${element.value}'); } } on AuthException catch (e) { - print(e.message); + safePrint('Error fetching user attributes: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/user_attributes/20_update_user_attribute.mdx b/src/fragments/lib-v1/auth/flutter/user_attributes/20_update_user_attribute.mdx index f490dd150d7..84acdb8dbe4 100644 --- a/src/fragments/lib-v1/auth/flutter/user_attributes/20_update_user_attribute.mdx +++ b/src/fragments/lib-v1/auth/flutter/user_attributes/20_update_user_attribute.mdx @@ -1,51 +1,79 @@ To update a single user attribute, call `updateUserAttribute`: ```dart -Future updateUserAttribute() async { +Future updateUserEmail({ + required String newEmail, +}) async { try { final result = await Amplify.Auth.updateUserAttribute( - userAttributeKey: CognitoUserAttributeKey.email, - value: 'email@email.com', + userAttributeKey: AuthUserAttributeKey.email, + value: newEmail, ); - if (result.nextStep.updateAttributeStep == 'CONFIRM_ATTRIBUTE_WITH_CODE') { - var destination = result.nextStep.codeDeliveryDetails?.destination; - print('Confirmation code sent to $destination'); - } else { - print('Update completed'); - } - } on AmplifyException catch (e) { - print(e.message); + _handleUpdateUserAttributeResult(result); + } on AuthException catch (e) { + safePrint('Error updating user attribute: ${e.message}'); } } ``` +User attribute updates may require additional verification before they're complete. Check the +`UpdateUserAttributeResult` returned from `Amplify.Auth.updateUserAttribute` to see which next +step, if any, is required. When the update is complete, the next step will be `done`. + +```dart +void _handleUpdateUserAttributeResult( + UpdateUserAttributeResult result, +) { + switch (result.nextStep.updateAttributeStep) { + case AuthUpdateAttributeStep.confirmAttributeWithCode: + final codeDeliveryDetails = result.nextStep.codeDeliveryDetails!; + _handleCodeDelivery(codeDeliveryDetails); + break; + case AuthUpdateAttributeStep.done: + safePrint('Successfully updated attribute'); + break; + } +} + +void _handleCodeDelivery(AuthCodeDeliveryDetails codeDeliveryDetails) { + safePrint( + 'A confirmation code has been sent to ${codeDeliveryDetails.destination}. ' + 'Please check your ${codeDeliveryDetails.deliveryMedium.name} for the code.', + ); +} +``` + To update multiple user attributes at a time, call `updateUserAttributes`: ```dart -Future updateMultipleUserAttributes() async { +Future updateUserAttributes() async { const attributes = [ AuthUserAttribute( - userAttributeKey: CognitoUserAttributeKey.email, + userAttributeKey: AuthUserAttributeKey.email, value: 'email@email.com', ), AuthUserAttribute( - userAttributeKey: CognitoUserAttributeKey.familyName, + userAttributeKey: AuthUserAttributeKey.familyName, value: 'MyFamilyName', ), ]; - try { - final result = await Amplify.Auth.updateUserAttributes(attributes: attributes); + final result = await Amplify.Auth.updateUserAttributes( + attributes: attributes, + ); result.forEach((key, value) { - if (value.nextStep.updateAttributeStep == 'CONFIRM_ATTRIBUTE_WITH_CODE') { - final destination = value.nextStep.codeDeliveryDetails?.destination; - print('Confirmation code sent to $destination for $key'); - } else { - print('Update completed for $key'); + switch (value.nextStep.updateAttributeStep) { + case AuthUpdateAttributeStep.confirmAttributeWithCode: + final destination = value.nextStep.codeDeliveryDetails?.destination; + safePrint('Confirmation code sent to $destination for $key'); + break; + case AuthUpdateAttributeStep.done: + safePrint('Update completed for $key'); + break; } }); - } on AmplifyException catch (e) { - print(e.message); + } on AuthException catch (e) { + safePrint('Error updating user attributes: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/user_attributes/30_confirm_attribute.mdx b/src/fragments/lib-v1/auth/flutter/user_attributes/30_confirm_attribute.mdx index e04c052c92d..2427f9300ce 100644 --- a/src/fragments/lib-v1/auth/flutter/user_attributes/30_confirm_attribute.mdx +++ b/src/fragments/lib-v1/auth/flutter/user_attributes/30_confirm_attribute.mdx @@ -2,12 +2,11 @@ Future verifyAttributeUpdate() async { try { await Amplify.Auth.confirmUserAttribute( - userAttributeKey: CognitoUserAttributeKey.email, + userAttributeKey: AuthUserAttributeKey.email, confirmationCode: '390739', ); - print('Attribute verified'); - } on AmplifyException catch (e) { - print(e.message); + } on AuthException catch (e) { + safePrint('Error confirming attribute update: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/user_attributes/40_resend_code.mdx b/src/fragments/lib-v1/auth/flutter/user_attributes/40_resend_code.mdx index 6575a74904c..168c7783efe 100644 --- a/src/fragments/lib-v1/auth/flutter/user_attributes/40_resend_code.mdx +++ b/src/fragments/lib-v1/auth/flutter/user_attributes/40_resend_code.mdx @@ -1,13 +1,12 @@ ```dart Future resendVerificationCode() async { try { - final result = await Amplify.Auth.resendUserAttributeConfirmationCode( - userAttributeKey: CognitoUserAttributeKey.email, + final result = await Amplify.Auth.sendUserAttributeConfirmationCode( + userAttributeKey: AuthUserAttributeKey.email, ); - final destination = result.codeDeliveryDetails.destination; - print('Confirmation code set to $destination'); - } on AmplifyException catch (e) { - print(e.message); + _handleCodeDelivery(result.codeDeliveryDetails); + } on AuthException catch (e) { + safePrint('Error resending code: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/auth/flutter/user_attributes/50_custom_attributes.mdx b/src/fragments/lib-v1/auth/flutter/user_attributes/50_custom_attributes.mdx new file mode 100644 index 00000000000..28223df913b --- /dev/null +++ b/src/fragments/lib-v1/auth/flutter/user_attributes/50_custom_attributes.mdx @@ -0,0 +1,26 @@ +## Custom attributes +Amplify Flutter supports [standard OIDC user attributes](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) as well as custom attributes. Custom attributes can be instantiated via the custom attribute constructor: + +```dart +Future signUp({ + required String username, + required String password, + required String email, + required String customValue, +}) async { + final userAttributes = { + AuthUserAttributeKey.email: email, + // Create and pass a custom attribute + const CognitoUserAttributeKey.custom('my-custom-attribute'): customValue + }; + await Amplify.Auth.signUp( + username: username, + password: password, + options: SignUpOptions( + userAttributes: userAttributes, + ), + ); +} +``` + +When working with a Cognito UserPool, you can set up [custom attributes](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html#user-pool-settings-custom-attributes) via the Cognito console or AWS CLI. Although Cognito prepends a "custom:" prefix on the attribute name, there is no need for you to add this in Amplify Flutter's custom attribute constructor. diff --git a/src/fragments/lib-v1/auth/ios/signin/20_confirmSignUp.mdx b/src/fragments/lib-v1/auth/ios/signin/20_confirmSignUp.mdx index 0d27983b0bf..d06485e78f8 100644 --- a/src/fragments/lib-v1/auth/ios/signin/20_confirmSignUp.mdx +++ b/src/fragments/lib-v1/auth/ios/signin/20_confirmSignUp.mdx @@ -37,3 +37,9 @@ func confirmSignUp(for username: String, with confirmationCode: String) -> AnyCa
+ +You will know the sign up flow is complete if you see the following in your console window: + +```console +Confirm signUp succeeded +``` diff --git a/src/fragments/lib-v1/auth/ios/signin/30_signIn.mdx b/src/fragments/lib-v1/auth/ios/signin/30_signIn.mdx index 8a62595f166..c726a2df517 100644 --- a/src/fragments/lib-v1/auth/ios/signin/30_signIn.mdx +++ b/src/fragments/lib-v1/auth/ios/signin/30_signIn.mdx @@ -37,3 +37,9 @@ func signIn(username: String, password: String) -> AnyCancellable { + +You will know the sign in flow is complete if you see the following in your console window: + +```console +Sign in succeeded +``` diff --git a/src/fragments/lib-v1/auth/native_common/access_credentials/common.mdx b/src/fragments/lib-v1/auth/native_common/access_credentials/common.mdx index dd5f85a26df..7622a3aa205 100644 --- a/src/fragments/lib-v1/auth/native_common/access_credentials/common.mdx +++ b/src/fragments/lib-v1/auth/native_common/access_credentials/common.mdx @@ -2,7 +2,7 @@ An intentional decision with Amplify Auth was to avoid any public methods exposi With Auth, you simply sign in and it handles everything else needed to keep the credentials up to date and vend them to the other categories. -However, if you need to access them in relation to working with an API outside Amplify or want access to AWS specific identifying information (e.g. IdentityId), you can access these implementation details by casting the result of fetchAuthSession as follows: +However, if you need to access them in relation to working with an API outside Amplify or want access to AWS specific identifying information (e.g. IdentityId), you can access these implementation by following the example below: import android0 from '/src/fragments/lib-v1/auth/android/access_credentials/10_fetchAuthSession.mdx'; diff --git a/src/fragments/lib-v1/auth/native_common/guest_access/common.mdx b/src/fragments/lib-v1/auth/native_common/guest_access/common.mdx index e726fe990c9..e93be0150c8 100644 --- a/src/fragments/lib-v1/auth/native_common/guest_access/common.mdx +++ b/src/fragments/lib-v1/auth/native_common/guest_access/common.mdx @@ -4,8 +4,11 @@ Follow these steps to enable guest access on your Auth category: ```console amplify update auth -What do you want to do? Walkthrough all the auth configurations -Select the authentication/authorization services that you want to use: [choose whatever you initially selected - default is User Sign-Up, Sign-In, connected with AWS IAM controls] + +What do you want to do? +❯ Walkthrough all the auth configurations +Select the authentication/authorization services that you want to use: +❯ [choose whatever you initially selected - default is User Sign-Up, Sign-In, connected with AWS IAM controls] Allow unauthenticated logins? (Provides scoped down permissions that you can control via AWS IAM) ❯ Yes No diff --git a/src/fragments/lib-v1/auth/native_common/signin/common.mdx b/src/fragments/lib-v1/auth/native_common/signin/common.mdx index 67ec2f0181e..fb0e17af91d 100644 --- a/src/fragments/lib-v1/auth/native_common/signin/common.mdx +++ b/src/fragments/lib-v1/auth/native_common/signin/common.mdx @@ -44,12 +44,6 @@ import flutter8 from '/src/fragments/lib-v1/auth/flutter/signin/20_confirmSignUp -You will know the sign up flow is complete if you see the following in your console window: - -```console -Confirm signUp succeeded -``` - ## Sign in a user Implement a UI to get the username and password from the user. After the user enters the username and password you can start the sign in flow by calling the following method: @@ -66,12 +60,6 @@ import flutter11 from '/src/fragments/lib-v1/auth/flutter/signin/30_signIn.mdx'; -You will know the sign in flow is complete if you see the following in your console window: - -```console -Sign in succeeded -``` - You have now successfully registered a user and authenticated with that user's username and password with Amplify. The Authentication category supports other mechanisms for authentication such as web UI based sign in, sign in using other providers etc that you can explore in the other sections. import flutter12 from '/src/fragments/lib-v1/auth/flutter/signin/60_runtime_auth_flow.mdx'; diff --git a/src/fragments/lib-v1/auth/native_common/signin_next_steps/common.mdx b/src/fragments/lib-v1/auth/native_common/signin_next_steps/common.mdx index 01bb95e9eb9..c3310606b28 100644 --- a/src/fragments/lib-v1/auth/native_common/signin_next_steps/common.mdx +++ b/src/fragments/lib-v1/auth/native_common/signin_next_steps/common.mdx @@ -1,41 +1,83 @@ +import flutterMaintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + After a user has finished signup, they can proceed to sign in. Amplify Auth signin flows can be multi step processes. The required steps are determined by the configuration you provided when you ran `amplify add auth`. Depending on the configuration, you may need to call various APIs to finish authenticating a user's signin attempt. To identify the next step in a signin flow, inspect the `nextStep` parameter in the signin result. import ios0 from '/src/fragments/lib-v1/auth/ios/signin_next_steps/10_signin.mdx'; - +import flutter0 from '/src/fragments/lib-v1/auth/flutter/signin_next_steps/10_signin.mdx'; + + ### Confirm signin with SMS MFA import ios1 from '/src/fragments/lib-v1/auth/ios/signin_next_steps/20_confirm_sms_mfa.mdx'; - +import flutter1 from '/src/fragments/lib-v1/auth/flutter/signin_next_steps/20_confirm_sms_mfa.mdx'; + + + + +### Confirm signin with TOTP MFA + + +import flutter7 from '/src/fragments/lib-v1/auth/flutter/signin_next_steps/80_totp.mdx'; + + + + +### Continue signin with MFA Selection + + +import flutter8 from '/src/fragments/lib-v1/auth/flutter/signin_next_steps/90_mfa_selection.mdx'; + + + + +### Continue signin with TOTP Setup + + +import flutter9 from '/src/fragments/lib-v1/auth/flutter/signin_next_steps/100_totp_setup.mdx'; + + ### Confirm signin with custom challenge import ios2 from '/src/fragments/lib-v1/auth/ios/signin_next_steps/30_confirm_custom_challenge.mdx'; - +import flutter2 from '/src/fragments/lib-v1/auth/flutter/signin_next_steps/30_confirm_custom_challenge.mdx'; + + ### Confirm signin with new password import ios3 from '/src/fragments/lib-v1/auth/ios/signin_next_steps/40_confirm_new_password.mdx'; - +import flutter3 from '/src/fragments/lib-v1/auth/flutter/signin_next_steps/40_confirm_new_password.mdx'; + + ### Reset password import ios4 from '/src/fragments/lib-v1/auth/ios/signin_next_steps/50_reset_password.mdx'; - +import flutter4 from '/src/fragments/lib-v1/auth/flutter/signin_next_steps/50_reset_password.mdx'; + + ### Confirm Signup import ios5 from '/src/fragments/lib-v1/auth/ios/signin_next_steps/60_confirm_signup.mdx'; - +import flutter5 from '/src/fragments/lib-v1/auth/flutter/signin_next_steps/60_confirm_signup.mdx'; + + ### Done import ios6 from '/src/fragments/lib-v1/auth/ios/signin_next_steps/70_done.mdx'; - +import flutter6 from '/src/fragments/lib-v1/auth/flutter/signin_next_steps/70_done.mdx'; + + diff --git a/src/fragments/lib-v1/auth/native_common/signout/common.mdx b/src/fragments/lib-v1/auth/native_common/signout/common.mdx index 134180c05f7..10d42b1ceb1 100644 --- a/src/fragments/lib-v1/auth/native_common/signout/common.mdx +++ b/src/fragments/lib-v1/auth/native_common/signout/common.mdx @@ -12,15 +12,21 @@ import flutter2 from '/src/fragments/lib-v1/auth/flutter/signout/10_local_signou + Calling signOut without any options will just delete the local cache and keychain of the user. If you would like to sign out of all devices, invoke the signOut api with advanced options. [Amazon Cognito now supports token revocation](https://aws.amazon.com/about-aws/whats-new/2021/06/amazon-cognito-now-supports-targeted-sign-out-through-refresh-token-revocation/) and the latest Amplify version will revoke Amazon Cognito tokens if the application is online. This means that the Cognito refresh token cannot be used anymore to generate new Access and Id Tokens. + + + +Calling signOut without any options will delete the local cache of user data and revoke the Amazon Cognito tokens if the application is online. This means that the Cognito refresh token cannot be used anymore to generate new Access and Id Tokens. + Access and Id Tokens are short-lived (60 minutes by default but can be set from 5 minutes to 1 day). After revocation, these tokens cannot be used with Cognito User Pools anymore. However, they are still valid when used with other services like AppSync or API Gateway. For limiting subsequent calls to these other services after invalidating tokens, we recommend lowering token expiration time for your app client in the Cognito User Pools console. If you are using the Amplify CLI this can be accessed by running `amplify console auth`. -Token revocation is enabled automatically on new Amazon Cognito User Pools, however existing User Pools must enable this feature, [using the Cognito Console or AWS CLI](https://docs.aws.amazon.com/cognito/latest/developerguide/token-revocation.html). +Token revocation is enabled automatically on new Amazon Cognito user pools, however existing User Pools must enable this feature, [using the Cognito Console or AWS CLI](https://docs.aws.amazon.com/cognito/latest/developerguide/token-revocation.html). import android3 from '/src/fragments/lib-v1/auth/android/signout/20_global_signout.mdx'; diff --git a/src/fragments/lib-v1/auth/native_common/user_attributes/common.mdx b/src/fragments/lib-v1/auth/native_common/user_attributes/common.mdx index 708833a35d0..9290a1463ef 100644 --- a/src/fragments/lib-v1/auth/native_common/user_attributes/common.mdx +++ b/src/fragments/lib-v1/auth/native_common/user_attributes/common.mdx @@ -46,9 +46,9 @@ import flutter8 from '/src/fragments/lib-v1/auth/flutter/user_attributes/30_conf -## Resend verification code +## Send user attribute verification code -If the code has expired or the user needs to resend the confirmation code, invoke the resend api as shown below: +If an attribute needs to be verified while the user is authenticated, invoke the api as shown below: import ios9 from '/src/fragments/lib-v1/auth/ios/user_attributes/40_resend_code.mdx'; @@ -61,3 +61,7 @@ import android10 from '/src/fragments/lib-v1/auth/android/user_attributes/40_res import flutter11 from '/src/fragments/lib-v1/auth/flutter/user_attributes/40_resend_code.mdx'; + +import flutter12 from '/src/fragments/lib-v1/auth/flutter/user_attributes/50_custom_attributes.mdx'; + + diff --git a/src/fragments/lib-v1/datastore/flutter/data-access/delete-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/data-access/delete-snippet.mdx index 96a56af58fd..846a2ed4aff 100644 --- a/src/fragments/lib-v1/datastore/flutter/data-access/delete-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/data-access/delete-snippet.mdx @@ -1,19 +1,21 @@ Below, you query for an instance with an `id` of `123`, and then delete it, if found: ```dart +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + Future deletePostsWithId() async { - final oldPosts = await Amplify.DataStore.query( + final postToDelete = (await Amplify.DataStore.query( Post.classType, where: Post.ID.eq('123'), - ); - // Query can return more than one posts with a different predicate - // For this example, it is ensured that it will return one post - final oldPost = oldPosts.first; + )) + .first; + try { - await Amplify.DataStore.delete(oldPost); - print('Deleted a post'); + await Amplify.DataStore.delete(postToDelete); } on DataStoreException catch (e) { - print('Delete failed: $e'); + safePrint('Something went wrong deleting model: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/data-access/query-basic-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/data-access/query-basic-snippet.mdx index 1f20a330042..0b88f1155b9 100644 --- a/src/fragments/lib-v1/datastore/flutter/data-access/query-basic-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/data-access/query-basic-snippet.mdx @@ -1,10 +1,14 @@ ```dart -Future readFromDatabase() async { +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + +Future queryPosts() async { try { final posts = await Amplify.DataStore.query(Post.classType); - print('Posts: $posts'); + safePrint('Posts: $posts'); } on DataStoreException catch (e) { - print('Query failed: $e'); + safePrint('Something went wrong querying posts: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/data-access/query-pagination-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/data-access/query-pagination-snippet.mdx index c66642a327c..31791263e6a 100644 --- a/src/fragments/lib-v1/datastore/flutter/data-access/query-pagination-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/data-access/query-pagination-snippet.mdx @@ -1,12 +1,18 @@ ```dart -// Do not forget to import the following with the other imports at the top of the file -import 'package:amplify_datastore_plugin_interface/amplify_datastore_plugin_interface.dart'; +import 'package:amplify_flutter/amplify_flutter.dart'; -Future fetchPostsWithPagination(int page) async { - final posts = await Amplify.DataStore.query( - Post.classType, - pagination: QueryPagination(page: page, limit: 25), - ); - print('Posts: $posts'); +import 'models/ModelProvider.dart'; + +Future queryPostsWithPagination(int page) async { + try { + final posts = await Amplify.DataStore.query( + Post.classType, + pagination: QueryPagination(page: page, limit: 25), + ); + safePrint('Posts: $posts'); + } on DataStoreException catch (e) { + safePrint('Something went wrong querying posts: ${e.message}'); + } } + ``` diff --git a/src/fragments/lib-v1/datastore/flutter/data-access/query-predicate-multiple-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/data-access/query-predicate-multiple-snippet.mdx index 8250d980d3b..6d02c430249 100644 --- a/src/fragments/lib-v1/datastore/flutter/data-access/query-predicate-multiple-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/data-access/query-predicate-multiple-snippet.mdx @@ -1,9 +1,17 @@ ```dart -Future fetchPublishedWithRatingTwoPosts() async { - final posts = await Amplify.DataStore.query( - Post.classType, - where: Post.RATING.eq(2).and(Post.STATUS.eq(PostStatus.ACTIVE)), - ); - print('Posts: $posts'); +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + +Future queryPublishedPostsWithRatingFour() async { + try { + final posts = await Amplify.DataStore.query( + Post.classType, + where: Post.RATING.eq(4).and(Post.STATUS.eq(PostStatus.ACTIVE)), + ); + safePrint('Published posts that have a rating 4: $posts'); + } on DataStoreException catch (e) { + safePrint('Something went wrong querying posts: ${e.message}'); + } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/data-access/query-predicate-or-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/data-access/query-predicate-or-snippet.mdx index e43137184fe..d0a62af37e1 100644 --- a/src/fragments/lib-v1/datastore/flutter/data-access/query-predicate-or-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/data-access/query-predicate-or-snippet.mdx @@ -1,9 +1,17 @@ ```dart -Future fetchPublishedOrWithRatingTwoPosts() async { - final posts = await Amplify.DataStore.query( - Post.classType, - where: Post.RATING.eq(2).or(Post.STATUS.eq(PostStatus.ACTIVE)), - ); - print('Posts: $posts'); +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + +Future queryPublishedOrWithRatingTwoPosts() async { + try { + final posts = await Amplify.DataStore.query( + Post.classType, + where: Post.RATING.eq(2).or(Post.STATUS.eq(PostStatus.ACTIVE)), + ); + safePrint('Posts that are published, or have a rating 2: $posts'); + } on DataStoreException catch (e) { + safePrint('Something went wrong querying posts: ${e.message}'); + } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/data-access/query-predicate-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/data-access/query-predicate-snippet.mdx index a61b759b8eb..a49163d59e6 100644 --- a/src/fragments/lib-v1/datastore/flutter/data-access/query-predicate-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/data-access/query-predicate-snippet.mdx @@ -1,9 +1,17 @@ ```dart -Future fetchPostsMoreThanFourRating() async { - final posts = await Amplify.DataStore.query( - Post.classType, - where: Post.RATING.ge(4), - ); - print('Posts: $posts'); +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + +Future queryPostsWithRatingGreaterThanFour() async { + try { + final posts = await Amplify.DataStore.query( + Post.classType, + where: Post.RATING.ge(4), + ); + safePrint('Posts that have a rating > 4: $posts'); + } on DataStoreException catch (e) { + safePrint('Something went wrong querying posts: ${e.message}'); + } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/data-access/query-sort-multiple-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/data-access/query-sort-multiple-snippet.mdx index 95c7fff1fec..ddb36b63dd0 100644 --- a/src/fragments/lib-v1/datastore/flutter/data-access/query-sort-multiple-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/data-access/query-sort-multiple-snippet.mdx @@ -1,12 +1,20 @@ ```dart -Future fetchPostsFirstInAscendingRatingOrderThenDescendingTitleOrder() async { - final posts = await Amplify.DataStore.query( - Post.classType, - sortBy: [ - Post.RATING.ascending(), - Post.TITLE.descending(), - ], - ); - print('Posts: $posts'); +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + +Future queryPostsFirstInAscendingRatingOrderThenDescendingTitleOrder() async { + try { + final posts = await Amplify.DataStore.query( + Post.classType, + sortBy: [ + Post.RATING.ascending(), + Post.TITLE.descending(), + ], + ); + safePrint('Posts: $posts'); + } on DataStoreException catch (e) { + safePrint('Something went wrong querying posts: ${e.message}'); + } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/data-access/query-sort-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/data-access/query-sort-snippet.mdx index 77a6c8005c6..7b3d0cae273 100644 --- a/src/fragments/lib-v1/datastore/flutter/data-access/query-sort-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/data-access/query-sort-snippet.mdx @@ -1,9 +1,17 @@ ```dart -Future fetchPostsInAscendingOrder() async { - final posts = await Amplify.DataStore.query( - Post.classType, - sortBy: [Post.RATING.ascending()], - ); - print('Posts: $posts'); +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + +Future queryPostsInAscendingOrderByRating() async { + try { + final posts = await Amplify.DataStore.query( + Post.classType, + sortBy: [Post.RATING.ascending()], + ); + safePrint('Posts: $posts'); + } on DataStoreException catch (e) { + safePrint('Something went wrong querying posts: ${e.message}'); + } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/data-access/save-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/data-access/save-snippet.mdx index cdffb8b03cb..e3d64718c4b 100644 --- a/src/fragments/lib-v1/datastore/flutter/data-access/save-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/data-access/save-snippet.mdx @@ -1,4 +1,8 @@ ```dart +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + Future savePost() async { final newPost = Post( title: 'New Post being saved', @@ -6,6 +10,10 @@ Future savePost() async { status: PostStatus.INACTIVE, ); - await Amplify.DataStore.save(newPost); + try { + await Amplify.DataStore.save(newPost); + } on DataStoreException catch (e) { + safePrint('Something went wrong saving model: ${e.message}'); + } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/data-access/update-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/data-access/update-snippet.mdx index 4831855e993..bebac670813 100644 --- a/src/fragments/lib-v1/datastore/flutter/data-access/update-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/data-access/update-snippet.mdx @@ -1,13 +1,21 @@ ```dart +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + Future updatePost() async { - final postsWithId = await Amplify.DataStore.query( + final oldPost = (await Amplify.DataStore.query( Post.classType, where: Post.ID.eq('123'), - ); + )) + .first; - final oldPost = postsWithId.first; - final newPost = oldPost.copyWith(id: oldPost.id, title: 'Updated Title'); + final newPost = oldPost.copyWith(title: 'Updated Title'); - await Amplify.DataStore.save(newPost); + try { + await Amplify.DataStore.save(newPost); + } on DataStoreException catch (e) { + safePrint('Something went wrong updating model: ${e.message}'); + } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/datastore-events.mdx b/src/fragments/lib-v1/datastore/flutter/datastore-events.mdx index fe15ee25093..d87727b807c 100644 --- a/src/fragments/lib-v1/datastore/flutter/datastore-events.mdx +++ b/src/fragments/lib-v1/datastore/flutter/datastore-events.mdx @@ -11,9 +11,9 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State { - late StreamSubscription? hubSubscription; + late StreamSubscription hubSubscription; - // Initialize a boolean indicating if the network is up + // Initialize a boolean indicating if the network is up bool networkIsUp = false; // Initialize the libraries diff --git a/src/fragments/lib-v1/datastore/flutter/getting-started/10_preReq.mdx b/src/fragments/lib-v1/datastore/flutter/getting-started/10_preReq.mdx index a556eeac083..abbff194563 100644 --- a/src/fragments/lib-v1/datastore/flutter/getting-started/10_preReq.mdx +++ b/src/fragments/lib-v1/datastore/flutter/getting-started/10_preReq.mdx @@ -1,13 +1,5 @@ - [Install and configure Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) - - As of Amplify Flutter 0.3.0, CLI version{' '} - {'>'}=7.6.10 is required. When updating to 0.3.0, you must - re-generate your models with the CLI. You can do so by running{' '} - amplify codegen models. - - -- A Flutter application targeting Flutter SDK >= 2.10.0 (stable version) with Amplify libraries integrated - An iOS configuration targeting at least iOS 13.0 - - An Android configuration targeting at least Android API level 21 (Android 5.0) or above + - An Android configuration targeting at least Android API level 24 (Android 7.0) or above - For a full example of please follow the [project setup walkthrough](/gen1/[platform]/prev/start/project-setup/create-application/) diff --git a/src/fragments/lib-v1/datastore/flutter/getting-started/20_installLib.mdx b/src/fragments/lib-v1/datastore/flutter/getting-started/20_installLib.mdx index 79ee40c6d8f..f037d40c76f 100644 --- a/src/fragments/lib-v1/datastore/flutter/getting-started/20_installLib.mdx +++ b/src/fragments/lib-v1/datastore/flutter/getting-started/20_installLib.mdx @@ -3,15 +3,12 @@ Add the following dependencies to your `pubspec.yaml` file and install dependencies when asked: ```yaml -environment: - sdk: ">=2.15.0 <3.0.0" - dependencies: flutter: sdk: flutter - amplify_flutter: ^0.6.0 - amplify_datastore: ^0.6.0 + amplify_datastore: ^1.0.0 + amplify_flutter: ^1.0.0 ``` ### Update The Android Project @@ -37,4 +34,4 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } -``` \ No newline at end of file +``` diff --git a/src/fragments/lib-v1/datastore/flutter/getting-started/40_codegen.mdx b/src/fragments/lib-v1/datastore/flutter/getting-started/40_codegen.mdx deleted file mode 100644 index 6dd23646ca6..00000000000 --- a/src/fragments/lib-v1/datastore/flutter/getting-started/40_codegen.mdx +++ /dev/null @@ -1,2 +0,0 @@ -Coming Soon. Please use [Amplify CLI](/gen1/[platform]/prev/build-a-backend/more-features/datastore/set-up-datastore/#code-generation-amplify-cli) for now. - diff --git a/src/fragments/lib-v1/datastore/flutter/getting-started/50_initDataStore.mdx b/src/fragments/lib-v1/datastore/flutter/getting-started/50_initDataStore.mdx index f8d91d89a30..4b25b9ac87a 100644 --- a/src/fragments/lib-v1/datastore/flutter/getting-started/50_initDataStore.mdx +++ b/src/fragments/lib-v1/datastore/flutter/getting-started/50_initDataStore.mdx @@ -1,4 +1,4 @@ -To initialize the Amplify DataStore, use the `Amplify.addPlugin()` method to add the AWS DataStore Plugin. You also need to import the codegen dart file `ModelProvider.dart`. After that, finish configuring Amplify by calling `configure()`: +To initialize the Amplify DataStore, use the `Amplify.addPlugin()` method to add the Amplify DataStore Plugin. You also need to import the codegen dart file `ModelProvider.dart`. After that, finish configuring Amplify by calling `configure()`: ```dart import 'package:flutter/material.dart'; @@ -27,6 +27,7 @@ class _MyAppState extends State { final datastorePlugin = AmplifyDataStore(modelProvider: ModelProvider.instance); await Amplify.addPlugin(datastorePlugin); + try { await Amplify.configure(amplifyconfig); } on AmplifyAlreadyConfiguredException { diff --git a/src/fragments/lib-v1/datastore/flutter/getting-started/60_initDataStore.mdx b/src/fragments/lib-v1/datastore/flutter/getting-started/60_initDataStore.mdx index f8d91d89a30..4bab5bf10e3 100644 --- a/src/fragments/lib-v1/datastore/flutter/getting-started/60_initDataStore.mdx +++ b/src/fragments/lib-v1/datastore/flutter/getting-started/60_initDataStore.mdx @@ -27,6 +27,7 @@ class _MyAppState extends State { final datastorePlugin = AmplifyDataStore(modelProvider: ModelProvider.instance); await Amplify.addPlugin(datastorePlugin); + try { await Amplify.configure(amplifyconfig); } on AmplifyAlreadyConfiguredException { diff --git a/src/fragments/lib-v1/datastore/flutter/getting-started/60_saveSnippet.mdx b/src/fragments/lib-v1/datastore/flutter/getting-started/60_saveSnippet.mdx index 3efc9bdfd81..e3d64718c4b 100644 --- a/src/fragments/lib-v1/datastore/flutter/getting-started/60_saveSnippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/getting-started/60_saveSnippet.mdx @@ -1,5 +1,19 @@ ```dart -Post newPost = Post( - title: 'New Post being saved', rating: 15, status: PostStatus.INACTIVE); -await Amplify.DataStore.save(newPost); +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + +Future savePost() async { + final newPost = Post( + title: 'New Post being saved', + rating: 15, + status: PostStatus.INACTIVE, + ); + + try { + await Amplify.DataStore.save(newPost); + } on DataStoreException catch (e) { + safePrint('Something went wrong saving model: ${e.message}'); + } +} ``` diff --git a/src/fragments/lib-v1/datastore/flutter/getting-started/70_querySnippet.mdx b/src/fragments/lib-v1/datastore/flutter/getting-started/70_querySnippet.mdx index 1f20a330042..0b88f1155b9 100644 --- a/src/fragments/lib-v1/datastore/flutter/getting-started/70_querySnippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/getting-started/70_querySnippet.mdx @@ -1,10 +1,14 @@ ```dart -Future readFromDatabase() async { +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + +Future queryPosts() async { try { final posts = await Amplify.DataStore.query(Post.classType); - print('Posts: $posts'); + safePrint('Posts: $posts'); } on DataStoreException catch (e) { - print('Query failed: $e'); + safePrint('Something went wrong querying posts: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/getting-started/80_saveSnippet.mdx b/src/fragments/lib-v1/datastore/flutter/getting-started/80_saveSnippet.mdx deleted file mode 100644 index b6d7094919d..00000000000 --- a/src/fragments/lib-v1/datastore/flutter/getting-started/80_saveSnippet.mdx +++ /dev/null @@ -1,10 +0,0 @@ -```dart -Future savePost() async { - final newPost = Post( - title: 'New Post being saved', - rating: 15, - status: PostStatus.INACTIVE, - ); - await Amplify.DataStore.save(newPost); -} -``` diff --git a/src/fragments/lib-v1/datastore/flutter/relational/delete-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/relational/delete-snippet.mdx index 8ee68609d07..3a463e6cf09 100644 --- a/src/fragments/lib-v1/datastore/flutter/relational/delete-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/relational/delete-snippet.mdx @@ -1,13 +1,20 @@ ```dart -Future deletePostWithId(String id) async { - final postsWithId = await Amplify.DataStore.query( - Post.classType, - where: Post.ID.eq(id), - ); +import 'package:amplify_flutter/amplify_flutter.dart'; - for (final element in postsWithId) { - await Amplify.DataStore.delete(element); - print('Deleted a post'); +import 'models/ModelProvider.dart'; + +Future deletePostWithID123AndItsComments(String id) async { + try { + final post = (await Amplify.DataStore.query( + Post.classType, + where: Post.ID.eq(id), + )) + .first; + + // DataStore also deletes all associated comments with this operation + await Amplify.DataStore.delete(post); + } on DataStoreException catch (e) { + safePrint('Something went wrong deleting models: ${e.message}'); } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/relational/query-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/relational/query-snippet.mdx index 586843a240f..a8418faec7f 100644 --- a/src/fragments/lib-v1/datastore/flutter/relational/query-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/relational/query-snippet.mdx @@ -1,11 +1,19 @@ In order to retrieve all models that are related to a parent model, you can use query predicates with the query API: ```dart +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + Future fetchAllCommentsForPostId(String postId) async { - final comments = await Amplify.DataStore.query( - Comment.classType, - where: Comment.POST.eq(postId), - ); - print('Comments: $comments'); + try { + final comments = await Amplify.DataStore.query( + Comment.classType, + where: Comment.POST.eq(postId), + ); + safePrint('Comments: $comments'); + } on DataStoreException catch (e) { + safePrint('Something went wrong querying posts: ${e.message}'); + } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/relational/save-many-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/relational/save-many-snippet.mdx index 4e6cfccbd62..6b5280d8a9f 100644 --- a/src/fragments/lib-v1/datastore/flutter/relational/save-many-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/relational/save-many-snippet.mdx @@ -7,19 +7,30 @@ amplify codegen models Once it is regenerated, save your posts with many-to-many mode like the following: ```dart +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + Future savePostAndEditor() async { - final post = Post(title: 'My First Post'); + final post = Post( + title: 'My First Post', + rating: 10, + status: PostStatus.INACTIVE, + ); final editor = User(username: 'Nadia'); final postEditor = PostEditor(post: post, user: editor); - // first you save the post - await Amplify.DataStore.save(post); + try { + // first you save the post + await Amplify.DataStore.save(post); - // secondly, you save the editor/user - await Amplify.DataStore.save(editor); + // secondly, you save the editor/user + await Amplify.DataStore.save(editor); - // then you save the model that links a post with an editor - await Amplify.DataStore.save(postEditor); - print('Saved user, post and postEditor!'); + // then you save the model that links a post with an editor + await Amplify.DataStore.save(postEditor); + } on DataStoreException catch (e) { + safePrint('Something went wrong saving model: ${e.message}'); + } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/relational/save-snippet.mdx b/src/fragments/lib-v1/datastore/flutter/relational/save-snippet.mdx index 02269e246b7..39bde1a0ac5 100644 --- a/src/fragments/lib-v1/datastore/flutter/relational/save-snippet.mdx +++ b/src/fragments/lib-v1/datastore/flutter/relational/save-snippet.mdx @@ -1,4 +1,8 @@ ```dart +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + Future savePostAndComment() async { final post = Post( title: 'My Post with comments', @@ -6,13 +10,16 @@ Future savePostAndComment() async { status: PostStatus.ACTIVE, ); final comment = Comment( - post: post, // Directly pass in the post instance + post: post, // associate the comment to the post content: 'Loving Amplify DataStore!', ); - await Amplify.DataStore.save(post); - print('Post saved'); - await Amplify.DataStore.save(comment); - print('Comment saved'); + try { + // Make sure to safe the parent Post first + await Amplify.DataStore.save(post); + await Amplify.DataStore.save(comment); + } on DataStoreException catch (e) { + safePrint('Something went wrong querying posts: ${e.message}'); + } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/sync/10-installPlugin.mdx b/src/fragments/lib-v1/datastore/flutter/sync/10-installPlugin.mdx index bd15ecfb209..40309a0c7e6 100644 --- a/src/fragments/lib-v1/datastore/flutter/sync/10-installPlugin.mdx +++ b/src/fragments/lib-v1/datastore/flutter/sync/10-installPlugin.mdx @@ -5,7 +5,7 @@ Although DataStore presents a distinct API, its cloud synchronization functional Make sure you have the following plugin dependency in your `pubspec.yaml`. ```yaml -amplify_api: ^0.6.0 +amplify_api: ^1.0.0 ``` Locate your Amplify initialization code, and add an `AmplifyAPI()` plugin. Your initialization code should already include an `AmplifyDataStore()` plugin from previous steps. Note the new `import` statement for API towards the top of the file. @@ -13,11 +13,10 @@ Locate your Amplify initialization code, and add an `AmplifyAPI()` plugin. Your Be sure to import your API library first: ```dart -import 'package:amplify_flutter/amplify_flutter.dart'; -import 'package:amplify_datastore/amplify_datastore.dart'; -import 'package:amplify_datastore_plugin_interface/amplify_datastore_plugin_interface.dart'; -// Add the following line +// import the Amplify API plugin import 'package:amplify_api/amplify_api.dart'; +import 'package:amplify_datastore/amplify_datastore.dart'; +import 'package:amplify_flutter/amplify_flutter.dart'; import 'amplifyconfiguration.dart'; import 'models/ModelProvider.dart'; diff --git a/src/fragments/lib-v1/datastore/flutter/sync/20-savePredicate.mdx b/src/fragments/lib-v1/datastore/flutter/sync/20-savePredicate.mdx index 2229e0403e8..87f79a62598 100644 --- a/src/fragments/lib-v1/datastore/flutter/sync/20-savePredicate.mdx +++ b/src/fragments/lib-v1/datastore/flutter/sync/20-savePredicate.mdx @@ -1,12 +1,20 @@ ```dart +import 'package:amplify_flutter/amplify_flutter.dart'; + +import 'models/ModelProvider.dart'; + Future savePredicate(Post post) async { + final post = ...; // get post using the query API + final updatedPost = post.copyWith(title: '[Amplified]'); try { + // if the post title has changed to something else other than + // a string that starts with "[Amplify]", the save will be rejected await Amplify.DataStore.save( - post, + updatedPost, where: Post.TITLE.beginsWith("[Amplify]"), ); - } catch (e) { - print('Could not update post, maybe the title has been changed?'); + } on DataStoreException catch (e) { + safePrint('Could not update post: $e'); } } ``` diff --git a/src/fragments/lib-v1/datastore/flutter/sync/30-savePredicateComparison.mdx b/src/fragments/lib-v1/datastore/flutter/sync/30-savePredicateComparison.mdx index 1968cd7b33d..f23dc414689 100644 --- a/src/fragments/lib-v1/datastore/flutter/sync/30-savePredicateComparison.mdx +++ b/src/fragments/lib-v1/datastore/flutter/sync/30-savePredicateComparison.mdx @@ -13,7 +13,7 @@ Future savePredicateRemotely(Post post) async { post, where: Post.TITLE.beginsWith('[Amplify]'), ); - } catch (e) { + } on DataStoreException catch (e) { ... } } diff --git a/src/fragments/lib-v1/datastore/flutter/sync/40-clear.mdx b/src/fragments/lib-v1/datastore/flutter/sync/40-clear.mdx index b14ef340527..ddce9e1b273 100644 --- a/src/fragments/lib-v1/datastore/flutter/sync/40-clear.mdx +++ b/src/fragments/lib-v1/datastore/flutter/sync/40-clear.mdx @@ -1,11 +1,16 @@ ```dart -StreamSubscription hubSubscription = Amplify.Hub.listen([HubChannel.Auth], (hubEvent) { +import 'dart:async'; + +import 'package:amplify_flutter/amplify_flutter.dart'; + +final hubSubscription = + Amplify.Hub.listen(HubChannel.Auth, (AuthHubEvent hubEvent) async { if (hubEvent.eventName == 'SIGNED_OUT') { try { await Amplify.DataStore.clear(); - print('DataStore is cleared.'); + safePrint('DataStore is cleared as the user has signed out.'); } on DataStoreException catch (e) { - print('Failed to clear DataStore: $e'); + safePrint('Failed to clear DataStore: $e'); } } }); diff --git a/src/fragments/lib-v1/datastore/flutter/sync/50-selectiveSync.mdx b/src/fragments/lib-v1/datastore/flutter/sync/50-selectiveSync.mdx index f86629c34e0..8f2b4223c90 100644 --- a/src/fragments/lib-v1/datastore/flutter/sync/50-selectiveSync.mdx +++ b/src/fragments/lib-v1/datastore/flutter/sync/50-selectiveSync.mdx @@ -1,11 +1,17 @@ ## Selectively syncing a subset of your data -By default, DataStore downloads the entire contents of your cloud data source to your local device. The max number of records that will be stored is configurable [here](/gen1/[platform]/prev/build-a-backend/more-features/datastore/conflict-resolution/#custom-configuration). +By default, DataStore fetches all the records that you’re authorized to access from your cloud data source to your local device. The maximum number of records that will be stored locally is configurable [here](/gen1/[platform]/prev/build-a-backend/more-features/datastore/conflict-resolution/). -You can utilize selective sync to only persist a subset of your data instead. +You can utilize selective sync to persist a subset of your data instead. Selective sync works by applying predicates to the base and delta sync queries, as well as to incoming subscriptions. + + +Note that selective sync is applied on top of authorization rules you’ve defined on your schema with the `@auth` directive. For more information see the [Setup authorization rules](/gen1/[platform]/prev/build-a-backend/more-features/datastore/authz-rules-setup/) section. + + + ```dart void _configureAmplify() async { // Update AmplifyDataStore instance like below diff --git a/src/fragments/lib-v1/datastore/native_common/getting-started.mdx b/src/fragments/lib-v1/datastore/native_common/getting-started.mdx index 97f61720d6b..1601abb75a8 100644 --- a/src/fragments/lib-v1/datastore/native_common/getting-started.mdx +++ b/src/fragments/lib-v1/datastore/native_common/getting-started.mdx @@ -99,9 +99,9 @@ enum PostStatus { Now you will to convert the platform-agnostic `schema.graphql` into platform-specific data structures. DataStore relies on code generation to guarantee schemas are correctly converted to platform code. -Like the initial setup, models can be generated either using the IDE integration or Amplify CLI directly. + - +Like the initial setup, models can be generated either using the IDE integration or Amplify CLI directly. ### Code generation: Platform integration @@ -113,10 +113,6 @@ import android13 from '/src/fragments/lib-v1/datastore/android/getting-started/4 -import flutter14 from '/src/fragments/lib-v1/datastore/flutter/getting-started/40_codegen.mdx'; - - - import codegenPlatformIntegration from '/src/fragments/lib-v1/datastore/native_common/codegen-platform-integration.mdx'; @@ -194,7 +190,7 @@ import android25 from '/src/fragments/lib-v1/datastore/android/getting-started/7 -import flutter26 from '/src/fragments/lib-v1/datastore/flutter/getting-started/80_saveSnippet.mdx'; +import flutter26 from '/src/fragments/lib-v1/datastore/flutter/getting-started/60_saveSnippet.mdx'; diff --git a/src/fragments/lib-v1/flutter-maintenance.mdx b/src/fragments/lib-v1/flutter-maintenance.mdx index ba3e0be8264..4b0f4ecf7f6 100644 --- a/src/fragments/lib-v1/flutter-maintenance.mdx +++ b/src/fragments/lib-v1/flutter-maintenance.mdx @@ -1,9 +1,9 @@ -Amplify Flutter v0 is now in **Maintenance Mode** until July 19th, 2024. This means that we will continue to include updates to ensure compatibility with backend services and security. No new features will be introduced in v0. +Amplify Flutter v1 is now in **Maintenance Mode** until April 30th, 2025. This means that we will continue to include updates to ensure compatibility with backend services and security. No new features will be introduced in v1. -Please use the latest version (v1) of [Amplify Flutter](/gen1/[platform]/tools/libraries/) to get started. +Please use the latest version (v2) of [Amplify Flutter](/gen1/[platform]/tools/libraries/) to get started. -If you are currently using v0, follow [these instructions](/gen1/[platform]/start/project-setup/upgrade-guide/) to upgrade to v1. +If you are currently using v1, follow [these instructions](/gen1/[platform]/start/project-setup/upgrade-guide/) to upgrade to v2. diff --git a/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/10_example.mdx b/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/10_example.mdx index 2b5cc5cefc8..f88e8cc39d4 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/10_example.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/10_example.mdx @@ -3,6 +3,6 @@ Future createAndMutateTodo() async { final todo = Todo(name: 'my first todo', description: 'todo description'); final request = ModelMutations.create(todo); final response = await Amplify.API.mutate(request: request).response; - print('Response: $response'); + safePrint('Response: $response'); } ``` diff --git a/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/20_custom.mdx b/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/20_custom.mdx index bbdf664657e..ceadbaa8b2b 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/20_custom.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/20_custom.mdx @@ -1,6 +1,6 @@ ```dart const getTodo = 'getTodo'; -String graphQLDocument = '''query GetTodo(\$id: ID!) { +const graphQLDocument = '''query GetTodo(\$id: ID!) { $getTodo(id: \$id) { id name @@ -19,6 +19,6 @@ Then, query for the Todo by a todo id: ```dart Future queryTodo(GraphQLRequest getTodoRequest) async { final response = await Amplify.API.query(request: getTodoRequest).response; - print('Response: $response'); + safePrint('Response: $response'); } ``` diff --git a/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/30_nested.mdx b/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/30_nested.mdx index 528bcd8542e..df65160c220 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/30_nested.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/30_nested.mdx @@ -1,6 +1,6 @@ ```dart const getPost = 'getPost'; -String graphQLDocument = '''query GetPost(\$id: ID!) { +const graphQLDocument = '''query GetPost(\$id: ID!) { $getPost(id: \$id) { id title @@ -25,8 +25,10 @@ final getPostRequest = GraphQLRequest( Then, query for the `Post` with nested comments included in decoded response: ```dart -Future queryPostWithNestedComments(GraphQLRequest getPostRequest) async { - final response = await Amplify.API.query(request: getTodoRequest).response; - print('Response $response'); +Future queryPostWithNestedComments( + GraphQLRequest getPostRequest, +) async { + final response = await Amplify.API.query(request: getPostRequest).response; + safePrint('Response $response'); } ``` diff --git a/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/40_multiple.mdx b/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/40_multiple.mdx index 5240683d683..8bd40c4b2bc 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/40_multiple.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/40_multiple.mdx @@ -1,7 +1,7 @@ ```dart const getTodo = 'getTodo'; const getPost = 'getPost'; -String graphQLDocument = ''' +const graphQLDocument = ''' query GetPostAndTodo(\$todoId: ID!, \$postId: ID!) { $getTodo(id: \$todoId) { id @@ -16,11 +16,8 @@ String graphQLDocument = ''' '''; final multiOperationRequest = GraphQLRequest( - document: graphQLDocument, - variables: { - 'todoId': someTodoId, - 'postId': somePostId - }, + document: graphQLDocument, + variables: {'todoId': someTodoId, 'postId': somePostId}, ); ``` Notice here that `modelType` and `decodePath` are omitted. When these decoding variables are omitted, the plugin simply returns the result as a raw `String` from the response. @@ -33,12 +30,18 @@ import 'dart:convert'; ... -Future queryMultiOperationRequest(GraphQLRequest operation) async { - final response = await Amplify.API.query(request: multiOperationRequest).response; +Future queryMultiOperationRequest( + GraphQLRequest operation, +) async { + final response = + await Amplify.API.query(request: multiOperationRequest).response; if (response.data != null) { - final jsonData = (json.decode(response.data) as Map).cast(); - final post = Post.fromJson((jsonData[getPost] as Map).cast); - final todo = Todo.fromJson((jsonData[getTodo] as Map).cast); + final jsonData = + (json.decode(response.data) as Map).cast(); + final post = + Post.fromJson((jsonData[getPost] as Map).cast); + final todo = + Todo.fromJson((jsonData[getTodo] as Map).cast); } } ``` diff --git a/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/50_interceptor.mdx b/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/50_interceptor.mdx index 60c1cc63943..af23e74869e 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/50_interceptor.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/advanced-workflows/50_interceptor.mdx @@ -1 +1,35 @@ -Custom headers and interceptors are only supported in developer preview. Please follow the open [Github Issue](https://github.com/aws-amplify/amplify-flutter/issues/798). +The simplest option for GraphQL requests is to use the `headers` property of a `GraphQLRequest`. + +```dart +Future queryWithCustomHeaders() async { + final operation = Amplify.API.query( + request: GraphQLRequest( + document: graphQLDocumentString, + headers: {'customHeader': 'someValue'}, + ), + ); + final response = await operation.response; + final data = response.data; + safePrint('data: $data'); +} +``` + +Another option is to use the `baseHttpClient` property of the API plugin which can customize headers or otherwise alter HTTP functionality for all HTTP calls. + +```dart +// First create a custom HTTP client implementation to extend HTTP functionality. +class MyHttpRequestInterceptor extends AWSBaseHttpClient { + @override + Future transformRequest( + AWSBaseHttpRequest request, + ) async { + request.headers.putIfAbsent('customHeader', () => 'someValue'); + return request; + } +} + +// Then you can pass an instance of this client to `baseHttpClient` when you configure Amplify. +await Amplify.addPlugins([ + AmplifyAPI(baseHttpClient: MyHttpRequestInterceptor()), +]); +``` diff --git a/src/fragments/lib-v1/graphqlapi/flutter/authz.mdx b/src/fragments/lib-v1/graphqlapi/flutter/authz.mdx index 05ba9072b60..4643acd1a8a 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/authz.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/authz.mdx @@ -108,7 +108,7 @@ The `friendly_name` illustrated here is created from Amplify CLI prompt. There a "authorizationType": "API_KEY", "apiKey": "[API_KEY]" }, - "[FRIENDLY-NAME-API-WITH-IAM": { + "[FRIENDLY-NAME-API-WITH-IAM"]: { "endpointType": "GraphQL", "endpoint": "[GRAPHQL-ENDPOINT]", "region": "[REGION]", diff --git a/src/fragments/lib-v1/graphqlapi/flutter/authz/10_userpool.mdx b/src/fragments/lib-v1/graphqlapi/flutter/authz/10_userpool.mdx index 06a31f8a7d2..8871f1a55ba 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/authz/10_userpool.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/authz/10_userpool.mdx @@ -1,21 +1,21 @@ In case you have not added the Cognito libraries to your application, be sure to add them: ```yaml - environment: - sdk: ">=2.15.0 <3.0.0" +dependencies: + flutter: + sdk: flutter - dependencies: - flutter: - sdk: flutter - - amplify_flutter: ^0.6.0 - amplify_api: ^0.6.0 + amplify_flutter: ^1.0.0 + amplify_api: ^1.0.0 # Be sure that this is added - amplify_auth_cognito: ^0.6.0 + amplify_auth_cognito: ^1.0.0 ``` Afterwards add the following code to your app before you configure Amplify: ```dart -await Amplify.addPlugins([AmplifyAuthCognito(), AmplifyAPI(modelProvider: ModelProvider.instance)]); +await Amplify.addPlugins([ + AmplifyAuthCognito(), + AmplifyAPI(modelProvider: ModelProvider.instance), +]); ``` diff --git a/src/fragments/lib-v1/graphqlapi/flutter/authz/2X_add_plugin.mdx b/src/fragments/lib-v1/graphqlapi/flutter/authz/2X_add_plugin.mdx index 7c6f578e3f1..488197283df 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/authz/2X_add_plugin.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/authz/2X_add_plugin.mdx @@ -1,12 +1,14 @@ Then, include it, along with any other auth providers, in the call to `addPlugin`. ```dart -await Amplify.addPlugin(AmplifyAPI( +await Amplify.addPlugin( + AmplifyAPI( authProviders: const [ - CustomOIDCProvider(), - CustomFunctionProvider(), + CustomOIDCProvider(), + CustomFunctionProvider(), ], -)); + ), +); ``` diff --git a/src/fragments/lib-v1/graphqlapi/flutter/authz/30_multi.mdx b/src/fragments/lib-v1/graphqlapi/flutter/authz/30_multi.mdx index 9e22d8128ed..35f7c1ae3e5 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/authz/30_multi.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/authz/30_multi.mdx @@ -1,15 +1,39 @@ -When you have configured multiple APIs, you can specify the name of the API as a parameter as the target for an operation: +When you have multiple authorization modes, you can specify the mode with the `authorizationMode` parameter. You can also specify the API name with the `apiName` parameter. ```dart -Future main() async { +Future mutateWithApiKey() async { final operation = Amplify.API.mutate( request: GraphQLRequest( - document: graphQLDocumentString, - apiName: '[FRIENDLY-NAME-API-WITH-API-KEY]', + document: graphQLDocumentString, + authorizationMode: APIAuthorizationType.apiKey, ), ); final response = await operation.response; final data = response.data; - print('data: $data'); + safePrint('data: $data'); +} + +Future mutateWithIam() async { + final operation = Amplify.API.mutate( + request: GraphQLRequest( + document: graphQLDocumentString, + authorizationMode: APIAuthorizationType.iam, + ), + ); + final response = await operation.response; + final data = response.data; + safePrint('data: $data'); +} + +Future mutateByApiName() async { + final operation = Amplify.API.mutate( + request: GraphQLRequest( + document: graphQLDocumentString, + apiName: '[FRIENDLY-NAME-API-WITH-API-KEY]', + ), + ); + final response = await operation.response; + final data = response.data; + safePrint('data: $data'); } ``` diff --git a/src/fragments/lib-v1/graphqlapi/flutter/authz/auth_mode.mdx b/src/fragments/lib-v1/graphqlapi/flutter/authz/auth_mode.mdx deleted file mode 100644 index dae403b57f2..00000000000 --- a/src/fragments/lib-v1/graphqlapi/flutter/authz/auth_mode.mdx +++ /dev/null @@ -1 +0,0 @@ -In developer preview, you have the option to use the `authorizationMode` parameter to avoid referencing `friendly_name_` from the `amplifyconfiguration.dart` file (see code snippet below). \ No newline at end of file diff --git a/src/fragments/lib-v1/graphqlapi/flutter/getting-started/10_preReq.mdx b/src/fragments/lib-v1/graphqlapi/flutter/getting-started/10_preReq.mdx index 242473bd189..16c98fc9109 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/getting-started/10_preReq.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/getting-started/10_preReq.mdx @@ -1,5 +1,3 @@ * [Install and configure Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) -* A Flutter application targeting Flutter SDK >= 2.10.0 (stable version) with Amplify libraries integrated - * An iOS configuration targeting at least iOS 11.0 - * An Android configuration targeting at least Android API level 21 (Android 5.0) or above - * For a full example please follow the [project setup walkthrough](/gen1/[platform]/prev/start/project-setup/create-application/) + +Amplify Flutter 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](/gen1/[platform]/prev/start/project-setup/platform-setup/) guide for more details on platform specific setup. diff --git a/src/fragments/lib-v1/graphqlapi/flutter/getting-started/20_installLib.mdx b/src/fragments/lib-v1/graphqlapi/flutter/getting-started/20_installLib.mdx index 4a4f0fef357..1915c594d61 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/getting-started/20_installLib.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/getting-started/20_installLib.mdx @@ -1,12 +1,9 @@ Add the following dependencies to your `pubspec.yaml` file and install dependencies when asked: ```yaml -environment: - sdk: ">=2.15.0 <3.0.0" - dependencies: flutter: sdk: flutter - amplify_flutter: ^0.6.0 - amplify_api: ^0.6.0 + amplify_flutter: ^1.0.0 + amplify_api: ^1.0.0 ``` diff --git a/src/fragments/lib-v1/graphqlapi/flutter/getting-started/30_initapi.mdx b/src/fragments/lib-v1/graphqlapi/flutter/getting-started/30_initapi.mdx index 4f0ece449de..28361a48898 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/getting-started/30_initapi.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/getting-started/30_initapi.mdx @@ -3,35 +3,47 @@ To initialize the Amplify API category you call `Amplify.addPlugin()` method. To Your code should look like this: ```dart -import 'package:amplify_flutter/amplify_flutter.dart'; import 'package:amplify_api/amplify_api.dart'; -import 'package:amplify_example_application/models/ModelProvider.dart'; - +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:flutter/material.dart'; import 'amplifyconfiguration.dart'; +import 'models/ModelProvider.dart'; class MyApp extends StatefulWidget { + const MyApp({super.key}); + @override - _MyAppState createState() => _MyAppState(); + State createState() => _MyAppState(); } class _MyAppState extends State { - @override - void initState() { - super.initState(); - _configureAmplify(); + @override + void initState() { + super.initState(); + _configureAmplify(); + } + + Future _configureAmplify() async { + final api = AmplifyAPI(modelProvider: ModelProvider.instance); + await Amplify.addPlugin(api); + + try { + await Amplify.configure(amplifyconfig); + } on Exception catch (e) { + safePrint('An error occurred configuring Amplify: $e'); } + } - Future _configureAmplify() async { - final api = AmplifyAPI(modelProvider: ModelProvider.instance); - await Amplify.addPlugin(api); - - try { - await Amplify.configure(amplifyconfig); - } on AmplifyAlreadyConfiguredException { - safePrint( - 'Tried to reconfigure Amplify; this can occur when your app restarts on Android.'); - } - } + @override + Widget build(BuildContext context) { + return const MaterialApp( + home: Scaffold( + body: Center( + child: Text('Home'), + ), + ), + ); + } } ``` diff --git a/src/fragments/lib-v1/graphqlapi/flutter/mutate-data.mdx b/src/fragments/lib-v1/graphqlapi/flutter/mutate-data.mdx index 7c3ea90991b..f2be159778a 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/mutate-data.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/mutate-data.mdx @@ -14,7 +14,7 @@ Future updateTodo(Todo originalTodo) async { final request = ModelMutations.update(todoWithNewName); final response = await Amplify.API.mutate(request: request).response; - print('Response: $response'); + safePrint('Response: $response'); } ``` @@ -24,7 +24,7 @@ To delete the `Todo`: Future deleteTodo(Todo todoToDelete) async { final request = ModelMutations.delete(todoToDelete); final response = await Amplify.API.mutate(request: request).response; - print('Response: $response'); + safePrint('Response: $response'); } ``` @@ -33,6 +33,6 @@ Future deleteTodo(Todo todoToDelete) async { Future deleteTodoById(Todo todoToDelete) async { final request = ModelMutations.deleteById(Todo.classType, '8e0dd2fc-2f4a-4dc4-b47f-2052eda10775'); final response = await Amplify.API.mutate(request: request).response; - print('Response: $response'); + safePrint('Response: $response'); } ``` diff --git a/src/fragments/lib-v1/graphqlapi/flutter/query-data.mdx b/src/fragments/lib-v1/graphqlapi/flutter/query-data.mdx index 8f55c9991e0..8edf0a8a278 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/query-data.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/query-data.mdx @@ -4,18 +4,21 @@ Now that you were able to make a mutation, take the `id` from the created `Todo` ```dart Future queryItem(Todo queriedTodo) async { - try { - final request = ModelQueries.get(Todo.classType, queriedTodo.id); - final response = await Amplify.API.query(request: request).response; - final todo = response.data; - if (todo == null) { - print('errors: ${response.errors}'); - } - return todo; - } on ApiException catch (e) { - print('Query failed: $e'); - return null; - } + try { + final request = ModelQueries.get( + Todo.classType, + queriedTodo.modelIdentifier, + ); + final response = await Amplify.API.query(request: request).response; + final todo = response.data; + if (todo == null) { + safePrint('errors: ${response.errors}'); + } + return todo; + } on ApiException catch (e) { + safePrint('Query failed: $e'); + return null; + } } ``` @@ -31,14 +34,14 @@ Future> queryListItems() async { final todos = response.data?.items; if (todos == null) { - print('errors: ${response.errors}'); - return []; + safePrint('errors: ${response.errors}'); + return const []; } return todos; } on ApiException catch (e) { - print('Query failed: $e'); + safePrint('Query failed: $e'); + return const []; } - return []; } ``` @@ -57,7 +60,8 @@ Future> queryPaginatedListItems() async { // Indicates there are > 100 todos and you can get the request for the next set. if (firstPageData?.hasNextResult ?? false) { final secondRequest = firstPageData!.requestForNextResult; - final secondResult = await Amplify.API.query(request: secondRequest!).response; + final secondResult = + await Amplify.API.query(request: secondRequest!).response; return secondResult.data?.items ?? []; } else { return firstPageData?.items ?? []; @@ -76,8 +80,8 @@ Supported operators: - `ge` - greater than or equal - `lt` - less than - `le` - less than or equal -- `between` - Matches models where the given field begins with the provided value. -- `beginsWith` - Matches models where the given field is between the provided start and end values. +- `between` - Matches models where the given field is between the provided start and end values. +- `beginsWith` - Matches models where the given field begins with the provided value. - `contains` - Matches models where the given field contains the provided value. ### Basic Equality Operator @@ -88,7 +92,10 @@ Query for equality on a model's attribute. const blogTitle = 'Test Blog 1'; final queryPredicate = Blog.NAME.eq(blogTitle); -final request = ModelQueries.list(Blog.classType, where: queryPredicate); +final request = ModelQueries.list( + Blog.classType, + where: queryPredicate, +); final response = await Amplify.API.query(request: request).response; final blogFromResponse = response.data?.items.first; ``` @@ -100,8 +107,10 @@ Get all Posts by parent ID ```dart final blogId = blog.id; -final request = - ModelQueries.list(Post.classType, where: Post.BLOG.eq(blogId)); +final request = ModelQueries.list( + Post.classType, + where: Post.BLOG.eq(blogId), +); final response = await Amplify.API.query(request: request).response; final data = response.data?.items ?? []; ``` @@ -112,9 +121,12 @@ Return Posts with a rating less than 5. ```dart const rating = 5; - -final request = ModelQueries.list(Post.classType, where: Post.RATING.lt(rating)); + +final request = ModelQueries.list( + Post.classType, + where: Post.RATING.lt(rating), +); final response = await Amplify.API.query(request: request).response; final data = response.data?.items ?? []; -``` \ No newline at end of file +``` diff --git a/src/fragments/lib-v1/graphqlapi/flutter/subscribe-data.mdx b/src/fragments/lib-v1/graphqlapi/flutter/subscribe-data.mdx index b920d68e3a2..11ced1c1351 100644 --- a/src/fragments/lib-v1/graphqlapi/flutter/subscribe-data.mdx +++ b/src/fragments/lib-v1/graphqlapi/flutter/subscribe-data.mdx @@ -10,13 +10,13 @@ Stream> subscribe() { final Stream> operation = Amplify.API .subscribe( subscriptionRequest, - onEstablished: () => print('Subscription established'), + onEstablished: () => safePrint('Subscription established'), ) // Listens to only 5 elements .take(5) .handleError( - (error) { - print('Error in subscription stream: $error'); + (Object error) { + safePrint('Error in subscription stream: $error'); }, ); return operation; @@ -37,25 +37,150 @@ void subscribe() { final subscriptionRequest = ModelSubscriptions.onCreate(Todo.classType); final Stream> operation = Amplify.API.subscribe( subscriptionRequest, - onEstablished: () => print('Subscription established'), + onEstablished: () => safePrint('Subscription established'), ); subscription = operation.listen( (event) { - print('Subscription event data received: ${event.data}'); + safePrint('Subscription event data received: ${event.data}'); }, - onError: (Object e) => print('Error in subscription stream: $e'), + onError: (Object e) => safePrint('Error in subscription stream: $e'), ); } void unsubscribe() { subscription?.cancel(); + subscription = null; } ``` -Note that in addition to an `onCreate` subscription, you can also call `.onUpdate()` or `.onDelete()`. +In addition to an `onCreate` subscription, you can also call `.onUpdate()` or `.onDelete()`. ```dart final onUpdateSubscriptionRequest = ModelSubscriptions.onUpdate(Todo.classType); // or final onDeleteSubscriptionRequest = ModelSubscriptions.onDelete(Todo.classType); ``` + +## Subscription connection status + +Now that you set up the application and are using subscriptions, you may want to know when the subscription is closed, or reflect to your users when the subscription isn’t healthy. You can monitor the subscription status for changes via `Amplify.Hub` + +```dart +Amplify.Hub.listen( + HubChannel.Api, + (ApiHubEvent event) { + if (event is SubscriptionHubEvent) { + safePrint(event.status); + } + }, +); +``` + +#### SubscriptionStatus + +- **`connected`** - Connected and working with no issues +- **`connecting`** - Attempting to connect (both initial connection and reconnection) +- **`pendingDisconnect`** - Connection has no active subscriptions and is shutting down +- **`disconnected`** - Connection has no active subscriptions and is disconnected +- **`failed`** - Connection had a failure and has been disconnected + +## Automated Reconnection + +Under the hood, we will attempt to maintain a healthy web socket connection through network changes. For example, if a device’s connection changes from Wi-Fi to 5g network, the plugin will attempt to reconnect using the new network. + +Likewise, when disconnected from the internet unexpectedly, the subscription will attempt to reconnect using an exponential retry/back off strategy. By default, we will make 8 recovery attempts over about 50 seconds. If we cannot make a successful connection, then the web socket will be closed. You can customize this strategy when configuring the API plugin through `RetryOptions`. + +```dart +Future _configureAmplify() async { + final apiPlugin = AmplifyAPI( + modelProvider: ModelProvider.instance, + // Optional config + subscriptionOptions: const GraphQLSubscriptionOptions( + retryOptions: RetryOptions(maxAttempts: 10), + ), + ); + await Amplify.addPlugin(apiPlugin); + + try { + await Amplify.configure(amplifyconfig); + } on AmplifyAlreadyConfiguredException { + safePrint( + "Tried to reconfigure Amplify; this can occur when your app restarts on Android."); + } +} +``` + + + +**Important**: While offline, your application will miss messages and will not automatically catch up when reconnection happens. Depending on your use case, you may want to take action to catch up when your app comes back online. The following example solves this problem by retrieving all data on reconnection. + + + +```dart +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:amplify_api/amplify_api.dart'; +import './models/ModelProvider.dart'; // <--- Update import to reflect your project +import 'dart:async'; + +// ... + +List allTodos = []; +SubscriptionStatus prevSubscriptionStatus = SubscriptionStatus.disconnected; +StreamSubscription>? subscription; + +/// ... + +// Init listeners +Amplify.Hub.listen( + HubChannel.Api, + (ApiHubEvent event) { + if (event is SubscriptionHubEvent) { + if (prevSubscriptionStatus == SubscriptionStatus.connecting && + event.status == SubscriptionStatus.connected) { + getTodos(); // refetch todos + } + prevSubscriptionStatus = event.status; + } + }, +); + +subscribe(); + +/// ... + +Future getTodos() async { + try { + final request = ModelQueries.list(Todo.classType); + final response = await Amplify.API.query(request: request).response; + + final todos = response.data?.items ?? []; + if (response.errors.isNotEmpty) { + safePrint('errors: ${response.errors}'); + } + + setState(() { + allTodos = todos; + }); + } on ApiException catch (e) { + safePrint('Query failed: $e'); + return; + } +} + +void subscribe() { + final subscriptionRequest = ModelSubscriptions.onCreate(Todo.classType); + final Stream> operation = Amplify.API.subscribe( + subscriptionRequest, + onEstablished: () => safePrint('Subscription established'), + ); + subscription = operation.listen( + (event) { + setState(() { + allTodos.add(event.data); + }); + }, + onError: (Object e) => safePrint('Error in subscription stream: $e'), + ); +} + +``` diff --git a/src/fragments/lib-v1/graphqlapi/native_common/authz/common.mdx b/src/fragments/lib-v1/graphqlapi/native_common/authz/common.mdx index e9b8cba1557..c6e245b3953 100644 --- a/src/fragments/lib-v1/graphqlapi/native_common/authz/common.mdx +++ b/src/fragments/lib-v1/graphqlapi/native_common/authz/common.mdx @@ -275,10 +275,6 @@ You can now configure a single GraphQL API to deliver private and public data. P As discussed in the above linked documentation, certain fields may be protected by different authorization types. This can lead the same query, mutation, or subscription to have different responses based on the authorization sent with the request; Therefore, it is recommended to use the different `friendly_name_` as the `apiName` parameter in the `Amplify.API` call to reference each authorization type. -import flutter18 from '/src/fragments/lib-v1/graphqlapi/flutter/authz/auth_mode.mdx'; - - - The following snippets highlight the new values in the `amplifyconfiguration.json`/`.dart` and the client code configurations. The `friendly_name` illustrated here is created from Amplify CLI prompt. There are 4 clients in this configuration that connect to the same API except that they use different `AuthMode`. diff --git a/src/fragments/lib-v1/push-notifications/android/getting_started/fcm-cli-resources.mdx b/src/fragments/lib-v1/push-notifications/android/getting_started/fcm-cli-resources.mdx new file mode 100644 index 00000000000..2a42b844f3e --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/android/getting_started/fcm-cli-resources.mdx @@ -0,0 +1,19 @@ +Choose _FCM_ when promoted: + +```console +? Choose the push notification channel to enable. + APNS |  Apple Push Notifications +❯ FCM | » Firebase Push Notifications + In-App Messaging + Email + SMS + +? Provide your pinpoint resource name: + `yourPinpointResourceName` + +? Apps need authorization to send analytics events. Do you want to allow guests and unauthenticated users to send analytics events? (we recommend you allow this when getting started) (Y/n) + 'Y' + +``` + +The CLI will prompt for your _Server Key_, paste the **Token** you copied while [setting up push notification services](/gen1/[platform]/prev/build-a-backend/push-notifications/set-up-push-service/). diff --git a/src/fragments/lib-v1/push-notifications/android/getting_started/fcm-pre-req.mdx b/src/fragments/lib-v1/push-notifications/android/getting_started/fcm-pre-req.mdx new file mode 100644 index 00000000000..807758da472 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/android/getting_started/fcm-pre-req.mdx @@ -0,0 +1 @@ +Push Notifications are delivered via Firebase Cloud Messaging (FCM). In order to use FCM, you need to register your app on the Firebase console. See [Setting up push notification services](/gen1/[platform]/prev/build-a-backend/push-notifications/set-up-push-service/) for more information. diff --git a/src/fragments/lib-v1/push-notifications/android/setup_push_service/setup-fcm.mdx b/src/fragments/lib-v1/push-notifications/android/setup_push_service/setup-fcm.mdx new file mode 100644 index 00000000000..155f8dc74df --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/android/setup_push_service/setup-fcm.mdx @@ -0,0 +1,28 @@ +## Setting Up FCM for Push Notifications + +Push notifications for Android apps are sent using Firebase Cloud Messaging (FCM). Before you can send push notifications to Android devices, you must perform the following steps: + +- [Create a Firebase project](https://firebase.google.com/docs/cloud-messaging/android/first-message#create_a_firebase_project). +- [Register your app with Firebase](https://firebase.google.com/docs/cloud-messaging/android/first-message#register_your_app_with_firebase) +- [Add a Firebase configuration file](https://firebase.google.com/docs/cloud-messaging/android/first-message#add_a_firebase_configuration_file) + +Next, you will need to access your **ServerKey** (Referred to as **ApiKey** in the CLI setup): + +- Open the [Firebase console](https://console.firebase.google.com/). +- Choose your Firebase project. +- Select the gear icon located in the top left hand corner of your screen, then select **Project settings**. + +![image](/images/push-notifications/setup-fcm/project-settings.png) + +- Select the **Cloud Messaging** tab. +- Select the three vertical dots next to **Cloud Messaging API (Legacy)**, then select **Manage API in Google Cloud Console**. ![The three dot menu button is circled in the cloud messaging tab.](/images/push-notifications/firebaseconsole.png) + +![image](/images/push-notifications/setup-fcm/manage-api.png) + +- In the new tab, select the **Enable** button. +- Return to the previous page and refresh it. +- Copy the **Server key** token + +![image](/images/push-notifications/setup-fcm/server-id.png) + +Return to [Provisioning resources through CLI](/gen1/[platform]/prev/build-a-backend/push-notifications/set-up-push-notifications/#provisioning-resources-through-cli) with the copied **Token** diff --git a/src/fragments/lib-v1/push-notifications/common/app-badge-count.mdx b/src/fragments/lib-v1/push-notifications/common/app-badge-count.mdx new file mode 100644 index 00000000000..0aae320132d --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/common/app-badge-count.mdx @@ -0,0 +1,34 @@ +The app badge count, when set, can be seen on your app's icon on a user's device. Amplify provides you with simple helpers to manipulate this number. + + + App badge count helpers are safe to call (but will be ignored) even when your + app is running on platforms where badges are not supported. + + +## Get the current badge count + +Use `getBadgeCount` to get the current app badge count. You might need to do this to calculate the value when setting the badge count. + +import flutterGetBadgeCount from '/src/fragments/lib-v1/push-notifications/flutter/app_badge_count/get-badge-count.mdx'; +import reactNativeGetBadgeCount from '/src/fragments/lib-v1/push-notifications/react-native/app_badge_count/get-badge-count.mdx'; + + + +## Update the badge count + +Use `setBadgeCount` to set the current app badge count. Setting the badge count to `0` (zero) will remove the badge from your app's icon. + +import flutterSetBadgeCount from '/src/fragments/lib-v1/push-notifications/flutter/app_badge_count/set-badge-count.mdx'; +import reactNativeSetBadgeCount from '/src/fragments/lib-v1/push-notifications/react-native/app_badge_count/set-badge-count.mdx'; + + diff --git a/src/fragments/lib-v1/push-notifications/react-native/enable_rich_notifications/enable-rich-notifications.mdx b/src/fragments/lib-v1/push-notifications/common/enable-rich-notifications.mdx similarity index 81% rename from src/fragments/lib-v1/push-notifications/react-native/enable_rich_notifications/enable-rich-notifications.mdx rename to src/fragments/lib-v1/push-notifications/common/enable-rich-notifications.mdx index 7d9466f5550..31032b8acd3 100644 --- a/src/fragments/lib-v1/push-notifications/react-native/enable_rich_notifications/enable-rich-notifications.mdx +++ b/src/fragments/lib-v1/push-notifications/common/enable-rich-notifications.mdx @@ -27,9 +27,15 @@ Amplify currently supports adding images to your notifications, but there are so 2. Add `AmplifyUtilsNotifications` to the extension you created above. -import addNotificationsPod from '/src/fragments/lib-v1/push-notifications/react-native/enable_rich_notifications/add-notifications-pod.mdx'; - - +import flutterAddNotificationsPod from '/src/fragments/lib-v1/push-notifications/flutter/enable_rich_notifications/add-notifications-pod.mdx'; +import reactNativeAddNotificationsPod from '/src/fragments/lib-v1/push-notifications/react-native/enable_rich_notifications/add-notifications-pod.mdx'; + + 4. Open `.xcworkspace` located inside the `/ios` folder of your application project with Xcode. diff --git a/src/fragments/lib-v1/push-notifications/react-native/getting_started/cross-platform-prereq.mdx b/src/fragments/lib-v1/push-notifications/common/getting_started/cross-platform-prereq.mdx similarity index 81% rename from src/fragments/lib-v1/push-notifications/react-native/getting_started/cross-platform-prereq.mdx rename to src/fragments/lib-v1/push-notifications/common/getting_started/cross-platform-prereq.mdx index 861dd81592e..0839ad3bae0 100644 --- a/src/fragments/lib-v1/push-notifications/react-native/getting_started/cross-platform-prereq.mdx +++ b/src/fragments/lib-v1/push-notifications/common/getting_started/cross-platform-prereq.mdx @@ -5,7 +5,7 @@ You should have [completed the CLI and project setup steps.](/gen1/[platform]/pr An application targeting at least iOS 13.0, using Xcode 14.1 or later. -import apnsPreReq from '/src/fragments/lib/push-notifications/ios/getting_started/apns-pre-req.mdx'; +import apnsPreReq from '/src/fragments/lib-v1/push-notifications/ios/getting_started/apns-pre-req.mdx'; @@ -18,7 +18,7 @@ Using Amplify Push Notifications with APNs requires the following capabilities: To add these capabilities: -import iosSetEntitlements from '/src/fragments/lib/push-notifications/ios/getting_started/ios-set-entitlements.mdx'; +import iosSetEntitlements from '/src/fragments/lib-v1/push-notifications/ios/getting_started/ios-set-entitlements.mdx'; @@ -27,7 +27,7 @@ import iosSetEntitlements from '/src/fragments/lib/push-notifications/ios/gettin An application targeting at least Android SDK API level 24. -import fcmPreReq from '/src/fragments/lib/push-notifications/android/getting_started/fcm-pre-req.mdx'; +import fcmPreReq from '/src/fragments/lib-v1/push-notifications/android/getting_started/fcm-pre-req.mdx'; diff --git a/src/fragments/lib-v1/push-notifications/common/getting_started/getting-started.mdx b/src/fragments/lib-v1/push-notifications/common/getting_started/getting-started.mdx new file mode 100644 index 00000000000..da8e0de1ea3 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/common/getting_started/getting-started.mdx @@ -0,0 +1,91 @@ +The Push Notifications category allows you to integrate push notifications in your app with Amazon Pinpoint targeting, campaign, and journey management support. You can segment your users, trigger push notifications to your app, and record metrics in Pinpoint when users receive or open notifications. Amazon Pinpoint helps you to create messaging campaigns and journeys targeted to specific user segments or demographics and collect interaction metrics with push notifications. + +import reactNativeExpoCallout from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/expo-callout.mdx'; + + + +## Prerequisites + +import crossPlatformPreReq from '/src/fragments/lib-v1/push-notifications/common/getting_started/cross-platform-prereq.mdx'; + + + +## Set up backend resources + +To use Push Notifications with Amplify, you have the option to either have the Amplify CLI setup resources for you, or you can use an existing Amazon Pinpoint resource in your AWS account. + + + + +> Prerequisite: [Install and configure the Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) + + + +Push Notifications requires version **10.8.0+** of the Amplify CLI. You can check your current version with `amplify -version` and upgrade to the latest version with `amplify upgrade`. + + + +To start provisioning push notification resources in the backend, go to your project directory and execute the command: + +```sh +amplify add notifications +``` + +import flutterCliResources from '/src/fragments/lib-v1/push-notifications/flutter/getting_started/20_cli_resources.mdx'; +import reactNativeCliResources from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/20_cli_resources.mdx'; + + + + + + +import flutterExistingResources from '/src/fragments/lib-v1/push-notifications/flutter/getting_started/30_existing_resources.mdx'; +import reactNativeExistingResources from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/30_existing_resources.mdx'; + + + + + + +## Install Amplify Libraries + +import flutterInstallLib from '/src/fragments/lib-v1/push-notifications/flutter/getting_started/40_install_lib.mdx'; +import reactNativeInstallLib from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/40_install_lib.mdx'; + + + +import reactNativeIntegrateNativeModule from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/50_integrate_native_modules.mdx'; + + + +## Initialize Amplify Push Notifications + +import flutterInitPN from '/src/fragments/lib-v1/push-notifications/flutter/getting_started/50_init_push_notifications.mdx'; +import reactNativeInitPN from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/60_init_push_notifications.mdx'; + + diff --git a/src/fragments/lib-v1/push-notifications/common/identify-user.mdx b/src/fragments/lib-v1/push-notifications/common/identify-user.mdx new file mode 100644 index 00000000000..0eafd6a930d --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/common/identify-user.mdx @@ -0,0 +1,27 @@ +This call identifies the current user (which could be unauthenticated or authenticated) to Amazon Pinpoint. The user ID can be any string which identifies the user in the context of your application. + +## Get the user ID from Amplify Auth + +import flutterGetUser from '/src/fragments/lib-v1/push-notifications/flutter/identify_user/10_get_auth_user.mdx'; +import reactNativeGetUser from '/src/fragments/lib-v1/push-notifications/react-native/identify_user/10_get_auth_user.mdx'; + + + +## Identify the user to Amazon Pinpoint + +Once you have a string that identifies the current user (either from the Auth category as shown above or through your own application logic), you can identify the user to Amazon Pinpoint with the following: + +import flutterSendToPinpoint from '/src/fragments/lib-v1/push-notifications/flutter/identify_user/20_send_to_pinpoint.mdx'; +import reactNativeSendToPinpoint from '/src/fragments/lib-v1/push-notifications/react-native/identify_user/20_send_to_pinpoint.mdx'; + + diff --git a/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/interact-with-notifications.mdx b/src/fragments/lib-v1/push-notifications/common/interact_with_notifications/interact-with-notifications.mdx similarity index 83% rename from src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/interact-with-notifications.mdx rename to src/fragments/lib-v1/push-notifications/common/interact_with_notifications/interact-with-notifications.mdx index 90e56e17cf2..8c10c0a96ce 100644 --- a/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/interact-with-notifications.mdx +++ b/src/fragments/lib-v1/push-notifications/common/interact_with_notifications/interact-with-notifications.mdx @@ -4,7 +4,7 @@ Push notifications are powerful engagement tools for your users as they can be d - **Background state**: Your app is still running but is not currently active and visible. The user is usually on the home screen or in another app. - **Terminated state**: Your app is no longer running, even in the background. The user can initiate this by swiping your app away in the app switcher. -import notificationLifecycle from '/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/notification-lifecycle.mdx'; +import notificationLifecycle from '/src/fragments/lib-v1/push-notifications/common/interact_with_notifications/notification-lifecycle.mdx'; -import notificationReceived from '/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/notification-received.mdx'; +import notificationReceived from '/src/fragments/lib-v1/push-notifications/common/interact_with_notifications/notification-received.mdx'; -import notificationOpened from '/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/notification-opened.mdx'; +import notificationOpened from '/src/fragments/lib-v1/push-notifications/common/interact_with_notifications/notification-opened.mdx'; +import flutterTerminology from '/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/terminology.mdx'; +import reactNativeTerminology from '/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/terminology.mdx'; + + ### In a foreground state diff --git a/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/notification-opened.mdx b/src/fragments/lib-v1/push-notifications/common/interact_with_notifications/notification-opened.mdx similarity index 63% rename from src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/notification-opened.mdx rename to src/fragments/lib-v1/push-notifications/common/interact_with_notifications/notification-opened.mdx index b91abb22fc5..8e0de17153c 100644 --- a/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/notification-opened.mdx +++ b/src/fragments/lib-v1/push-notifications/common/interact_with_notifications/notification-opened.mdx @@ -13,9 +13,15 @@ To help you with this, Amplify provides two ways of handling notifications being Add `onNotificationOpened` listeners to respond to a push notification being opened while your app is in a foreground **or** background state. -import onNotificationOpened from '/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/on-notification-opened.mdx'; +import flutterOnNotificationOpened from '/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/on-notification-opened.mdx'; +import reactNativeOnNotificationOpened from '/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/on-notification-opened.mdx'; - + ### getLaunchNotification @@ -27,6 +33,12 @@ Calling `getLaunchNotification` _consumes_ the launch notification and will yiel - Another notification was opened while your app was running (either in foreground or background) - Your app was brought back to the foreground by some other means (e.g. user tapped the app icon) -import getLaunchNotification from '/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/get-launch-notification.mdx'; +import flutterGetLaunchNotification from '/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/get-launch-notification.mdx'; +import reactNativeGetLaunchNotification from '/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/get-launch-notification.mdx'; - + diff --git a/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/notification-received.mdx b/src/fragments/lib-v1/push-notifications/common/interact_with_notifications/notification-received.mdx similarity index 64% rename from src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/notification-received.mdx rename to src/fragments/lib-v1/push-notifications/common/interact_with_notifications/notification-received.mdx index 9e38c559b18..489bbe3bd03 100644 --- a/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/notification-received.mdx +++ b/src/fragments/lib-v1/push-notifications/common/interact_with_notifications/notification-received.mdx @@ -15,9 +15,15 @@ Notifications received while your app is in the foreground state do not get disp Add `onNotificationReceivedInForeground` listeners to respond to a push notification being received while your app is in a foreground state. -import onForegroundNotification from '/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/on-foreground-notification.mdx'; +import flutterOnForegroundNotification from '/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/on-foreground-notification.mdx'; +import reactNativeOnForegroundNotification from '/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/on-foreground-notification.mdx'; - + ### Notification received in background @@ -27,6 +33,12 @@ Add `onNotificationReceivedInBackground` listeners to respond to a push notifica For background notifications to be handled while your app is terminated, it is important to note: -import onBackgroundNotification from '/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/on-background-notification.mdx'; +import flutterOnBackgroundNotification from '/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/on-background-notification.mdx'; +import reactNativeOnBackgroundNotification from '/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/on-background-notification.mdx'; - + diff --git a/src/fragments/lib-v1/push-notifications/react-native/receive-device-token.mdx b/src/fragments/lib-v1/push-notifications/common/receive-device-token.mdx similarity index 61% rename from src/fragments/lib-v1/push-notifications/react-native/receive-device-token.mdx rename to src/fragments/lib-v1/push-notifications/common/receive-device-token.mdx index 4306662f232..88d87abc058 100644 --- a/src/fragments/lib-v1/push-notifications/react-native/receive-device-token.mdx +++ b/src/fragments/lib-v1/push-notifications/common/receive-device-token.mdx @@ -9,12 +9,12 @@ A token will be received by your app: - On every app launch, including the first install - When a token changes (this may happen if the service invalidates the token for any reason) -```js -const myTokenReceivedHandler = (token) => { - // Do something with the received token -}; +import flutterOnTokenReceived from '/src/fragments/lib-v1/push-notifications/flutter/receive_device_token/on-token-received.mdx'; +import reactNativeOnTokenReceived from '/src/fragments/lib-v1/push-notifications/react-native/receive_device_token/on-token-received.mdx'; -const listener = Notifications.Push.onTokenReceived(myTokenReceivedHandler); - -listener.remove(); // Remember to remove the listener when it is no longer needed -``` + diff --git a/src/fragments/lib-v1/push-notifications/react-native/request-permissions.mdx b/src/fragments/lib-v1/push-notifications/common/request-permissions.mdx similarity index 62% rename from src/fragments/lib-v1/push-notifications/react-native/request-permissions.mdx rename to src/fragments/lib-v1/push-notifications/common/request-permissions.mdx index b6eed9f6641..1c9a56ebf5b 100644 --- a/src/fragments/lib-v1/push-notifications/react-native/request-permissions.mdx +++ b/src/fragments/lib-v1/push-notifications/common/request-permissions.mdx @@ -11,15 +11,15 @@ The first step to request permissions from your user is to understand the curren - **Granted** - Permissions have been granted by the user. No further actions are needed and their app is ready to display notifications. - **Denied** - Permissions have been denied by the user. Further attempts to request permissions will no longer trigger a permission dialog. Your app should now either degrade gracefully or prompt your user to grant the permissions needed in their device settings. - - If you use TypeScript for your development, the string values returned can be - represented as PushNotificationPermissionStatus enum members as well. - +import flutterGetPermissionStatus from '/src/fragments/lib-v1/push-notifications/flutter/request_permissions/get-permission-status.mdx'; +import reactNativeGetPermissionStatus from '/src/fragments/lib-v1/push-notifications/react-native/request_permissions/get-permission-status.mdx'; -```js -const status = await Notifications.Push.getPermissionStatus(); -// 'SHOULD_REQUEST' | 'SHOULD_EXPLAIN_THEN_REQUEST' | 'GRANTED' | 'DENIED' -``` + ## Request permissions @@ -36,48 +36,26 @@ Amplify requests all supported notification permissions by default. But you can - **Sound**: When set to true, requests the ability to play a sound in response to notifications. - **Badge**: When set to true, requests the ability to update the app's badge. -```js -const permissions = { - // permissions are true by default - // alert: true - sound: false, - badge: false -}; +import flutterRequestPermissions from '/src/fragments/lib-v1/push-notifications/flutter/request_permissions/request-permissions.mdx'; +import reactNativeRequestPermissions from '/src/fragments/lib-v1/push-notifications/react-native/request_permissions/request-permissions.mdx'; -const result = await Notifications.Push.requestPermissions(permissions); -// true if granted (or already granted), false otherwise -``` + ## Sample permissions flow Use `getPermissionStatus()` and `requestPermissions()` together to handle permission request flows. Below is a sample implementation of the expected logic. - - Remember, if you use TypeScript for your development, you can use the - PushNotificationPermissionStatus enum for comparison as well! - +import flutterSamplePermissionsFlow from '/src/fragments/lib-v1/push-notifications/flutter/request_permissions/sample-permissions-flow.mdx'; +import reactNativeSamplePermissionsFlow from '/src/fragments/lib-v1/push-notifications/react-native/request_permissions/sample-permissions-flow.mdx'; -```js -async function handlePermissions() { - const status = await Notifications.Push.getPermissionStatus(); - if (status === 'GRANTED') { - // no further action is required, user has already granted permissions - return; - } - if (status === 'DENIED') { - // further attempts to request permissions will no longer do anything - myFunctionToGracefullyDegradeMyApp(); - return; - } - if (status === 'SHOULD_REQUEST') { - // go ahead and request permissions from the user - await Notifications.Push.requestPermissions(); - } - if (status === 'SHOULD_EXPLAIN_THEN_REQUEST') { - // you should display some explanation to your user before requesting permissions - await myFunctionExplainingPermissionsRequest(); - // then request permissions - await Notifications.Push.requestPermissions(); - } -} -``` + diff --git a/src/fragments/lib-v1/push-notifications/common/setup_push_service/cross-platform-setup.mdx b/src/fragments/lib-v1/push-notifications/common/setup_push_service/cross-platform-setup.mdx new file mode 100644 index 00000000000..fb804f2fa37 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/common/setup_push_service/cross-platform-setup.mdx @@ -0,0 +1,16 @@ + + + +import apnsSetup from '/src/fragments/lib-v1/push-notifications/ios/setup_push_service/setup-apns.mdx'; + + + + + + +import fcmSetup from '/src/fragments/lib-v1/push-notifications/android/setup_push_service/setup-fcm.mdx'; + + + + + diff --git a/src/fragments/lib-v1/push-notifications/common/setup_push_service/setup-push-service.mdx b/src/fragments/lib-v1/push-notifications/common/setup_push_service/setup-push-service.mdx new file mode 100644 index 00000000000..9ebb560a581 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/common/setup_push_service/setup-push-service.mdx @@ -0,0 +1,8 @@ +import crossPlatformSetupService from '/src/fragments/lib-v1/push-notifications/common/setup_push_service/cross-platform-setup.mdx'; + + diff --git a/src/fragments/lib-v1/push-notifications/common/testing.mdx b/src/fragments/lib-v1/push-notifications/common/testing.mdx new file mode 100644 index 00000000000..ebffa1395c2 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/common/testing.mdx @@ -0,0 +1 @@ +You can create messaging campaigns and send push notifications to your app with Amazon Pinpoint! Just follow these instructions on [Amazon Pinpoint User Guide](https://docs.aws.amazon.com/pinpoint/latest/userguide/messages-mobile.html) for the next steps. diff --git a/src/fragments/lib-v1/push-notifications/flutter/app_badge_count/get-badge-count.mdx b/src/fragments/lib-v1/push-notifications/flutter/app_badge_count/get-badge-count.mdx new file mode 100644 index 00000000000..caa63be458d --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/app_badge_count/get-badge-count.mdx @@ -0,0 +1,3 @@ +```dart +final count = await Amplify.Notifications.Push.getBadgeCount(); +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/app_badge_count/set-badge-count.mdx b/src/fragments/lib-v1/push-notifications/flutter/app_badge_count/set-badge-count.mdx new file mode 100644 index 00000000000..7ff9be6aac9 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/app_badge_count/set-badge-count.mdx @@ -0,0 +1,3 @@ +```dart +Amplify.Notifications.Push.setBadgeCount(42); +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/enable_rich_notifications/add-notifications-pod.mdx b/src/fragments/lib-v1/push-notifications/flutter/enable_rich_notifications/add-notifications-pod.mdx new file mode 100644 index 00000000000..3c26a9e9cf0 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/enable_rich_notifications/add-notifications-pod.mdx @@ -0,0 +1,19 @@ +```ruby +target 'MyNotificationServiceExtension' do # Replace with your service extension + use_frameworks! + + pod "AmplifyUtilsNotifications" +end +``` + +3. Install the new pods by running `pod install` in the `ios/` of your application project. + + + + Note: You will first need to [install Cocoapods](https://guides.cocoapods.org/using/getting-started.html) to enable the `pod` command. + + +```bash +cd ios +pod install +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/getting_started/20_cli_resources.mdx b/src/fragments/lib-v1/push-notifications/flutter/getting_started/20_cli_resources.mdx new file mode 100644 index 00000000000..2bbd0015fc0 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/getting_started/20_cli_resources.mdx @@ -0,0 +1,20 @@ + + + +import apnsCliResources from '/src/fragments/lib-v1/push-notifications/ios/getting_started/apns-cli-resources.mdx'; + + + +Upon completion, `amplifyconfiguration.dart` will be updated to reference the newly provisioned backend push notifications resources. Note that this file should already be generated for you by the Amplify CLI as a part of your project if you followed the [project setup walkthrough](/gen1/[platform]/prev/start/project-setup/prerequisites/). + + + + +import fcmCliResources from '/src/fragments/lib-v1/push-notifications/android/getting_started/fcm-cli-resources.mdx'; + + + +Upon completion, `amplifyconfiguration.dart` will be updated to reference the newly provisioned backend push notifications resources. Note that this file should already be generated for you by the Amplify CLI as a part of your project if you followed the [project setup walkthrough](/gen1/[platform]/prev/start/project-setup/prerequisites/). + + + diff --git a/src/fragments/lib-v1/push-notifications/flutter/getting_started/30_existing_resources.mdx b/src/fragments/lib-v1/push-notifications/flutter/getting_started/30_existing_resources.mdx new file mode 100644 index 00000000000..fadde219181 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/getting_started/30_existing_resources.mdx @@ -0,0 +1,17 @@ +Existing Amazon Pinpoint resources can be used with Amplify Push Notifications by configuring `amplifyconfiguration.dart` with **Application ID** and **Region**. + +```dart +{ + "notifications": { + "plugins": { + "awsPinpointPushNotificationsPlugin": { + "appId": "", + "region": "" + } + } + } +} +``` + +- **appId**: Amazon Pinpoint application ID +- **region**: AWS Region where the resources are provisioned (e.g. `us-east-1`) diff --git a/src/fragments/lib-v1/push-notifications/flutter/getting_started/40_install_lib.mdx b/src/fragments/lib-v1/push-notifications/flutter/getting_started/40_install_lib.mdx new file mode 100644 index 00000000000..56d59da635c --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/getting_started/40_install_lib.mdx @@ -0,0 +1,14 @@ +In your project directory, you should first install the necessary dependencies for using Amplify Push Notifications. + +1. Open `pubspec.yaml` at the root of your Flutter project with a text editor. + +2. Add the necessary libraries into the `dependencies` block: + +```yaml +dependencies: + amplify_auth_cognito: ^1.0.0 + amplify_flutter: ^1.0.0 + amplify_push_notifications_pinpoint: ^1.0.0 + flutter: + sdk: flutter +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/getting_started/50_init_push_notifications.mdx b/src/fragments/lib-v1/push-notifications/flutter/getting_started/50_init_push_notifications.mdx new file mode 100644 index 00000000000..efc780e3857 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/getting_started/50_init_push_notifications.mdx @@ -0,0 +1,48 @@ +To initialize Amplify Push Notifications, you will need to configure Amplify add the Auth and Push Notifications plugins. To complete initialization, call `Amplify.configure` + +Your resulting code should look something like the following: + +```dart +// Example main.dart + +import 'package:amplify_auth_cognito/amplify_auth_cognito.dart'; +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:amplify_push_notifications_pinpoint/amplify_push_notifications_pinpoint.dart'; + +import 'amplifyconfiguration.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({Key? key}) : super(key: key); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + @override + void initState() { + super.initState(); + _configureAmplify(); + } + + Future _configureAmplify() async { + try { + final authPlugin = AmplifyAuthCognito(); + final pushPlugin = AmplifyPushNotificationsPinpoint(); + await Amplify.addPlugins([authPlugin, pushPlugin]); + await Amplify.configure(amplifyconfig); + } on Exception catch (e) { + safePrint('An error occurred configuring Amplify: $e'); + } + } + + @override + Widget build(BuildContext context) { + // Your application UI + } +} +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/identify_user/10_get_auth_user.mdx b/src/fragments/lib-v1/push-notifications/flutter/identify_user/10_get_auth_user.mdx new file mode 100644 index 00000000000..5888548398c --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/identify_user/10_get_auth_user.mdx @@ -0,0 +1,7 @@ +If the user is signed in through [Amplify.Auth.signIn](/gen1/flutter/prev/build-a-backend/auth/enable-sign-in/), then you can retrieve the current user's ID as shown below: + +```dart +final user = await Amplify.Auth.getCurrentUser(); + +final userId = user.userId +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/identify_user/20_send_to_pinpoint.mdx b/src/fragments/lib-v1/push-notifications/flutter/identify_user/20_send_to_pinpoint.mdx new file mode 100644 index 00000000000..c87bd36d22c --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/identify_user/20_send_to_pinpoint.mdx @@ -0,0 +1,12 @@ +```dart +final userProfile = AWSPinpointUserProfile( + userAttributes: { + 'hobbies': ['cooking', 'knitting'] + }, +); + +await Amplify.Notifications.Push.identifyUser( + userId: userId, + userProfile: userProfile, +); +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/get-launch-notification.mdx b/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/get-launch-notification.mdx new file mode 100644 index 00000000000..bfb5c0bbea3 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/get-launch-notification.mdx @@ -0,0 +1,5 @@ +```dart +final launchNotification = Amplify.Notifications.Push.launchNotification; + +... // Take further action with the `launchNotification` +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/on-background-notification.mdx b/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/on-background-notification.mdx new file mode 100644 index 00000000000..0092f590427 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/on-background-notification.mdx @@ -0,0 +1,33 @@ +1. You should add this listener to your app's `main` function but _before_ `runApp` and `Amplify.configure` to avoid missing events. +2. The listener function added should be a [top-level global](https://dart.dev/language/functions#lexical-scope) or [static method](https://dart.dev/language/classes#static-methods) so that it can be invoked even when the app is terminated. +3. Notifications received in a terminated state will be considered low priority by Android unless you increase the priority of the payload by setting the `priority` to `high` (you will need to create a _Raw Message_ in Amazon Pinpoint to set this priority). + +```dart +// Example main.dart + +// Note: This handler does not *need* to be async, but it can be! +Future myAsyncNotificationReceivedHandler( + PushNotificationMessage notification) async { + // Process the received push notification message in the background +} + +void main() { + // Needed to enable background API in the killed state. + WidgetsFlutterBinding.ensureInitialized(); + + final authPlugin = AmplifyAuthCognito(); + final notificationsPlugin = AmplifyPushNotificationsPinpoint(); + + // Should be added in the main function to avoid missing events. + notificationsPlugin.onNotificationReceivedInBackground( + myAsyncNotificationReceivedHandler + ); + + await Amplify.addPlugins([authPlugin, notificationsPlugin]); + if (!Amplify.isConfigured) { + await Amplify.configure(amplifyconfig); + } + + runApp(const MyApp()); +} +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/on-foreground-notification.mdx b/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/on-foreground-notification.mdx new file mode 100644 index 00000000000..6b5c1e96ca0 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/on-foreground-notification.mdx @@ -0,0 +1,12 @@ +```dart +void myNotificationReceivedHandler(PushNotificationMessage notification) { + // Respond to the received push notification message in realtime +} + +final subscription = Amplify + .Notifications.Push.onNotificationReceivedInForeground + .listen(myNotificationReceivedHandler); + +// Remember to cancel the subscription when it is no longer needed +subscription.cancel(); +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/on-notification-opened.mdx b/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/on-notification-opened.mdx new file mode 100644 index 00000000000..727f13ccdf4 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/on-notification-opened.mdx @@ -0,0 +1,11 @@ +```dart +void myNotificationOpenedHandler(PushNotificationMessage notification) { + // Take further action with the opened push notification message +} + +final subscription = Amplify.Notifications.Push.onNotificationOpened + .listen(myNotificationReceivedHandler); + +// Remember to cancel the subscription when it is no longer needed +subscription.cancel(); +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/terminology.mdx b/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/terminology.mdx new file mode 100644 index 00000000000..232c986ed1a --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/interact_with_notifications/terminology.mdx @@ -0,0 +1,2 @@ +- **Native layer** - This is the native (e.g. iOS or Android) application layer on top of which Flutter apps are built. +- **Application layer** - This is your Flutter app. diff --git a/src/fragments/lib-v1/push-notifications/flutter/receive_device_token/on-token-received.mdx b/src/fragments/lib-v1/push-notifications/flutter/receive_device_token/on-token-received.mdx new file mode 100644 index 00000000000..414ce087d00 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/receive_device_token/on-token-received.mdx @@ -0,0 +1,11 @@ +```dart +void myTokenReceivedHandler(String token) { + // Do something with the received token +} + +final subscription = Amplify.Notifications.Push.onTokenReceived + .listen(myTokenReceivedHandler); + +// Remember to cancel the subscription when it is no longer needed +subscription.cancel(); +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/request_permissions/get-permission-status.mdx b/src/fragments/lib-v1/push-notifications/flutter/request_permissions/get-permission-status.mdx new file mode 100644 index 00000000000..5dbd90ef4aa --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/request_permissions/get-permission-status.mdx @@ -0,0 +1,7 @@ +```dart +final status = await Amplify.Notifications.Push.getPermissionStatus(); +// PushNotificationPermissionStatus.shouldRequest | +// PushNotificationPermissionStatus.shouldExplainThenRequest | +// PushNotificationPermissionStatus.granted | +// PushNotificationPermissionStatus.denied +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/request_permissions/request-permissions.mdx b/src/fragments/lib-v1/push-notifications/flutter/request_permissions/request-permissions.mdx new file mode 100644 index 00000000000..73ff8a187b3 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/request_permissions/request-permissions.mdx @@ -0,0 +1,9 @@ +```dart +final result = await Amplify.Notifications.Push.requestPermissions( + // permissions are true by default + // alert: true + badge: true, + sound: true, +); +// true if granted (or already granted), false otherwise +``` diff --git a/src/fragments/lib-v1/push-notifications/flutter/request_permissions/sample-permissions-flow.mdx b/src/fragments/lib-v1/push-notifications/flutter/request_permissions/sample-permissions-flow.mdx new file mode 100644 index 00000000000..f70a2885a17 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/flutter/request_permissions/sample-permissions-flow.mdx @@ -0,0 +1,21 @@ +```dart +void handlePermissions() async { + final status = await Amplify.Notifications.Push.getPermissionStatus(); + switch (status) { + case PushNotificationPermissionStatus.granted: + // no further action is required, user has already granted permissions + break; + case PushNotificationPermissionStatus.denied: + // further attempts to request permissions will no longer do anything + continueWithoutPushNotifications(); + case PushNotificationPermissionStatus.shouldRequest: + // go ahead and request permissions from the user + await Amplify.Notifications.Push.requestPermissions(); + case PushNotificationPermissionStatus.shouldExplainThenRequest: + // you should display some explanation to your user before requesting permissions + await explainUpcomingPermissionRequest(); + // then request permissions + await Amplify.Notifications.Push.requestPermissions(); + } +} +``` diff --git a/src/fragments/lib-v1/push-notifications/ios/getting_started/apns-cli-resources.mdx b/src/fragments/lib-v1/push-notifications/ios/getting_started/apns-cli-resources.mdx new file mode 100644 index 00000000000..d207f6c5a53 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/ios/getting_started/apns-cli-resources.mdx @@ -0,0 +1,48 @@ +Choose _APNS_ when promoted: + +```console +? Choose the push notification channel to enable. +❯ APNS |  Apple Push Notifications + FCM | » Firebase Push Notifications + In-App Messaging + Email + SMS +``` + +Follow the setup prompts: + + + +Authorizing the app for analytics events is crucial for unauthenticated users, particularly if you intend to configure Amplify or send push notifications to your users before their authentication. If authorization is declined, please keep in mind that updating the Cognito user pool would become necessary, and any subsequent updates could potentially result in the deletion of the current user data. + + + +```console +? Provide your pinpoint resource name: › [pinpoint_resource_name] +? Apps need authorization to send analytics events. Do you want to allow guests + and unauthenticated users to send analytics events? (we recommend you allow this + when getting started) (Y/n) +``` + +Choose _Certificate_ when promoted: + +```console +? Choose authentication method used for APNs +> Certificate +Key +``` + +The CLI will prompt for your _p12 certificate path_, enter a path relative to the location where you ran the command. + + + + +Note: If you receive this error: +```console +🛑 Command failed: openssl pkcs12 -in [path_to_your_cert].p12 -out [output_path].pem -nodes -passin pass: +Error outputting keys and certificates +``` + +Please try using LibreSSL 3.3.6 instead of OpenSSL. [See issue #12969](https://github.com/aws-amplify/amplify-cli/issues/12969) + + \ No newline at end of file diff --git a/src/fragments/lib-v1/push-notifications/ios/getting_started/apns-pre-req.mdx b/src/fragments/lib-v1/push-notifications/ios/getting_started/apns-pre-req.mdx new file mode 100644 index 00000000000..d7f1d9b0e50 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/ios/getting_started/apns-pre-req.mdx @@ -0,0 +1,7 @@ +Push Notifications are delivered via the Apple Push Notification service (APNs). In order to use APNs, you need to setup credentials (keys or certificates) and export your credentials to a p12 file. See [Setting up push notification services](/gen1/[platform]/prev/build-a-backend/push-notifications/set-up-push-service/) for more information. + + + +Testing with APNs on the iOS simulator requires an Apple Silicon Mac running macOS 13+, Xcode 14+, and iOS simulator 16+. If your development environment does not meet all these requirements, then you must run on a real device to get an APNs token. + + diff --git a/src/fragments/lib-v1/push-notifications/ios/getting_started/ios-set-entitlements.mdx b/src/fragments/lib-v1/push-notifications/ios/getting_started/ios-set-entitlements.mdx new file mode 100644 index 00000000000..b98709a1ffd --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/ios/getting_started/ios-set-entitlements.mdx @@ -0,0 +1,25 @@ +1. Open your Xcode project, go to project settings, select your main application target, select the **Signing and Capabilities** tab, and click **+ Capability** in the upper left corner of the editor pane to bring up the Capabilities dialog. + +![Arrow pointing to the plus capability icon.](/images/push-notifications/xcode-entitlements/01_add-capability.png) + + + +Xcode can be a little finicky with this step. If the Capabilities dialog is empty, try switching to a different tab and then switching back to **Signing and Capabilities** + + + +2. Type **push** in the filter box and double-click **Push Notifications** to add the capability. + +![The word push typed into the search bar capabilities and push notifications is a result.](/images/push-notifications/xcode-entitlements/02_add-push.png) + +3. Repeat step 1 to open the Capabilities dialog and then type **back** in the filter box and double-click **Background Modes** to add the capability. + +![The word back typed into the search bar and background modes is a result.](/images/push-notifications/xcode-entitlements/03_add-background.png) + +4. Select **Change All** when prompted. + +5. Under **Background Modes**, select **Remote Notifications** + +![Remote notifications box is selected in the background modes section.](/images/push-notifications/xcode-entitlements/04_background-remote-notifications.png) + +6. Select **Change All** again when prompted. diff --git a/src/fragments/lib-v1/push-notifications/ios/setup_push_service/setup-apns.mdx b/src/fragments/lib-v1/push-notifications/ios/setup_push_service/setup-apns.mdx new file mode 100644 index 00000000000..e0eab422d15 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/ios/setup_push_service/setup-apns.mdx @@ -0,0 +1,160 @@ +## Setting Up APNs for Push Notifications + +Push notifications for iOS apps are sent using Apple Push Notification service (APNs). Before you can send push notifications to iOS devices, you must create an app ID on the Apple Developer portal, and you must create the required certificates. + +This section describes how to use the Apple Developer portal to obtain iOS and APNs credentials. These credentials enable you to create an iOS project that can receive push notifications. + +After completing the steps in this section, you will have the following items in your Apple Developer account: + +- An app ID. +- An SSL certificate, which authorizes you to send push notifications to your app through APNs. +- A registration for your test device, such as an iPhone, with your Apple Developer account. +- An iOS distribution certificate, which enables you to install your app on your test device. +- A provisioning profile, which allows your app to run on your test device. + +Before you begin, you must have an account with the Apple Developer Program as an individual or as part of an organization, and you must have agent or admin privileges in that account. + +**Topics** + +- [Step 1: Create an App ID](#step-1-create-an-app-id) +- [Step 2: Create an APNs SSL Certificate](#step-2-create-an-apns-ssl-certificate) +- [Step 3: Register a Test Device](#step-3-register-a-test-device) +- [Step 4: Create an iOS Distribution Certificate](#step-4-create-an-ios-distribution-certificate) +- [Step 5: Create a Provisioning Profile](#step-5-create-a-provisioning-profile) + +### Step 1: Create an App ID + +Create an app ID to identify your app in the Apple Developer portal. You need this ID when you create an SSL certificate for sending push notifications, when you create an iOS distribution certificate, and when you create a provisioning profile. + +If you already have an ID assigned to your app, you can skip this step. You can use an existing app ID provided it doesn't contain a wildcard character ("\*"). + +**To assign an App ID to your app** + +- Sign in to your [Apple Developer account](https://developer.apple.com/membercenter/index.action) and navigate to [Identifiers](https://developer.apple.com/account/resources/identifiers/list) +- In the **Identifiers** pane, click the **Register an App ID** button, or click the blue **(+)** button in the header. + +![image](/images/push-notifications/setup-apns/create-identifier--add.png) + +- In the **Register a new identifier** pane, Select **App IDs** and click **Continue**. + +![image](/images/push-notifications/setup-apns/create-identifier--app-id.png) + +- In the **Register a new identifier** pane, Select **App** and click **Continue**. + +![image](/images/push-notifications/setup-apns/create-identifier--app-type.png) + +- In the **Registering an App ID** pane, for **Description**, type a name for your app ID that makes it easy to recognize later. +- For **App ID Prefix**, select **Explicit**, and type a bundle ID for your app. If you already have an app, use the bundle ID assigned to it. You can find this ID in the app project in Xcode on your Mac. Otherwise, take note of the bundle ID because you will assign it to your app in Xcode later. + +![image](/images/push-notifications/setup-apns/create-identifier--bundle-id.png) + +- Under **Capabilities**, select **Push Notifications**. + +![image](/images/push-notifications/setup-apns/create-identifier--enable-PN.png) + +- Click **Continue**. In the **Confirm your App ID** pane, check that all values were entered correctly. +- Click **Register** to register the new app ID. + +### Step 2: Create an APNs SSL Certificate + +You will create and download a certificate from your Apple Developer account. Then, you will install the certificate in Keychain Access and export it as a `.p12` file. Which will allow Pinpoint to communicate with Apple's Push Notification Service (APNS). + +If you already have an SSL certificate for your app, you can skip this step. + +**To create an SSL certificate for push notifications** + +- Under **Certificates, Identifiers & Profiles**, navigate to [Identifiers](https://developer.apple.com/account/resources/identifiers/list) +- From the list of iOS Identifiers, click the app that you created in [Step 1: Create an App ID](#step-1-create-an-app-id). +- Under **Capabilities**, locate **Push Notifications**, click **Configure** + +![image](/images/push-notifications/setup-apns/create-certificate--configure.png) + +- In the **Production SSL Certificate** section, click **Create Certificate**. + +![image](/images/push-notifications/setup-apns/create-certificate--create.png) + +- Follow these [instructions](https://developer.apple.com/help/account/create-certificates/create-a-certificate-signing-request) for creating a local Certificate Signing Request (CSR) file using the Keychain Access application on your Mac. + +![image](/images/push-notifications/setup-apns/create-certificate--cert-assistant.png) + +- In the **Create a New Certificate** pane, click **Choose File**, and then select the CSR file you created and click **Continue**. + +![image](/images/push-notifications/setup-apns/create-certificate--upload-cert.png) + +- When the certificate is ready, click **Download** to save the certificate to your computer. It should be called `aps.cer`. +- Double-click the downloaded certificate to install it to the Keychain on your Mac. +- On your Mac, start the Keychain Access application. +- Click the **My Certificates** chip in the toolbar to find the certificate you just added. The certificate will be named "Apple Push Services:`com.my.app.id`", where `com.my.app.id` is the bundle ID you configured in the previous step. + +![image](/images/push-notifications/setup-apns/create-certificate--my-cert.png) + +- Right-click the push certificate and then select **Export "Apple Push Services:`com.my.app.id`"** from the context menu to export a file containing the certificate. + +![image](/images/push-notifications/setup-apns/create-certificate--export-cert.png) + +- Name the file `amplify` and ensure that `p12` is selected as the export format, then save it to your computer. Click **OK** to skip adding a password to the certificate. Then enter your computer password to complete the export. + + + +Note: The remaining steps are done automatically in Xcode when the "Automatically manage signing" box is checked. + + + +### Step 3: Register a Test Device + +Register a test device with your Apple Developer account so that you can test your app on that device. Later, you associate this test device with your provisioning profile, which allows your app to launch on your device. + +If you already have a registered device, you can skip this step. + +**To add a device** + +- Under **Certificates, Identifiers & Profiles**, navigate to [Devices](https://developer.apple.com/account/resources/devices/list) +- In the **Devices** pane, click the **Register a Device** button, or click the blue **(+)** button in the header. +- In the **Register Device** section, choose the platform of device that you want to add, such as **iOS**. +- For **Device Name**, type a name that is easy to recognize later. +- For **Device ID (UDID)**, type the unique device ID. For an iPhone, you can find the UDID by completing the following steps: + + - Connect your iPhone to your Mac with a USB cable. + - Open the Xcode app. + - In the Xcode menu bar navigate to **Window > Devices and Simulators** + - Xcode will display the summary page for your connected devices, select desired device. + - In the header, the summary page provides information about your iPhone. The value next to **Identifier: ** is your **UDID**. + - Select and copy your **UDID**. + - Paste your UDID into the **UDID** field in the Apple Developer website. + - Click **Continue**. + +- On the **Register a New Device** pane, verify the details for your device, and click **Register**. +- Your device name and identifier have been added to the list of devices. Click **Done**. + +### Step 4: Create an iOS Distribution Certificate + +An iOS distribution certificate enables you to install your app on a test device and deliver push notifications to that device. You specify your iOS distribution certificate later when you create a provisioning profile for your app. + +If you already have an iOS distribution certificate, you can skip this step. + +**To create an iOS distribution certificate** + +- Under **Certificates, Identifiers & Profiles**, navigate to [Certificates](https://developer.apple.com/account/resources/certificates/list) +- In the **Certificates** pane, click the blue **(+)** button in the header. The **Create a New Certificate** pane is shown. +- In the **Software** section, select **iOS Distribution (App Store and Ad Hoc)**, and then click **Continue**. +- Follow these [instructions](https://developer.apple.com/help/account/create-certificates/create-a-certificate-signing-request) for creating a Certificate Signing Request (CSR) file. You use the Keychain Access application on your Mac to create the request and save it on your local disk. +- In the **Create a New Certificate** pane, click **Choose File**, and then select the CSR file you created and click **Continue**. +- When the certificate is ready, click **Download** to save the certificate to your computer. +- Double-click the downloaded certificate to install it in Keychain on your Mac. + +### Step 5: Create a Provisioning Profile + +A provisioning profile allows your app to run on your test device. You create and download a provisioning profile from your Apple Developer account and then install the provisioning profile in Xcode. + +**To create a provisioning profile** + +- Under **Certificates, Identifiers & Profiles**, navigate to [Profiles](https://developer.apple.com/account/resources/profiles/list) +- In the **Profiles** pane, click the **Generate a profile** button, or click the blue **(+)** button in the header. The **Register a New Provisioning Profile** pane is shown. +- Under the **Distribution** section, select **Ad Hoc**, and then click **Continue**. +- For **Select an App ID**, select the app ID you created for your app, and then click **Continue**. +- Select your iOS Distribution certificate and then click **Continue**. +- For **Select devices**, select the device that you registered for testing, and click **Continue**. +- Type a name for this provisioning profile, such as `ApnsDistributionProfile`, and click **Generate**. +- Click **Download** to download the generated provisioning profile. +- Install the provisioning profile by double-clicking the downloaded file. The Xcode app opens in response. +- To verify that the provisioning profile is installed, check the list of installed provisioning profiles in Xcode by following this [guide](https://help.apple.com/xcode/mac/current/#/devaafd622d2). diff --git a/src/fragments/lib-v1/push-notifications/react-native/app_badge_count/app-badge-count.mdx b/src/fragments/lib-v1/push-notifications/react-native/app_badge_count/app-badge-count.mdx deleted file mode 100644 index 486f663c7bf..00000000000 --- a/src/fragments/lib-v1/push-notifications/react-native/app_badge_count/app-badge-count.mdx +++ /dev/null @@ -1,22 +0,0 @@ -The app badge count, when set, can be seen on your app's icon on a user's device. Amplify provides you with simple helpers to manipulate this number. - - - App badge count helpers are safe to call (but will be ignored) even when your - app is running on platforms where badges are not supported. - - -## Get the current badge count - -Use `getBadgeCount` to get the current app badge count. You might need to do this to calculate the value when setting the badge count. - -import getBadgeCount from '/src/fragments/lib-v1/push-notifications/react-native/app_badge_count/get-badge-count.mdx'; - - - -## Update the badge count - -Use `setBadgeCount` to set the current app badge count. Setting the badge count to `0` (zero) will remove the badge from your app's icon. - -import setBadgeCount from '/src/fragments/lib-v1/push-notifications/react-native/app_badge_count/set-badge-count.mdx'; - - diff --git a/src/fragments/lib-v1/push-notifications/react-native/getting_started/20_cli_resources.mdx b/src/fragments/lib-v1/push-notifications/react-native/getting_started/20_cli_resources.mdx index 5119fcd55c4..3c8ce6fa6dd 100644 --- a/src/fragments/lib-v1/push-notifications/react-native/getting_started/20_cli_resources.mdx +++ b/src/fragments/lib-v1/push-notifications/react-native/getting_started/20_cli_resources.mdx @@ -1,7 +1,7 @@ -import apnsCliResources from '/src/fragments/lib/push-notifications/ios/getting_started/apns-cli-resources.mdx'; +import apnsCliResources from '/src/fragments/lib-v1/push-notifications/ios/getting_started/apns-cli-resources.mdx'; @@ -10,7 +10,7 @@ Upon completion, `aws-exports.js` will be updated to reference the newly provisi -import fcmCliResources from '/src/fragments/lib/push-notifications/android/getting_started/fcm-cli-resources.mdx'; +import fcmCliResources from '/src/fragments/lib-v1/push-notifications/android/getting_started/fcm-cli-resources.mdx'; diff --git a/src/fragments/lib-v1/push-notifications/react-native/getting_started/getting-started.mdx b/src/fragments/lib-v1/push-notifications/react-native/getting_started/getting-started.mdx deleted file mode 100644 index a20ed1b4860..00000000000 --- a/src/fragments/lib-v1/push-notifications/react-native/getting_started/getting-started.mdx +++ /dev/null @@ -1,62 +0,0 @@ -The Push Notifications category allows you to integrate push notifications in your app with Amazon Pinpoint targeting, campaign, and journey management support. You can segment your users, trigger push notifications to your app, and record metrics in Pinpoint when users receive or open notifications. Amazon Pinpoint helps you to create messaging campaigns and journeys targeted to specific user segments or demographics and collect interaction metrics with push notifications. - -import expoCallout from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/expo-callout.mdx'; - - - -## Prerequisites - -import crossPlatformPreReq from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/cross-platform-prereq.mdx'; - - - -## Set up backend resources - -To use Push Notifications with Amplify, you have the option to either have the Amplify CLI setup resources for you, or you can use an existing Amazon Pinpoint resource in your AWS account. - - - - -> Prerequisite: [Install and configure the Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) - - - -Push Notifications requires version **10.8.0+** of the Amplify CLI. You can check your current version with `amplify -version` and upgrade to the latest version with `amplify upgrade`. - - - -To start provisioning push notification resources in the backend, go to your project directory and execute the command: - -```sh -amplify add notifications -``` - -import cliResources from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/20_cli_resources.mdx'; - - - - - - -import existingResources from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/30_existing_resources.mdx'; - - - - - - -## Install Amplify Libraries - -import installLib from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/40_install_lib.mdx'; - - - -import integrateNativeModule from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/50_integrate_native_modules.mdx'; - - - -## Initialize Amplify Push Notifications - -import initPN from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/60_init_push_notifications.mdx'; - - diff --git a/src/fragments/lib-v1/push-notifications/react-native/identify_user/identify-user.mdx b/src/fragments/lib-v1/push-notifications/react-native/identify_user/identify-user.mdx deleted file mode 100644 index af229e891a6..00000000000 --- a/src/fragments/lib-v1/push-notifications/react-native/identify_user/identify-user.mdx +++ /dev/null @@ -1,15 +0,0 @@ -This call identifies the current user (which could be unauthenticated or authenticated) to Amazon Pinpoint. The user ID can be any string which identifies the user in the context of your application. - -## Get the user ID from Amplify Auth - -import getUser from '/src/fragments/lib-v1/push-notifications/react-native/identify_user/10_get_auth_user.mdx'; - - - -## Identify the user to Amazon Pinpoint - -Once you have a string that identifies the current user (either from the Auth category as shown above or through your own application logic), you can identify the user to Amazon Pinpoint with the following: - -import sendToPinpoint from '/src/fragments/lib-v1/push-notifications/react-native/identify_user/20_send_to_pinpoint.mdx'; - - diff --git a/src/fragments/lib-v1/push-notifications/react-native/receive_device_token/on-token-received.mdx b/src/fragments/lib-v1/push-notifications/react-native/receive_device_token/on-token-received.mdx new file mode 100644 index 00000000000..35f763cb4fc --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/react-native/receive_device_token/on-token-received.mdx @@ -0,0 +1,9 @@ +```js +const myTokenReceivedHandler = (token) => { + // Do something with the received token +}; + +const listener = Notifications.Push.onTokenReceived(myTokenReceivedHandler); + +listener.remove(); // Remember to remove the listener when it is no longer needed +``` diff --git a/src/fragments/lib-v1/push-notifications/react-native/request_permissions/get-permission-status.mdx b/src/fragments/lib-v1/push-notifications/react-native/request_permissions/get-permission-status.mdx new file mode 100644 index 00000000000..f5a63cf7320 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/react-native/request_permissions/get-permission-status.mdx @@ -0,0 +1,9 @@ + + If you use TypeScript for your development, the string values returned can be + represented as PushNotificationPermissionStatus enum members as well. + + +```js +const status = await Notifications.Push.getPermissionStatus(); +// 'SHOULD_REQUEST' | 'SHOULD_EXPLAIN_THEN_REQUEST' | 'GRANTED' | 'DENIED' +``` diff --git a/src/fragments/lib-v1/push-notifications/react-native/request_permissions/request-permissions.mdx b/src/fragments/lib-v1/push-notifications/react-native/request_permissions/request-permissions.mdx new file mode 100644 index 00000000000..216c3a505c1 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/react-native/request_permissions/request-permissions.mdx @@ -0,0 +1,11 @@ +```js +const permissions = { + // permissions are true by default + // alert: true + sound: false, + badge: false +}; + +const result = await Notifications.Push.requestPermissions(permissions); +// true if granted (or already granted), false otherwise +``` diff --git a/src/fragments/lib-v1/push-notifications/react-native/request_permissions/sample-permissions-flow.mdx b/src/fragments/lib-v1/push-notifications/react-native/request_permissions/sample-permissions-flow.mdx new file mode 100644 index 00000000000..241c0e40118 --- /dev/null +++ b/src/fragments/lib-v1/push-notifications/react-native/request_permissions/sample-permissions-flow.mdx @@ -0,0 +1,29 @@ + + Remember, if you use TypeScript for your development, you can use the + PushNotificationPermissionStatus enum for comparison as well! + + +```js +async function handlePermissions() { + const status = await Notifications.Push.getPermissionStatus(); + if (status === 'GRANTED') { + // no further action is required, user has already granted permissions + return; + } + if (status === 'DENIED') { + // further attempts to request permissions will no longer do anything + myFunctionToGracefullyDegradeMyApp(); + return; + } + if (status === 'SHOULD_REQUEST') { + // go ahead and request permissions from the user + await Notifications.Push.requestPermissions(); + } + if (status === 'SHOULD_EXPLAIN_THEN_REQUEST') { + // you should display some explanation to your user before requesting permissions + await myFunctionExplainingPermissionsRequest(); + // then request permissions + await Notifications.Push.requestPermissions(); + } +} +``` diff --git a/src/fragments/lib-v1/restapi/flutter/delete.mdx b/src/fragments/lib-v1/restapi/flutter/delete.mdx index 4876e72f0d2..98061be5ff8 100644 --- a/src/fragments/lib-v1/restapi/flutter/delete.mdx +++ b/src/fragments/lib-v1/restapi/flutter/delete.mdx @@ -1,17 +1,15 @@ ## DELETE requests ```dart - Future deleteTodo() async { - try { - const options = RestOptions(path: '/todo/1'); - final restOperation = Amplify.API.delete(restOptions: options); - final response = await restOperation.response; - print('DELETE call succeeded'); - print(response.body); - } on ApiException catch (e) { - print('DELETE call failed: $e'); - } +Future deleteTodo() async { + try { + final restOperation = Amplify.API.delete('todo/1'); + final response = await restOperation.response; + safePrint('DELETE call succeeded: ${response.decodeBody()}'); + } on ApiException catch (e) { + safePrint('DELETE call failed: $e'); } +} ``` diff --git a/src/fragments/lib-v1/restapi/flutter/fetch.mdx b/src/fragments/lib-v1/restapi/flutter/fetch.mdx index e694249c2cd..51dff8cb570 100644 --- a/src/fragments/lib-v1/restapi/flutter/fetch.mdx +++ b/src/fragments/lib-v1/restapi/flutter/fetch.mdx @@ -5,10 +5,9 @@ To make a GET request use `Amplify.API.get`: ```dart Future getTodo() async { try { - const options = RestOptions(path: '/todo'); - final restOperation = Amplify.API.get(restOptions: options); + final restOperation = Amplify.API.get('todo'); final response = await restOperation.response; - print('GET call succeeded: ${response.body}'); + print('GET call succeeded: ${response.decodeBody()}'); } on ApiException catch (e) { print('GET call failed: $e'); } @@ -56,14 +55,12 @@ Then you can use query parameters in your path as follows: ```dart Future getTodo() async { try { - const options = RestOptions( - path: '/todo', - queryParameters: {'q' : 'test'}, + final restOperation = Amplify.API.get( + 'todo', + queryParameters: {'q': 'test'}, ); - final restOperation = Amplify.API.get(restOptions: options); final response = await restOperation.response; - print('GET call succeeded'); - print(response.body); + print('GET call succeeded: ${response.decodeBody()}'); } on ApiException catch (e) { print('GET call failed: $e'); } diff --git a/src/fragments/lib-v1/restapi/flutter/getting-started/10_preReq.mdx b/src/fragments/lib-v1/restapi/flutter/getting-started/10_preReq.mdx index 649105f720b..16c98fc9109 100644 --- a/src/fragments/lib-v1/restapi/flutter/getting-started/10_preReq.mdx +++ b/src/fragments/lib-v1/restapi/flutter/getting-started/10_preReq.mdx @@ -1,5 +1,3 @@ * [Install and configure Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) -* A Flutter application targeting Flutter SDK >= 2.10.0 with Amplify libraries integrated - * An iOS configuration targeting at least iOS 11.0 - * An Android configuration targeting at least Android API level 21 (Android 5.0) or above - * For a full example please follow the [project setup walkthrough](/gen1/[platform]/prev/start/project-setup/create-application/) + +Amplify Flutter 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](/gen1/[platform]/prev/start/project-setup/platform-setup/) guide for more details on platform specific setup. diff --git a/src/fragments/lib-v1/restapi/flutter/getting-started/20_installLib.mdx b/src/fragments/lib-v1/restapi/flutter/getting-started/20_installLib.mdx index 846577c8c89..37263d971e6 100644 --- a/src/fragments/lib-v1/restapi/flutter/getting-started/20_installLib.mdx +++ b/src/fragments/lib-v1/restapi/flutter/getting-started/20_installLib.mdx @@ -1,13 +1,10 @@ Add the following dependencies to your `pubspec.yaml` file and install dependencies when asked. Please keep in mind that Auth plugin is needed for IAM authorization mode, which is default for REST API: ```yaml -environment: - sdk: ">=2.15.0 <3.0.0" - dependencies: flutter: sdk: flutter - amplify_flutter: ^0.6.0 - amplify_api: ^0.6.0 - amplify_auth_cognito: ^0.6.0 + amplify_flutter: ^1.0.0 + amplify_api: ^1.0.0 + amplify_auth_cognito: ^1.0.0 ``` diff --git a/src/fragments/lib-v1/restapi/flutter/getting-started/40_postTodo.mdx b/src/fragments/lib-v1/restapi/flutter/getting-started/40_postTodo.mdx index 865100e4080..6da2cc0630b 100644 --- a/src/fragments/lib-v1/restapi/flutter/getting-started/40_postTodo.mdx +++ b/src/fragments/lib-v1/restapi/flutter/getting-started/40_postTodo.mdx @@ -1,20 +1,15 @@ ```dart -import 'dart:typed_data'; - Future postTodo() async { try { - final options = RestOptions( - path: '/todo', - body: Uint8List.fromList('{\'name\':\'Mow the lawn\'}'.codeUnits) - ); final restOperation = Amplify.API.post( - restOptions: options + 'todo', + body: HttpPayload.json({'name': 'Mow the lawn'}), ); final response = await restOperation.response; print('POST call succeeded'); - print(response.body); + print(response.decodeBody()); } on ApiException catch (e) { print('POST call failed: $e'); } } -``` \ No newline at end of file +``` diff --git a/src/fragments/lib-v1/restapi/flutter/update.mdx b/src/fragments/lib-v1/restapi/flutter/update.mdx index 97fec79acf4..1b8ae841c41 100644 --- a/src/fragments/lib-v1/restapi/flutter/update.mdx +++ b/src/fragments/lib-v1/restapi/flutter/update.mdx @@ -5,16 +5,15 @@ To update an item via the API endpoint: ```dart Future updateTodo() async { try { - final options = RestOptions( - path: '/todo/1', - body: Uint8List.fromList('{\'name\':\'Mow the lawn\'}'.codeUnits), + final restOperation = Amplify.API.put( + 'todo/1', + body: HttpPayload.json({'name': 'Mow the lawn'}), ); - final restOperation = Amplify.API.put(restOptions: options); final response = await restOperation.response; print('PUT call succeeded'); - print(response.body); + print(response.decodeBody()); } on ApiException catch (e) { print('PUT call failed: $e'); } } -``` \ No newline at end of file +``` diff --git a/src/fragments/lib-v1/storage/existing-resources.mdx b/src/fragments/lib-v1/storage/existing-resources.mdx index 6452da3f114..65a5a304967 100644 --- a/src/fragments/lib-v1/storage/existing-resources.mdx +++ b/src/fragments/lib-v1/storage/existing-resources.mdx @@ -6,7 +6,7 @@ amplify import storage For more details, see how to [Use an existing S3 bucket or DynamoDB table](/gen1/[platform]/build-a-backend/storage/import/). -If you are not using the Amplify CLI, an existing Amazon S3 bucket can be used by referencing it in your `amplifyconfiguration.json` file. +If you are not using the Amplify CLI, an existing Amazon S3 bucket can be used by referencing it in your Amplify configuration file. When you are not using Amplify CLI, adding existing Amazon S3 bucket to your application may require configuring bucket access permissions. e.g. Enabling read/write access to the cognito user pool that you are using with the Amplify Auth category. diff --git a/src/fragments/lib-v1/storage/flutter/configureaccess/10_protected_upload.mdx b/src/fragments/lib-v1/storage/flutter/configureaccess/10_protected_upload.mdx index 076422e037b..005422a52ea 100644 --- a/src/fragments/lib-v1/storage/flutter/configureaccess/10_protected_upload.mdx +++ b/src/fragments/lib-v1/storage/flutter/configureaccess/10_protected_upload.mdx @@ -1,35 +1,25 @@ ```dart -import 'dart:io'; -import 'package:path_provider/path_provider.dart'; +import 'package:amplify_flutter/amplify_flutter.dart'; -Future uploadProtected() async { - // Create a dummy file - const exampleString = 'Example file contents'; - final tempDir = await getTemporaryDirectory(); - final exampleFile = File(tempDir.path + '/example.txt') - ..createSync() - ..writeAsStringSync(exampleString); - - // Set the access level to `protected` for the current user - // Note: A user must be logged in through Cognito Auth - // for this to work. - final uploadOptions = S3UploadFileOptions( +Future uploadProtectedFile({ + required String filePath, + required String key, +}) async { + final awsFile = AWSFile.fromPath(filePath); + const options = StorageUploadFileOptions( accessLevel: StorageAccessLevel.protected, ); - // Upload the file to S3 with protected access try { - final UploadFileResult result = await Amplify.Storage.uploadFile( - local: exampleFile, - key: 'ExampleKey', - options: uploadOptions, - onProgress: (progress) { - safePrint('Fraction completed: ${progress.getFractionCompleted()}'); - } - ); - safePrint('Successfully uploaded file: ${result.key}'); + final uploadResult = await Amplify.Storage.uploadFile( + localFile: awsFile, + key: key, + options: options, + ).result; + safePrint('Uploaded file: ${uploadResult.uploadedItem.key}'); } on StorageException catch (e) { - safePrint('Error uploading protected file: $e'); + safePrint('Something went wrong uploading file: ${e.message}'); + rethrow; } } -``` \ No newline at end of file +``` diff --git a/src/fragments/lib-v1/storage/flutter/configureaccess/20_protected_download.mdx b/src/fragments/lib-v1/storage/flutter/configureaccess/20_protected_download.mdx index 43323c9bb1b..e5eacf5dffd 100644 --- a/src/fragments/lib-v1/storage/flutter/configureaccess/20_protected_download.mdx +++ b/src/fragments/lib-v1/storage/flutter/configureaccess/20_protected_download.mdx @@ -1,35 +1,33 @@ ```dart -import 'dart:io'; -import 'package:path_provider/path_provider.dart'; +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:amplify_storage_s3/amplify_storage_s3.dart'; -Future downloadProtected(String cognitoIdentityId) async { - // Create a file to store downloaded contents - final documentsDir = await getApplicationDocumentsDirectory(); - final filepath = documentsDir.path + '/example.txt'; - final file = File(filepath); - - // Set access level and Cognito Identity ID. - // Note: `targetIdentityId` is only needed when downloading - // protected files of a user other than the one currently - // logged in. - final downloadOptions = S3DownloadFileOptions( +Future downloadProtectedFile({ + required String key, + required String targetIdentityId, + required String downloadPath, +}) async { + final awsFile = AWSFile.fromPath(downloadPath); + final options = StorageDownloadFileOptions( + // specify that the file has a protected access level accessLevel: StorageAccessLevel.protected, - - // e.g. us-west-2:2f41a152-14d1-45ff-9715-53e20751c7ee - targetIdentityId: cognitoIdentityId, + // specify the identity ID of the user who uploaded this file + pluginOptions: S3DownloadFilePluginOptions.forIdentity( + targetIdentityId, + ), ); - // Download protected file and read contents try { - await Amplify.Storage.downloadFile( - key: 'ExampleKey', - local: file, - options: downloadOptions, - ); - final contents = file.readAsStringSync(); - safePrint('Got protected file with contents: $contents'); + final result = await Amplify.Storage.downloadFile( + key: key, + localFile: awsFile, + options: options, + ).result; + + safePrint('Downloaded file is located at: ${result.localFile.path}'); } on StorageException catch (e) { - safePrint('Error downloading protected file: $e'); + safePrint('Something went wrong downloading the file: ${e.message}'); + rethrow; } } -``` \ No newline at end of file +``` diff --git a/src/fragments/lib-v1/storage/flutter/configureaccess/30_private_upload.mdx b/src/fragments/lib-v1/storage/flutter/configureaccess/30_private_upload.mdx index eca58ec392c..d0d2370a31e 100644 --- a/src/fragments/lib-v1/storage/flutter/configureaccess/30_private_upload.mdx +++ b/src/fragments/lib-v1/storage/flutter/configureaccess/30_private_upload.mdx @@ -1,20 +1,26 @@ ```dart -Future uploadPrivateFile() async { - ... - // Update the uploadOptions - final uploadOptions = S3UploadFileOptions( - // Add the private access level +import 'package:amplify_flutter/amplify_flutter.dart'; + +Future uploadPrivateFile({ + required String filePath, + required String key, +}) async { + final awsFile = AWSFile.fromPath(filePath); + const options = StorageUploadFileOptions( accessLevel: StorageAccessLevel.private, ); try { - final UploadFileResult result = await Amplify.Storage.uploadFile( - ... - //Be sure to use the options - options: uploadOptions, - ... - ); - safePrint('Successfully uploaded file: ${result.key}'); - }... + final uploadResult = await Amplify.Storage.uploadFile( + localFile: awsFile, + key: key, + options: options, + ).result; + + safePrint('Uploaded file: ${uploadResult.uploadedItem.key}'); + } on StorageException catch (e) { + safePrint('Something went wrong uploading file: ${e.message}'); + rethrow; + } } ``` diff --git a/src/fragments/lib-v1/storage/flutter/configureaccess/40_private_download.mdx b/src/fragments/lib-v1/storage/flutter/configureaccess/40_private_download.mdx index 9d92e716c0b..1cbd1a7ca6d 100644 --- a/src/fragments/lib-v1/storage/flutter/configureaccess/40_private_download.mdx +++ b/src/fragments/lib-v1/storage/flutter/configureaccess/40_private_download.mdx @@ -1,19 +1,26 @@ ```dart -Future downloadPrivateFile(String cognitoIdentityId) async { - ... - final downloadOptions = S3DownloadFileOptions( - // Add the private access level - accessLevel: StorageAccessLevel.private - ... +import 'package:amplify_flutter/amplify_flutter.dart'; + +Future downloadPrivateFile({ + required String key, + required String downloadPath, +}) async { + final awsFile = AWSFile.fromPath(downloadPath); + const options = StorageDownloadFileOptions( + accessLevel: StorageAccessLevel.private, ); try { - await Amplify.Storage.downloadFile( - ... - // Be sure to use the correct options - options: downloadOptions, - ); - ... - }... + final result = await Amplify.Storage.downloadFile( + key: key, + localFile: awsFile, + options: options, + ).result; + + safePrint('Downloaded file is located at: ${result.localFile.path}'); + } on StorageException catch (e) { + safePrint('Something went wrong downloading the file: ${e.message}'); + rethrow; + } } ``` diff --git a/src/fragments/lib-v1/storage/flutter/copy.mdx b/src/fragments/lib-v1/storage/flutter/copy.mdx new file mode 100644 index 00000000000..0b603a8f400 --- /dev/null +++ b/src/fragments/lib-v1/storage/flutter/copy.mdx @@ -0,0 +1,28 @@ +You can copy an existing file to a different location in your S3 bucket. User who initiates a copy operation should have read permission on the copy source file. + +```dart +import 'package:amplify_flutter/amplify_flutter.dart'; + +Future copy({ + required String sourceKey, + required String destinationKey, +}) async { + try { + final result = await Amplify.Storage.copy( + source: StorageItemWithAccessLevel( + storageItem: StorageItem(key: sourceKey), + accessLevel: StorageAccessLevel.guest, + ), + destination: StorageItemWithAccessLevel( + storageItem: StorageItem(key: destinationKey), + accessLevel: StorageAccessLevel.private, + ), + ).result; + + safePrint('Copied file: ${result.copiedItem.key}'); + } on StorageException catch (e) { + safePrint('Error copying file: ${e.message}'); + rethrow; + } +} +``` diff --git a/src/fragments/lib-v1/storage/flutter/download.mdx b/src/fragments/lib-v1/storage/flutter/download.mdx index f31bcfeea8e..e7b4a508985 100644 --- a/src/fragments/lib-v1/storage/flutter/download.mdx +++ b/src/fragments/lib-v1/storage/flutter/download.mdx @@ -6,46 +6,104 @@ You can download file to a local directory using `Amplify.Storage.downloadFile`. You can use the [path_provider](https://pub.dev/packages/path_provider) package to create a local file in the user's documents directory where you can store the downloaded data. + + + + ```dart -import 'dart:io'; import 'package:path_provider/path_provider.dart'; -Future downloadFile() async { +Future downloadToLocalFile(String key) async { final documentsDir = await getApplicationDocumentsDirectory(); final filepath = documentsDir.path + '/example.txt'; - final file = File(filepath); + try { + final result = await Amplify.Storage.downloadFile( + key: key, + localFile: AWSFile.fromPath(filepath), + onProgress: (progress) { + safePrint('Fraction completed: ${progress.fractionCompleted}'); + }, + ).result; + safePrint('Downloaded file is located at: ${result.localFile.path}'); + } on StorageException catch (e) { + safePrint(e.message); + } +} +``` + + + + + +On Web, the download process will be handled by the browser. You can provide the downloaded file name by specifying the `path` parameter of `AWSFile.fromPath`. E.g. this instructs the browser to download the file `download.txt`. + +```dart +Future downloadToLocalFileOnWeb(String key) async { try { final result = await Amplify.Storage.downloadFile( - key: 'ExampleKey', - local: file, + key: key, + localFile: AWSFile.fromPath('download.txt'), + ).result; + + safePrint('Downloaded file: ${result.downloadedItem.key}'); + } on StorageException catch (e) { + safePrint(e.message); + } +} +``` + + + + + +## Download data + +You can download a file to in-memory buffer with `Amplify.Storage.downloadData`: + +```dart +Future downloadToMemory(String key) async { + try { + final result = await Amplify.Storage.downloadData( + key: key, onProgress: (progress) { - safePrint('Fraction completed: ${progress.getFractionCompleted()}'); + safePrint('Fraction completed: ${progress.fractionCompleted}'); }, - ); - final contents = result.file.readAsStringSync(); - // Or you can reference the file that is created above - // final contents = file.readAsStringSync(); - safePrint('Downloaded contents: $contents'); + ).result; + + safePrint('Downloaded data: ${result.bytes}'); } on StorageException catch (e) { - safePrint('Error downloading file: $e'); + safePrint(e.message); } } ``` ## Generate a download URL -You can get a downloadable URL for the file in storage by its key using `Amplify.Storage.getUrl`. +You can get a downloadable URL for the file in storage by its key and access level. + +When creating a downloadable URL, you can choose to check if the file exists by setting `validateObjectExistence` to `true` in `S3GetUrlPluginOptions`. If the file is inaccessible or does not exist, a `StorageException` is thrown. This allows you to check if an object exists during generating the presigned URL, which you can then use to download that object. ```dart -Future getDownloadUrl() async { +Future getDownloadUrl({ + required String key, + required StorageAccessLevel accessLevel, +}) async { try { - final result = await Amplify.Storage.getUrl(key: 'ExampleKey'); - // NOTE: This code is only for demonstration - // Your debug console may truncate the printed url string - safePrint('Got URL: ${result.url}'); + final result = await Amplify.Storage.getUrl( + key: key, + options: const StorageGetUrlOptions( + accessLevel: accessLevel, + pluginOptions: S3GetUrlPluginOptions( + validateObjectExistence: true, + expiresIn: Duration(days: 1), + ), + ), + ).result; + return result.url.toString(); } on StorageException catch (e) { - safePrint('Error getting download URL: $e'); + safePrint('Could not get a downloadable URL: ${e.message}'); + rethrow; } } ``` diff --git a/src/fragments/lib-v1/storage/flutter/get-properties.mdx b/src/fragments/lib-v1/storage/flutter/get-properties.mdx new file mode 100644 index 00000000000..a2d601a3778 --- /dev/null +++ b/src/fragments/lib-v1/storage/flutter/get-properties.mdx @@ -0,0 +1,18 @@ +You can get file properties and metadata without downloading the file using `Amplify.Storage.getProperties`. + +```dart +import 'package:amplify_flutter/amplify_flutter.dart'; + +Future getFileProperties() async { + try { + final result = await Amplify.Storage.getProperties( + key: 'example.txt', + ).result; + + safePrint('File size: ${result.storageItem.size}'); + } on StorageException catch (e) { + safePrint('Could not retrieve properties: ${e.message}'); + rethrow; + } +} +``` diff --git a/src/fragments/lib-v1/storage/flutter/getting-started/10_preReq.mdx b/src/fragments/lib-v1/storage/flutter/getting-started/10_preReq.mdx index 145d78787e1..16c98fc9109 100644 --- a/src/fragments/lib-v1/storage/flutter/getting-started/10_preReq.mdx +++ b/src/fragments/lib-v1/storage/flutter/getting-started/10_preReq.mdx @@ -1,6 +1,3 @@ -* A Flutter application targeting Flutter SDK >= 2.10.0 (stable version) with Amplify libraries integrated - * An iOS configuration targeting at least iOS 11.0 - * An Android configuration targeting at least Android API level 21 (Android 5.0) or above - * For a full example please follow the [project setup walkthrough](/gen1/[platform]/prev/start/project-setup/create-application/) +* [Install and configure Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) -You can see [an example Amplify Storage + Flutter application here](https://github.com/aws-amplify/amplify-flutter/tree/main/packages/storage/amplify_storage_s3/example). +Amplify Flutter 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](/gen1/[platform]/prev/start/project-setup/platform-setup/) guide for more details on platform specific setup. diff --git a/src/fragments/lib-v1/storage/flutter/getting-started/20_installLib.mdx b/src/fragments/lib-v1/storage/flutter/getting-started/20_installLib.mdx index 482751e9412..b98ab0d90aa 100644 --- a/src/fragments/lib-v1/storage/flutter/getting-started/20_installLib.mdx +++ b/src/fragments/lib-v1/storage/flutter/getting-started/20_installLib.mdx @@ -1,13 +1,11 @@ Add the following dependency to your **app**'s `pubspec.yaml` along with others you added above in **Prerequisites**: ```yaml -environment: - sdk: ">=2.15.0 <3.0.0" - dependencies: - amplify_auth_cognito: ^0.6.0 - amplify_flutter: ^0.6.0 - amplify_storage_s3: ^0.6.0 flutter: sdk: flutter + + amplify_auth_cognito: ^1.0.0 + amplify_flutter: ^1.0.0 + amplify_storage_s3: ^1.0.0 ``` diff --git a/src/fragments/lib-v1/storage/flutter/list.mdx b/src/fragments/lib-v1/storage/flutter/list.mdx index 85c4dedc4c2..98387d0c559 100644 --- a/src/fragments/lib-v1/storage/flutter/list.mdx +++ b/src/fragments/lib-v1/storage/flutter/list.mdx @@ -1,15 +1,64 @@ You can list all files uploaded under a given path. +This will list all files located under path `album` that: +* have `private` access level +* are in the root of `album/` (the result doesn't include files under any sub path) + +```dart +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:amplify_storage_s3/amplify_storage_s3.dart'; + +Future listAlbum() async { + try { + String? nextToken; + bool hasNextPage; + do { + final result = await Amplify.Storage.list( + path: 'album/', + options: StorageListOptions( + accessLevel: StorageAccessLevel.private, + pageSize: 50, + nextToken: nextToken, + pluginOptions: const S3ListPluginOptions( + excludeSubPaths: true, + ), + ), + ).result; + safePrint('Listed items: ${result.items}'); + nextToken = result.nextToken; + hasNextPage = result.hasNextPage; + } while (hasNextPage); + } on StorageException catch (e) { + safePrint('Error listing files: ${e.message}'); + rethrow; + } +} +``` + +Pagination is enabled by default. The default `pageSize` is `1000` if it is not set in the `StorageListOptions`. + +You can also list all files under a given path without pagination by using the `pluginOptions` and `S3ListPluginOptions.listAll()` constructor. + This will list all public files (i.e. those with `guest` access level): ```dart -Future listItems() async { +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:amplify_storage_s3/amplify_storage_s3.dart'; + +Future listAllWithGuestAccessLevel() async { try { - final result = await Amplify.Storage.list(); - final items = result.items; - safePrint('Got items: $items'); + final result = await Amplify.Storage.list( + options: const StorageListOptions( + accessLevel: StorageAccessLevel.guest, + pluginOptions: S3ListPluginOptions.listAll(), + ), + ).result; + + safePrint('Listed items: ${result.items}'); } on StorageException catch (e) { - safePrint('Error listing items: $e'); + safePrint('Error listing files: ${e.message}'); + rethrow; } } ``` +import { delimiter } from "path" diff --git a/src/fragments/lib/storage/flutter/move.mdx b/src/fragments/lib-v1/storage/flutter/move.mdx similarity index 99% rename from src/fragments/lib/storage/flutter/move.mdx rename to src/fragments/lib-v1/storage/flutter/move.mdx index 030793cf718..a2b3572df3e 100644 --- a/src/fragments/lib/storage/flutter/move.mdx +++ b/src/fragments/lib-v1/storage/flutter/move.mdx @@ -2,7 +2,6 @@ You can move an existing file to a different folder location in your S3 bucket. ```dart import 'package:amplify_flutter/amplify_flutter.dart'; - Future movePrivateFile({ required String sourceKey, required String destinationKey, @@ -18,12 +17,10 @@ Future movePrivateFile({ accessLevel: StorageAccessLevel.private, ), ).result; - safePrint('Moved file to: ${result.movedItem.key}'); } on StorageException catch (e) { safePrint('Something went wrong moving the file: ${e.message}'); rethrow; } } - ``` \ No newline at end of file diff --git a/src/fragments/lib-v1/storage/flutter/remove.mdx b/src/fragments/lib-v1/storage/flutter/remove.mdx index b97a5f9ad3f..ad4d42004a1 100644 --- a/src/fragments/lib-v1/storage/flutter/remove.mdx +++ b/src/fragments/lib-v1/storage/flutter/remove.mdx @@ -1,14 +1,50 @@ ## Remove a file -Remove a file uploaded to S3 by using `Amplify.Storage.remove` and specifying the key: +You can remove a single file using `Amplify.Storage.remove` with the `key` and its associated access level: ```dart -Future removeFile(String key) async { +import 'package:amplify_flutter/amplify_flutter.dart'; + +Future removeFile({ + required String key, + required StorageAccessLevel accessLevel, +}) async { + try { + final result = await Amplify.Storage.remove( + key: key, + options: StorageRemoveOptions( + accessLevel: accessLevel, + ), + ).result; + safePrint('Removed file: ${result.removedItem.key}'); + } on StorageException catch (e) { + safePrint('Error deleting file: ${e.message}'); + rethrow; + } +} +``` + +## Remove multiple files + +You can remove multiple files using `Amplify.Storage.removeMany` with the `keys`, the files to be removed in a batch should have the same access level: + +```dart +import 'package:amplify_flutter/amplify_flutter.dart'; + +Future removePrivateFiles({ + required List keys, +}) async { try { - final result = await Amplify.Storage.remove(key: key); - safePrint('Removed file: ${result.key}'); + final result = await Amplify.Storage.removeMany( + keys: keys, + options: const StorageRemoveManyOptions( + accessLevel: StorageAccessLevel.private, + ), + ).result; + safePrint('Removed files: ${result.removedItems}'); } on StorageException catch (e) { - safePrint('Error deleting file: $e'); + safePrint('Error deleting files: ${e.message}'); + rethrow; } } ``` diff --git a/src/fragments/lib-v1/storage/flutter/upload.mdx b/src/fragments/lib-v1/storage/flutter/upload.mdx index 73ca38cc046..343d98e9fe9 100644 --- a/src/fragments/lib-v1/storage/flutter/upload.mdx +++ b/src/fragments/lib-v1/storage/flutter/upload.mdx @@ -1,120 +1,264 @@ ## Upload File -To upload to S3 from a file, specify the key and the local file to be uploaded. A file can be created locally, or retrieved from the user's device using a package such as [image_picker](https://pub.dev/packages/image_picker) or [file_picker](https://pub.dev/packages/file_picker). +To upload to S3 from a file, specify the `key` to upload the file to and the `localFile` to be uploaded. `localFile` can be an instance of `AWSFile` created from either an OS platform `File` instance or the result of Flutter file picker plugins such as [file_picker](https://pub.dev/packages/file_picker). -### Upload a local file +### Upload platform `File` -import flutter0 from "/src/fragments/lib-v1/storage/flutter/upload/upload-create-file.mdx"; + - +**Note**: To use `AWSFilePlatform`, add [aws_common](https://pub.dev/packages/aws_common) package to your Flutter project +by running: `flutter pub add aws_common` -### Upload file with Flutter file picker packages + - - - +import flutter0 from "/src/fragments/lib-v1/storage/flutter/upload/upload-create-file.mdx"; -Make sure to follow the setup instructions on the image_picker [homepage](https://pub.dev/packages/image_picker). + -```dart -import 'dart:io'; +### Upload with Flutter file_picker plugin -import 'package:image_picker/image_picker.dart'; +The [file picker](https://pub.dev/packages/file_picker) plugin can be used to retrieve arbitrary file types from the user's device. -final picker = ImagePicker(); +```dart +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:file_picker/file_picker.dart'; Future uploadImage() async { - // Select image from user's gallery - final pickedFile = await picker.pickImage(source: ImageSource.gallery); + // Select a file from the device + final result = await FilePicker.platform.pickFiles( + type: FileType.custom, + withData: false, + // Ensure to get file stream for better performance + withReadStream: true, + allowedExtensions: ['jpg', 'png', 'gif'], + ); - if (pickedFile == null) { - safePrint('No image selected'); + if (result == null) { + safePrint('No file selected'); return; } - // Upload image with the current time as the key - final key = DateTime.now().toString(); - final file = File(pickedFile.path); + // Upload file with its filename as the key + final platformFile = result.files.single; try { final result = await Amplify.Storage.uploadFile( - local: file, - key: key, + localFile: AWSFile.fromStream( + platformFile.readStream!, + size: platformFile.size, + ), + key: platformFile.name, onProgress: (progress) { - safePrint('Fraction completed: ${progress.getFractionCompleted()}'); + safePrint('Fraction completed: ${progress.fractionCompleted}'); }, - ); - safePrint('Successfully uploaded image: ${result.key}'); + ).result; + safePrint('Successfully uploaded file: ${result.uploadedItem.key}'); } on StorageException catch (e) { - safePrint('Error uploading image: $e'); + safePrint('Error uploading file: $e'); + rethrow; } } ``` - +## Upload Data - +To upload to S3 from a data object, specify the `key` and `data`, where `data` is an instance of `S3DataPayload` created from various data formats. -The [file_picker](https://pub.dev/packages/file_picker) package can be used to retrieve arbitrary file types from the user's device. + + ```dart -import 'dart:io'; +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:amplify_storage_s3/amplify_storage_s3.dart'; -import 'package:file_picker/file_picker.dart'; +Future uploadStringData({ + required String dataString, + required String key, +}) async { + try { + final result = await Amplify.Storage.uploadData( + data: S3DataPayload.string( + dataString, + contentType: 'text/plain', + ), + key: key, + ).result; -Future uploadFile() async { - // Select a file from the device - final result = await FilePicker.platform.pickFiles(); + safePrint('Uploaded data: ${result.uploadedItem.key}'); + } on StorageException catch (e) { + safePrint('Error uploading data: ${e.message}'); + rethrow; + } +} +``` + - if (result == null) { - safePrint('No file selected'); - return; + +```dart +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:amplify_storage_s3/amplify_storage_s3.dart'; + +Future uploadJsonObject({ + required Map json, + required String key, +}) async { + try { + final result = await Amplify.Storage.uploadData( + data: S3DataPayload.json(json), + key: key, + ).result; + + safePrint('Uploaded data: ${result.uploadedItem.key}'); + } on StorageException catch (e) { + safePrint('Error uploading data: ${e.message}'); + rethrow; } +} +``` + - // Upload file with its filename as the key - final platformFile = result.files.single; - final path = platformFile.path!; - final key = platformFile.name; - final file = File(path); + + +`S3DataPayload.dataUrl()` parses the provided data URL string, and throws exception if the data URL is invalid. See more info about [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs). + +```dart +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:amplify_storage_s3/amplify_storage_s3.dart'; +Future uploadDataUrl({ + required String dataUrl, + required String key, +}) async { try { - final result = await Amplify.Storage.uploadFile( - local: file, + final result = await Amplify.Storage.uploadData( + data: S3DataPayload.dataUrl(dataUrl), key: key, - onProgress: (progress) { - safePrint('Fraction completed: ${progress.getFractionCompleted()}'); - }, - ); - safePrint('Successfully uploaded file: ${result.key}'); + ).result; + + safePrint('Uploaded data: ${result.uploadedItem.key}'); } on StorageException catch (e) { - safePrint('Error uploading file: $e'); + safePrint('Error uploading data: ${e.message}'); + rethrow; } } ``` + + + +```dart +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:amplify_storage_s3/amplify_storage_s3.dart'; + +Future uploadBytes({ + required List bytes, + required String key, + required String contentType, +}) async { + try { + final result = await Amplify.Storage.uploadData( + data: S3DataPayload.bytes( + bytes, + contentType: contentType, + ), + key: key, + ).result; + safePrint('Uploaded data: ${result.uploadedItem.key}'); + } on StorageException catch (e) { + safePrint('Error uploading data: ${e.message}'); + rethrow; + } +} +``` ## Upload Options -You may attach metadata while uploading data or a file via specifying `metadata` in options. +You can attach metadata while uploading data or a file by specifying the `metadata` property in options. If you want the `metadata` to be included in the upload result, you can set the `getProperties` flag to `true` in options. ```dart -await Amplify.Storage.uploadFile( - key: 'file', - local: File('path/to/file'), - options: S3UploadFileOptions( - metadata: const { - 'project': 'ExampleProject', - }, - ), -); +Future uploadWithOptions() async { + // When uploading data, use `StorageUploadDataOptions` + final uploadDataOperation = Amplify.Storage.uploadData( + data: S3DataPayload.string( + 'example', + contentType: 'text/plain', + ), + key: 'example.txt', + options: const StorageUploadDataOptions( + metadata: { + 'project': 'ExampleProject', + }, + pluginOptions: S3UploadDataPluginOptions( + getProperties: true, + ), + ), + ); + final uploadDataResult = await uploadDataOperation.result; + safePrint( + 'Uploaded data with metadata: ${uploadDataResult.uploadedItem.metadata}', + ); + + // When uploading a file, use `StorageUploadFileOptions` + final uploadFileOperation = Amplify.Storage.uploadFile( + localFile: AWSFile.fromPath('path/to/example.txt'), + key: 'example.txt', + options: const StorageUploadFileOptions( + metadata: { + 'project': 'ExampleProject', + }, + pluginOptions: S3UploadFilePluginOptions( + getProperties: true, + ), + ), + ); + final uploadFileResult = await uploadFileOperation.result; + safePrint( + 'Uploaded file with metadata: ${uploadFileResult.uploadedItem.metadata}', + ); +} ``` -In S3 console, you should see the metadata attached to your file. You can learn more about the different access levels in [File access levels](/gen1/[platform]/prev/build-a-backend/storage/configure-access/). +The [`Amplify.Storage.getProperties` API](/gen1/[platform]/build-a-backend/storage/get-properties/) allows you to retrieve metadata without downloading the file. + +In S3 console, you should see the metadata attached to your file as the following. ![S3 Metadata](/images/s3_metadata.png) +## Control of Upload Operations + +A call to `Amplify.Storage.uploadFile` or `Amplify.Storage.uploadData` returns a reference to the operation that is performing the upload. + +To cancel the upload (for example, in response to the user pressing a Cancel button), simply call `.cancel()` on the returned upload operation. + +```dart +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:amplify_storage_s3/amplify_storage_s3.dart'; + +S3UploadFileOperation? uploadOperation; + +Future uploadFile(String path) async { + try { + final storagePlugin = Amplify.Storage.getPlugin(AmplifyStorageS3.pluginKey); + uploadOperation = storagePlugin.uploadFile( + localFile: AWSFile.fromPath(path), + key: 'example_file.txt', + ); + + final result = await uploadOperation!.result; + safePrint('Uploaded ${result.uploadedItem.key}'); + } on StorageException catch (e) { + safePrint('Error uploading file: ${e.message}'); + } +} + +void cancelUpload() { + uploadOperation?.cancel(); + uploadOperation = null; +} +``` + ## Multipart upload Amplify will automatically perform a S3 multipart upload for files larger than 5MB. For more information about S3's multipart upload support, see [Uploading and copying objects using multipart upload](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html). diff --git a/src/fragments/lib-v1/storage/flutter/upload/upload-create-file.mdx b/src/fragments/lib-v1/storage/flutter/upload/upload-create-file.mdx index 1cd1711909a..6afe6a44946 100644 --- a/src/fragments/lib-v1/storage/flutter/upload/upload-create-file.mdx +++ b/src/fragments/lib-v1/storage/flutter/upload/upload-create-file.mdx @@ -1,37 +1,49 @@ + + +```dart +import 'dart:io' as io; -import flutter0 from "/src/fragments/lib-v1/storage/flutter/path-provider.mdx"; +import 'package:amplify_storage_s3/amplify_storage_s3.dart'; +import 'package:aws_common/vm.dart'; - +Future uploadIOFile(io.File file) async { + final awsFile = AWSFilePlatform.fromFile(file); + try { + final uploadResult = await Amplify.Storage.uploadFile( + localFile: awsFile, + key: 'upload/file.png', + ).result; + safePrint('Uploaded file: ${uploadResult.uploadedItem.key}'); + } on StorageException catch (e) { + safePrint('Error uploading file: ${e.message}'); + rethrow; + } +} +``` + + ```dart -import 'dart:io'; - -import 'package:path_provider/path_provider.dart'; +import 'dart:html' as html; -Future createAndUploadFile() async { - // Create a dummy file - const exampleString = 'Example file contents'; - final tempDir = await getTemporaryDirectory(); +import 'package:amplify_storage_s3/amplify_storage_s3.dart'; +import 'package:aws_common/web.dart'; - final exampleFile = File('${tempDir.path}/example.txt'); - await exampleFile.create(); - await exampleFile.writeAsString(exampleString); - - // Upload the file to S3 +Future uploadHtmlFile(html.File file) async { + final awsFile = AWSFilePlatform.fromFile(file); try { - final result = await Amplify.Storage.uploadFile( - local: exampleFile, - key: 'ExampleKey', - onProgress: (progress) { - safePrint('Fraction completed: ${progress.getFractionCompleted()}'); - }, - ); - safePrint('Successfully uploaded file: ${result.key}'); + final uploadResult = await Amplify.Storage.uploadFile( + localFile: awsFile, + key: 'upload/file.png', + ).result; + safePrint('Uploaded file: ${uploadResult.uploadedItem.key}'); } on StorageException catch (e) { - safePrint('Error uploading file: $e'); - } finally { - await exampleFile.delete(); + safePrint('Error uploading file: ${e.message}'); + rethrow; } } ``` + + + diff --git a/src/fragments/lib-v1/storage/native_common/configureaccess/common.mdx b/src/fragments/lib-v1/storage/native_common/configureaccess/common.mdx index b85ced3b646..a3d8fd9f709 100644 --- a/src/fragments/lib-v1/storage/native_common/configureaccess/common.mdx +++ b/src/fragments/lib-v1/storage/native_common/configureaccess/common.mdx @@ -30,9 +30,9 @@ import flutter2 from '/src/fragments/lib-v1/storage/flutter/configureaccess/10_p -This will upload with the prefix `/protected/[IDENTITY_ID]/` followed by the key. +This will upload with the prefix `/protected/[IDENTITY_ID]/` followed by the `key`. -For other users to read the file, you must specify the user ID of the creating user in the passed options. +For other users to read the file, you must specify the access level as `protected` and the identity ID of the user who uploaded it in the options. import ios3 from '/src/fragments/lib-v1/storage/ios/configureaccess/20_protected_download.mdx'; @@ -62,7 +62,7 @@ import flutter8 from '/src/fragments/lib-v1/storage/flutter/configureaccess/30_p -This will upload with the prefix `/private/[IDENTITY_ID]/`, followed by the key. +This will upload with the prefix `/private/[IDENTITY_ID]/`, followed by the `key`. For the user to read the file, specify the same access level (`private`) and key you used to upload: @@ -85,3 +85,114 @@ import ios10 from '/src/fragments/lib-v1/storage/ios/configureaccess/50_customiz import android11 from '/src/fragments/lib-v1/storage/android/configureaccess/50_customization.mdx'; + + + +## Customization + +### Customize Object Key Path + +You can customize your key path by defining a prefix resolver: + +```dart +import 'package:amplify_auth_cognito/amplify_auth_cognito.dart'; +import 'package:amplify_flutter/amplify_flutter.dart'; +import 'package:amplify_storage_s3/amplify_storage_s3.dart'; + +// Define your own prefix resolver, which implements the `S3PrefixResolver`. +class MyPrefixResolver implements S3PrefixResolver { + const MyPrefixResolver(); + + @override + Future resolvePrefix({ + required StorageAccessLevel accessLevel, + String? identityId, + }) async { + if (accessLevel == StorageAccessLevel.guest) { + return 'myPublicPrefix/'; + } + + final String accessLevelPrefix; + + if (accessLevel == StorageAccessLevel.protected) { + accessLevelPrefix = 'myProtectedPrefix/'; + } else { + accessLevelPrefix = 'myPrivatePrefix/'; + } + + final targetIdentityId = identityId ?? await getCurrentUserIdentityId(); + + return '$accessLevelPrefix$targetIdentityId/'; + } + + Future getCurrentUserIdentityId() async { + final authPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); + final authSession = await authPlugin.fetchAuthSession(); + return authSession.identityIdResult.value; + } +} +``` + +Then, configure the Storage plugin with your custom prefix resolver: + +```dart +final storagePlugin = AmplifyStorageS3( + prefixResolver: const MyPrefixResolver(), +); +... +await Amplify.addPlugin(storagePlugin); +``` + +Add the IAM policy that corresponds with the prefixes defined above to enable read, write and delete operation for all the objects under path _myPublicPrefix/_: + +```json +{ + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "s3:GetObject", + "s3:PutObject", + "s3:DeleteObject" + ], + "Resource": ["arn:aws:s3:::your-s3-bucket/myPublicPrefix/*"] + } + ] +} +``` + +If you want to have custom _private_ path prefix like `myPrivatePrefix/`, you need to add it into your IAM policy: + +```json +{ + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "s3:GetObject", + "s3:PutObject", + "s3:DeleteObject" + ], + "Resource": [ + "arn:aws:s3:::your-s3-bucket/myPrivatePrefix/${cognito-identity.amazonaws.com:sub}/*" + ] + } + ] +} +``` + +## Passthrough PrefixResolver + +If you would like no prefix resolution logic, such as performing S3 requests at the root of the bucket, you can use the `PassThroughPrefixResolver` provided by the `amplify_storage_s3` package. + +```dart +import 'package:amplify_storage_s3/amplify_storage_s3.dart'; + +final storagePlugin = AmplifyStorageS3( + prefixResolver: const PassThroughPrefixResolver(), +); + +await Amplify.addPlugin(storagePlugin); +``` + + diff --git a/src/fragments/lib-v1/storage/native_common/getting-started/common.mdx b/src/fragments/lib-v1/storage/native_common/getting-started/common.mdx index 028c4148e39..6851efeaca8 100644 --- a/src/fragments/lib-v1/storage/native_common/getting-started/common.mdx +++ b/src/fragments/lib-v1/storage/native_common/getting-started/common.mdx @@ -1,9 +1,5 @@ The Amplify Storage category provides an interface for managing user content for your app in public, protected, or private storage buckets. The Storage category comes with default built-in support for Amazon Simple Storage Service (S3). The Amplify CLI helps you to create and configure the storage buckets for your app. The Amplify AWS S3 Storage plugin leverages [Amazon S3](https://aws.amazon.com/s3). -import flutter0 from '/src/fragments/lib-v1/storage/flutter/getting-started/50_developerPreview.mdx'; - - - ## Goal To setup and configure your application with Amplify Storage and go through a simple upload file example @@ -134,4 +130,4 @@ Congratulations! You've uploaded a file to an s3 bucket. Check out the following - [Escape Hatch](/gen1/[platform]/prev/build-a-backend/storage/sdk/) - + \ No newline at end of file diff --git a/src/fragments/lib/analytics/flutter/getting-started/10_preReq.mdx b/src/fragments/lib/analytics/flutter/getting-started/10_preReq.mdx index 2f5067ce258..1acefc397fd 100644 --- a/src/fragments/lib/analytics/flutter/getting-started/10_preReq.mdx +++ b/src/fragments/lib/analytics/flutter/getting-started/10_preReq.mdx @@ -1,11 +1,3 @@ -- A Flutter application targeting Flutter SDK >=3.3.0 with Amplify libraries integrated +* [Install and configure Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) - The following are also required, depending on which platforms you are targeting: - - - An iOS configuration targeting at least iOS 13.0 and XCode version >=13.2 - - An Android configuration targeting at least Android API level 24 (Android 7.0) or above - - Any browser supported by Flutter for Web (you can check the list of supported browsers [here](https://docs.flutter.dev/development/platform-integration/web/faq#which-web-browsers-are-supported-by-flutter)) - - Any Windows OS meeting Flutter minimums - - macOS version 10.15 or higher - - Any Ubuntu Linux distribution meeting Flutter minimums - - For a full example please follow the [project setup walkthrough](/gen1/[platform]/start/project-setup/create-application/) +Amplify Flutter 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](/gen1/[platform]/start/project-setup/platform-setup/) guide for more details on platform specific setup. diff --git a/src/fragments/lib/analytics/flutter/getting-started/20_installLib.mdx b/src/fragments/lib/analytics/flutter/getting-started/20_installLib.mdx index c88f89f169f..c2ae6929167 100644 --- a/src/fragments/lib/analytics/flutter/getting-started/20_installLib.mdx +++ b/src/fragments/lib/analytics/flutter/getting-started/20_installLib.mdx @@ -5,11 +5,8 @@ In your Flutter project directory, open **pubspec.yaml**. Add Analytics by adding these libraries into your dependencies block: ```yaml -environment: - sdk: '>=2.18.0 <4.0.0' - dependencies: - amplify_analytics_pinpoint: ^1.0.0 - amplify_auth_cognito: ^1.0.0 - amplify_flutter: ^1.0.0 + amplify_analytics_pinpoint: ^2.0.0 + amplify_auth_cognito: ^2.0.0 + amplify_flutter: ^2.0.0 ``` diff --git a/src/fragments/lib/analytics/flutter/getting-started/30_initAnalytics.mdx b/src/fragments/lib/analytics/flutter/getting-started/30_initAnalytics.mdx index 42ff7438720..5b4dfc96134 100644 --- a/src/fragments/lib/analytics/flutter/getting-started/30_initAnalytics.mdx +++ b/src/fragments/lib/analytics/flutter/getting-started/30_initAnalytics.mdx @@ -18,7 +18,7 @@ Future _configureAmplify() async { -When running your app on MacOS you will need to enable keychain sharing in Xcode, as described in the [Project setup guide](/gen1/[platform]/start/project-setup/platform-setup/#enable-keychain). +When running your app on macOS you will need to enable keychain sharing in Xcode, as described in the [Project setup guide](/gen1/[platform]/start/project-setup/platform-setup/#enable-keychain). diff --git a/src/fragments/lib/analytics/flutter/record.mdx b/src/fragments/lib/analytics/flutter/record.mdx index a454009f2c9..f544a439cdb 100644 --- a/src/fragments/lib/analytics/flutter/record.mdx +++ b/src/fragments/lib/analytics/flutter/record.mdx @@ -28,7 +28,7 @@ However, it can take upwards of 30 minutes for the event to display in the Filte ## Flush events -Events have default configuration to flush out to the network every 30 seconds. If you would like to change this, update `amplifyconfiguration.dart` with the value in milliseconds you would like for `autoFlushEventsInterval`. This configuration will flush events every 10 seconds: +Events have default configuration to flush out to the network every 30 seconds. To modify the default value, assign the `autoFlushEventsInterval` property to your desired duration. The example below configures the plugin to flush events every 10 seconds. ```json { diff --git a/src/fragments/lib/auth/flutter/getting_started/10_preReq.mdx b/src/fragments/lib/auth/flutter/getting_started/10_preReq.mdx index 1b967b963bb..1acefc397fd 100644 --- a/src/fragments/lib/auth/flutter/getting_started/10_preReq.mdx +++ b/src/fragments/lib/auth/flutter/getting_started/10_preReq.mdx @@ -1,3 +1,3 @@ -A Flutter application targeting Flutter SDK >= 3.3.0 with Amplify libraries integrated. +* [Install and configure Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) -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](/gen1/[platform]/start/project-setup/platform-setup/) guide for more details on platform specific setup. +Amplify Flutter 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](/gen1/[platform]/start/project-setup/platform-setup/) guide for more details on platform specific setup. diff --git a/src/fragments/lib/auth/flutter/signin_web_ui/10_cli_setup.mdx b/src/fragments/lib/auth/flutter/signin_web_ui/10_cli_setup.mdx index 7611f0f2d31..d82b75a9ac0 100644 --- a/src/fragments/lib/auth/flutter/signin_web_ui/10_cli_setup.mdx +++ b/src/fragments/lib/auth/flutter/signin_web_ui/10_cli_setup.mdx @@ -20,7 +20,7 @@ In terminal, navigate to your project, run `amplify add auth` (or if you've alre ? Enter your redirect signout URI: `myapp://` ? Do you want to add another redirect signout URI - `No` + `Yes` ? Enter your redirect signout URI: `http://localhost:3000/` ? Do you want to add another redirect signout URI diff --git a/src/fragments/lib/auth/flutter/signin_web_ui/20_platform_specific_setup.mdx b/src/fragments/lib/auth/flutter/signin_web_ui/20_platform_specific_setup.mdx index 8a9b656f5db..5ad4064fde8 100644 --- a/src/fragments/lib/auth/flutter/signin_web_ui/20_platform_specific_setup.mdx +++ b/src/fragments/lib/auth/flutter/signin_web_ui/20_platform_specific_setup.mdx @@ -4,11 +4,11 @@ Sign-in with web UI will display the sign-in UI inside a webview. After the sign ## Platform Setup -

Web

+### Web To use Hosted UI in your Flutter web application locally, you must run the app with the `--web-port=3000` argument (with the value being whichever port you assigned to localhost host when configuring your redirect URIs). -

Android

+### Android Add the following `queries` element to the `AndroidManifest.xml` file in your app's `android/app/src/main` directory, as well as the following `intent-filter` to the `MainActivity` in the same file. @@ -36,12 +36,12 @@ Replace `myapp` with your redirect URI scheme as necessary: ``` -

macOS

+### macOS Open XCode and enable the App Sandbox capability and then select "Incoming Connections (Server)" under "Network". ![Incoming Connections setting selected in the App Sandbox section of the runner signing and capabilities tab.](/images/project-setup/flutter/mac/xcode-entitlements.png) -

iOS, Windows and Linux

+### iOS, Windows and Linux No specific platform configuration is required. diff --git a/src/fragments/lib/auth/flutter/signin_with_custom_flow/40_custom_challenge.mdx b/src/fragments/lib/auth/flutter/signin_with_custom_flow/40_custom_challenge.mdx index 25d5d2e5831..4bfbd82ef9f 100644 --- a/src/fragments/lib/auth/flutter/signin_with_custom_flow/40_custom_challenge.mdx +++ b/src/fragments/lib/auth/flutter/signin_with_custom_flow/40_custom_challenge.mdx @@ -19,6 +19,4 @@ Once the user provides the correct response, they should be authenticated in you Special Handling on ConfirmSignIn During a `confirmSignIn` call, if `failAuthentication: true` is returned by the Lambda, the session of the request gets invalidated by Cognito, and a `NotAuthorizedException` is thrown. To recover, the user must initiate a new sign in by calling `Amplify.Auth.signIn`. - -Exception: `NotAuthorizedException` with a message `Invalid session for the user.` diff --git a/src/fragments/lib/auth/flutter/user_attributes/40_resend_code.mdx b/src/fragments/lib/auth/flutter/user_attributes/40_resend_code.mdx index 160240595ae..168c7783efe 100644 --- a/src/fragments/lib/auth/flutter/user_attributes/40_resend_code.mdx +++ b/src/fragments/lib/auth/flutter/user_attributes/40_resend_code.mdx @@ -1,7 +1,7 @@ ```dart Future resendVerificationCode() async { try { - final result = await Amplify.Auth.resendUserAttributeConfirmationCode( + final result = await Amplify.Auth.sendUserAttributeConfirmationCode( userAttributeKey: AuthUserAttributeKey.email, ); _handleCodeDelivery(result.codeDeliveryDetails); diff --git a/src/fragments/lib/auth/flutter/user_attributes/50_custom_attributes.mdx b/src/fragments/lib/auth/flutter/user_attributes/50_custom_attributes.mdx index db31a1933f5..28223df913b 100644 --- a/src/fragments/lib/auth/flutter/user_attributes/50_custom_attributes.mdx +++ b/src/fragments/lib/auth/flutter/user_attributes/50_custom_attributes.mdx @@ -2,7 +2,7 @@ Amplify Flutter supports [standard OIDC user attributes](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims) as well as custom attributes. Custom attributes can be instantiated via the custom attribute constructor: ```dart -Future _signUp({ +Future signUp({ required String username, required String password, required String email, @@ -23,4 +23,4 @@ Future _signUp({ } ``` -When working with a Cognito UserPool, you can set up [custom attributes](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html#user-pool-settings-custom-attributes) via the Cognito console or AWS CLI. Although Cognito prepends a "custom:" prefix on the attribute name, there is no need for you to add this in Amplify Flutter's custom attribute constructor. \ No newline at end of file +When working with a Cognito UserPool, you can set up [custom attributes](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-settings-attributes.html#user-pool-settings-custom-attributes) via the Cognito console or AWS CLI. Although Cognito prepends a "custom:" prefix on the attribute name, there is no need for you to add this in Amplify Flutter's custom attribute constructor. diff --git a/src/fragments/lib/auth/native_common/user_attributes/common.mdx b/src/fragments/lib/auth/native_common/user_attributes/common.mdx index c211bb825e2..159cc1035f3 100644 --- a/src/fragments/lib/auth/native_common/user_attributes/common.mdx +++ b/src/fragments/lib/auth/native_common/user_attributes/common.mdx @@ -46,9 +46,9 @@ import flutter8 from '/src/fragments/lib/auth/flutter/user_attributes/30_confirm -## Resend verification code +## Send user attribute verification code -If the code has expired or the user needs to resend the confirmation code, invoke the resend api as shown below: +If an attribute needs to be verified while the user is authenticated, invoke the send api as shown below: import ios9 from '/src/fragments/lib/auth/ios/user_attributes/40_resend_code.mdx'; diff --git a/src/fragments/lib/datastore/flutter/conflict.mdx b/src/fragments/lib/datastore/flutter/conflict.mdx index 313d7fc3a7c..14d0b53bedf 100644 --- a/src/fragments/lib/datastore/flutter/conflict.mdx +++ b/src/fragments/lib/datastore/flutter/conflict.mdx @@ -1,7 +1,9 @@ -DataStore has a few optional configurations, such as the ability to specify a custom handler for error messages that take place in any part of the system. +DataStore has a few optional configurations, such as the ability to specify a custom handler for error messages that take place in any part of the system. You can also specify a custom conflict handler that runs if a mutation is rejected by AWS AppSync during one of the conflict resolution strategies. Finally you can configure the number of records to sync as an upper bound on items (per-Model) which will be stored locally on the device, as well as a custom interval in minutes which is an override of the default 24 hour "base query" which runs as part of the Delta Sync process. +To modify these settings, utilize the `options` parameter within the `AmplifyDataStore()` constructor. + ### Custom configuration fields - `errorHandler` - handler function executed when Datastore encounters an unhandled error during its background operations. - `conflictHandler` - handler function when there is a conflict between two local and remote model instances in a sync operation. @@ -18,29 +20,30 @@ Future initializeDataStoreWithConflictResolution() async { try { final datastorePlugin = AmplifyDataStore( modelProvider: ModelProvider.instance, - errorHandler: ((error) { - print('Custom ErrorHandler received: $error'); - }), - conflictHandler: (ConflictData data) { - final localData = data.local; - final remoteData = data.remote; - - if (localData is Post && remoteData is Post) { - final mergedPostData = Post( - // always favor the title from the local Post data - title: localData.title, - rating: remoteData.rating, - ); - - return ConflictResolutionDecision.retry(mergedPostData); - } - - return ConflictResolutionDecision.applyRemote(); - }, - // Sync configuration defaults: - syncInterval: 86400, - syncMaxRecords: 10000, - syncPageSize: 1000, + options: DataStorePluginOptions( + errorHandler: ((error) { + print('Custom ErrorHandler received: $error'); + }), + conflictHandler: (ConflictData data) { + final localData = data.local; + final remoteData = data.remote; + + if (localData is Post && remoteData is Post) { + final mergedPostData = Post( + // always favor the title from the local Post data + title: localData.title, + rating: remoteData.rating, + ); + return ConflictResolutionDecision.retry(mergedPostData); + } + + return const ConflictResolutionDecision.applyRemote(); + }, + // Sync configuration defaults: + syncInterval: 86400, + syncMaxRecords: 10000, + syncPageSize: 1000, + ), ); await Amplify.addPlugin(datastorePlugin); diff --git a/src/fragments/lib/datastore/flutter/getting-started/10_preReq.mdx b/src/fragments/lib/datastore/flutter/getting-started/10_preReq.mdx index 25eaf32221e..ee192c94cde 100644 --- a/src/fragments/lib/datastore/flutter/getting-started/10_preReq.mdx +++ b/src/fragments/lib/datastore/flutter/getting-started/10_preReq.mdx @@ -1,6 +1,2 @@ - [Install and configure Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) - -- A Flutter application targeting Flutter SDK >= 3.3.0 with Amplify libraries integrated - - An iOS configuration targeting at least iOS 13.0 - - An Android configuration targeting at least Android API level 24 (Android 7.0) or above - - For a full example of please follow the [project setup walkthrough](/gen1/[platform]/start/project-setup/create-application/) +- An iOS configuration targeting at least iOS 13.0 and an Android configuration targeting at least Android API level 24 (Android 7.0). For a full example of please follow the [project setup walkthrough](/gen1/[platform]/start/project-setup/create-application/) diff --git a/src/fragments/lib/datastore/flutter/getting-started/20_installLib.mdx b/src/fragments/lib/datastore/flutter/getting-started/20_installLib.mdx index 07c5c650583..e10d4d2f91d 100644 --- a/src/fragments/lib/datastore/flutter/getting-started/20_installLib.mdx +++ b/src/fragments/lib/datastore/flutter/getting-started/20_installLib.mdx @@ -3,16 +3,12 @@ Add the following dependencies to your `pubspec.yaml` file and install dependencies when asked: ```yaml -environment: - sdk: ">=2.18.0 <4.0.0" - flutter: ">=3.3.0" - dependencies: flutter: sdk: flutter - amplify_datastore: ^1.0.0-supports-only-mobile - amplify_flutter: ^1.0.0 + amplify_datastore: 2.0.0 + amplify_flutter: ^2.0.0 ``` ### Update The Android Project @@ -38,4 +34,4 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } -``` \ No newline at end of file +``` diff --git a/src/fragments/lib/datastore/flutter/sync/10-installPlugin.mdx b/src/fragments/lib/datastore/flutter/sync/10-installPlugin.mdx index 7a970096c6d..9363a06e762 100644 --- a/src/fragments/lib/datastore/flutter/sync/10-installPlugin.mdx +++ b/src/fragments/lib/datastore/flutter/sync/10-installPlugin.mdx @@ -5,7 +5,7 @@ Although DataStore presents a distinct API, its cloud synchronization functional Make sure you have the following plugin dependency in your `pubspec.yaml`. ```yaml -amplify_api: ^1.0.0 +amplify_api: ^2.0.0 ``` Locate your Amplify initialization code, and add an `AmplifyAPI()` plugin. Your initialization code should already include an `AmplifyDataStore()` plugin from previous steps. Note the new `import` statement for API towards the top of the file. diff --git a/src/fragments/lib/datastore/flutter/sync/50-selectiveSync.mdx b/src/fragments/lib/datastore/flutter/sync/50-selectiveSync.mdx index 44ca569f74a..af3fe9d5bae 100644 --- a/src/fragments/lib/datastore/flutter/sync/50-selectiveSync.mdx +++ b/src/fragments/lib/datastore/flutter/sync/50-selectiveSync.mdx @@ -17,13 +17,15 @@ void _configureAmplify() async { // Update AmplifyDataStore instance like below final datastorePlugin = AmplifyDataStore( modelProvider: ModelProvider.instance, - syncExpressions: [ - DataStoreSyncExpression(Post.classType, () => Post.RATING.gt(5)), - DataStoreSyncExpression( - Comment.classType, - () => Comment.POST.beginsWith('This'), - ) - ], + options: DataStorePluginOptions( + syncExpressions: [ + DataStoreSyncExpression(Post.classType, () => Post.RATING.gt(5)), + DataStoreSyncExpression( + Comment.classType, + () => Comment.POST.beginsWith('This'), + ), + ], + ), ); ... } @@ -49,12 +51,14 @@ int rating = 5; Future initialize() async { final dataStorePlugin = AmplifyDataStore( modelProvider: ModelProvider.instance, - syncExpressions: [ - DataStoreSyncExpression( - Post.classType, - () => Post.RATING.gt(rating), - ), - ], + options: DataStorePluginOptions( + syncExpressions: [ + DataStoreSyncExpression( + Post.classType, + () => Post.RATING.gt(rating), + ), + ], + ), ); await Amplify.addPlugin(dataStorePlugin); @@ -113,20 +117,21 @@ You can also have your sync expression return `QueryPredicateConstant.all` in or int rating = 5; Future initialize() async { - var dataStorePlugin = AmplifyDataStore( + final dataStorePlugin = AmplifyDataStore( modelProvider: ModelProvider.instance, - syncExpressions: [ - DataStoreSyncExpression( - Post.classType, - () { - if (rating > 0) { - return QueryPredicate.all; - } - - return Post.RATING.gt(rating); - }, - ), - ], + options: DataStorePluginOptions( + syncExpressions: [ + DataStoreSyncExpression( + Post.classType, + () { + if (rating > 0) { + return QueryPredicate.all; + } + return Post.RATING.gt(rating); + }, + ), + ], + ), ); await Amplify.addPlugin(dataStorePlugin); @@ -173,12 +178,14 @@ Future initializeSingleEquals() async { // Using eq operator with the primary key of the GSI final singleEqualsStore = AmplifyDataStore( modelProvider: ModelProvider.instance, - syncExpressions: [ - DataStoreSyncExpression( - User.classType, - () => User.LASTNAME.eq("Doe"), - ), - ], + options: DataStorePluginOptions( + syncExpressions: [ + DataStoreSyncExpression( + User.classType, + () => User.LASTNAME.eq("Doe"), + ), + ], + ), ); await Amplify.addPlugin(singleEqualsStore); @@ -189,12 +196,14 @@ Future initializeChainedEquals() async { // chaining the gt operator with the sort key final chainedEqualGtStore = AmplifyDataStore( modelProvider: ModelProvider.instance, - syncExpressions: [ - DataStoreSyncExpression( - User.classType, - () => User.LASTNAME.eq("Doe").and(User.CREATEDAT.gt("2020-10-10")), - ), - ], + options: DataStorePluginOptions( + syncExpressions: [ + DataStoreSyncExpression( + User.classType, + () => User.LASTNAME.eq("Doe").and(User.CREATEDAT.gt("2020-10-10")), + ), + ], + ), ); await Amplify.addPlugin(chainedEqualGtStore); diff --git a/src/fragments/lib/flutter.mdx b/src/fragments/lib/flutter.mdx index 7319007f8b2..0564e19914e 100644 --- a/src/fragments/lib/flutter.mdx +++ b/src/fragments/lib/flutter.mdx @@ -2,7 +2,7 @@ Welcome to the Amplify Flutter documentation. To stay up to date with the latest changes and provide feedback, please take a look at our [GitHub repo](https://github.com/aws-amplify/amplify-flutter) or join us on [Discord](https://discord.gg/jWVbPfC). -The stable release of Amplify Flutter currently supports iOS, Android, Web, Windows, MacOS, and Linux as target platforms. Currently Push Notifications and DataStore are supported for only iOS and Android. +The stable release of Amplify Flutter currently supports iOS, Android, Web, Windows, macOS, and Linux as target platforms. Currently Push Notifications and DataStore are supported for only iOS and Android. Get Started diff --git a/src/fragments/lib/graphqlapi/flutter/advanced-workflows/50_interceptor.mdx b/src/fragments/lib/graphqlapi/flutter/advanced-workflows/50_interceptor.mdx index af23e74869e..e590075b55c 100644 --- a/src/fragments/lib/graphqlapi/flutter/advanced-workflows/50_interceptor.mdx +++ b/src/fragments/lib/graphqlapi/flutter/advanced-workflows/50_interceptor.mdx @@ -30,6 +30,10 @@ class MyHttpRequestInterceptor extends AWSBaseHttpClient { // Then you can pass an instance of this client to `baseHttpClient` when you configure Amplify. await Amplify.addPlugins([ - AmplifyAPI(baseHttpClient: MyHttpRequestInterceptor()), + AmplifyAPI( + options: APIPluginOptions( + baseHttpClient: MyHttpRequestInterceptor(), + ), + ), ]); ``` diff --git a/src/fragments/lib/graphqlapi/flutter/authz/10_userpool.mdx b/src/fragments/lib/graphqlapi/flutter/authz/10_userpool.mdx index 87463a8cf3f..eead34a283a 100644 --- a/src/fragments/lib/graphqlapi/flutter/authz/10_userpool.mdx +++ b/src/fragments/lib/graphqlapi/flutter/authz/10_userpool.mdx @@ -1,17 +1,14 @@ In case you have not added the Cognito libraries to your application, be sure to add them: ```yaml - environment: - sdk: ">=2.18.0 <4.0.0" - dependencies: flutter: sdk: flutter - amplify_flutter: ^1.0.0 - amplify_api: ^1.0.0 + amplify_flutter: ^2.0.0 + amplify_api: ^2.0.0 # Be sure that this is added - amplify_auth_cognito: ^1.0.0 + amplify_auth_cognito: ^2.0.0 ``` Afterwards add the following code to your app before you configure Amplify: @@ -19,6 +16,8 @@ Afterwards add the following code to your app before you configure Amplify: ```dart await Amplify.addPlugins([ AmplifyAuthCognito(), - AmplifyAPI(modelProvider: ModelProvider.instance), + AmplifyAPI( + options: APIPluginOptions(modelProvider: ModelProvider.instance), + ), ]); ``` diff --git a/src/fragments/lib/graphqlapi/flutter/authz/2X_add_plugin.mdx b/src/fragments/lib/graphqlapi/flutter/authz/2X_add_plugin.mdx index 488197283df..55ca1bb10c3 100644 --- a/src/fragments/lib/graphqlapi/flutter/authz/2X_add_plugin.mdx +++ b/src/fragments/lib/graphqlapi/flutter/authz/2X_add_plugin.mdx @@ -3,10 +3,12 @@ Then, include it, along with any other auth providers, in the call to `addPlugin ```dart await Amplify.addPlugin( AmplifyAPI( - authProviders: const [ - CustomOIDCProvider(), - CustomFunctionProvider(), - ], + options: APIPluginOptions( + authProviders: const [ + CustomOIDCProvider(), + CustomFunctionProvider(), + ], + ), ), ); ``` diff --git a/src/fragments/lib/graphqlapi/flutter/getting-started/10_preReq.mdx b/src/fragments/lib/graphqlapi/flutter/getting-started/10_preReq.mdx index eb0c1c4160c..1acefc397fd 100644 --- a/src/fragments/lib/graphqlapi/flutter/getting-started/10_preReq.mdx +++ b/src/fragments/lib/graphqlapi/flutter/getting-started/10_preReq.mdx @@ -1,12 +1,3 @@ * [Install and configure Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) -* A Flutter application targeting Flutter SDK >= 3.3.0 with Amplify libraries integrated - - The following are also required, depending on which platforms you are targeting: - * An iOS configuration targeting at least iOS 13.0 and XCode version >=13.2 - * An Android configuration targeting at least Android API level 24 (Android 7.0) or above - * Any browser supported by Flutter for Web (you can check the list of supported browsers [here](https://docs.flutter.dev/development/platform-integration/web/faq#which-web-browsers-are-supported-by-flutter)) - * Any Windows OS meeting Flutter minimums - * macOS version 10.15 or higher - * Any Ubuntu Linux distribution meeting Flutter minimums - * For a full example please follow the [project setup walkthrough](/gen1/[platform]/start/project-setup/create-application/) +Amplify Flutter 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](/gen1/[platform]/start/project-setup/platform-setup/) guide for more details on platform specific setup. diff --git a/src/fragments/lib/graphqlapi/flutter/getting-started/20_installLib.mdx b/src/fragments/lib/graphqlapi/flutter/getting-started/20_installLib.mdx index bd8d918655c..f743690b75b 100644 --- a/src/fragments/lib/graphqlapi/flutter/getting-started/20_installLib.mdx +++ b/src/fragments/lib/graphqlapi/flutter/getting-started/20_installLib.mdx @@ -1,12 +1,9 @@ Add the following dependencies to your `pubspec.yaml` file and install dependencies when asked: ```yaml -environment: - sdk: ">=2.18.0 <4.0.0" - dependencies: flutter: sdk: flutter - amplify_flutter: ^1.0.0 - amplify_api: ^1.0.0 + amplify_flutter: ^2.0.0 + amplify_api: ^2.0.0 ``` diff --git a/src/fragments/lib/graphqlapi/flutter/getting-started/30_initapi.mdx b/src/fragments/lib/graphqlapi/flutter/getting-started/30_initapi.mdx index 28361a48898..4ca80abdf3c 100644 --- a/src/fragments/lib/graphqlapi/flutter/getting-started/30_initapi.mdx +++ b/src/fragments/lib/graphqlapi/flutter/getting-started/30_initapi.mdx @@ -25,7 +25,9 @@ class _MyAppState extends State { } Future _configureAmplify() async { - final api = AmplifyAPI(modelProvider: ModelProvider.instance); + final api = AmplifyAPI( + options: APIPluginOptions(modelProvider: ModelProvider.instance), + ); await Amplify.addPlugin(api); try { diff --git a/src/fragments/lib/graphqlapi/flutter/subscribe-data.mdx b/src/fragments/lib/graphqlapi/flutter/subscribe-data.mdx index 11ced1c1351..94f62434e52 100644 --- a/src/fragments/lib/graphqlapi/flutter/subscribe-data.mdx +++ b/src/fragments/lib/graphqlapi/flutter/subscribe-data.mdx @@ -93,10 +93,11 @@ Likewise, when disconnected from the internet unexpectedly, the subscription wil ```dart Future _configureAmplify() async { final apiPlugin = AmplifyAPI( - modelProvider: ModelProvider.instance, - // Optional config - subscriptionOptions: const GraphQLSubscriptionOptions( - retryOptions: RetryOptions(maxAttempts: 10), + options: APIPluginOptions( + modelProvider: ModelProvider.instance, + subscriptionOptions: const GraphQLSubscriptionOptions( + retryOptions: RetryOptions(maxAttempts: 10), + ), ), ); await Amplify.addPlugin(apiPlugin); diff --git a/src/fragments/lib/project-setup/flutter/create-application/60_dependencies.mdx b/src/fragments/lib/project-setup/flutter/create-application/60_dependencies.mdx index 1ec7f28bd14..51d0fde18b1 100644 --- a/src/fragments/lib/project-setup/flutter/create-application/60_dependencies.mdx +++ b/src/fragments/lib/project-setup/flutter/create-application/60_dependencies.mdx @@ -1,12 +1,8 @@ ```yaml -environment: - sdk: ">=2.18.0 <4.0.0" - flutter: ">=3.3.0" - dependencies: flutter: sdk: flutter - amplify_flutter: ^1.0.0 - amplify_auth_cognito: ^1.0.0 -``` \ No newline at end of file + amplify_flutter: ^2.0.0 + amplify_auth_cognito: ^2.0.0 +``` diff --git a/src/fragments/lib/project-setup/flutter/platform-setup/macos.mdx b/src/fragments/lib/project-setup/flutter/platform-setup/macos.mdx index 7656a345740..2e02deac857 100644 --- a/src/fragments/lib/project-setup/flutter/platform-setup/macos.mdx +++ b/src/fragments/lib/project-setup/flutter/platform-setup/macos.mdx @@ -14,7 +14,7 @@ Open your project in Xcode and select Runner, Targets -> Runner and then the "Ge Select Runner, Project -> Runner and then the "Info" tab. Update "macOS Deployment Target" to 10.15 or higher. -![Setting the macOS version to 10.15 or higher in the MacOS Deployment Target tab of the Runner info section.](/images/project-setup/flutter/mac/project-min-deployment-version.png) +![Setting the macOS version to 10.15 or higher in the macOS Deployment Target tab of the Runner info section.](/images/project-setup/flutter/mac/project-min-deployment-version.png) ### Enable Network Calls diff --git a/src/fragments/lib/project-setup/flutter/prereq/prereq.mdx b/src/fragments/lib/project-setup/flutter/prereq/prereq.mdx index 7742a29feec..fbc8bc76cf5 100644 --- a/src/fragments/lib/project-setup/flutter/prereq/prereq.mdx +++ b/src/fragments/lib/project-setup/flutter/prereq/prereq.mdx @@ -1,3 +1,2 @@ - Install a stable version of [Flutter](https://flutter.dev/docs/get-started/install). - - Use version 3.3.0 or higher - Setup your [IDE](https://flutter.dev/docs/get-started/editor?tab=androidstudio) diff --git a/src/fragments/lib/project-setup/flutter/upgrade-guide/upgrade-guide.mdx b/src/fragments/lib/project-setup/flutter/upgrade-guide/upgrade-guide.mdx index b10fc254546..ceeb317e427 100644 --- a/src/fragments/lib/project-setup/flutter/upgrade-guide/upgrade-guide.mdx +++ b/src/fragments/lib/project-setup/flutter/upgrade-guide/upgrade-guide.mdx @@ -1,328 +1,208 @@ -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. +Amplify Flutter v2 has changes in Auth, Analytics, Storage, GraphQL API & DataStore that may require migration when upgrading from v1. -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 +## Auth -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](/gen1/[platform]/start/project-setup/platform-setup/) guide for more details on platform-specific setup. +#### Time Based One Time Password (TOTP) Exception Handling -## Platform Setup +In Amplify Flutter v1, `Amplify.Auth.verifyTotpSetup()` throws an `EnableSoftwareTokenMfaException` if the provided code was incorrect. In Amplify Flutter v2 a `CodeMismatchException` is thrown. -The requirements for individual platforms have been modified in some cases. If you haven't done so already, go through the [project setup](/gen1/[platform]/start/project-setup/platform-setup/) guide -to make sure each platform has been configured correctly. +#### Auth Flow in Amplify Config -## Auth +In Amplify Flutter v1, the value of `“authenticationFlowType”` from the Amplify configuration object is used as the default `AuthenticationFlowType` if none is provided when calling `Amplify.Auth.signIn()`. -- `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](https://github.com/aws-amplify/amplify-flutter/issues/760#issuecomment-1453559459) 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. +In Amplify Flutter v2, default type is `AuthenticationFlowType.userSrpAuth`. To change the authentication flow type, you can provide a value in the sign in options under `options` parameter.. ```dart -// v0 -try { - final session = await Amplify.Auth.fetchAuthSession( - options: CognitoFetchAuthSessionOptions(getAWSCredentials: true), - ) as CognitoAuthSession; - - final accessToken = session.userPoolTokens?.accessToken; - safePrint('accessToken: $accessToken'); - - final identityId = session.identityId; - safePrint('identityId: $identityId'); -} on AuthException catch (e) { - // fetchAuthSession will fail if any of the underlying operations fail. - safePrint('The operation failed: ${e.message}'); -} +await Amplify.Auth.signIn( + username: 'username', + password: 'password', + options: const SignInOptions( + pluginOptions: CognitoSignInPluginOptions( + authFlowType: AuthenticationFlowType.userSrpAuth, + ), + ), +) ``` -```dart -// v1 -final session = await Amplify.Auth.getPlugin( - AmplifyAuthCognito.pluginKey, -).fetchAuthSession(); - -// AuthResult.value will throw if the underlying operation failed. -try { - final accessToken = session.userPoolTokensResult.value.accessToken.toJson(); - safePrint('accessToken: $accessToken'); -} on SignedOutException { - // the user is not signed in. -} on SessionExpiredException { - // the users session has expired. -} on NetworkException { - // the access and/or id token is expired but cannot be refreshed because the - // user is offline. -} -// `AuthResult.valueOrNull` will return the identityId if present or null, -// but will never throw even if the underlying operation failed. -final identityId = session.identityIdResult.valueOrNull; -safePrint('identityId: $identityId'); -``` +## Analytics + +#### Configuration of `autoFlushEventsInterval` -- The next step returned from auth operations are now enums. See the example below for how to migrate for `Auth.signUp()`. +In Amplify Flutter v1, the `autoFlushEventsInterval` value was configured manually in the `amplifyconfiguration.dart` file. In Amplify Flutter v2, the `autoFlushEventsInterval` value is not read from `amplifyconfiguration.dart` but instead is passed directly to the `AmplifyAnalyticsPinpoint()` constructor under the `AnalyticsPinpointPluginOptions`. The default value for `autoFlushEventsInterval` remains at 30 seconds, consistent with the behavior in Amplify Flutter v1. + +## Storage + +#### `key` and `StorageAccessLevel` have been replaced by `StoragePath` + +In Amplify Flutter v1, files operations were performed based on a `key` and an `AccessLevel`. Amplify translated these values to a full file path. The `AccessLevel` (guest, protected, or private) determined what prefix to use (`“public/”`, `“protected//”`, or `“private//”`). Amplify Flutter v1 forms the full path using the prefix and the provided key. + +Amplify Flutter v2 allows for full control of the path of the storage object. Storage paths can be constructed either from a static string or from the current users identity id using the `StoragePath` class. + +To migrate from v1 to v2, replace all uses of `key` with `path` and remove uses of `StorageAccessLevel`. The path should include the prefix that was previously added by Amplify automatically. + +##### Example Migration for Public Files ```dart -// v0 -final result = await Amplify.Auth.signUp(/* ... */); -switch (result.nextStep?.signUpStep) { - case 'CONFIRM_SIGN_UP': - // prompt user to confirm sign in using nextStep.codeDeliveryDetails - break; - case 'DONE': - // sign up is complete, user can be taken to an authenticated state. - break; -} +// upload file to "public/file.txt" in Amplify Flutter v1 +Amplify.Storage.uploadFile( + localFile: file, + key: 'file.txt', + options: StorageUploadFileOptions( + accessLevel: StorageAccessLevel.guest, + ), +); ``` ```dart -// v1 -final result = await Amplify.Auth.signUp(/* ... */); -switch (result.nextStep.signUpStep) { - case AuthSignUpStep.confirmSignUp: - // prompt user to confirm sign in using nextStep.codeDeliveryDetails - break; - case AuthSignUpStep.done: - // sign up is complete, user can be taken to an authenticated state. - break; -} +// upload file to "public/file.txt" in Amplify Flutter v2 +Amplify.Storage.uploadFile( + localFile: file, + path: const StoragePath.fromString("public/file.txt"), +); ``` -- 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. +##### Example Migration for Protected Files ```dart -// v0 -final hubSubscription = Amplify.Hub.listen([HubChannel.Auth], (hubEvent) { - switch(hubEvent.eventName) { - case 'SIGNED_IN': - safePrint('User is signed in.'); - break; - case 'SIGNED_OUT': - safePrint('User is signed out.'); - break; - case 'SESSION_EXPIRED': - safePrint('The session has expired.'); - break; - case 'USER_DELETED': - safePrint('The user has been deleted.'); - break; - } -}); +// upload file to "protected//file.txt" in Amplify Flutter v1 +Amplify.Storage.uploadFile( + localFile: file, + key: 'file.txt', + options: StorageUploadFileOptions( + accessLevel: StorageAccessLevel.protected, + ), +); ``` ```dart -// v1 -final subscription = Amplify.Hub.listen(HubChannel.Auth, (AuthHubEvent event) { - switch (event.type) { - case AuthHubEventType.signedIn: - safePrint('User is signed in.'); - break; - case AuthHubEventType.signedOut: - safePrint('User is signed out.'); - break; - case AuthHubEventType.sessionExpired: - safePrint('The session has expired.'); - break; - case AuthHubEventType.userDeleted: - safePrint('The user has been deleted.'); - break; - } -}); +// upload file to "protected//file.txt" in Amplify Flutter v2 +Amplify.Storage.uploadFile( + localFile: file, + path: StoragePath.fromIdentityId( + (identityId) => "protected/$identityId/file.txt", + ), +); ``` -- 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()`. +##### Example Migration for Private Files ```dart -// v0 -final result = await Amplify.Auth.signIn( - username: username, - password: password, - options: const CognitoSignInOptions( - clientMetadata: {'data': 'value'}, +// upload file to "private//file.txt" in Amplify Flutter v1 +Amplify.Storage.uploadFile( + localFile: file, + key: 'file.txt', + options: StorageUploadFileOptions( + accessLevel: StorageAccessLevel.private, ), ); ``` ```dart -// v1 -final result = await Amplify.Auth.signIn( - username: username, - password: password, - options: const SignInOptions( - pluginOptions: CognitoSignInPluginOptions( - clientMetadata: {'data': 'value'}, - ), - ), +// upload file to "private//file.txt" in Amplify Flutter v2 +Amplify.Storage.uploadFile( + localFile: file, + path: StoragePath.fromIdentityId( + (identityId) => "private/$identityId/file.txt", + ), ); ``` -### 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`. - -```xml - - - - - - - ... - - - - - - - - - ... - -``` +#### Prefix Resolver has been removed -Start by removing this code, then update the `AndroidManifest.xml` as follows, replacing `myapp` with your custom URI scheme you configured in your backend. +With the introduction of `StoragePath`, there is no need for a prefix resolver. Simply specify the full path when constructing the `StoragePath`. -```diff -+ -+ -+ -+ -+ - - ... - -+ -+ -+ -+ -+ -+ - -- -- -- -- -- -- -- -- - ... - +#### Delimiter has been moved to the List API Options + +In Amplify Flutter v1 `AmplifyStorageS3` accepted a `delimiter` option. This was used in list operations to determine sub paths. This option is now available as part of the List API options. + +```dart +final result = Amplify.Storage.list( + path: const StoragePath.fromString('public/album/'), + options: const StorageListOptions( + pageSize: 50, + pluginOptions: S3ListPluginOptions( + excludeSubPaths: true, + delimiter: '/' + ), + ), +); ``` - +## DataStore - +#### Configuration +In Amplify Flutter v1, the `AmplifyDataStore()` constructor allows for the customization of the plugin through optional parameters such as errorHandler, conflictHandler, syncExpression, syncInterval, syncMaxRecords, syncPageSize, and authModeStrategy. In Amplify Flutter v2, these parameters have been relocated under DataStorePluginOptions, with default values that mirror those of Amplify Flutter v1. -For iOS, v0 required the following changes be made to your `Info.plist`. +## Models & Query Predicates (GraphQL API & DataStore) -```xml - +#### Configuration - - +In Amplify Flutter v1, the `AmplifyAPI()` constructor accepts optional parameters such as authProviders, baseHttpClient, modelProvider, and subscriptionOptions. In Amplify Flutter v2, these parameters have been relocated to `APIPluginOptions`. - CFBundleURLTypes - - - CFBundleURLSchemes - - myapp - - - +#### Generated Models - - -``` +##### Model.fromJson() -In v1, these changes are no longer required and can be safely removed without any effect on social sign-in (Hosted UI). +Amplify Flutter v2 now decodes AppSync GraphQL responses in their original shape (#816). In doing so, we’ve removed the previous transformations that happened after receiving an AppSync response. This requires data Models to be regenerated with the latest Amplify CLI. After upgrading to Amplify Flutter v2 please run the following: -```diff -- CFBundleURLTypes -- -- -- CFBundleURLSchemes -- -- myapp -- -- -- +```bash +$ amplify upgrade +# from the root of your Flutter project +$ amplify codegen models ``` - +##### Model.getId() - +Amplify Flutter v2 removes all usage of the generated `Model.getId()` method. Replace all instances of `Model.getId()` and with `Model.modelIdentifier`. -Web and Desktop platforms were not supported in v0. +#### Nested Model Query Predicates -Follow the instructions [here](/gen1/[platform]/build-a-backend/auth/add-social-provider/#platform-setup) to configure Web and Desktop platforms for social sign-in (Hosted UI) in v1. +Query predicates for nested model relationships when passing Model.id for equal and not equal operations are no longer supported. Please update your query predicates to use Model.modelIdentifier, for example: - +```diff +- Post.BLOG.eq(blog1.id) ++ Post.BLOG.eq(blog1.modelIdentifier) + +``` +Or - +```diff +- Post.BLOG.eq("1234") => ++ Post.BLOG.eq(BlogModelIdentifier(id: "1234")) + +``` -## Analytics +#### Support For Only Custom Primary Key Enabled Back-Ends -- `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 `AnalyticsException`s for specific exception cases. - -## API - -- REST API methods have breaking changes. See [REST API docs](/gen1/[platform]/build-a-backend/restapi/set-up-rest-api/) 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](/gen1/[platform]/build-a-backend/graphqlapi/query-data/) 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. +Amplify Flutter V2 no longer supports the Amplify CLI feature flag `respectprimarykeyattributesonconnectionfield` to be **disabled**. -## Storage +_If this feature flag is already enabled in your current project you can skip this section._ -- 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`: +| `graphql.schema` | Feature Flag On | Feature Flag off | +| -----------------| --------------- | ---------------- | +| **has `@primaryKey`**| No further action required | Start at step 1. | +| **no `@primaryKey`** |No further action required | Skip to step 4. | -```dart -// v0 -final result = await Amplify.Storage.uploadFile( - local: exampleFile, - key: 'ExampleKey', -); -print('Uploaded file key: ${result.key}') - -// v1 -final result = await Amplify.Storage.uploadFile( - localFile: exampleFile, - key: 'ExampleKey', -).result; -print('Uploaded file key: ${result.uploadedItem.key}'); -``` +If the feature flag is turned off **AND** your model schema has the `@primaryKey` annotation, please follow these migration steps. -See [storage docs](/gen1/[platform]/build-a-backend/storage/set-up-storage/) for more detailed examples of other storage methods. +If the feature flag is turned off, but your `graphql.schema` does not contain references to `@primaryKey`, jump to step 4. + +1. Remove `@primaryKey` references from your `graphql.schema` +2. Run `$ amplify push` +3. Run `$ amplify codegen models` +4. Enabled the feature flag: + 1. Locate `amplify/cli.json` inside your project + 2. Enable the feature flag: +```json +"features": { + "graphqltransformer": { + ... + "respectprimarykeyattributesonconnectionfield": true, + ... + }, +} +``` +5. Run `$ amplify push` +6. Run `$ amplify codegen models` diff --git a/src/fragments/lib/push-notifications/flutter/getting_started/40_install_lib.mdx b/src/fragments/lib/push-notifications/flutter/getting_started/40_install_lib.mdx index f7b05a3157c..5b0501bb004 100644 --- a/src/fragments/lib/push-notifications/flutter/getting_started/40_install_lib.mdx +++ b/src/fragments/lib/push-notifications/flutter/getting_started/40_install_lib.mdx @@ -5,13 +5,10 @@ In your project directory, you should first install the necessary dependencies f 2. Add the necessary libraries into the `dependencies` block: ```yaml -environment: - sdk: '>=2.18.0 <4.0.0' - dependencies: - amplify_auth_cognito: ^1.0.0 - amplify_flutter: ^1.0.0 - amplify_push_notifications_pinpoint: ^1.0.0 + amplify_auth_cognito: ^2.0.0 + amplify_flutter: ^2.0.0 + amplify_push_notifications_pinpoint: ^2.0.0 flutter: sdk: flutter ``` diff --git a/src/fragments/lib/restapi/flutter/getting-started/10_preReq.mdx b/src/fragments/lib/restapi/flutter/getting-started/10_preReq.mdx index eb0c1c4160c..1acefc397fd 100644 --- a/src/fragments/lib/restapi/flutter/getting-started/10_preReq.mdx +++ b/src/fragments/lib/restapi/flutter/getting-started/10_preReq.mdx @@ -1,12 +1,3 @@ * [Install and configure Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) -* A Flutter application targeting Flutter SDK >= 3.3.0 with Amplify libraries integrated - - The following are also required, depending on which platforms you are targeting: - * An iOS configuration targeting at least iOS 13.0 and XCode version >=13.2 - * An Android configuration targeting at least Android API level 24 (Android 7.0) or above - * Any browser supported by Flutter for Web (you can check the list of supported browsers [here](https://docs.flutter.dev/development/platform-integration/web/faq#which-web-browsers-are-supported-by-flutter)) - * Any Windows OS meeting Flutter minimums - * macOS version 10.15 or higher - * Any Ubuntu Linux distribution meeting Flutter minimums - * For a full example please follow the [project setup walkthrough](/gen1/[platform]/start/project-setup/create-application/) +Amplify Flutter 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](/gen1/[platform]/start/project-setup/platform-setup/) guide for more details on platform specific setup. diff --git a/src/fragments/lib/restapi/flutter/getting-started/20_installLib.mdx b/src/fragments/lib/restapi/flutter/getting-started/20_installLib.mdx index b2f66c3f692..47fa517c3ee 100644 --- a/src/fragments/lib/restapi/flutter/getting-started/20_installLib.mdx +++ b/src/fragments/lib/restapi/flutter/getting-started/20_installLib.mdx @@ -1,13 +1,10 @@ Add the following dependencies to your `pubspec.yaml` file and install dependencies when asked. Please keep in mind that Auth plugin is needed for IAM authorization mode, which is default for REST API: ```yaml -environment: - sdk: ">=2.18.0 <4.0.0" - dependencies: flutter: sdk: flutter - amplify_flutter: ^1.0.0 - amplify_api: ^1.0.0 - amplify_auth_cognito: ^1.0.0 + amplify_flutter: ^2.0.0 + amplify_api: ^2.0.0 + amplify_auth_cognito: ^2.0.0 ``` diff --git a/src/fragments/lib/storage/flutter/configureaccess/10_protected_upload.mdx b/src/fragments/lib/storage/flutter/configureaccess/10_protected_upload.mdx deleted file mode 100644 index 39f717d7168..00000000000 --- a/src/fragments/lib/storage/flutter/configureaccess/10_protected_upload.mdx +++ /dev/null @@ -1,25 +0,0 @@ -```dart -import 'package:amplify_flutter/amplify_flutter.dart'; - -Future uploadProtectedFile({ - required String filePath, - required String key, -}) async { - final awsFile = AWSFile.fromPath(filePath); - const options = StorageUploadFileOptions( - accessLevel: StorageAccessLevel.protected, - ); - - try { - final uploadResult = await Amplify.Storage.uploadFile( - localFile: awsFile, - key: key, - options: options, - ).result; - safePrint('Uploaded file: ${uploadResult.uploadedItem.key}'); - } on StorageException catch (e) { - safePrint('Something went wrong uploading file: ${e.message}'); - rethrow; - } -} -``` \ No newline at end of file diff --git a/src/fragments/lib/storage/flutter/configureaccess/20_protected_download.mdx b/src/fragments/lib/storage/flutter/configureaccess/20_protected_download.mdx deleted file mode 100644 index c5c4a0106a3..00000000000 --- a/src/fragments/lib/storage/flutter/configureaccess/20_protected_download.mdx +++ /dev/null @@ -1,33 +0,0 @@ -```dart -import 'package:amplify_flutter/amplify_flutter.dart'; -import 'package:amplify_storage_s3/amplify_storage_s3.dart'; - -Future downloadProtectedFile({ - required String key, - required String targetIdentityId, - required String downloadPath, -}) async { - final awsFile = AWSFile.fromPath(downloadPath); - final options = StorageDownloadFileOptions( - // specify that the file has a protected access level - accessLevel: StorageAccessLevel.protected, - // specify the identity ID of the user who uploaded this file - pluginOptions: S3DownloadFilePluginOptions.forIdentity( - targetIdentityId, - ), - ); - - try { - final result = await Amplify.Storage.downloadFile( - key: key, - localFile: awsFile, - options: options, - ).result; - - safePrint('Downloaded file is located at: ${result.localFile.path}'); - } on StorageException catch (e) { - safePrint('Something went wrong downloading the file: ${e.message}'); - rethrow; - } -} -``` \ No newline at end of file diff --git a/src/fragments/lib/storage/flutter/configureaccess/30_private_upload.mdx b/src/fragments/lib/storage/flutter/configureaccess/30_private_upload.mdx deleted file mode 100644 index d0d2370a31e..00000000000 --- a/src/fragments/lib/storage/flutter/configureaccess/30_private_upload.mdx +++ /dev/null @@ -1,26 +0,0 @@ -```dart -import 'package:amplify_flutter/amplify_flutter.dart'; - -Future uploadPrivateFile({ - required String filePath, - required String key, -}) async { - final awsFile = AWSFile.fromPath(filePath); - const options = StorageUploadFileOptions( - accessLevel: StorageAccessLevel.private, - ); - - try { - final uploadResult = await Amplify.Storage.uploadFile( - localFile: awsFile, - key: key, - options: options, - ).result; - - safePrint('Uploaded file: ${uploadResult.uploadedItem.key}'); - } on StorageException catch (e) { - safePrint('Something went wrong uploading file: ${e.message}'); - rethrow; - } -} -``` diff --git a/src/fragments/lib/storage/flutter/configureaccess/40_private_download.mdx b/src/fragments/lib/storage/flutter/configureaccess/40_private_download.mdx deleted file mode 100644 index 1cbd1a7ca6d..00000000000 --- a/src/fragments/lib/storage/flutter/configureaccess/40_private_download.mdx +++ /dev/null @@ -1,26 +0,0 @@ -```dart -import 'package:amplify_flutter/amplify_flutter.dart'; - -Future downloadPrivateFile({ - required String key, - required String downloadPath, -}) async { - final awsFile = AWSFile.fromPath(downloadPath); - const options = StorageDownloadFileOptions( - accessLevel: StorageAccessLevel.private, - ); - - try { - final result = await Amplify.Storage.downloadFile( - key: key, - localFile: awsFile, - options: options, - ).result; - - safePrint('Downloaded file is located at: ${result.localFile.path}'); - } on StorageException catch (e) { - safePrint('Something went wrong downloading the file: ${e.message}'); - rethrow; - } -} -``` diff --git a/src/fragments/lib/storage/flutter/configureaccess/50_customization.mdx b/src/fragments/lib/storage/flutter/configureaccess/50_customization.mdx deleted file mode 100644 index 5b597f9af9c..00000000000 --- a/src/fragments/lib/storage/flutter/configureaccess/50_customization.mdx +++ /dev/null @@ -1,104 +0,0 @@ -## Customization - -### Customize Object Key Path - -You can customize your key path by defining a prefix resolver: - -```dart -import 'package:amplify_auth_cognito/amplify_auth_cognito.dart'; -import 'package:amplify_flutter/amplify_flutter.dart'; -import 'package:amplify_storage_s3/amplify_storage_s3.dart'; - -// Define your own prefix resolver, which implements the `S3PrefixResolver`. -class MyPrefixResolver implements S3PrefixResolver { - const MyPrefixResolver(); - - @override - Future resolvePrefix({ - required StorageAccessLevel accessLevel, - String? identityId, - }) async { - if (accessLevel == StorageAccessLevel.guest) { - return 'myPublicPrefix/'; - } - - final String accessLevelPrefix; - - if (accessLevel == StorageAccessLevel.protected) { - accessLevelPrefix = 'myProtectedPrefix/'; - } else { - accessLevelPrefix = 'myPrivatePrefix/'; - } - - final targetIdentityId = identityId ?? await getCurrentUserIdentityId(); - - return '$accessLevelPrefix$targetIdentityId/'; - } - - Future getCurrentUserIdentityId() async { - final authPlugin = Amplify.Auth.getPlugin(AmplifyAuthCognito.pluginKey); - final authSession = await authPlugin.fetchAuthSession(); - return authSession.identityIdResult.value; - } -} - -``` - -Then configure the storage plugin with the resolver. - -```dart -final storagePlugin = AmplifyStorageS3( - prefixResolver: const MyPrefixResolver(), -); -... -await Amplify.addPlugin(storagePlugin); -``` - -Add the IAM policy that corresponds with the prefixes defined above to enable read, write and delete operation for all the objects under path *myPublicPrefix/*: -```json -{ - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "s3:GetObject", - "s3:PutObject", - "s3:DeleteObject" - ], - "Resource": ["arn:aws:s3:::your-s3-bucket/myPublicPrefix/*"] - } - ] -} - -``` -If you want to have custom *private* path prefix like *`myPrivatePrefix/`*, you need to add it into your IAM policy: -```json -{ - "Statement": [ - { - "Effect": "Allow", - "Action": [ - "s3:GetObject", - "s3:PutObject", - "s3:DeleteObject" - ], - "Resource": [ - "arn:aws:s3:::your-s3-bucket/myPrivatePrefix/${cognito-identity.amazonaws.com:sub}/*" - ] - } - ] -} -``` -#### Passthrough PrefixResolver - -If you would like no prefix resolution logic, such as performing S3 requests at the root of the bucket, you can use the `PassThroughPrefixResolver` provided by the `amplify_storage_s3` package. - -```dart -import 'package:amplify_storage_s3/amplify_storage_s3.dart'; - -final storagePlugin = AmplifyStorageS3( - prefixResolver: const PassThroughPrefixResolver(), -); - -await Amplify.addPlugin(storagePlugin); -``` diff --git a/src/fragments/lib/storage/flutter/copy.mdx b/src/fragments/lib/storage/flutter/copy.mdx index 22804ff46d7..41f724a09b7 100644 --- a/src/fragments/lib/storage/flutter/copy.mdx +++ b/src/fragments/lib/storage/flutter/copy.mdx @@ -1,28 +1,15 @@ You can copy an existing file to a different location in your S3 bucket. User who initiates a copy operation should have read permission on the copy source file. ```dart -import 'package:amplify_flutter/amplify_flutter.dart'; - -Future copyGuestFileToPrivate({ - required String sourceKey, - required String destinationKey, -}) async { +Future copy() async { try { final result = await Amplify.Storage.copy( - source: StorageItemWithAccessLevel( - storageItem: StorageItem(key: sourceKey), - accessLevel: StorageAccessLevel.guest, - ), - destination: StorageItemWithAccessLevel( - storageItem: StorageItem(key: destinationKey), - accessLevel: StorageAccessLevel.private, - ), + source: const StoragePath.fromString('album/2024/1.jpg'), + destination: const StoragePath.fromString('shared/2024/1.jpg'), ).result; - - safePrint('Copied file: ${result.copiedItem.key}'); + safePrint('Copied file: ${result.copiedItem.path}'); } on StorageException catch (e) { - safePrint('Error copying file: ${e.message}'); - rethrow; + safePrint(e); } } -``` \ No newline at end of file +``` diff --git a/src/fragments/lib/storage/flutter/download.mdx b/src/fragments/lib/storage/flutter/download.mdx index b6350bbbb22..fdc80d8a6bb 100644 --- a/src/fragments/lib/storage/flutter/download.mdx +++ b/src/fragments/lib/storage/flutter/download.mdx @@ -11,20 +11,17 @@ You can use the [path_provider](https://pub.dev/packages/path_provider) package ```dart +import 'package:amplify_flutter/amplify_flutter.dart'; import 'package:path_provider/path_provider.dart'; -Future downloadToLocalFile(String key) async { +Future downloadFile() async { final documentsDir = await getApplicationDocumentsDirectory(); - final filepath = documentsDir.path + '/example.txt'; + final filepath = '${documentsDir.path}/example.txt'; try { final result = await Amplify.Storage.downloadFile( - key: key, + path: const StoragePath.fromString('public/example.txt'), localFile: AWSFile.fromPath(filepath), - onProgress: (progress) { - safePrint('Fraction completed: ${progress.fractionCompleted}'); - }, ).result; - safePrint('Downloaded file is located at: ${result.localFile.path}'); } on StorageException catch (e) { safePrint(e.message); @@ -39,14 +36,15 @@ Future downloadToLocalFile(String key) async { On Web, the download process will be handled by the browser. You can provide the downloaded file name by specifying the `path` parameter of `AWSFile.fromPath`. E.g. this instructs the browser to download the file `download.txt`. ```dart -Future downloadToLocalFileOnWeb(String key) async { +import 'package:amplify_flutter/amplify_flutter.dart'; + +Future downloadFile() async { try { final result = await Amplify.Storage.downloadFile( - key: key, + path: const StoragePath.fromString('public/example.txt'), localFile: AWSFile.fromPath('download.txt'), ).result; - - safePrint('Downloaded file: ${result.downloadedItem.key}'); + safePrint('Downloaded file: ${result.downloadedItem.path}'); } on StorageException catch (e) { safePrint(e.message); } @@ -62,15 +60,11 @@ Future downloadToLocalFileOnWeb(String key) async { You can download a file to in-memory buffer with `Amplify.Storage.downloadData`: ```dart -Future downloadToMemory(String key) async { +Future download() async { try { final result = await Amplify.Storage.downloadData( - key: key, - onProgress: (progress) { - safePrint('Fraction completed: ${progress.fractionCompleted}'); - }, + path: const StoragePath.fromString('public/example.txt'), ).result; - safePrint('Downloaded data: ${result.bytes}'); } on StorageException catch (e) { safePrint(e.message); @@ -78,9 +72,101 @@ Future downloadToMemory(String key) async { } ``` +## Download Progress + +To track progress of the download, use the progress listener callback. + + + + + +```dart +final operation = Amplify.Storage.downloadFile( + localFile: AWSFile.fromPath('/path/to/local/file.txt'), + path: const StoragePath.fromString('public/example.txt'), + onProgress: (progress) { + safePrint('fraction totalBytes: ${progress.totalBytes}'); + safePrint('fraction transferredBytes: ${progress.transferredBytes}'); + safePrint('fraction completed: ${progress.fractionCompleted}'); + } +); +``` + + + + + +```dart +final operation = Amplify.Storage.downloadData( + path: const StoragePath.fromString('public/example.txt'), + onProgress: (progress) { + safePrint('fraction totalBytes: ${progress.totalBytes}'); + safePrint('fraction transferredBytes: ${progress.transferredBytes}'); + safePrint('fraction completed: ${progress.fractionCompleted}'); + }, +); +``` + + + + + +## Control of Download Operations + +A call to `Amplify.Storage.downloadFile` or `Amplify.Storage.downloadData` returns a reference to the operation that is performing the upload. + +To cancel the upload (for example, in response to the user pressing a Cancel button), simply call `.cancel()` on the returned upload operation. Download operations also allows for the operation to be paused and resumed. + + + + + +```dart +Future download() async { + final operation = Amplify.Storage.downloadFile( + localFile: AWSFile.fromPath('/path/to/local/file.txt'), + path: const StoragePath.fromString('public/example.txt'), + ); + + // pause operation + await operation.pause(); + + // resume operation + await operation.resume(); + + // cancel operation + await operation.cancel(); +} +``` + + + + + +```dart +Future download() async { + final operation = Amplify.Storage.downloadData( + path: const StoragePath.fromString('public/example.txt'), + ); + + // pause operation + await operation.pause(); + + // resume operation + await operation.resume(); + + // cancel operation + await operation.cancel(); +} +``` + + + + + ## Generate a download URL -You can get a downloadable URL for the file in storage by its key and access level. +You can get a downloadable URL for the file in storage by its path. When creating a downloadable URL, you can choose to check if the file exists by setting `validateObjectExistence` to `true` in `S3GetUrlPluginOptions`. If the file is inaccessible or does not exist, a `StorageException` is thrown. @@ -88,25 +174,20 @@ This allows you to check if an object exists during generating the presigned URL that object. ```dart -Future getDownloadUrl({ - required String key, - required StorageAccessLevel accessLevel, -}) async { +Future getDownloadUrl() async { try { final result = await Amplify.Storage.getUrl( - key: key, + path: const StoragePath.fromString('public/example.txt'), options: const StorageGetUrlOptions( - accessLevel: accessLevel, pluginOptions: S3GetUrlPluginOptions( validateObjectExistence: true, expiresIn: Duration(days: 1), ), ), ).result; - return result.url.toString(); + safePrint('url: ${result.url}'); } on StorageException catch (e) { - safePrint('Could not get a downloadable URL: ${e.message}'); - rethrow; + safePrint(e.message); } } ``` diff --git a/src/fragments/lib/storage/flutter/get-properties.mdx b/src/fragments/lib/storage/flutter/get-properties.mdx index a2d601a3778..97169de98cc 100644 --- a/src/fragments/lib/storage/flutter/get-properties.mdx +++ b/src/fragments/lib/storage/flutter/get-properties.mdx @@ -1,18 +1,14 @@ You can get file properties and metadata without downloading the file using `Amplify.Storage.getProperties`. ```dart -import 'package:amplify_flutter/amplify_flutter.dart'; - Future getFileProperties() async { try { final result = await Amplify.Storage.getProperties( - key: 'example.txt', + path: const StoragePath.fromString('example.txt'), ).result; - safePrint('File size: ${result.storageItem.size}'); } on StorageException catch (e) { - safePrint('Could not retrieve properties: ${e.message}'); - rethrow; + safePrint(e.message); } } ``` diff --git a/src/fragments/lib/storage/flutter/getting-started/10_preReq.mdx b/src/fragments/lib/storage/flutter/getting-started/10_preReq.mdx index 9338778fc30..1acefc397fd 100644 --- a/src/fragments/lib/storage/flutter/getting-started/10_preReq.mdx +++ b/src/fragments/lib/storage/flutter/getting-started/10_preReq.mdx @@ -1,11 +1,3 @@ -* A Flutter application targeting Flutter SDK >= 3.3.0 with Amplify libraries integrated +* [Install and configure Amplify CLI](/gen1/[platform]/tools/cli/start/set-up-cli/) - The following are also required, depending on which platforms you are targeting: - - * An iOS configuration targeting at least iOS 13.0 and XCode version >=13.2 - * An Android configuration targeting at least Android API level 24 (Android 7.0) or above - * Any browser supported by Flutter for Web (you can check the list of supported browsers [here](https://docs.flutter.dev/development/platform-integration/web/faq#which-web-browsers-are-supported-by-flutter)) - * Any Windows OS meeting Flutter minimums - * macOS version 10.15 or higher - * Any Ubuntu Linux distribution meeting Flutter minimums - * For a full example please follow the [project setup walkthrough](/gen1/[platform]/start/project-setup/create-application/) +Amplify Flutter 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](/gen1/[platform]/start/project-setup/platform-setup/) guide for more details on platform specific setup. diff --git a/src/fragments/lib/storage/flutter/getting-started/20_installLib.mdx b/src/fragments/lib/storage/flutter/getting-started/20_installLib.mdx index a88dceb3b9f..0896d193a2f 100644 --- a/src/fragments/lib/storage/flutter/getting-started/20_installLib.mdx +++ b/src/fragments/lib/storage/flutter/getting-started/20_installLib.mdx @@ -1,15 +1,11 @@ Add the following dependency to your **app**'s `pubspec.yaml` along with others you added above in **Prerequisites**: ```yaml -environment: - sdk: ">=2.18.0 <4.0.0" - flutter: ">=3.3.0" - dependencies: flutter: sdk: flutter - amplify_auth_cognito: ^1.0.0 - amplify_flutter: ^1.0.0 - amplify_storage_s3: ^1.0.0 -``` \ No newline at end of file + amplify_auth_cognito: ^2.0.0 + amplify_flutter: ^2.0.0 + amplify_storage_s3: ^2.0.0 +``` diff --git a/src/fragments/lib/storage/flutter/list.mdx b/src/fragments/lib/storage/flutter/list.mdx index 0b176d699d6..149be23d0cb 100644 --- a/src/fragments/lib/storage/flutter/list.mdx +++ b/src/fragments/lib/storage/flutter/list.mdx @@ -4,23 +4,22 @@ This will list all files located under path `album` that: * have `private` access level * are in the root of `album/` (the result doesn't include files under any sub path) -```dart -import 'package:amplify_flutter/amplify_flutter.dart'; -import 'package:amplify_storage_s3/amplify_storage_s3.dart'; +`excludeSubPaths` can be used to exclude nested paths. `/` is used by as the delimiter for nested paths. This can be customized with the `delimiter` option. +```dart Future listAlbum() async { try { String? nextToken; bool hasNextPage; do { final result = await Amplify.Storage.list( - path: 'album/', + path: const StoragePath.fromString('public/album/'), options: StorageListOptions( - accessLevel: StorageAccessLevel.private, pageSize: 50, nextToken: nextToken, pluginOptions: const S3ListPluginOptions( excludeSubPaths: true, + delimiter: '/', ), ), ).result; @@ -29,8 +28,7 @@ Future listAlbum() async { hasNextPage = result.hasNextPage; } while (hasNextPage); } on StorageException catch (e) { - safePrint('Error listing files: ${e.message}'); - rethrow; + safePrint(e.message); } } ``` @@ -42,22 +40,17 @@ You can also list all files under a given path without pagination by using the ` This will list all public files (i.e. those with `guest` access level): ```dart -import 'package:amplify_flutter/amplify_flutter.dart'; -import 'package:amplify_storage_s3/amplify_storage_s3.dart'; - -Future listAllWithGuestAccessLevel() async { +Future listAll() async { try { final result = await Amplify.Storage.list( + path: const StoragePath.fromString('public/'), options: const StorageListOptions( - accessLevel: StorageAccessLevel.guest, pluginOptions: S3ListPluginOptions.listAll(), ), ).result; - safePrint('Listed items: ${result.items}'); } on StorageException catch (e) { - safePrint('Error listing files: ${e.message}'); - rethrow; + safePrint(e.message); } } -``` \ No newline at end of file +``` diff --git a/src/fragments/lib/storage/flutter/remove.mdx b/src/fragments/lib/storage/flutter/remove.mdx index 48587ba1ed6..d742f7e4f31 100644 --- a/src/fragments/lib/storage/flutter/remove.mdx +++ b/src/fragments/lib/storage/flutter/remove.mdx @@ -1,50 +1,36 @@ ## Remove a file -You can remove a single file using `Amplify.Storage.remove` with the `key` and its associated access level: +You can remove a single file using `Amplify.Storage.remove`. ```dart -import 'package:amplify_flutter/amplify_flutter.dart'; - -Future removeFile({ - required String key, - required StorageAccessLevel accessLevel, -}) async { +Future removeFile() async { try { final result = await Amplify.Storage.remove( - key: key, - options: StorageRemoveOptions( - accessLevel: accessLevel, - ), + path: const StoragePath.fromString('public/file.txt'), ).result; - safePrint('Removed file: ${result.removedItem.key}'); + safePrint('Removed file: ${result.removedItem.path}'); } on StorageException catch (e) { - safePrint('Error deleting file: ${e.message}'); - rethrow; + safePrint(e.message); } } ``` ## Remove multiple files -You can remove multiple files using `Amplify.Storage.removeMany` with the `keys`, the files to be removed in a batch should have the same access level: +You can remove multiple files using `Amplify.Storage.removeMany`. ```dart -import 'package:amplify_flutter/amplify_flutter.dart'; - -Future removePrivateFiles({ - required List keys, -}) async { +Future remove() async { try { final result = await Amplify.Storage.removeMany( - keys: keys, - options: const StorageRemoveManyOptions( - accessLevel: StorageAccessLevel.private, - ), + paths: [ + const StoragePath.fromString('public/file-1.txt'), + const StoragePath.fromString('public/file-2.txt'), + ], ).result; safePrint('Removed files: ${result.removedItems}'); } on StorageException catch (e) { - safePrint('Error deleting files: ${e.message}'); - rethrow; + safePrint(e.message); } } -``` \ No newline at end of file +``` diff --git a/src/fragments/lib/storage/flutter/upload.mdx b/src/fragments/lib/storage/flutter/upload.mdx index 533b7f458b6..91bf001e46d 100644 --- a/src/fragments/lib/storage/flutter/upload.mdx +++ b/src/fragments/lib/storage/flutter/upload.mdx @@ -1,6 +1,6 @@ ## Upload File -To upload to S3 from a file, specify the `key` and the `localFile` to be uploaded, where the `localFile` can be an instance of `AWSFile` created from either an OS platform `File` instance or the result of Flutter file picker plugins such as [file_picker](https://pub.dev/packages/file_picker). +To upload to S3 from a file, specify the `path` to upload the file to and the `localFile` to be uploaded. `localFile` can be an instance of `AWSFile` created from either an OS platform `File` instance or the result of Flutter file picker plugins such as [file_picker](https://pub.dev/packages/file_picker). ### Upload platform `File` @@ -13,50 +13,67 @@ by running: `flutter pub add aws_common` - + ```dart -import 'dart:io' as io; +import 'package:amplify_flutter/amplify_flutter.dart'; + +Future uploadFile() async { + try { + final result = await Amplify.Storage.uploadFile( + localFile: AWSFile.fromPath('/path/to/local/file.txt'), + path: const StoragePath.fromString('public/file.txt'), + ).result; + safePrint('Uploaded file: ${result.uploadedItem.path}'); + } on StorageException catch (e) { + safePrint(e.message); + } +} +``` + + + + -import 'package:amplify_storage_s3/amplify_storage_s3.dart'; +```dart +import 'dart:io' show File; + +import 'package:amplify_flutter/amplify_flutter.dart'; import 'package:aws_common/vm.dart'; -Future uploadIOFile(io.File file) async { - final awsFile = AWSFilePlatform.fromFile(file); +Future uploadFile(File file) async { try { - final uploadResult = await Amplify.Storage.uploadFile( - localFile: awsFile, - key: 'upload/file.png', + final result = await Amplify.Storage.uploadFile( + localFile: AWSFilePlatform.fromFile(file), + path: const StoragePath.fromString('public/file.png'), ).result; - safePrint('Uploaded file: ${uploadResult.uploadedItem.key}'); + safePrint('Uploaded file: ${result.uploadedItem.path}'); } on StorageException catch (e) { - safePrint('Error uploading file: ${e.message}'); - rethrow; + safePrint(e.message); } } ``` - + ```dart -import 'dart:html' as html; +import 'dart:html' show File; -import 'package:amplify_storage_s3/amplify_storage_s3.dart'; +import 'package:amplify_flutter/amplify_flutter.dart'; import 'package:aws_common/web.dart'; -Future uploadHtmlFile(html.File file) async { +Future uploadFile(File file) async { final awsFile = AWSFilePlatform.fromFile(file); try { - final uploadResult = await Amplify.Storage.uploadFile( + final result = await Amplify.Storage.uploadFile( localFile: awsFile, - key: 'upload/file.png', + path: const StoragePath.fromString('public/file.png'), ).result; - safePrint('Uploaded file: ${uploadResult.uploadedItem.key}'); + safePrint('Uploaded file: ${result.uploadedItem.path}'); } on StorageException catch (e) { - safePrint('Error uploading file: ${e.message}'); - rethrow; + safePrint(e.message); } } ``` @@ -88,7 +105,7 @@ Future uploadImage() async { return; } - // Upload file with its filename as the key + // Upload file using the filename final platformFile = result.files.single; try { final result = await Amplify.Storage.uploadFile( @@ -96,48 +113,39 @@ Future uploadImage() async { platformFile.readStream!, size: platformFile.size, ), - key: platformFile.name, + path: StoragePath.fromString('public/${platformFile.name}'), onProgress: (progress) { safePrint('Fraction completed: ${progress.fractionCompleted}'); }, ).result; - safePrint('Successfully uploaded file: ${result.uploadedItem.key}'); + safePrint('Successfully uploaded file: ${result.uploadedItem.path}'); } on StorageException catch (e) { - safePrint('Error uploading file: $e'); - rethrow; + safePrint(e.message); } } ``` ## Upload Data -To upload to S3 from a data object, specify the `key` and `data`, where `data` is an instance of `S3DataPayload` created from various data formats. +To upload to S3 from a data object, specify the `path` to upload the file to and the `data` to upload. `data` should be an instance of `StorageDataPayload` created from various data formats. ```dart -import 'package:amplify_flutter/amplify_flutter.dart'; -import 'package:amplify_storage_s3/amplify_storage_s3.dart'; - -Future uploadStringData({ - required String dataString, - required String key, -}) async { +Future uploadData() async { try { final result = await Amplify.Storage.uploadData( - data: S3DataPayload.string( - dataString, + data: StorageDataPayload.string( + 'hello world', contentType: 'text/plain', ), - key: key, + path: const StoragePath.fromString('public/example.txt'), ).result; - - safePrint('Uploaded data: ${result.uploadedItem.key}'); + safePrint('Uploaded data: ${result.uploadedItem.path}'); } on StorageException catch (e) { - safePrint('Error uploading data: ${e.message}'); - rethrow; + safePrint(e.message); } } ``` @@ -147,23 +155,21 @@ Future uploadStringData({ ```dart -import 'package:amplify_flutter/amplify_flutter.dart'; -import 'package:amplify_storage_s3/amplify_storage_s3.dart'; - -Future uploadJsonObject({ - required Map json, - required String key, -}) async { +Future uploadData() async { try { final result = await Amplify.Storage.uploadData( - data: S3DataPayload.json(json), - key: key, + data: StorageDataPayload.json({ + 'title': 'example', + 'author': { + 'firstName': 'Jane', + 'lastName': 'Doe', + }, + }), + path: const StoragePath.fromString('public/example.json'), ).result; - - safePrint('Uploaded data: ${result.uploadedItem.key}'); + safePrint('Uploaded data: ${result.uploadedItem.path}'); } on StorageException catch (e) { - safePrint('Error uploading data: ${e.message}'); - rethrow; + safePrint(e.message); } } ``` @@ -172,26 +178,19 @@ Future uploadJsonObject({ -`S3DataPayload.dataUrl()` parses the provided data URL string, and throws exception if the data URL is invalid. See more info about [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs). +`StorageDataPayload.dataUrl()` parses the provided data URL string, and throws exception if the data URL is invalid. See more info about [data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URLs). ```dart -import 'package:amplify_flutter/amplify_flutter.dart'; -import 'package:amplify_storage_s3/amplify_storage_s3.dart'; - -Future uploadDataUrl({ - required String dataUrl, - required String key, -}) async { +Future uploadData() async { + const dataUrl = 'data:text/plain;charset=utf-8;base64,aGVsbG8gd29ybGQ='; try { final result = await Amplify.Storage.uploadData( - data: S3DataPayload.dataUrl(dataUrl), - key: key, + data: StorageDataPayload.dataUrl(dataUrl), + path: const StoragePath.fromString('public/example.txt'), ).result; - - safePrint('Uploaded data: ${result.uploadedItem.key}'); + safePrint('Uploaded data: ${result.uploadedItem.path}'); } on StorageException catch (e) { - safePrint('Error uploading data: ${e.message}'); - rethrow; + safePrint(e.message); } } ``` @@ -201,27 +200,19 @@ Future uploadDataUrl({ ```dart -import 'package:amplify_flutter/amplify_flutter.dart'; -import 'package:amplify_storage_s3/amplify_storage_s3.dart'; - -Future uploadBytes({ - required List bytes, - required String key, - required String contentType, -}) async { +Future uploadBytes() async { try { + final bytes = 'hello world'.codeUnits; final result = await Amplify.Storage.uploadData( - data: S3DataPayload.bytes( + data: StorageDataPayload.bytes( bytes, - contentType: contentType, + contentType: 'text/plain', ), - key: key, + path: const StoragePath.fromString('public/example.txt'), ).result; - - safePrint('Uploaded data: ${result.uploadedItem.key}'); + safePrint('Uploaded data: ${result.uploadedItem.path}'); } on StorageException catch (e) { - safePrint('Error uploading data: ${e.message}'); - rethrow; + safePrint(e.message); } } ``` @@ -230,53 +221,90 @@ Future uploadBytes({ +## Upload Progress + +To track progress of the upload, use the progress listener callback. + + + + + +```dart +final operation = Amplify.Storage.uploadFile( + localFile: AWSFile.fromPath('/path/to/local/file.txt'), + path: const StoragePath.fromString('public/example.txt'), + onProgress: (progress) { + safePrint('fraction totalBytes: ${progress.totalBytes}'); + safePrint('fraction transferredBytes: ${progress.transferredBytes}'); + safePrint('fraction completed: ${progress.fractionCompleted}'); + } +); +``` + + + + + +```dart +final operation = Amplify.Storage.uploadData( + data: StorageDataPayload.string('hello world'), + path: const StoragePath.fromString('public/example.txt'), + onProgress: (progress) { + safePrint('fraction totalBytes: ${progress.totalBytes}'); + safePrint('fraction transferredBytes: ${progress.transferredBytes}'); + safePrint('fraction completed: ${progress.fractionCompleted}'); + }, +); +``` + + + + + ## Upload Options You can attach metadata while uploading data or a file by specifying the `metadata` property in options. If you want the `metadata` to be included in the upload result, you can set the `getProperties` flag to `true` in options. + + + + ```dart -Future uploadWithOptions() async { - // When uploading data, use `StorageUploadDataOptions` - final uploadDataOperation = Amplify.Storage.uploadData( - data: S3DataPayload.string( - 'example', - contentType: 'text/plain', - ), - key: 'example.txt', - options: const StorageUploadDataOptions( - metadata: { - 'project': 'ExampleProject', - }, - pluginOptions: S3UploadDataPluginOptions( - getProperties: true, - ), +final result = await Amplify.Storage.uploadFile( + localFile: AWSFile.fromPath('/path/to/local/file.txt'), + path: const StoragePath.fromString('public/example.txt'), + options: const StorageUploadFileOptions( + metadata: {'key': 'value'}, + pluginOptions: S3UploadFilePluginOptions( + getProperties: true, ), - ); - final uploadDataResult = await uploadDataOperation.result; - safePrint( - 'Uploaded data with metadata: ${uploadDataResult.uploadedItem.metadata}', - ); + ), +).result +safePrint('metadata: ${result.uploadedItem.metadata}'); +``` - // When uploading a file, use `StorageUploadFileOptions` - final uploadFileOperation = Amplify.Storage.uploadFile( - localFile: AWSFile.fromPath('path/to/example.txt'), - key: 'example.txt', - options: const StorageUploadFileOptions( - metadata: { - 'project': 'ExampleProject', - }, - pluginOptions: S3UploadFilePluginOptions( - getProperties: true, - ), + + + + +```dart +final result = await Amplify.Storage.uploadData( + data: StorageDataPayload.string('example'), + path: const StoragePath.fromString('public/example.txt'), + options: const StorageUploadDataOptions( + metadata: {'key': 'value'}, + pluginOptions: S3UploadDataPluginOptions( + getProperties: true, ), - ); - final uploadFileResult = await uploadFileOperation.result; - safePrint( - 'Uploaded file with metadata: ${uploadFileResult.uploadedItem.metadata}', - ); -} + ), +).result; +safePrint('metadata: ${result.uploadedItem.metadata}'); ``` + + + + The [`Amplify.Storage.getProperties` API](/gen1/[platform]/build-a-backend/storage/get-properties/) allows you to retrieve metadata without downloading the file. @@ -291,33 +319,50 @@ A call to `Amplify.Storage.uploadFile` or `Amplify.Storage.uploadData` returns a To cancel the upload (for example, in response to the user pressing a Cancel button), simply call `.cancel()` on the returned upload operation. +`Amplify.Storage.uploadFile` also allows for the operation to be paused and resumed. + + + + + ```dart -import 'package:amplify_flutter/amplify_flutter.dart'; -import 'package:amplify_storage_s3/amplify_storage_s3.dart'; +Future upload() async { + final operation = Amplify.Storage.uploadFile( + localFile: AWSFile.fromPath('/path/to/local/file.txt'), + path: const StoragePath.fromString('public/example.txt'), + ); -S3UploadFileOperation? uploadOperation; + // pause operation + await operation.pause(); -Future uploadFile(String path) async { - try { - final storagePlugin = Amplify.Storage.getPlugin(AmplifyStorageS3.pluginKey); - uploadOperation = storagePlugin.uploadFile( - localFile: AWSFile.fromPath(path), - key: 'example_file.txt', - ); - - final result = await uploadOperation!.result; - safePrint('Uploaded ${result.uploadedItem.key}'); - } on StorageException catch (e) { - safePrint('Error uploading file: ${e.message}'); - } + // resume operation + await operation.resume(); + + // cancel operation + await operation.cancel(); } +``` + + -void cancelUpload() { - uploadOperation?.cancel(); - uploadOperation = null; + + +```dart +Future upload() async { + final operation = Amplify.Storage.uploadData( + data: StorageDataPayload.string('example'), + path: const StoragePath.fromString('public/example.txt'), + ); + + // cancel operation + await operation.cancel(); } ``` + + + + ## Multipart Upload Amplify will automatically perform a S3 multipart upload for files larger than 5MB. For more information about S3's multipart upload support, see [Uploading and copying objects using multipart upload](https://docs.aws.amazon.com/AmazonS3/latest/userguide/mpuoverview.html). diff --git a/src/fragments/lib/storage/native_common/configureaccess/common.mdx b/src/fragments/lib/storage/native_common/configureaccess/common.mdx index ae97d196503..668359ed34f 100644 --- a/src/fragments/lib/storage/native_common/configureaccess/common.mdx +++ b/src/fragments/lib/storage/native_common/configureaccess/common.mdx @@ -36,10 +36,6 @@ import android1 from '/src/fragments/lib/storage/android/configureaccess/10_prot -import flutter2 from '/src/fragments/lib/storage/flutter/configureaccess/10_protected_upload.mdx'; - - - This will upload with the prefix `/protected/[IDENTITY_ID]/` followed by the `key`. For other users to read the file, you must specify the access level as `protected` and the identity ID of the user who uploaded it in the options. @@ -52,10 +48,6 @@ import android4 from '/src/fragments/lib/storage/android/configureaccess/20_prot -import flutter5 from '/src/fragments/lib/storage/flutter/configureaccess/20_protected_download.mdx'; - - - ## Private Access Create an options object specifying the private access level to only allow an object to be accessed by the uploading user @@ -68,10 +60,6 @@ import android7 from '/src/fragments/lib/storage/android/configureaccess/30_priv -import flutter8 from '/src/fragments/lib/storage/flutter/configureaccess/30_private_upload.mdx'; - - - This will upload with the prefix `/private/[IDENTITY_ID]/`, followed by the `key`. For the uploading user to read the file, specify the same access level (`private`) and key you used to upload: @@ -84,10 +72,6 @@ import android10 from '/src/fragments/lib/storage/android/configureaccess/40_pri -import flutter11 from '/src/fragments/lib/storage/flutter/configureaccess/40_private_download.mdx'; - - - import ios10 from '/src/fragments/lib/storage/ios/configureaccess/50_customization.mdx'; @@ -95,7 +79,3 @@ import ios10 from '/src/fragments/lib/storage/ios/configureaccess/50_customizati import android11 from '/src/fragments/lib/storage/android/configureaccess/50_customization.mdx'; - -import flutter12 from '/src/fragments/lib/storage/flutter/configureaccess/50_customization.mdx'; - - diff --git a/src/fragments/start/getting-started/flutter/prereq.mdx b/src/fragments/start/getting-started/flutter/prereq.mdx index 7af15482fc0..21cb28b227b 100644 --- a/src/fragments/start/getting-started/flutter/prereq.mdx +++ b/src/fragments/start/getting-started/flutter/prereq.mdx @@ -1 +1 @@ -- [Flutter](https://flutter.dev/docs/get-started/install) version 3.3 or higher +- [Flutter](https://flutter.dev/docs/get-started/install) diff --git a/src/fragments/start/getting-started/flutter/setup.mdx b/src/fragments/start/getting-started/flutter/setup.mdx index d596ba937db..0d2b24036ae 100644 --- a/src/fragments/start/getting-started/flutter/setup.mdx +++ b/src/fragments/start/getting-started/flutter/setup.mdx @@ -15,14 +15,11 @@ Amplify Flutter is distributed via [pub.dev](https://pub.dev/packages/amplify_fl 1. From your project root directory, find and modify your `pubspec.yaml` and add the Amplify plugins to the project dependencies. ```yaml - environment: - sdk: ">=2.18.0 <3.0.0" - dependencies: - amplify_api: ^1.0.0 - amplify_auth_cognito: ^1.0.0 - amplify_authenticator: ^1.0.0 - amplify_flutter: ^1.0.0 + amplify_api: ^2.0.0 + amplify_auth_cognito: ^2.0.0 + amplify_authenticator: ^2.0.0 + amplify_flutter: ^2.0.0 flutter: sdk: flutter go_router: ^6.5.5 diff --git a/src/pages/[platform]/build-a-backend/add-aws-services/analytics/set-up-analytics/index.mdx b/src/pages/[platform]/build-a-backend/add-aws-services/analytics/set-up-analytics/index.mdx index 50a807205d6..6c4d61a2d6f 100644 --- a/src/pages/[platform]/build-a-backend/add-aws-services/analytics/set-up-analytics/index.mdx +++ b/src/pages/[platform]/build-a-backend/add-aws-services/analytics/set-up-analytics/index.mdx @@ -60,17 +60,7 @@ For more information on how to use the `visionos-preview` branch, see [Platform -- A Flutter application targeting Flutter SDK >=3.3.0 with Amplify libraries integrated - - The following are also required, depending on which platforms you are targeting: - - - An iOS configuration targeting at least iOS 13.0 and XCode version >=13.2 - - An Android configuration targeting at least Android API level 24 (Android 7.0) or above - - Any browser supported by Flutter for Web (you can check the list of supported browsers [here](https://docs.flutter.dev/development/platform-integration/web/faq#which-web-browsers-are-supported-by-flutter)) - - Any Windows OS meeting Flutter minimums - - macOS version 10.15 or higher - - Any Ubuntu Linux distribution meeting Flutter minimums - +Amplify Flutter requires a minimum target platform for iOS (13.0), Android (API level 24), and macOS (10.15). Refer to [Flutter's supported deployment platforms](https://docs.flutter.dev/reference/supported-platforms) when targeting Web, Windows, or Linux. ## Set up Analytics backend @@ -181,13 +171,10 @@ In your Flutter project directory, open **pubspec.yaml**. Add Analytics by adding these libraries into your dependencies block: ```yaml -environment: - sdk: '>=2.18.0 <4.0.0' - dependencies: - amplify_analytics_pinpoint: ^1.0.0 - amplify_auth_cognito: ^1.0.0 - amplify_flutter: ^1.0.0 + amplify_analytics_pinpoint: ^2.0.0 + amplify_auth_cognito: ^2.0.0 + amplify_flutter: ^2.0.0 ``` @@ -325,7 +312,7 @@ Future _configureAmplify() async { -When running your app on MacOS you will need to enable keychain sharing in Xcode, as described in the [Project setup guide](/gen1/[platform]/start/project-setup/platform-setup/#enable-keychain). +When running your app on macOS you will need to enable keychain sharing in Xcode, as described in the [Project setup guide](/gen1/[platform]/start/project-setup/platform-setup/#enable-keychain). diff --git a/src/pages/[platform]/build-a-backend/auth/customize-auth-lifecycle/custom-auth-flows/index.mdx b/src/pages/[platform]/build-a-backend/auth/customize-auth-lifecycle/custom-auth-flows/index.mdx index 525fd1cf10a..c4c093a03e8 100644 --- a/src/pages/[platform]/build-a-backend/auth/customize-auth-lifecycle/custom-auth-flows/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/customize-auth-lifecycle/custom-auth-flows/index.mdx @@ -614,9 +614,8 @@ RxAmplify.Auth.signIn("username", "password", options) The Auth category can be configured to perform a [custom authentication flow](https://docs.aws.amazon.com/cognito/latest/developerguide/user-pool-lambda-challenge.html) defined by you. The following guide shows how to setup a simple passwordless authentication flow. ## 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](/[platform]/build-a-backend/auth/connect-your-frontend/sign-in/#platform-setup) for more details on platform specific setup. +Amplify requires a minimum target platform for iOS (13.0), Android (API level 24), and macOS (10.15). Refer to [Flutter's supported deployment platforms](https://docs.flutter.dev/reference/supported-platforms) when targeting Web, Windows, or Linux. Additional setup is required for some target platforms. Please see the [platform setup](/[platform]/build-a-backend/auth/connect-your-frontend/sign-in/#platform-setup) for more details on platform specific setup. ## Configure Auth diff --git a/src/pages/[platform]/build-a-backend/auth/set-up-auth/index.mdx b/src/pages/[platform]/build-a-backend/auth/set-up-auth/index.mdx index 07616f7bdf5..d2881bdab24 100644 --- a/src/pages/[platform]/build-a-backend/auth/set-up-auth/index.mdx +++ b/src/pages/[platform]/build-a-backend/auth/set-up-auth/index.mdx @@ -368,14 +368,10 @@ flutter pub add amplify_authenticator or you can update your `pubspec.yaml` file with the following ```yaml -environment: - sdk: ">=2.18.0 <4.0.0" - flutter: ">=3.3.0" - dependencies: - amplify_flutter: ^1.0.0 - amplify_auth_cognito: ^1.0.0 - amplify_authenticator: ^1.0.0 + amplify_flutter: ^2.0.0 + amplify_auth_cognito: ^2.0.0 + amplify_authenticator: ^2.0.0 ``` and run the following command to download the libraries. diff --git a/src/pages/[platform]/build-a-backend/data/set-up-data/index.mdx b/src/pages/[platform]/build-a-backend/data/set-up-data/index.mdx index 10c79f13bbc..55c396b77fb 100644 --- a/src/pages/[platform]/build-a-backend/data/set-up-data/index.mdx +++ b/src/pages/[platform]/build-a-backend/data/set-up-data/index.mdx @@ -301,13 +301,10 @@ Drag and drop the **AmplifyModels** folder into your Xcode project to add the ge From your project root directory, find and modify your **pubspec.yaml** and add the Amplify plugins to the project dependencies. ```yaml title="pubspec.yaml" -environment: - sdk: ">=2.18.0 <3.0.0" - dependencies: // highlight-start - amplify_api: ^1.0.0 - amplify_flutter: ^1.0.0 + amplify_api: ^2.0.0 + amplify_flutter: ^2.0.0 // highlight-end flutter: sdk: flutter diff --git a/src/pages/[platform]/build-a-backend/storage/set-up-storage/index.mdx b/src/pages/[platform]/build-a-backend/storage/set-up-storage/index.mdx index a12f6a6af35..26991065c4b 100644 --- a/src/pages/[platform]/build-a-backend/storage/set-up-storage/index.mdx +++ b/src/pages/[platform]/build-a-backend/storage/set-up-storage/index.mdx @@ -425,34 +425,20 @@ Note that because the storage category requires auth, you will need to either co ### Prerequisites -* A Flutter application targeting Flutter SDK >= 3.3.0 with Amplify libraries integrated - - The following are also required, depending on which platforms you are targeting: - - * An iOS configuration targeting at least iOS 13.0 and XCode version >=13.2 - * An Android configuration targeting at least Android API level 24 (Android 7.0) or above - * Any browser supported by Flutter for Web (you can check the list of supported browsers [here](https://docs.flutter.dev/development/platform-integration/web/faq#which-web-browsers-are-supported-by-flutter)) - * Any Windows OS meeting Flutter minimums - * macOS version 10.15 or higher - * Any Ubuntu Linux distribution meeting Flutter minimums - * For a full example please follow the [project setup walkthrough](/[platform]/start/quickstart/) +Amplify Flutter requires a minimum target platform for iOS (13.0), Android (API level 24), and macOS (10.15). Refer to [Flutter's supported deployment platforms](https://docs.flutter.dev/reference/supported-platforms) when targeting Web, Windows, or Linux. ### Install Amplify library Add the following dependency to your **app**'s `pubspec.yaml` along with others you added above in **Prerequisites**: ```yaml -environment: - sdk: ">=2.18.0 <4.0.0" - flutter: ">=3.3.0" - dependencies: flutter: sdk: flutter - amplify_auth_cognito: ^1.0.0 - amplify_flutter: ^1.0.0 - amplify_storage_s3: ^1.0.0 + amplify_auth_cognito: ^2.0.0 + amplify_flutter: ^2.0.0 + amplify_storage_s3: ^2.0.0 ``` ### Configure Amplify in project diff --git a/src/pages/[platform]/start/quickstart/index.mdx b/src/pages/[platform]/start/quickstart/index.mdx index 16923bcdc4c..9b4e1ecc60f 100644 --- a/src/pages/[platform]/start/quickstart/index.mdx +++ b/src/pages/[platform]/start/quickstart/index.mdx @@ -1255,8 +1255,7 @@ Before you get started, make sure you have the following installed: - [git](https://git-scm.com/) v2.14.1 or later - You will also need to [create an AWS Account](https://portal.aws.amazon.com/billing/signup). Note that AWS Amplify is part of the [AWS Free Tier](https://aws.amazon.com/amplify/pricing/). - Configure your AWS account to use with Amplify [instructions](/[platform]/start/account-setup/). -- A Flutter version higher than 3.3.0 - +- A stable version of [Flutter](https://docs.flutter.dev/get-started/install). You can follow the [official documentation](https://flutter.dev/docs/get-started/install) to install Flutter on your machine and check the [editor documentation](https://docs.flutter.dev/get-started/editor) for setting up your editor. @@ -1311,9 +1310,9 @@ To use the Authenticator, you need to add the following dependencies to your pro ```yaml title="pubspec.yaml" dependencies: - amplify_flutter: ^1.0.0 - amplify_auth_cognito: ^1.0.0 - amplify_authenticator: ^1.0.0 + amplify_flutter: ^2.0.0 + amplify_auth_cognito: ^2.0.0 + amplify_authenticator: ^2.0.0 ``` You will add: @@ -1432,7 +1431,7 @@ Once you are done, add the API dependencies to your project. You will add `ampli ```yaml title="pubspec.yaml" dependencies: - amplify_api: ^1.0.0 + amplify_api: ^2.0.0 ``` diff --git a/src/pages/gen1/[platform]/build-a-backend/existing-resources/cdk/index.mdx b/src/pages/gen1/[platform]/build-a-backend/existing-resources/cdk/index.mdx index db21da1c9be..afe0d3ee387 100644 --- a/src/pages/gen1/[platform]/build-a-backend/existing-resources/cdk/index.mdx +++ b/src/pages/gen1/[platform]/build-a-backend/existing-resources/cdk/index.mdx @@ -352,15 +352,12 @@ description: "A new Flutter project." publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 -environment: - sdk: '>=3.2.0 <4.0.0' - // highlight-start dependencies: flutter: sdk: flutter - amplify_flutter: ^1.0.0 - amplify_api: ^1.0.0 + amplify_flutter: ^2.0.0 + amplify_api: ^2.0.0 go_router: ^6.5.5 cupertino_icons: ^1.0.2 // highlight-end diff --git a/src/pages/gen1/[platform]/build-a-backend/existing-resources/cli/index.mdx b/src/pages/gen1/[platform]/build-a-backend/existing-resources/cli/index.mdx index a446e490041..4fef338fdde 100644 --- a/src/pages/gen1/[platform]/build-a-backend/existing-resources/cli/index.mdx +++ b/src/pages/gen1/[platform]/build-a-backend/existing-resources/cli/index.mdx @@ -152,14 +152,12 @@ description: A new Flutter project. publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 -environment: - sdk: '>=3.1.0 <4.0.0' dependencies: // highlight-start - amplify_api: ^1.0.0 - amplify_auth_cognito: ^1.0.0 - amplify_authenticator: ^1.0.0 - amplify_flutter: ^1.0.0 + amplify_api: ^2.0.0 + amplify_auth_cognito: ^2.0.0 + amplify_authenticator: ^2.0.0 + amplify_flutter: ^2.0.0 // highlight-end flutter: sdk: flutter diff --git a/src/pages/gen1/[platform]/build-a-backend/storage/configure-access/index.mdx b/src/pages/gen1/[platform]/build-a-backend/storage/configure-access/index.mdx index 92d95900ab0..83003dfbdd4 100644 --- a/src/pages/gen1/[platform]/build-a-backend/storage/configure-access/index.mdx +++ b/src/pages/gen1/[platform]/build-a-backend/storage/configure-access/index.mdx @@ -5,7 +5,6 @@ export const meta = { description: 'Learn about configuring different access levels in Amplify Storage. Objects can be public, protected, or private.', platforms: [ 'javascript', - 'flutter', 'swift', 'android', 'react-native', @@ -34,7 +33,6 @@ import common_configureaccess from '/src/fragments/lib/storage/native_common/con diff --git a/src/pages/gen1/[platform]/build-a-backend/storage/import/index.mdx b/src/pages/gen1/[platform]/build-a-backend/storage/import/index.mdx index db7be1818c0..2c0bd3cd33e 100644 --- a/src/pages/gen1/[platform]/build-a-backend/storage/import/index.mdx +++ b/src/pages/gen1/[platform]/build-a-backend/storage/import/index.mdx @@ -67,7 +67,28 @@ By default, Amplify Libraries assumes that S3 buckets are configured with the fo - `protected/{user_identity_id}/` - Readable by all users, but writable only by the creating user - `private/{user_identity_id}/` - Only accessible for the individual user -You can either configure your IAM role to use the Amplify-recommended policies or in your Amplify libraries configuration [overwrite the default storage path behavior](/gen1/[platform]/build-a-backend/storage/configure-access/#customize-object-key-path). + + +You can either configure your IAM role to use the Amplify-recommended policies or use [`path`](/gen1/[platform]/build-a-backend/storage/path/) to specify your own path. + + + + + +You can either configure your IAM role to use the Amplify-recommended policies or use [`StoragePath`](/gen1/[platform]/build-a-backend/storage/storagepath/) to specify your own path. + + It is highly recommended to review your S3 bucket's CORS settings. Review the [recommendation guide here](/gen1/[platform]/build-a-backend/storage/set-up-storage/#amazon-s3-bucket-cors-policy-setup). diff --git a/src/pages/gen1/[platform]/build-a-backend/storage/storagepath/index.mdx b/src/pages/gen1/[platform]/build-a-backend/storage/storagepath/index.mdx index 4b0927d0347..fb46c6884c2 100644 --- a/src/pages/gen1/[platform]/build-a-backend/storage/storagepath/index.mdx +++ b/src/pages/gen1/[platform]/build-a-backend/storage/storagepath/index.mdx @@ -5,7 +5,8 @@ export const meta = { description: "Learn more about constructing a StoragePath to use on Amplify Storage APIs", platforms: [ 'swift', - 'android' + 'android', + 'flutter' ] }; @@ -22,7 +23,7 @@ export function getStaticProps(context) { }; } - + You can use `StoragePath` to access, upload to, or download from to any path in your S3 bucket. The Amplify Gen 1 CLI automatically creates the following directories: - `public/`: Accessible by all users of your application - `protected//`: Readable by all users (you need to specify the identityID of the user who uploaded the file). Writable only by the creating user @@ -66,7 +67,14 @@ StoragePath.fromString("public/exampleFile.txt") - + +```dart +// Resolves to "public/exampleFile.txt" +const StoragePath.fromString('public/exampleFile.txt'); +``` + + + ## Create a StoragePath with user’s IdentityId You may want to construct a StoragePath that contains the Amplify Auth user’s IdentityId. We’ve created a helper function that injects the user’s IdentityId when a Storage API is called, since fetching an IdentityId from the Auth plugin is not synchronous. @@ -105,3 +113,13 @@ StoragePath.fromIdentityID { identityId in + + +```dart +// If the user's identityId was "123", +// the StoragePath would resolve to "private/123/exampleFile.txt" +StoragePath.fromIdentityId( + (String identityId) => 'private/$identityId/exampleFile.txt', +}; +``` + diff --git a/src/pages/gen1/[platform]/build-a-backend/storage/transfer-acceleration/index.mdx b/src/pages/gen1/[platform]/build-a-backend/storage/transfer-acceleration/index.mdx index 07fee3ffa11..4bc7ff48205 100644 --- a/src/pages/gen1/[platform]/build-a-backend/storage/transfer-acceleration/index.mdx +++ b/src/pages/gen1/[platform]/build-a-backend/storage/transfer-acceleration/index.mdx @@ -254,12 +254,12 @@ Set `useAccelerateEndpoint` to `true` in the corresponding Storage S3 plugin opt ```dart import 'package:amplify_storage_s3/amplify_storage_s3.dart'; -Future uploadFileUsingAcceleration(String filePath, String key) async { +Future uploadFileUsingAcceleration(String filePath, String path) async { final localFile = AWSFile.fromPath(filePath); try { final uploadFileOperation = Amplify.Storage.uploadFile( localFile: localFile, - key: key, + path: const StoragePath.fromString(path), options: const StorageUploadFileOptions( pluginOptions: S3UploadFilePluginOptions( useAccelerateEndpoint: true, @@ -268,7 +268,7 @@ Future uploadFileUsingAcceleration(String filePath, String key) async { ); final result = await uploadFileOperation.result; - safePrint('Uploaded file: ${result.uploadedItem.key}'); + safePrint('Uploaded file: ${result.uploadedItem.path}'); } on StorageException catch (error) { safePrint('Something went wrong uploading file: ${error.message}'); } diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/auth/advanced-workflows/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/auth/advanced-workflows/index.mdx index 350a21f999d..0f5a7137a50 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/auth/advanced-workflows/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/auth/advanced-workflows/index.mdx @@ -6,6 +6,7 @@ export const meta = { platforms: [ 'javascript', 'react-native', + 'flutter', 'angular', 'nextjs', 'react', @@ -26,6 +27,10 @@ export function getStaticProps(context) { }; } +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + ## Subscribing Events @@ -582,3 +587,7 @@ Note: To work with Service Interface Objects, your Amazon Cognito users' [IAM ro + +import flutter from '/src/fragments/lib-v1/auth/flutter/advanced/advanced.mdx'; + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/auth/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/auth/index.mdx index 4382a6d8ead..69d0fd67864 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/auth/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/auth/index.mdx @@ -33,4 +33,8 @@ export function getStaticProps(context) { }; } +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/auth/manage-mfa/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/auth/manage-mfa/index.mdx index 0a856800ddf..aa180892631 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/auth/manage-mfa/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/auth/manage-mfa/index.mdx @@ -27,6 +27,10 @@ export function getStaticProps(context) { }; } +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + The Auth category supports Multi-factor Authentication (MFA) for user sign-in flows. MFA is an extra layer of security used to make sure that users trying to gain access to an account are who they say they are. It requires users to provide additional information to verify their identity. Amplify Auth supports the MFA methods with Time-based-One-Time Passwords (TOTP) as well as text messages (SMS). In this guide we will review how you can set up MFA using TOTP and SMS and the tradeoffs between these methods to help you choose the right set up for your application. We’ll also review how to set up MFA to remember a device and reduce sign-in friction for your users. @@ -824,3 +828,7 @@ Now that you completed setting up multi-factor authentication you may also want + +import flows from '/src/fragments/lib-v1/auth/common/mfa/flows.mdx'; + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/graphqlapi/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/graphqlapi/index.mdx index 973dc7474cf..ccdbe62961f 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/graphqlapi/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/graphqlapi/index.mdx @@ -33,4 +33,8 @@ export function getStaticProps(context) { }; } +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/more-features/analytics/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/more-features/analytics/index.mdx index 2790f29420c..e3fbfded423 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/more-features/analytics/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/more-features/analytics/index.mdx @@ -33,4 +33,8 @@ export function getStaticProps(context) { }; } +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/more-features/datastore/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/more-features/datastore/index.mdx index 70a9612afbc..3230864bb41 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/more-features/datastore/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/more-features/datastore/index.mdx @@ -33,4 +33,8 @@ export function getStaticProps(context) { }; } +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/more-features/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/more-features/index.mdx index 5f463294830..4c015a086d0 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/more-features/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/more-features/index.mdx @@ -33,4 +33,8 @@ export function getStaticProps(context) { }; } +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/app-badge-count/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/app-badge-count/index.mdx index 7688917e69f..d9392cae89f 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/app-badge-count/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/app-badge-count/index.mdx @@ -3,7 +3,10 @@ import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; export const meta = { title: 'Add app badge count', description: 'Get and set the application badge count.', - platforms: ['react-native'] + platforms: [ + 'react-native', + 'flutter' + ] }; export const getStaticPaths = async () => { @@ -19,6 +22,10 @@ export function getStaticProps(context) { }; } -import appBadgeCount from '/src/fragments/lib-v1/push-notifications/react-native/app_badge_count/app-badge-count.mdx'; +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; - + + +import appBadgeCount from '/src/fragments/lib-v1/push-notifications/common/app-badge-count.mdx'; + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/enable-rich-notifications/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/enable-rich-notifications/index.mdx index 4eef23f2102..4504792ef18 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/enable-rich-notifications/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/enable-rich-notifications/index.mdx @@ -3,7 +3,10 @@ import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; export const meta = { title: 'Enable rich notifications', description: 'Enable rich notifications for you app.', - platforms: ['react-native'] + platforms: [ + 'react-native', + 'flutter' + ] }; export const getStaticPaths = async () => { @@ -19,6 +22,10 @@ export function getStaticProps(context) { }; } -import enableRichNotifications from '/src/fragments/lib-v1/push-notifications/react-native/enable_rich_notifications/enable-rich-notifications.mdx'; +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; - + + +import enableRichNotifications from '/src/fragments/lib-v1/push-notifications/common/enable-rich-notifications.mdx'; + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/identify-user/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/identify-user/index.mdx index 5bf874a1ab2..a9214feb6ac 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/identify-user/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/identify-user/index.mdx @@ -3,7 +3,10 @@ import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; export const meta = { title: 'Identify user to Amazon Pinpoint', description: 'Provide information about a user to Amazon Pinpoint.', - platforms: ['react-native'] + platforms: [ + 'react-native', + 'flutter' + ] }; export const getStaticPaths = async () => { @@ -19,6 +22,11 @@ export function getStaticProps(context) { }; } -import identifyUser from '/src/fragments/lib-v1/push-notifications/react-native/identify_user/identify-user.mdx'; +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + +import identifyUser from '/src/fragments/lib-v1/push-notifications/common/identify-user.mdx'; + + - diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/index.mdx index 8516e450773..d3f9efd4e0a 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/index.mdx @@ -5,10 +5,17 @@ export const meta = { title: 'Push Notifications', description: 'Drive customer engagement using push notifications with campaign analytics and targeting', - platforms: ['react-native'], + platforms: [ + 'react-native', + 'flutter' + ], route: '/gen1/[platform]/prev/build-a-backend/push-notifications' }; +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + export const getStaticPaths = async () => { return getCustomStaticPath(meta.platforms); }; diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/interact-with-notifications/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/interact-with-notifications/index.mdx index 4c17dbf7651..a7141b86513 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/interact-with-notifications/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/interact-with-notifications/index.mdx @@ -3,7 +3,10 @@ import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; export const meta = { title: 'Interact with notifications', description: 'Interact with push notifications through various events.', - platforms: ['react-native'] + platforms: [ + 'react-native', + 'flutter' + ] }; export const getStaticPaths = async () => { @@ -19,6 +22,11 @@ export function getStaticProps(context) { }; } -import interactWithNotifications from '/src/fragments/lib-v1/push-notifications/react-native/interact_with_notifications/interact-with-notifications.mdx'; +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + +import interactWithNotifications from '/src/fragments/lib-v1/push-notifications/common/interact_with_notifications/interact-with-notifications.mdx'; + + - diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/receive-device-token/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/receive-device-token/index.mdx index dfe6bc08733..3febdb8e73c 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/receive-device-token/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/receive-device-token/index.mdx @@ -3,7 +3,10 @@ import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; export const meta = { title: 'Receive a device token', description: 'Receive a device token for use with push notifications.', - platforms: ['react-native'] + platforms: [ + 'react-native', + 'flutter', + ] }; export const getStaticPaths = async () => { @@ -19,6 +22,11 @@ export function getStaticProps(context) { }; } -import receiveDeviceToken from '/src/fragments/lib-v1/push-notifications/react-native/receive-device-token.mdx'; +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + +import receiveDeviceToken from '/src/fragments/lib-v1/push-notifications/common/receive-device-token.mdx'; + + - diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/request-permissions/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/request-permissions/index.mdx index de3df5dd6ce..96f828012c3 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/request-permissions/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/request-permissions/index.mdx @@ -3,7 +3,10 @@ import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; export const meta = { title: 'Request permissions', description: 'Request permissions to display push notifications to users.', - platforms: ['react-native'] + platforms: [ + 'react-native', + 'flutter' + ] }; export const getStaticPaths = async () => { @@ -19,6 +22,10 @@ export function getStaticProps(context) { }; } -import requestPermissions from '/src/fragments/lib-v1/push-notifications/react-native/request-permissions.mdx'; +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; - + + +import requestPermissions from '/src/fragments/lib-v1/push-notifications/common/request-permissions.mdx'; + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/set-up-push-notifications/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/set-up-push-notifications/index.mdx index 03773d5a7a5..38405e3352a 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/set-up-push-notifications/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/set-up-push-notifications/index.mdx @@ -3,7 +3,10 @@ import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; export const meta = { title: 'Set up Amplify Push Notifications', description: 'Use of Amplify Push Notifications', - platforms: ['react-native'] + platforms: [ + 'react-native', + 'flutter' + ] }; export const getStaticPaths = async () => { @@ -19,6 +22,11 @@ export function getStaticProps(context) { }; } -import gettingStarted from '/src/fragments/lib-v1/push-notifications/react-native/getting_started/getting-started.mdx'; +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + +import gettingStarted from '/src/fragments/lib-v1/push-notifications/common/getting_started/getting-started.mdx'; + + - diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/set-up-push-service/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/set-up-push-service/index.mdx index fae4d6b20ff..47ea4590383 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/set-up-push-service/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/set-up-push-service/index.mdx @@ -4,7 +4,10 @@ export const meta = { title: 'Set up push notification services', description: 'Learn how to setup the various push notification services for your mobile app.', - platforms: ['react-native'] + platforms: [ + 'react-native', + 'flutter' + ] }; export const getStaticPaths = async () => { @@ -20,6 +23,11 @@ export function getStaticProps(context) { }; } -import crossPlatformSetupService from '/src/fragments/lib/push-notifications/common/setup_push_service/cross-platform-setup.mdx'; +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + +import crossPlatformSetupService from '/src/fragments/lib-v1/push-notifications/common/setup_push_service/setup-push-service.mdx'; + + - diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/test-notifications/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/test-notifications/index.mdx index 7e54b02245f..94612631f17 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/test-notifications/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/push-notifications/test-notifications/index.mdx @@ -3,7 +3,10 @@ import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; export const meta = { title: 'Test push notifications', description: 'Overview of testing your push notifications', - platforms: ['react-native'] + platforms: [ + 'react-native', + 'flutter' + ] }; export const getStaticPaths = async () => { @@ -19,6 +22,11 @@ export function getStaticProps(context) { }; } -import testing from '/src/fragments/lib/push-notifications/common/testing.mdx'; +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + +import testing from '/src/fragments/lib-v1/push-notifications/common/testing.mdx'; + + - diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/restapi/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/restapi/index.mdx index 0a22b77ff18..f2343ca7d97 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/restapi/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/restapi/index.mdx @@ -33,4 +33,8 @@ export function getStaticProps(context) { }; } +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/storage/copy/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/storage/copy/index.mdx index 510ff3236cb..c71654cc6bb 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/storage/copy/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/storage/copy/index.mdx @@ -9,7 +9,8 @@ export const meta = { 'angular', 'nextjs', 'react', - 'vue' + 'vue', + 'flutter' ] }; @@ -26,6 +27,10 @@ export function getStaticProps(context) { }; } +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + import js0 from '/src/fragments/lib-v1/storage/js/copy.mdx'; + +import flutter0 from '/src/fragments/lib-v1/storage/flutter/copy.mdx'; + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/storage/get-properties/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/storage/get-properties/index.mdx index 6f6c183fd2b..b8030089e2b 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/storage/get-properties/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/storage/get-properties/index.mdx @@ -8,7 +8,8 @@ export const meta = { 'angular', 'nextjs', 'react', - 'vue' + 'vue', + 'flutter' ] }; @@ -25,6 +26,10 @@ export function getStaticProps(context) { }; } +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + import js0 from '/src/fragments/lib-v1/storage/js/get-properties.mdx'; + +import flutter0 from '/src/fragments/lib-v1/storage/flutter/get-properties.mdx'; + + diff --git a/src/pages/gen1/[platform]/build-a-backend/storage/move/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/storage/move/index.mdx similarity index 83% rename from src/pages/gen1/[platform]/build-a-backend/storage/move/index.mdx rename to src/pages/gen1/[platform]/prev/build-a-backend/storage/move/index.mdx index 3988e173a4c..a3c648bc358 100644 --- a/src/pages/gen1/[platform]/build-a-backend/storage/move/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/storage/move/index.mdx @@ -1,5 +1,5 @@ import { getCustomStaticPath } from '@/utils/getCustomStaticPath'; - + export const meta = { title: 'Move files', description: "Learn more about how to move files using Amplify's storage category.", @@ -18,9 +18,9 @@ export function getStaticProps(context) { } }; } - -import flutter0 from '/src/fragments/lib/storage/flutter/move.mdx'; + +import flutter0 from '/src/fragments/lib-v1/storage/flutter/move.mdx'; diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/storage/transfer-acceleration/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/storage/transfer-acceleration/index.mdx index 8cc21b0b48e..264f922e0fc 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/storage/transfer-acceleration/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/storage/transfer-acceleration/index.mdx @@ -9,7 +9,8 @@ export const meta = { 'angular', 'nextjs', 'react', - 'vue' + 'vue', + 'flutter' ] }; @@ -38,3 +39,85 @@ import js from '/src/fragments/lib-v1/storage/js/transfer-acceleration.mdx'; vue: js }} /> + +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + + + +When you use Transfer Acceleration, additional data transfer charges might apply. For more information about pricing, see [Amazon S3 pricing](https://aws.amazon.com/s3/pricing/). + + + + + +You can enable [Transfer Acceleration](https://docs.aws.amazon.com/AmazonS3/latest/userguide/transfer-acceleration.html) for fast and secure transfer of files over long distances between your end user device and the S3 bucket. You can override the storage resource for this configuration and then leverage the `useAccelerateEndpoint` parameter to use the accelerated S3 endpoint. + +## Override storage resource + +Start by overriding your storage resources to enable Transfer Acceleration on your S3 bucket. + +```sh +$ amplify override storage +✅ Successfully generated "override.ts" folder at /amplify/backend/storage/accelerated-bucket +✔ Do you want to edit override.ts file now? (Y/n) · yes +Edit the file in your editor: /amplify/backend/storage/accelerated-bucket/override.ts +``` + +In the generated `override.ts` file use the following CDK snippet to enable transfer acceleration. + +```javascript +// amplify/backend/storage/accelerated-bucket/override.ts +import { AmplifyS3ResourceTemplate } from '@aws-amplify/cli-extensibility-helper'; + +export function override(resources: AmplifyS3ResourceTemplate) { + resources.s3Bucket.accelerateConfiguration = { + accelerationStatus: 'Enabled' + } +} +``` + +Next, deploy this storage resource: + +```sh +amplify push +``` + +## Use Transfer Acceleration on Storage Operations + +You can use transfer acceleration when calling the following APIs: + +* `getUrl` +* `downloadData` +* `downloadFile` +* `uploadData` +* `uploadFile` + +Set `useAccelerateEndpoint` to `true` in the corresponding Storage S3 plugin options to apply an accelerated S3 endpoint to the operation. For example, upload a file using transfer acceleration: + +```dart +import 'package:amplify_storage_s3/amplify_storage_s3.dart'; + +Future uploadFileUsingAcceleration(String filePath, String path) async { + final localFile = AWSFile.fromPath(filePath); + try { + final uploadFileOperation = Amplify.Storage.uploadFile( + localFile: localFile, + path: const StoragePath.fromString(path), + options: const StorageUploadFileOptions( + pluginOptions: S3UploadFilePluginOptions( + useAccelerateEndpoint: true, + ), + ), + ); + + final result = await uploadFileOperation.result; + safePrint('Uploaded file: ${result.uploadedItem.path}'); + } on StorageException catch (error) { + safePrint('Something went wrong uploading file: ${error.message}'); + } +} +``` + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/troubleshooting/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/troubleshooting/index.mdx index 53e4cd06e10..dd89802b73e 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/troubleshooting/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/troubleshooting/index.mdx @@ -31,4 +31,8 @@ export function getStaticProps(context) { }; } +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + diff --git a/src/pages/gen1/[platform]/prev/build-a-backend/troubleshooting/upgrade-amplify-packages/index.mdx b/src/pages/gen1/[platform]/prev/build-a-backend/troubleshooting/upgrade-amplify-packages/index.mdx index d019e64c7ac..45f17f0ca55 100644 --- a/src/pages/gen1/[platform]/prev/build-a-backend/troubleshooting/upgrade-amplify-packages/index.mdx +++ b/src/pages/gen1/[platform]/prev/build-a-backend/troubleshooting/upgrade-amplify-packages/index.mdx @@ -27,6 +27,10 @@ export function getStaticProps(context) { }; } +import flutter_maintenance from '/src/fragments/lib-v1/flutter-maintenance.mdx'; + + + import flutter1 from '/src/fragments/lib-v1/troubleshooting/flutter/upgrading.mdx'; diff --git a/src/pages/gen1/[platform]/start/getting-started/installation/index.mdx b/src/pages/gen1/[platform]/start/getting-started/installation/index.mdx index 829eaf905fe..8f6d251bd85 100644 --- a/src/pages/gen1/[platform]/start/getting-started/installation/index.mdx +++ b/src/pages/gen1/[platform]/start/getting-started/installation/index.mdx @@ -47,7 +47,7 @@ Before you begin, make sure you have the following installed: -- [Flutter](https://flutter.dev/docs/get-started/install) version 3.3 or higher +- [Flutter](https://flutter.dev/docs/get-started/install)