Front-End Web & Mobile

New in AWS Amplify Flutter version 0.4.0

This blog post was written by Abdallah Shaban – Senior Product Manager at AWS Amplify and Ashish Nanda – Tech Lead at AWS Amplify.

We are announcing the release of version 0.4.0 of the Amplify Flutter library. Please use this Github repo to inform the Amplify Flutter team about features or issues, or visit the Amplify Discord server under the #flutter-help channel.

Highlights

Below is a high level breakdown of the features we are announcing with Amplify Flutter version 0.4.0.

Auth category enhancements

  • Created an API in the Auth category that allows iOS developers to delete a user. Amplify Flutter currently offers this API only on the iOS platform in accordance with iOS App store policy changes that were in effect in January, 2022. It is recommended that you display widgets to call this API only on the iOS platform. We are planning on introducing this capability on Android at a later time.
try {
  await Amplify.Auth.deleteUser();
  print('Delete user succeeded');
} on Exception catch (e) {
  print('Delete user failed with error: $e');
}

API category enhancements

  • Added support for model-based GraphQL helpers. Running amplify codegen models (requires amplify CLI version of 7.6.19 or greater) will generate dart classes based on your GraphQL schema. The models can then be used to quickly generate request objects for queries, mutations, and subscriptions. The responses will then be automatically parsed into instances of the model. Below are some simple code snippets with more in-depth examples with our GraphQL API docs.

A simple example of saving a Todo instance in an app where you have Todo in your schema:

Todo todo = Todo(name: 'walk the dog', description: 'Spend at least 10 min with him.');
final request = ModelMutations.create(todo);
final response = await Amplify.API.mutate(request: request).response;
Todo? savedTodo = response.data;

Or fetch a Todo by id:

final request = ModelQueries.get(
      Todo.classType, 'my-todo-id-123');
final response = await Amplify.API.query(request: request).response;
Todo? todo = response.data;
  • Changed the interface for GraphQL subscriptions to use Dart Streams, allowing for better integration with developer workflows. When creating subscriptions, now, a Stream object will be returned to you. This Stream will continue producing events until either the subscription encounters an error, or you cancel the subscription. In the case of await for, this cancellation occurs when breaking out of the loop.
Future<void> subscribe() async {
  final graphQLDocument = '''subscription MySubscription {
    onCreateBlog {
      id
      name
      createdAt
    }
  }''';
  
  final Stream<GraphQLResponse<String>> operation = Amplify.API.subscribe(
    GraphQLRequest<String>(document: graphQLDocument),
    onEstablished: () => print('Subscription established'),
  );

  try {
    // Retrieve 5 events from the subscription
    var i = 0;
    await for (var event in operation) {
      i++;
      print('Subscription event data received: ${event.data}');
      if (i == 5) {
        break;
      }
    }
  } on Exception catch (e) {
    print('Error in subscription stream: $e');
  }
}

Alternatively, you can call Stream.listen to create a StreamSubscription object which can be programmatically canceled.

Future<void> subscribe() async {
  final graphQLDocument = '''subscription MySubscription {
    onCreateBlog {
      id
      name
      createdAt
    }
  }''';
  
  final Stream<GraphQLResponse<String>> operation = Amplify.API.subscribe(
    GraphQLRequest<String>(document: graphQLDocument),
    onEstablished: () => print('Subscription established'),
  );
  
  final StreamSubscription<GraphQLResponse<String>> subscription =
      operation.listen(
    (event) {
      print('Subscription event data received: ${event.data}');
    },
    onError: (Object e) => print('Error in subscription stream: $e'),
  );

  // ...

  // Cancel the subscription and close the underlying stream.
  subscription.cancel();
}

Breaking change: the data field is now nullable for GraphQLResponses to better reflect the GraphQL spec, which calls for null data in some circumstances, especially when there are errors.

Datastore enhancements

  • Amplify Flutter Datastore now supports QueryPredicates for Observe, Save, and Delete. Passing a QueryPredicate will allow you to observe only a subset of data based on the predicate. Using predicates with Save/Delete runs validations on remotely stored data before initiating those operations.
  • Amplify Flutter DataStore now supports generating models for custom (non-model) types and all generated models now include the createdAt and updatedAt fields. To take advantage of these features, make sure you are running CLI version >=7.6.10 and run amplify codegen models from your project directory.
type User @model {
    id: ID!
    username: String!
    attributes: [Attribute]
}

type Attribute {
    key: String!
    value: String!
}
import 'package:amplify_flutter/amplify_flutter.dart';

import 'models/ModelProvider.dart';

Future<void> createUser() async {
    final user = User(
        username: 'johndoe',
        attributes: [
            Attribute(key: 'eye_color', value: 'brown'),
            Attribute(key: 'favorite_movie', value: 'Casino Royale'),
        ],
    );
    
    await Amplify.DataStore.save(user);
}
  • Provided a fix for DateTime comparison issue #1245
  • Optimization for hub memory utilization
  • The Amplify Flutter API category for GraphQL and Datastore now support configuring custom domain to an app that you’ve deployed in the Amplify Console.

Core improvements

The amplify-core and amplify-flutter packages got a number of minor improvements.

  • Added support for Flutter 2.10.0. Please follow this guide to upgrade your project, specifically for updating the Kotlin dependency to be equal to or higher than 1.5.31.
  • The amplify_flutter package now requires you to import package:amplify_flutter/amplify_flutter.dart. This is to better align with library naming conventions in Dart and to provide consistency throughout the Amplify Flutter framework.

Before

import 'package:amplify_flutter/amplify.dart';

Now

import 'package:amplify_flutter/amplify_flutter.dart';
  • The amplify_flutter package now exports an AmplifyConfig type which allows you to create, introspect, and modify fully-typed configurations at runtime.
import 'dart:convert';

import 'package:amplify_flutter/amplify_flutter.dart';

import 'amplifyconfiguration.dart';

final parsedConfig = AmplifyConfig.fromJson(json.decode(amplifyconfig));

// Get all API endpoints
final endpoints = config.api!.awsPlugin!.endpoints;
print(endpoints);

// {
//     "myapi": {
//         "endpointType": "GraphQL",
//         "endpoint": "<ENDPOINT>",
//         "region": "us-west-2",
//         "authorizationType": "API_KEY",
//         "apiKey": "<API_KEY>"
//     }
// }


// Add an endpoint configuration
final myApi = endpoints['myapi'];
endpoints['myapi_IAM'] = myApi.copyWith(
  authorizationType: APIAuthorizationType.iam,
);

// Configure Amplify
await Amplify.configure(json.encode(parsedConfig));

In conclusion

We would love to have your feedback on this release and understand how we can help accelerate your productivity. Reach out to us our GitHub repository to help us prioritize features and enhancements.

Get started with building Flutter apps with Amplify please visit the Amplify Flutter.