CDKで構築された既存のAWSリソースに接続
このガイドでは、AWS Cloud Development Kit(AWS CDK)を使用して既に作成したAWSリソースに新しいアプリを接続する方法を説明します。AWS CDKは、最新のプログラミング言語を使用してクラウドインフラストラクチャをコードとして定義するためのオープンソースソフトウェア開発フレームワークです。このインフラストラクチャはAWS CloudFormationを通じてデプロイされます。
このガイドでは、Amplify Data CDKを使用してAWS AppSyncでGraphQL APIバックエンドを作成します。これはコアバックエンドを作成します。その後、FlutterアプリをビルドしてGraphQL APIと統合します。
開始する前に、以下のものが必要です:
- AWSアカウント:まだアカウントを持っていない場合は、AWSEnvironmentのセットアップチュートリアルに従って、簡単な概要を確認してください。
- Amplify CLIインストールおよび設定済み。
- テキストエディタ。このガイドではVS Codeを使用しますが、任意のIDEを使用できます。
- Flutter及びそのコマンドラインツールインストールおよび設定済み。
Amplify Data CDKコンストラクトを使用してGraphQL APIを構築
CDKは、コード内でクラウドインフラストラクチャを定義する簡単な方法を提供します。このセクションでは、CDKを使用して、アプリケーションのバックエンドリソースを構築します。
ステップ1: ターミナルで次のコマンドを実行して、CDKアプリ用のフォルダを作成します。
mkdir cdk-backendステップ2: cdk-backendフォルダに移動し、cdk initコマンドを実行して新しいCDKプロジェクトを作成し、優先言語を指定します。
cd cdk-backend cdk init --language typescriptステップ3: VS Codeまたは好みのIDEを使用して、新しく作成されたCDKプロジェクトを開きます。
ステップ4: ターミナルでcdk_backendルートフォルダに移動し、次のコマンドを実行してAWS Amplify Dataパッケージをインストールします。
npm install @aws-amplify/data-constructステップ5: 次のコードに示すようにcdk_backend/lib/cdk-backend-stack.tsファイルを更新して、AmplifyDataコンストラクトを使用してAWS AppSync APIを作成します。
import * as cdk from 'aws-cdk-lib';import { Construct } from 'constructs';import { AmplifyData, AmplifyDataDefinition} from '@aws-amplify/data-construct';
export class CdkBackendStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props);
new AmplifyData(this, 'AmplifyCdkData', { definition: AmplifyDataDefinition.fromString(/* GraphQL */ ` type Todo @model @auth(rules: [{ allow: public }]) { id: ID! name: String! description: String complete: Boolean } `), authorizationModes: { defaultAuthorizationMode: 'API_KEY', apiKeyConfig: { expires: cdk.Duration.days(30) } } }); }}ステップ6: 次のコマンドを実行してCDKスタックをデプロイします。
cdk deployステップ7: CDKがデプロイのリソースを準備し、次のプロンプトが表示されます。Yを入力してEnterキーを押します。
CDKはスタックをデプロイし、次の確認を表示します。デプロイされたAPIの詳細に注意してください。次のセクションで使用します。
CDKを使用してバックエンドAPIを構築したので、フロントエンドを接続できます。
Flutterアプリを構築してGraphQL APIに接続
このセクションでは、FlutterモバイルアプリをCDKで作成したGraphQL APIに接続します。まず、Flutterプロジェクトを初期化し、スキーマに一致するモデルを定義し、AmplifyでCRUD操作を統合します。その後、ToDoアイテムを管理するためのUIページを追加して、クエリとミューテーションを追加します。最後に、バックエンド詳細でAmplifyを構成し、アプリを実行して、既存のAPIとの完全な機能を実演します。
ステップ1: ターミナルで次のコマンドを実行してFlutterアプリを作成します。
flutter create flutter_todo_app --platforms=webステップ2: ターミナルで次のコマンドを実行して、新しく作成されたFlutterアプリを開きます。
cd flutter_todo_app code . -rステップ3: 次のコードに示すように、アプリのルートディレクトリのpubspec.yamlファイルを更新して、必要な依存関係を追加します。
name: flutter_todo_appdescription: "A new Flutter project."publish_to: 'none' # Remove this line if you wish to publish to pub.devversion: 1.0.0+1
dependencies: flutter: sdk: flutter amplify_flutter: ^2.0.0 amplify_api: ^2.0.0 go_router: ^6.5.5 cupertino_icons: ^1.0.2
dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0
flutter: uses-material-design: trueステップ4: ターミナルで次のコマンドを実行して、pubspec.yamlファイルに追加した依存関係をインストールします。
flutter pub getステップ5: 次のコマンドを実行して、@aws-amplify/cliパッケージをインストールします。
npm install @aws-amplify/cliステップ6: graphqlという名前の新しいフォルダを作成し、その中にファイルschema.graphqlを作成します。

ステップ7: 次の例に示すようにschema.graphqlファイルを更新して、CDKアプリで使用したものと同様のToDoデータモデルを定義します。
type Todo @model @auth(rules: [{ allow: public }]) { id: ID! name: String! description: String complete: Boolean}ステップ8: 次のコマンドを実行して、lib/modelsフォルダ内にGraphQLクライアントヘルパーモデルを生成します。
npx @aws-amplify/cli codegen models --model-schema ./graphql --target flutter --output-dir ./lib/models
ステップ9: libフォルダ内にtodo_item_page.dartファイルを作成し、次のコードで更新して、ユーザーがToDoアイテムを作成するためのフォームを提示します。送信されると、フォームはGraphQLミューテーションを開始して、データベース内のアイテムを追加または変更します。
import 'package:amplify_api/amplify_api.dart';import 'package:amplify_flutter/amplify_flutter.dart';import 'package:flutter/material.dart';import 'package:go_router/go_router.dart';
import '../models/ModelProvider.dart';
class ToDoItemPage extends StatefulWidget { const ToDoItemPage({ required this.todoItem, super.key, });
final Todo? todoItem;
State<ToDoItemPage> createState() => _ToDoItemPageState();}
class _ToDoItemPageState extends State<ToDoItemPage> { final _formKey = GlobalKey<FormState>(); final TextEditingController _nameController = TextEditingController(); final TextEditingController _descriptionController = TextEditingController();
late final String _nameText; late bool _isDone;
bool get _isCreate => _todoItem == null; Todo? get _todoItem => widget.todoItem;
void initState() { super.initState();
final todoItem = _todoItem; if (todoItem != null) { _nameController.text = todoItem.name; _descriptionController.text = todoItem.description ?? '';
_nameText = 'Update to-do Item'; _isDone = todoItem.complete ?? false; } else { _nameText = 'Create to-do Item'; _isDone = false; } }
void dispose() { _nameController.dispose(); _descriptionController.dispose();
super.dispose(); }
Future<void> submitForm() async { if (!_formKey.currentState!.validate()) { return; }
// If the form is valid, submit the data final name = _nameController.text; final description = _descriptionController.text; final complete = _isDone;
if (_isCreate) { // Create a new todo item final newEntry = Todo( name: name, description: description.isNotEmpty ? description : null, complete: complete, ); final request = ModelMutations.create(newEntry); final response = await Amplify.API.mutate(request: request).response; safePrint('Create result: $response'); } else { // Update todoItem instead final updateToDoItem = _todoItem!.copyWith( name: name, description: description.isNotEmpty ? description : null, complete: complete, ); final request = ModelMutations.update(updateToDoItem); final response = await Amplify.API.mutate(request: request).response; safePrint('Update result: $response'); }
// Navigate back to homepage after create/update executes if (mounted) { context.pop(); } }
Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(_nameText), ), body: Align( alignment: Alignment.topCenter, child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 800), child: Padding( padding: const EdgeInsets.all(16), child: SingleChildScrollView( child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ TextFormField( controller: _nameController, decoration: const InputDecoration( labelText: 'Name (required)', ), validator: (value) { if (value == null || value.isEmpty) { return 'Please enter a name'; } return null; }, ), TextFormField( controller: _descriptionController, decoration: const InputDecoration( labelText: 'Description', ), ), SwitchListTile( title: const Text('Done'), value: _isDone, onChanged: (bool value) { setState(() { _isDone = value; }); }, secondary: const Icon(Icons.done_all_outlined), ), const SizedBox(height: 20), ElevatedButton( onPressed: submitForm, child: Text(_nameText), ), ], ), ), ), ), ), ), ); }}ステップ10: libフォルダ内にhome_page.dart.dartファイルを作成し、次のコードで更新します。このページはGraphQLクエリを使用してToDoアイテムのリストを取得し、ListViewウィジェットで表示します。このページでは、GraphQLミューテーションを使用してToDoアイテムを削除することもできます。
import 'package:amplify_api/amplify_api.dart';import 'package:amplify_flutter/amplify_flutter.dart';import 'package:flutter/material.dart';import 'package:go_router/go_router.dart';
import '../models/ModelProvider.dart';
class HomePage extends StatefulWidget { const HomePage({super.key});
State<HomePage> createState() => _HomePageState();}
class _HomePageState extends State<HomePage> { var _todoItems = <Todo>[];
void initState() { super.initState(); _refreshTodoItems(); }
Future<void> _refreshTodoItems() async { try { final request = ModelQueries.list(Todo.classType); final response = await Amplify.API.query(request: request).response;
final todos = response.data?.items; if (response.hasErrors) { safePrint('errors: ${response.errors}'); return; } setState(() { _todoItems = todos!.whereType<Todo>().toList(); }); } on ApiException catch (e) { safePrint('Query failed: $e'); } }
Future<void> _deleteToDoItem(Todo todoItem) async { final request = ModelMutations.delete<Todo>(todoItem); final response = await Amplify.API.mutate(request: request).response; safePrint('Delete response: $response'); await _refreshTodoItems(); }
Future<void> _openToDoItem({Todo? todoItem}) async { await context.pushNamed('manage', extra: todoItem); // Refresh the entries when returning from the // todo item screen. await _refreshTodoItems(); }
Widget _buildRow({ required String name, required String description, required bool isDone, TextStyle? style, }) { return Row( children: [ Expanded( child: Text( name, textAlign: TextAlign.center, style: style, ), ), Expanded( child: Text( description, textAlign: TextAlign.center, style: style, ), ), Expanded( child: isDone ? const Icon(Icons.done) : const SizedBox(), ), ], ); }
Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( // Navigate to the page to create new todo item onPressed: _openToDoItem, child: const Icon(Icons.add), ), appBar: AppBar( title: const Text('To-Do List'), ), body: Center( child: Padding( padding: const EdgeInsets.only(top: 25), child: RefreshIndicator( onRefresh: _refreshTodoItems, child: Column( children: [ if (_todoItems.isEmpty) const Text('Use the \u002b sign to add new to-do items') else const SizedBox(height: 30), _buildRow( name: 'Name', description: 'Description', isDone: false, style: Theme.of(context).textTheme.titleMedium, ), const Divider(), Expanded( child: ListView.builder( itemCount: _todoItems.length, itemBuilder: (context, index) { final todoItem = _todoItems[index]; return Dismissible( key: ValueKey(todoItem), background: const ColoredBox( color: Colors.red, child: Padding( padding: EdgeInsets.only(right: 10), child: Align( alignment: Alignment.centerRight, child: Icon(Icons.delete, color: Colors.white), ), ), ), onDismissed: (_) => _deleteToDoItem(todoItem), child: ListTile( onTap: () => _openToDoItem( todoItem: todoItem, ), title: _buildRow( name: todoItem.name, description: todoItem.description ?? '', isDone: todoItem.complete ?? false, ), ), ); }, ), ), ], ), ), ), ), ); }}ステップ11: main.dartを更新して、前のセクションでCDKアプリを使用して作成したGraphQL APIの詳細を使用してAmplifyを構成します。
import 'package:amplify_api/amplify_api.dart';
import 'package:amplify_flutter/amplify_flutter.dart';import 'package:flutter/material.dart';import 'package:go_router/go_router.dart';
import 'models/ModelProvider.dart';import 'home_page.dart';import 'todo_item_page.dart';
Future<void> main() async { WidgetsFlutterBinding.ensureInitialized(); await _configureAmplify(); runApp(const MyApp());}
Future<void> _configureAmplify() async { try { final api = AmplifyAPI(modelProvider: ModelProvider.instance);
await Amplify.addPlugins([api]); const amplifyconfig = '''{ "api": { "plugins": { "awsAPIPlugin": { "flutter_todo_app": { "endpointType": "GraphQL", "endpoint": "<The-GraphQL-Endpoint>", "region": "<The-Region>", "authorizationType": "API_KEY", "apiKey": "<The-API-KEY-Value>" } } } }}''';
await Amplify.configure(amplifyconfig);
safePrint('Successfully configured'); } on Exception catch (e) { safePrint('Error configuring Amplify: $e'); }}
class MyApp extends StatelessWidget { const MyApp({super.key});
// GoRouter configuration static final _router = GoRouter( routes: [ GoRoute( path: '/', builder: (context, state) => const HomePage(), ), GoRoute( path: '/manage-todo-item', name: 'manage', builder: (context, state) => ToDoItemPage( todoItem: state.extra as Todo?, ), ), ], );
Widget build(BuildContext context) { return MaterialApp.router( routerConfig: _router, debugShowCheckedModeBanner: false, builder: (context, child) { return child!; }, ); }}ステップ12: 次のコマンドを使用してChromブラウザでアプリを実行します。
flutter run -d chrome結論
おめでとうございます!AWS Amplify Data CDKコンストラクトを使用してGraphQL APIバックエンドをAWS AppSyncで作成しました。次に、Amplifyライブラリを使用してアプリをそのAPIに接続しました。フィードバックがある場合は、GitHubの問題を残すか、Discordコミュニティに参加してください!
リソースのクリーンアップ
このデモアプリの実験が終了したら、予期しない費用が発生しないようにバックエンドリソースを削除することをお勧めします。これは、上記で作成したCDKアプリのルートフォルダで次のコマンドを実行することで実行できます。
cdk destroy