Flutter AWS Amplify

Build scalable Flutter apps with AWS cloud services

☁️ What is AWS Amplify?

AWS Amplify is a complete development platform for building scalable mobile and web apps. It provides authentication, APIs, storage, analytics, and more, all powered by AWS cloud infrastructure for enterprise-grade Flutter applications.


// Initialize Amplify
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';

await Amplify.addPlugins([
  AmplifyAuthCognito(),
]);
await Amplify.configure(amplifyconfig);
                                    

Key Amplify Features

🔒

Authentication

Secure user authentication with Amazon Cognito. Supports email/password, social logins, multi-factor authentication, and user management with enterprise-level security and compliance features.

await Amplify.Auth.signUp(
  username: email,
  password: password,
);
🔌

API (GraphQL/REST)

Create scalable APIs with AWS AppSync and API Gateway. Build GraphQL or REST APIs that automatically scale, with real-time capabilities and offline data synchronization built-in.

final request = GraphQLRequest(
  document: listTodos,
);
final response = await Amplify.API.query(request: request);
💾

Storage

Store files in Amazon S3 with built-in CDN. Upload images, videos, and documents with automatic compression, resizing, and global content delivery for fast access worldwide.

await Amplify.Storage.uploadFile(
  localFile: file,
  key: 'uploads/photo.jpg',
);
📊

Analytics

Track user behavior with Amazon Pinpoint. Monitor app usage, user engagement, and custom events to understand how users interact with your app and improve user experience.

await Amplify.Analytics.recordEvent(
  event: AnalyticsEvent('ButtonClicked'),
);

🔹 Setting Up Amplify

Install Amplify CLI and configure your Flutter project:

# Install Amplify CLI globally
npm install -g @aws-amplify/cli

# Configure Amplify with your AWS account
amplify configure

# Initialize Amplify in your Flutter project
amplify init

# Add authentication
amplify add auth

# Add API
amplify add api

# Push changes to AWS
amplify push
# pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  amplify_flutter: ^1.5.0
  amplify_auth_cognito: ^1.5.0
  amplify_api: ^1.5.0
  amplify_storage_s3: ^1.5.0

Setup steps:

1. Install Amplify CLI

2. Configure AWS credentials

3. Initialize Amplify in project

4. Add required services

5. Deploy to AWS cloud

🔹 Configuring Amplify in Flutter

Initialize Amplify in your Flutter app:

// main.dart
import 'package:flutter/material.dart';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_api/amplify_api.dart';
import 'package:amplify_storage_s3/amplify_storage_s3.dart';
import 'amplifyconfiguration.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await _configureAmplify();
  runApp(MyApp());
}

Future<void> _configureAmplify() async {
  try {
    // Add plugins
    await Amplify.addPlugins([
      AmplifyAuthCognito(),
      AmplifyAPI(),
      AmplifyStorageS3(),
    ]);

    // Configure Amplify
    await Amplify.configure(amplifyconfig);
    
    print('Amplify configured successfully');
  } catch (e) {
    print('Error configuring Amplify: $e');
  }
}

What this does:

✓ Initializes Amplify before app starts

✓ Adds authentication, API, and storage

✓ Connects to AWS services

🔹 Authentication with Cognito

Implement user sign up and sign in:

import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:flutter/material.dart';

class AuthService {
  // Sign Up
  Future<bool> signUp(String email, String password) async {
    try {
      final result = await Amplify.Auth.signUp(
        username: email,
        password: password,
        options: SignUpOptions(
          userAttributes: {
            AuthUserAttributeKey.email: email,
          },
        ),
      );
      return result.isSignUpComplete;
    } on AuthException catch (e) {
      print('Sign up error: ${e.message}');
      return false;
    }
  }

  // Confirm Sign Up
  Future<bool> confirmSignUp(String email, String code) async {
    try {
      final result = await Amplify.Auth.confirmSignUp(
        username: email,
        confirmationCode: code,
      );
      return result.isSignUpComplete;
    } on AuthException catch (e) {
      print('Confirmation error: ${e.message}');
      return false;
    }
  }

  // Sign In
  Future<bool> signIn(String email, String password) async {
    try {
      final result = await Amplify.Auth.signIn(
        username: email,
        password: password,
      );
      return result.isSignedIn;
    } on AuthException catch (e) {
      print('Sign in error: ${e.message}');
      return false;
    }
  }

  // Sign Out
  Future<void> signOut() async {
    try {
      await Amplify.Auth.signOut();
    } on AuthException catch (e) {
      print('Sign out error: ${e.message}');
    }
  }

  // Get Current User
  Future<AuthUser?> getCurrentUser() async {
    try {
      final user = await Amplify.Auth.getCurrentUser();
      return user;
    } on AuthException catch (e) {
      print('Get user error: ${e.message}');
      return null;
    }
  }
}

// Usage in a widget
class LoginScreen extends StatefulWidget {
  @override
  _LoginScreenState createState() => _LoginScreenState();
}

class _LoginScreenState extends State<LoginScreen> {
  final emailController = TextEditingController();
  final passwordController = TextEditingController();
  final authService = AuthService();

  Future<void> handleSignIn() async {
    final success = await authService.signIn(
      emailController.text,
      passwordController.text,
    );
    
    if (success) {
      Navigator.pushReplacement(
        context,
        MaterialPageRoute(builder: (context) => HomeScreen()),
      );
    } else {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Sign in failed')),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Login')),
      body: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            TextField(
              controller: emailController,
              decoration: InputDecoration(labelText: 'Email'),
            ),
            SizedBox(height: 16),
            TextField(
              controller: passwordController,
              decoration: InputDecoration(labelText: 'Password'),
              obscureText: true,
            ),
            SizedBox(height: 24),
            ElevatedButton(
              onPressed: handleSignIn,
              child: Text('Sign In'),
            ),
          ],
        ),
      ),
    );
  }
}

🔹 GraphQL API Operations

Query and mutate data with GraphQL:

import 'package:amplify_flutter/amplify_flutter.dart';

// Query - Fetch all todos
Future<List<Todo>> fetchTodos() async {
  try {
    const graphQLDocument = '''
      query ListTodos {
        listTodos {
          items {
            id
            name
            description
            completed
          }
        }
      }
    ''';

    final request = GraphQLRequest<String>(
      document: graphQLDocument,
    );

    final response = await Amplify.API.query(request: request).response;
    final data = response.data;
    
    // Parse and return todos
    return parseTodos(data);
  } on ApiException catch (e) {
    print('Query error: $e');
    return [];
  }
}

// Mutation - Create a new todo
Future<Todo?> createTodo(String name, String description) async {
  try {
    const graphQLDocument = '''
      mutation CreateTodo(\$name: String!, \$description: String!) {
        createTodo(input: {name: \$name, description: \$description}) {
          id
          name
          description
          completed
        }
      }
    ''';

    final request = GraphQLRequest<String>(
      document: graphQLDocument,
      variables: {
        'name': name,
        'description': description,
      },
    );

    final response = await Amplify.API.mutate(request: request).response;
    
    // Parse and return created todo
    return parseTodo(response.data);
  } on ApiException catch (e) {
    print('Mutation error: $e');
    return null;
  }
}

// Mutation - Update todo
Future<bool> updateTodo(String id, bool completed) async {
  try {
    const graphQLDocument = '''
      mutation UpdateTodo(\$id: ID!, \$completed: Boolean!) {
        updateTodo(input: {id: \$id, completed: \$completed}) {
          id
          completed
        }
      }
    ''';

    final request = GraphQLRequest<String>(
      document: graphQLDocument,
      variables: {
        'id': id,
        'completed': completed,
      },
    );

    await Amplify.API.mutate(request: request).response;
    return true;
  } on ApiException catch (e) {
    print('Update error: $e');
    return false;
  }
}

🔹 File Storage with S3

Upload and download files to Amazon S3:

import 'dart:io';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:image_picker/image_picker.dart';

class StorageService {
  // Upload file
  Future<String?> uploadFile(File file, String key) async {
    try {
      final result = await Amplify.Storage.uploadFile(
        localFile: AWSFile.fromPath(file.path),
        key: key,
        options: StorageUploadFileOptions(
          accessLevel: StorageAccessLevel.guest,
        ),
      ).result;

      print('Upload successful: ${result.uploadedItem.key}');
      return result.uploadedItem.key;
    } on StorageException catch (e) {
      print('Upload error: $e');
      return null;
    }
  }

  // Download file
  Future<File?> downloadFile(String key, String localPath) async {
    try {
      final result = await Amplify.Storage.downloadFile(
        key: key,
        localFile: AWSFile.fromPath(localPath),
      ).result;

      print('Download successful: ${result.localFile.path}');
      return File(result.localFile.path);
    } on StorageException catch (e) {
      print('Download error: $e');
      return null;
    }
  }

  // Get file URL
  Future<String?> getFileUrl(String key) async {
    try {
      final result = await Amplify.Storage.getUrl(
        key: key,
        options: StorageGetUrlOptions(
          accessLevel: StorageAccessLevel.guest,
          expires: 3600, // URL valid for 1 hour
        ),
      ).result;

      return result.url.toString();
    } on StorageException catch (e) {
      print('Get URL error: $e');
      return null;
    }
  }

  // List files
  Future<List<String>> listFiles() async {
    try {
      final result = await Amplify.Storage.list().result;
      return result.items.map((item) => item.key).toList();
    } on StorageException catch (e) {
      print('List error: $e');
      return [];
    }
  }

  // Delete file
  Future<bool> deleteFile(String key) async {
    try {
      await Amplify.Storage.remove(key: key).result;
      return true;
    } on StorageException catch (e) {
      print('Delete error: $e');
      return false;
    }
  }
}

// Usage example
class ImageUploadScreen extends StatelessWidget {
  final storageService = StorageService();

  Future<void> pickAndUploadImage() async {
    final picker = ImagePicker();
    final image = await picker.pickImage(source: ImageSource.gallery);
    
    if (image != null) {
      final file = File(image.path);
      final key = 'images/${DateTime.now().millisecondsSinceEpoch}.jpg';
      
      final uploadedKey = await storageService.uploadFile(file, key);
      
      if (uploadedKey != null) {
        final url = await storageService.getFileUrl(uploadedKey);
        print('Image URL: $url');
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Upload Image')),
      body: Center(
        child: ElevatedButton(
          onPressed: pickAndUploadImage,
          child: Text('Pick and Upload Image'),
        ),
      ),
    );
  }
}

🧠 Test Your Knowledge

Which AWS service does Amplify use for authentication?