In this section you'll integrate Amplify API with your app, and use the generated data model to create, update, query, and delete BudgetEntry items in your app.

First, replace the contents of your *main.dart* file with the following UI boilerplate code. Typically, you would break this file up into smaller modules but we've kept it as a single file here just for the tutorial.

```dart
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

import 'models/ModelProvider.dart';

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

Future<void> _configureAmplify() async {
  // To be filled in
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // GoRouter configuration
  static final _router = GoRouter(
    routes: [
      GoRoute(
        path: '/',
        builder: (context, state) => const HomeScreen(),
      ),
    ],
  );

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
      debugShowCheckedModeBanner: false,
    );
  }
}

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();
  }

  Future<void> _refreshBudgetEntries() async {
    // To be filled in
  }

  Future<void> _deleteBudgetEntry(BudgetEntry budgetEntry) async {
    // To be filled in
  }

  Future<void> _navigateToBudgetEntry({BudgetEntry? budgetEntry}) async {
    // To be filled in
  }

  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)}',
                          ),
                        ),
                      );
                    },
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
```

Go ahead and run your code now on any mobile, web, or desktop device and you should see an app with some titles
and a floating action button but not much else.

```bash
flutter run
```

![Budget Tracker home screen](/images/lib/getting-started/flutter/budget-tracker-home.png)

## Configure Amplify

Let's start by configuring Amplify when the application loads. At the top of the file, find the `_configureAmplify`
method and replace it with the following code.

```diff
+ 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 {
-  // To be filled in
+  try {
+    // Create the API plugin.
+    //
+    // If `ModelProvider.instance` is not available, try running 
+    // `amplify codegen models` from the root of your project.
+    final api = AmplifyAPI(modelProvider: ModelProvider.instance);
+
+    // Create the Auth plugin.
+    final auth = AmplifyAuthCognito();
+
+    // Add the plugins and configure Amplify for your app.
+    await Amplify.addPlugins([api, auth]);
+    await Amplify.configure(amplifyconfig);
+
+    safePrint('Successfully configured');
+  } on Exception catch (e) {
+    safePrint('Error configuring Amplify: $e');
+  }
}
```

Here, we're adding the API and Authentication plugins to our app and configuring Amplify with the
generated `amplifyconfiguration.dart` file.

There's one more step to complete the configuration of Auth and that is to wrap our application
in the Amplify Authenticator, which will provide a pre-built authentication flow with less than 
5 lines of code.

> You can learn more about the Authenticator and how to customize it [here](https://ui.docs.amplify.aws/flutter/connected-components/authenticator).

In the `MyApp` class, make the following change to add the Authenticator.

```diff
  @override
  Widget build(BuildContext context) {
-    return MaterialApp.router(
-      routerConfig: _router,
-      debugShowCheckedModeBanner: false,
-    );
+    return Authenticator(
+      child: MaterialApp.router(
+        routerConfig: _router,
+        debugShowCheckedModeBanner: false,
+        builder: Authenticator.builder(),
+      ),
+    );
  }
```

If you try running your app again, you should now see a sign-in screen instead of the home screen.
The Authenticator guards your application so that only signed-in users can access it.

![Budget Tracker with Authenticator](/images/lib/getting-started/flutter/budget-tracker-authenticator.png)

Try creating a user with the `Sign Up` form before continuing on to the next step!

## Manipulating data

### Creating a Budget Entry

Currently, the `+` button doesn't do anything. Let's hook that up to a budget entry screen where we can create
and update budget items.

First, add the following below the `_HomeScreenState` class.

```dart
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),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}
```

Then, add the route to the global `GoRouter`.

```diff
  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?,
+       ),
+     ),
    ],
  );
```

Finally, hook up the `+` button to navigate to this route when it's pressed.

```diff
  Future<void> _navigateToBudgetEntry({BudgetEntry? budgetEntry}) async {
-    // To be filled in
+    await context.pushNamed('manage', extra: budgetEntry);
  }
```

Now, if you click the `+` button you should be sent to the new screen.

![Budget Tracker create screen](/images/lib/getting-started/flutter/budget-tracker-create-screen.png)

Try creating a budget entry at this point! You should be able to enter values in the fields,
and get errors if a value entered does not match the schema.

When returning to the home screen, though, our budget entry is nowhere to be found! Let's fix that.

### Loading remote budget entries

In the `_HomeScreenState` class, we have a method called `_refreshBudgetEntries`. Let's populate
that to update the state class's `_budgetEntries` variable every time we call it.

To that, we'll use Amplify's model helpers which provide a quick way to create CRUD requests for 
your data. Here we're using the `ModelQueries.list` helper to retrieve all the budget entries 
and then passing the GraphQL request to the API category which will automatically transform 
the response into a list of `BudgetEntry` types.

```diff
  Future<void> _refreshBudgetEntries() async {
-    // To be filled in
+    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');
+    }
  }
```

Now we can modify `initState` so that the entries are refreshed every time the homepage loads.

```diff
  @override
  void initState() {
    super.initState();
+    _refreshBudgetEntries();
  }
```

And finally, we also want to refresh the entries anytime we return from the create/update screen
so that any changes are reflected in the list.

```diff
  Future<void> _navigateToBudgetEntry({BudgetEntry? budgetEntry}) async {
    await context.pushNamed('manage', extra: budgetEntry);
+    // Refresh the entries when returning from the
+    // budget entry screen.
+    await _refreshBudgetEntries();
  }
```

### Deleting budget entries

If you notice, right now you can swipe right on a budget entry to dismiss it. Let's modify the code
so that it properly deletes the entry when dismissed.

Modify the `_deleteBudgetEntry` function as follows. Again, we'll use Amplify model helpers to simplify
the creation of the GraphQL request.

```diff
  Future<void> _deleteBudgetEntry(BudgetEntry budgetEntry) async {
-    // To be filled in
+    final request = ModelMutations.delete<BudgetEntry>(budgetEntry);
+    final response = await Amplify.API.mutate(request: request).response;
+    safePrint('Delete response: $response');
+    await _refreshBudgetEntries();
  }
```

If you've made it this far, congratulations! You've built a fully working budget management application 🥳

![Completed budget tracker](/images/lib/getting-started/flutter/budget-tracker-complete.png)
