This article presents another avenue for Flutter developers to engage with Gen-AI. It demonstrates how to utilize Amazon Bedrock from Flutter via the AWS Amplify AI Kit.
While the official AWS Amplify documentation does not address this topic, there is a hidden path to integrating powerful AI capabilities into your Flutter projects using the Amplify AI Kit. This article will guide you through this uncharted territory.
I present a recipe recommendation app for the Flutter version based on the Amplify AI Kit documentation in this piece.
Here's what you can expect to find:
- Unofficial integration: Learn how to bridge the gap between Flutter and Amplify AI Kit, unlocking a world of AI possibilities for your applications.
- Practical application: Create a fully functional recipe recommendation app with AI-powered suggestions.
- Backend connection: Discover how to seamlessly connect your Flutter frontend with Amazon Bedrock through AWS AppSync.
Sample code
flutter_amplify_ai_kit
A new Flutter project.
Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
For help getting started with Flutter development, view the online documentation, which offers tutorials, samples, guidance on mobile development, and a full API reference.
Preparation
Please set up these tools.
$ flutter --version
Flutter 3.27.4 • channel stable • https://github.com/flutter/flutter.git
Framework • revision d8a9f9a52e (9 days ago) • 2025-01-31 16:07:18 -0500
Engine • revision 82bd5b7209
Tools • Dart 3.6.2 • DevTools 2.40.3
$ node --version
v20.18.1
$ npm --version
10.8.2
Steps
Create a project
$ flutter create flutter_amplify_ai_kit
...
$ cd flutter_amplify_ai_kit
$ flutter pub add amplify_api amplify_flutter amplify_auth_cognito amplify_authenticator
$ npm create amplify@latest
? Where should we create your project? (.) # Enter
Set up your AWS credentials.
Then, run Amplify Sandbox.
$ npx ampx sandbox --outputs-format dart --outputs-out-dir lib
...
[Sandbox] Watching for file changes...
File written: lib/amplify_outputs.dart
Set up backend resources
Here, we define resources on how to connect to Amazon Bedrock.
amplify/data/resource.ts
import { type ClientSchema, a, defineData } from '@aws-amplify/backend';
const schema = a.schema({
Dummy: a
.model({
dummy: a.string(),
})
.authorization((allow) => [allow.owner()]),
generateRecipe: a.generation({
aiModel: a.ai.model('Claude 3 Haiku'),
systemPrompt: 'You are a helpful assistant that generates recipes.',
})
.arguments({
description: a.string(),
})
.returns(
a.customType({
name: a.string(),
ingredients: a.string().array(),
instructions: a.string(),
})
)
.authorization((allow) => allow.authenticated()),
});
export type Schema = ClientSchema<typeof schema>;
export const data = defineData({
schema,
authorizationModes: {
defaultAuthorizationMode: 'userPool',
},
});
generateRecipe
is the name of the function called Flutter.
a.generation()
is the function defined by the Amplify Library. It can describe a Gen-AI model, a system prompt, its arguments, and return types.
When you save the code above, Sandbox will automatically rerun. Please wait until you finish with Sandbox, open another terminal, and execute the generate command as shown below.
$ npx ampx generate graphql-client-code --format modelgen --model-target dart --out ./lib/models
This allows you to use Model Types from the Flutter frontend.
Notice:
amplify/data/resource.ts
must include at least onea.model()
.
The generate command will export incomplete Types if you do not write anya.model()
.
Implement the Flutter frontend
Implementation lib/main.dart
like below.
import 'dart:convert';
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:flutter_amplify_ai_kit/models/ModelProvider.dart';
import 'amplify_outputs.dart';
Future<void> main() async {
try {
WidgetsFlutterBinding.ensureInitialized();
await _configureAmplify();
runApp(const MyApp());
} on AmplifyException catch (e) {
runApp(Text("Error configuring Amplify: ${e.message}"));
}
}
Future<void> _configureAmplify() async {
try {
await Amplify.addPlugins(
[
AmplifyAuthCognito(),
AmplifyAPI(
options: APIPluginOptions(
modelProvider: ModelProvider.instance,
),
),
],
);
await Amplify.configure(amplifyConfig);
safePrint('Successfully configured');
} on Exception catch (e) {
safePrint('Error configuring Amplify: $e');
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
final title = 'AI Kit Demo for Flutter';
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return Authenticator(
child: MaterialApp(
title: title,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
builder: Authenticator.builder(),
home: MyHomePage(title: title),
),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String requestRecipe = '';
String responseRecipeName = '';
List<String> responseRecipeIngredients = [];
String responseRecipeInstructions = '';
final TextEditingController _requestRecipeController =
TextEditingController();
@override
void dispose() {
_requestRecipeController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.start,
spacing: 16,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SignOutButton(),
],
),
TextField(
controller: _requestRecipeController,
decoration: InputDecoration(
labelText: 'Input your request',
border: OutlineInputBorder(),
),
onChanged: (text) {
safePrint("Current text: $text");
},
),
Card(
child: Column(
children: [
ListTile(
title: Text('Recipe'),
subtitle: Text(responseRecipeName),
),
const Divider(),
ListTile(
title: Text('Ingredients'),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: responseRecipeIngredients.map((ingredient) {
return Text(ingredient);
}).toList(),
),
),
const Divider(),
ListTile(
title: Text('Instructions'),
subtitle: Text(responseRecipeInstructions),
),
],
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: _generateRecipe,
tooltip: 'Generate Recipe',
child: const Icon(Icons.send),
),
);
}
_generateRecipe() async {
try {
const graphQLDocument = '''
query GenerateRecipe(\$description: String!) {
generateRecipe(description: \$description) {
name
ingredients
instructions
}
}
''';
if (_requestRecipeController.text.isEmpty) {
return;
}
final generateRecipeRequest = GraphQLRequest<String>(
document: graphQLDocument,
variables: <String, String>{
"description": _requestRecipeController.text,
},
);
final response =
await Amplify.API.query(request: generateRecipeRequest).response;
safePrint(response);
Map<String, dynamic> jsonMap = json.decode(response.data!);
safePrint(jsonMap);
GenerateRecipeReturnType generateRecipeResponse =
GenerateRecipeReturnType.fromJson(jsonMap['generateRecipe']);
safePrint(generateRecipeResponse);
setState(() {
responseRecipeName = generateRecipeResponse.name ?? '';
responseRecipeIngredients = generateRecipeResponse.ingredients ?? [];
responseRecipeInstructions = generateRecipeResponse.instructions ?? '';
});
} on ApiException catch (e) {
safePrint('Query failed: $e');
}
}
}
_generateRecipe()
is the central part of the Amazon Bedrock call.
graphQLDocument
is the GraphQL query to call generateRecipe
previously defined in amplify/data/resource.ts
.
Use Amplify.API.query()
to connect to Amazon Bedrock via AWS AppSync.
Finally, we can use Amazon Bedrock from Flutter via the AWS Amplify AI Kit!
Check
Let's run and check this app.
$ flutter run -d chrome
Clean up
If you want to finish and delete Sandbox, you can just run the steps below.
$ npx ampx sandbox delete
? Are you sure you want to delete all the resources in your sandbox environment (This can't be undone)? (y/N) y
...
[Sandbox] Finished deleting.
$
Top comments (0)