Name:
interface
Value:
Amplify has re-imagined the way frontend developers build fullstack applications. Develop and deploy without the hassle.

Page updated Aug 3, 2024

Maintenance ModeYou are viewing Amplify Gen 1 documentation. Amplify Gen 1 has entered maintenance mode and will reach end of life on May 1, 2027. New project should use Amplify Gen 2. For existing Gen 1 projects, a migration guide and tooling are available to help you upgrade. Switch to the latest Gen 2 docs →

Amplify CLIで既存のAWSリソースに接続する

AWS Amplify CLI(コマンドラインインターフェース)は、認証、データベース、ストレージなどのクラウドリソースをコマンドラインでアプリケーション用にプロビジョニングするシンプルなワークフローを提供します。

このガイドでは、Amplify CLIを使用して既に作成したバックエンドリソースに新しいFlutterモバイルアプリを接続する方法を学びます。

ウェブアプリを接続しますか?Amplify CLIを使用して既存のバックエンドをReactと統合するためのガイドも提供しています。Reactガイドを確認してください。

モバイルアプリを既存のAWSリソースに接続する

このガイドでは、既存のFlutterアプリ用にAmplifyで作成されたAWSリソースに新しいFlutterアプリを接続する手順を説明します。既存のアプリがない場合は、このFlutterガイドに従って、Amplify AuthおよびAPIリソースを使用する予算トラッカーアプリを作成できます。

開始する前に、以下が必要です:

  • 既存のFlutterアプリ
  • AWSアカウント:アカウントがない場合は、AWSEnvironmentの設定チュートリアルに従って、簡単な概要を取得してください。
  • インストールおよび構成されたAmplify CLI。
  • インストールおよび構成されたFlutter。
  • Flutterのコマンドラインツールと組み合わせたテキストエディタ。このガイドではVS Codeを使用しますが、好みのIDEを使用できます。

AWSバックエンド詳細を検索する

バックエンドリソースを新しいアプリに接続する前に、まず既存のアプリ用にプロビジョニングされたAWS環境の詳細を見つける必要があります。

ステップ1: 既存のアプリで、<Your-App>/amplify/team-provider-info.jsonファイルを開きます。

Amplifyアプリのファイルディレクトリ内のteam-provider-info.jsonファイル。

ステップ2: team-provider-info.jsonファイルで、以下を記録します:

  1. 使用する環境
  2. 必要な環境のAmplifyAppId
team-provider-info.jsonファイル内の環境とAmplifyAppId。

Flutterアプリを作成する

必要なバックエンド詳細を収集したので、新しいFlutterアプリの構築を開始できます。

ステップ1: ターミナルで以下のコマンドを実行して、Flutterアプリを作成します。

flutter create amplify_connect_resources

ステップ2: ターミナルで以下のコマンドを実行して、新しく作成されたFlutterアプリをVS Codeで開きます。

cd amplify_connect_resources
code . -r

VS Codeを使用して作成されたアプリを開きます。

ステップ3: アプリのルートフォルダに移動し、ターミナルで以下のコマンドを実行してAmplifyバックエンドをアプリにインポートします。

amplify pull --appId <The_App_ID> --envName <The_App_Env>

ステップ4: AWS認証方法を選択します。この例では、AWSプロファイルを使用しています。選択したプロファイルがAWSリソースへのアクセスと変更に必要な権限を持っていることを確認してください。AWSプロファイルの設定の詳細については、Amplify CLIの構成を参照してください。

デフォルトエディタ、アプリタイプ、構成ファイルの保存場所に関するプロンプトのデフォルト値を受け入れます。次に、「modifying this backend」質問にはいと答えてください。Amplify CLIはバックエンドを初期化し、プロジェクトをクラウドに接続します。

? Select the authentication method you want to use: AWS profile
For more information on AWS Profiles, see:
https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html
? Please choose the profile you want to use AwsWest1
Amplify AppID found: dorapq24trw9r. Amplify App name is:amplifyBudget
Backend environment dev found in Amplify Console app: amplifyBudget
? Choose your default editor: Visual Studio Code
Choose the type of app that you're building · flutter
Please tell us about your project
? Where do you want to store your configuration file? ./lib/
? Do you plan on modifying this backend? Yes
Fetching updates to backend environment: dev from the cloud. Building resource api/amplifyBudget
Fetching updates to backend environment: dev from the cloud. GraphQL schema compiled successfully.
Edit your schema at /Users/malakamm/development/amplify_connect_resources/amplify/backend/api/amplifyBudget/schema.graphql or place .graphql files in a directory at /Users/malakamm/development/amplify_connect_resources/amplify/backend/api/amplifyBudget/schema
Successfully pulled backend environment dev from the cloud.
Successfully pulled backend environment dev from the cloud.
Run 'amplify pull' to sync future upstream changes.

Amplify CLIは、Amplifyプロジェクトとバックエンド詳細を含むamplifyという名前の新しいフォルダをアプリのルートフォルダに追加します。また、lib/フォルダにamplifyconfiguration.dartという新しいDartファイルも追加します。アプリはこのファイルをランタイムで使用して、プロビジョニングしたバックエンドリソースを見つけて接続します。

Amplifyアプリのファイルディレクトリ内のAmplifyフォルダとamplifyconfiguration.dartファイル。

ステップ5: アプリルートディレクトリのpubspec.yamlファイルを更新して、必要なパッケージを追加します。この例では、このガイドで作成されたアプリと同じパッケージを使用します。これを行うには、次のようにpubspec.yamlを更新します。

pubspec.yaml
name: budget_tracker
description: A new Flutter project.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
dependencies:
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
cupertino_icons: ^1.0.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.0
flutter:
uses-material-design: true

ステップ6:このコマンドを使用して、GraphQLスキーマへのタイプセーフインタラクションを有効にするために必要なDartファイルを生成します。

amplify codegen models

Amplify CLIは、lib/modelsフォルダにDartファイルを生成します。

Amplifyアプリのファイルディレクトリ内のmodelsフォルダ。

ステップ7: main.dartファイルを以下のコードで更新して、Amplify Authenticatorを導入し、Amplify APIを統計アプリに統合して、BudgetEntry項目を作成、更新、クエリ、削除します。通常、このファイルをより小さなモジュールに分割しますが、このガイドでは1つのファイルとして保持しました。

main.dart
import 'package:amplify_api/amplify_api.dart';
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_authenticator/amplify_authenticator.dart';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'amplifyconfiguration.dart';
import 'models/ModelProvider.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await _configureAmplify();
runApp(const MyApp());
}
Future<void> _configureAmplify() async {
try {
final api = AmplifyAPI(modelProvider: ModelProvider.instance);
final auth = AmplifyAuthCognito();
await Amplify.addPlugins([api, auth]);
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 HomeScreen(),
),
GoRoute(
path: '/manage-budget-entry',
name: 'manage',
builder: (context, state) => ManageBudgetEntryScreen(
budgetEntry: state.extra as BudgetEntry?,
),
),
],
);
@override
Widget build(BuildContext context) {
return Authenticator(
child: MaterialApp.router(
routerConfig: _router,
debugShowCheckedModeBanner: false,
builder: Authenticator.builder(),
),
);
}
}
class LoadingScreen extends StatelessWidget {
const LoadingScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
var _budgetEntries = <BudgetEntry>[];
@override
void initState() {
super.initState();
_refreshBudgetEntries();
}
Future<void> _refreshBudgetEntries() async {
try {
final request = ModelQueries.list(BudgetEntry.classType);
final response = await Amplify.API.query(request: request).response;
final todos = response.data?.items;
if (response.hasErrors) {
safePrint('errors: ${response.errors}');
return;
}
setState(() {
_budgetEntries = todos!.whereType<BudgetEntry>().toList();
});
} on ApiException catch (e) {
safePrint('Query failed: $e');
}
}
Future<void> _deleteBudgetEntry(BudgetEntry budgetEntry) async {
final request = ModelMutations.delete<BudgetEntry>(budgetEntry);
final response = await Amplify.API.mutate(request: request).response;
safePrint('Delete response: $response');
await _refreshBudgetEntries();
}
Future<void> _navigateToBudgetEntry({BudgetEntry? budgetEntry}) async {
await context.pushNamed('manage', extra: budgetEntry);
// Refresh the entries when returning from the
// budget entry screen.
await _refreshBudgetEntries();
}
double _calculateTotalBudget(List<BudgetEntry?> items) {
var totalAmount = 0.0;
for (final item in items) {
totalAmount += item?.amount ?? 0;
}
return totalAmount;
}
Widget _buildRow({
required String title,
required String description,
required String amount,
TextStyle? style,
}) {
return Row(
children: [
Expanded(
child: Text(
title,
textAlign: TextAlign.center,
style: style,
),
),
Expanded(
child: Text(
description,
textAlign: TextAlign.center,
style: style,
),
),
Expanded(
child: Text(
amount,
textAlign: TextAlign.center,
style: style,
),
),
],
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
floatingActionButton: FloatingActionButton(
// Navigate to the page to create new budget entries
onPressed: _navigateToBudgetEntry,
child: const Icon(Icons.add),
),
appBar: AppBar(
title: const Text('Budget Tracker'),
),
body: Center(
child: Padding(
padding: const EdgeInsets.only(top: 25),
child: RefreshIndicator(
onRefresh: _refreshBudgetEntries,
child: Column(
children: [
if (_budgetEntries.isEmpty)
const Text('Use the \u002b sign to add new budget entries')
else
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Show total budget from the list of all BudgetEntries
Text(
'Total Budget: \$ ${_calculateTotalBudget(_budgetEntries).toStringAsFixed(2)}',
style: const TextStyle(fontSize: 24),
)
],
),
const SizedBox(height: 30),
_buildRow(
title: 'Title',
description: 'Description',
amount: 'Amount',
style: Theme.of(context).textTheme.titleMedium,
),
const Divider(),
Expanded(
child: ListView.builder(
itemCount: _budgetEntries.length,
itemBuilder: (context, index) {
final budgetEntry = _budgetEntries[index];
return Dismissible(
key: ValueKey(budgetEntry),
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: (_) => _deleteBudgetEntry(budgetEntry),
child: ListTile(
onTap: () => _navigateToBudgetEntry(
budgetEntry: budgetEntry,
),
title: _buildRow(
title: budgetEntry.title,
description: budgetEntry.description ?? '',
amount:
'\$ ${budgetEntry.amount.toStringAsFixed(2)}',
),
),
);
},
),
),
],
),
),
),
),
);
}
}
class ManageBudgetEntryScreen extends StatefulWidget {
const ManageBudgetEntryScreen({
required this.budgetEntry,
super.key,
});
final BudgetEntry? budgetEntry;
@override
State<ManageBudgetEntryScreen> createState() =>
_ManageBudgetEntryScreenState();
}
class _ManageBudgetEntryScreenState extends State<ManageBudgetEntryScreen> {
final _formKey = GlobalKey<FormState>();
final TextEditingController _titleController = TextEditingController();
final TextEditingController _descriptionController = TextEditingController();
final TextEditingController _amountController = TextEditingController();
late final String _titleText;
bool get _isCreate => _budgetEntry == null;
BudgetEntry? get _budgetEntry => widget.budgetEntry;
@override
void initState() {
super.initState();
final budgetEntry = _budgetEntry;
if (budgetEntry != null) {
_titleController.text = budgetEntry.title;
_descriptionController.text = budgetEntry.description ?? '';
_amountController.text = budgetEntry.amount.toStringAsFixed(2);
_titleText = 'Update budget entry';
} else {
_titleText = 'Create budget entry';
}
}
@override
void dispose() {
_titleController.dispose();
_descriptionController.dispose();
_amountController.dispose();
super.dispose();
}
Future<void> submitForm() async {
if (!_formKey.currentState!.validate()) {
return;
}
// If the form is valid, submit the data
final title = _titleController.text;
final description = _descriptionController.text;
final amount = double.parse(_amountController.text);
if (_isCreate) {
// Create a new budget entry
final newEntry = BudgetEntry(
title: title,
description: description.isNotEmpty ? description : null,
amount: amount,
);
final request = ModelMutations.create(newEntry);
final response = await Amplify.API.mutate(request: request).response;
safePrint('Create result: $response');
} else {
// Update budgetEntry instead
final updateBudgetEntry = _budgetEntry!.copyWith(
title: title,
description: description.isNotEmpty ? description : null,
amount: amount,
);
final request = ModelMutations.update(updateBudgetEntry);
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();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_titleText),
),
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: _titleController,
decoration: const InputDecoration(
labelText: 'Title (required)',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a title';
}
return null;
},
),
TextFormField(
controller: _descriptionController,
decoration: const InputDecoration(
labelText: 'Description',
),
),
TextFormField(
controller: _amountController,
keyboardType: const TextInputType.numberWithOptions(
signed: false,
decimal: true,
),
decoration: const InputDecoration(
labelText: 'Amount (required)',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter an amount';
}
final amount = double.tryParse(value);
if (amount == null || amount <= 0) {
return 'Please enter a valid amount';
}
return null;
},
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: submitForm,
child: Text(_titleText),
),
],
),
),
),
),
),
),
);
}
}

ステップ8: 以下のコマンドを使用してアプリを実行し、認証フローを使用してユーザーを作成します。次に、いくつかの予算項目を作成します。

flutter run

結論

おめでとうございます!新しいFlutterアプリは、AWS Amplifyを通じて別のアプリからのAWSリソースに接続されました。この統合により、アプリはユーザー管理用の認証リソースと、Amazon DynamoDBで支援されたスケーラブルなGraphQL APIにアクセスできます。

リソースをクリーンアップする

このデモアプリの実験が終わったら、予期しない費用の発生を避けるためにバックエンドリソースを削除することをお勧めします。これは、アプリのルートフォルダで以下のコマンドを実行することで行えます。

amplify delete

このデモアプリを本番対応アプリに拡張したい場合は、認可やストレージなどの追加リソースを追加する必要がある場合があります。Build&connectバックエンドセクションを参照して、他のバックエンドリソースを追加および接続する方法に関するガイドを確認してください。