使用 AWS Amplify 构建 Flutter 移动应用程序 - 第 1 部分

创建适用于 iOS 和 Android 的行程规划器应用程序

模块 3:添加 API

概述

在本模块中,您将使用 Amplify CLI 向应用程序中添加一个用于检索行程数据和将此类数据持久化的 API。您将创建一种 GraphQL API,该 API 使用由 Amazon DynamoDB(一种 NoSQL 数据库)提供支持的 AWS AppSync(一种 GraphQL 托管服务)。

要完成的目标

  • 向应用程序中添加 Amplify API
  • 向应用程序中添加行程数据模型
  • 对行程功能执行 CRUD 操作和工作流
  • 实现行程列表 UI

 最短完成时间

20 分钟

操作步骤

向应用程序中添加 Amplify API

步骤 1:前往应用程序的根文件夹,然后在终端运行以下命令来预配 Amplify API 资源。

amplify add api

步骤 2:若要创建 GraphQL API,请在系统提示时输入以下内容:

? Select from one of the below mentioned services: GraphQL
? Here is the GraphQL API that we will create. Select a setting to edit or continue A
uthorization modes: API key (default, expiration time: 7 days from now)
? Choose the default authorization type for the API Amazon Cognito User Pool
Use a Cognito user pool configured as a part of this project.
? Configure additional auth types? No
? Here is the GraphQL API that we will create. Select a setting to edit or continue C
ontinue
? Choose a schema template: Blank Schema
✅ GraphQL schema compiled successfully.

Amplify CLI 将为该 API 添加一个新文件夹,其中包含 schema.graphql 文件,您可以在该文件中定义应用程序模型。

步骤 3:打开 schema.graphql 文件并使用以下代码更新该文件,以定义行程模型。

type Trip @model @auth(rules: [{ allow: owner }]) {
  id: ID!
  tripName: String!
  destination: String!
  startDate: AWSDate!
  endDate: AWSDate!
  tripImageUrl: String
  tripImageKey: String
}

向应用程序中添加行程数据模型

步骤 1:在应用程序的根文件夹中运行以下命令以生成行程模型文件。

amplify codegen models

Amplify CLI 将在 lib/models 文件夹中生成 dart 文件。

步骤 2:运行 amplify push 命令,在云中创建资源。 

步骤 3:按下 Enter 键。Amplify CLI 将部署资源并显示确认消息,如屏幕截图中所示。

步骤 4:打开 main.dart 文件并更新 _configureAmplify() 函数(如代码中所示),以添加 Amplify DataStore 和 API 插件。

Future<void> _configureAmplify() async {
  await Amplify.addPlugins([
    AmplifyAuthCognito(),
    AmplifyAPI(modelProvider: ModelProvider.instance),
  ]);
  await Amplify.configure(amplifyconfig);
}

main.dart 文件现在看上去应当与下面的代码段相似。

import 'package:amplify_api/amplify_api.dart';
import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
import 'package:amplify_flutter/amplify_flutter.dart';
import 'package:amplify_trips_planner/models/ModelProvider.dart';
import 'package:amplify_trips_planner/trips_planner_app.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import 'amplifyconfiguration.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  try {
    await _configureAmplify();
  } on AmplifyAlreadyConfiguredException {
    debugPrint('Amplify configuration failed.');
  }

  runApp(
    const ProviderScope(
      child: TripsPlannerApp(),
    ),
  );
}

Future<void> _configureAmplify() async {
  await Amplify.addPlugins([
    AmplifyAuthCognito(),
    AmplifyAPI(modelProvider: ModelProvider.instance),
  ]);
  await Amplify.configure(amplifyconfig);
}

对行程功能执行 CRUD 操作和工作流

步骤 1:在 lib/features/trip/services 文件夹中新建一个 dart 文件并将其命名为 trips_api_service.dart

步骤 2:打开 trips_datastore_service.dart 文件并使用以下代码段更新该文件,以创建包含如下函数的 TripsAPIService

    • getTrips - 此函数将在 Amplify API 中查询正在进行和即将发生的行程并返回这些行程的列表。
    • getPastTrips - 此函数将在 Amplify API 中查询过去的行程并返回这些行程的列表。
    • getTrip - 此函数将在 Amplify API 中查询特定行程。
    • addTripdeleteTrip updateTrip 分别用于在 Amplify API 中添加、删除和更新行程。
    import 'dart:async';
    
    import 'package:amplify_api/amplify_api.dart';
    import 'package:amplify_flutter/amplify_flutter.dart';
    import 'package:amplify_trips_planner/models/ModelProvider.dart';
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    
    final tripsAPIServiceProvider = Provider<TripsAPIService>((ref) {
      final service = TripsAPIService();
      return service;
    });
    
    class TripsAPIService {
      TripsAPIService();
    
      Future<List<Trip>> getTrips() async {
        try {
          final request = ModelQueries.list(Trip.classType);
          final response = await Amplify.API.query(request: request).response;
    
          final trips = response.data?.items;
          if (trips == null) {
            safePrint('getTrips errors: ${response.errors}');
            return const [];
          }
          trips.sort(
            (a, b) =>
                a!.startDate.getDateTime().compareTo(b!.startDate.getDateTime()),
          );
          return trips
              .map((e) => e as Trip)
              .where(
                (element) => element.endDate.getDateTime().isAfter(DateTime.now()),
              )
              .toList();
        } on Exception catch (error) {
          safePrint('getTrips failed: $error');
    
          return const [];
        }
      }
    
      Future<List<Trip>> getPastTrips() async {
        try {
          final request = ModelQueries.list(Trip.classType);
          final response = await Amplify.API.query(request: request).response;
    
          final trips = response.data?.items;
          if (trips == null) {
            safePrint('getPastTrips errors: ${response.errors}');
            return const [];
          }
          trips.sort(
            (a, b) =>
                a!.startDate.getDateTime().compareTo(b!.startDate.getDateTime()),
          );
          return trips
              .map((e) => e as Trip)
              .where(
                (element) => element.endDate.getDateTime().isBefore(DateTime.now()),
              )
              .toList();
        } on Exception catch (error) {
          safePrint('getPastTrips failed: $error');
    
          return const [];
        }
      }
    
      Future<void> addTrip(Trip trip) async {
        try {
          final request = ModelMutations.create(trip);
          final response = await Amplify.API.mutate(request: request).response;
    
          final createdTrip = response.data;
          if (createdTrip == null) {
            safePrint('addTrip errors: ${response.errors}');
            return;
          }
        } on Exception catch (error) {
          safePrint('addTrip failed: $error');
        }
      }
    
      Future<void> deleteTrip(Trip trip) async {
        try {
          await Amplify.API
              .mutate(
                request: ModelMutations.delete(trip),
              )
              .response;
        } on Exception catch (error) {
          safePrint('deleteTrip failed: $error');
        }
      }
    
      Future<void> updateTrip(Trip updatedTrip) async {
        try {
          await Amplify.API
              .mutate(
                request: ModelMutations.update(updatedTrip),
              )
              .response;
        } on Exception catch (error) {
          safePrint('updateTrip failed: $error');
        }
      }
    
      Future<Trip> getTrip(String tripId) async {
        try {
          final request = ModelQueries.get(
            Trip.classType,
            TripModelIdentifier(id: tripId),
          );
          final response = await Amplify.API.query(request: request).response;
    
          final trip = response.data!;
          return trip;
        } on Exception catch (error) {
          safePrint('getTrip failed: $error');
          rethrow;
        }
      }
    }

    步骤 3:在 lib/features/trip/data 文件夹内部创建一个新的 dart 文件并将其命名为 trips_repository.dart

    步骤 4:打开 trips_repository.dart 文件并使用以下代码更新该文件:

    import 'package:amplify_trips_planner/features/trip/service/trips_api_service.dart';
    import 'package:amplify_trips_planner/models/Trip.dart';
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    
    final tripsRepositoryProvider = Provider<TripsRepository>((ref) {
      final tripsAPIService = ref.read(tripsAPIServiceProvider);
      return TripsRepository(tripsAPIService);
    });
    
    class TripsRepository {
      TripsRepository(this.tripsAPIService);
    
      final TripsAPIService tripsAPIService;
    
      Future<List<Trip>> getTrips() {
        return tripsAPIService.getTrips();
      }
    
      Future<List<Trip>> getPastTrips() {
        return tripsAPIService.getPastTrips();
      }
    
      Future<void> add(Trip trip) async {
        return tripsAPIService.addTrip(trip);
      }
    
      Future<void> update(Trip updatedTrip) async {
        return tripsAPIService.updateTrip(updatedTrip);
      }
    
      Future<void> delete(Trip deletedTrip) async {
        return tripsAPIService.deleteTrip(deletedTrip);
      }
    
      Future<Trip> getTrip(String tripId) async {
        return tripsAPIService.getTrip(tripId);
      }
    }

    步骤 5: lib/feature/trip/controller 文件夹中新建一个 dart 文件并将其命名为 trips_list_controller.dart

    步骤 6:打开 trips_list_controller.dart 文件并使用以下代码更新该文件。该 UI 将使用控制器添加新行程,方法是创建行程条目并将其作为参数传递给 tripsRepository.add(trip) 函数。

     

    注意:VSCode 会因缺少 trips_list_controller.g.dart 文件而报错。
    您将在下一步中修复此错误。

    import 'dart:async';
    
    import 'package:amplify_flutter/amplify_flutter.dart';
    import 'package:amplify_trips_planner/features/trip/data/trips_repository.dart';
    import 'package:amplify_trips_planner/models/ModelProvider.dart';
    import 'package:riverpod_annotation/riverpod_annotation.dart';
    
    part 'trips_list_controller.g.dart';
    
    @riverpod
    class TripsListController extends _$TripsListController {
      Future<List<Trip>> _fetchTrips() async {
        final tripsRepository = ref.read(tripsRepositoryProvider);
        final trips = await tripsRepository.getTrips();
        return trips;
      }
    
      @override
      FutureOr<List<Trip>> build() async {
        return _fetchTrips();
      }
    
      Future<void> addTrip({
        required String name,
        required String destination,
        required String startDate,
        required String endDate,
      }) async {
        final trip = Trip(
          tripName: name,
          destination: destination,
          startDate: TemporalDate(DateTime.parse(startDate)),
          endDate: TemporalDate(DateTime.parse(endDate)),
        );
    
        state = const AsyncValue.loading();
    
        state = await AsyncValue.guard(() async {
          final tripsRepository = ref.read(tripsRepositoryProvider);
          await tripsRepository.add(trip);
          return _fetchTrips();
        });
      }
    
      Future<void> removeTrip(Trip trip) async {
        state = const AsyncValue.loading();
        state = await AsyncValue.guard(() async {
          final tripsRepository = ref.read(tripsRepositoryProvider);
          await tripsRepository.delete(trip);
    
          return _fetchTrips();
        });
      }
    }

    步骤 7:前往应用程序的根文件夹,然后在终端运行以下命令。 

    dart run build_runner build -d

    这将在 lib/feature/trip/controller 文件夹中生成 trips_list_controller.g.dart 文件。

    实现行程列表 UI

    步骤 1:在 lib/common/ui 文件夹中新建一个 dart 文件并将其命名为 bottomsheet_text_form_field.dart

    步骤 2:打开 bottomsheet_text_form_field.dart 文件并使用以下代码段更新该文件,以创建 BottomSheetTextFormField 窗口部件,通过该窗口部件可以构建 TextFormField,该应用程序将在表单中使用 TextFormField 创建新行程。

    import 'package:flutter/material.dart';
    
    class BottomSheetTextFormField extends StatelessWidget {
      const BottomSheetTextFormField({
        required this.labelText,
        required this.controller,
        required this.keyboardType,
        this.onTap,
        super.key,
      });
    
      final String labelText;
      final TextEditingController controller;
      final TextInputType keyboardType;
      final void Function()? onTap;
    
      @override
      Widget build(BuildContext context) {
        return TextFormField(
          controller: controller,
          keyboardType: keyboardType,
          autofocus: true,
          autocorrect: false,
          textInputAction: TextInputAction.next,
          validator: (value) {
            if (value == null || value.isEmpty) {
              return 'Please enter a value';
            }
    
            return null;
          },
          decoration: InputDecoration(
            labelText: labelText,
          ),
          onTap: onTap,
        );
      }
    }

    步骤 3:在 lib/common/utils 文件夹中新建一个 dart 文件并将其命名为 date_time_formatter.dart

    步骤 4:打开 date_time_formatter.dart 文件并使用以下代码更新该文件,以创建 DateTimeFormatter 扩展来格式化 DateTime 值。

    import 'package:intl/intl.dart';
    
    extension DateTimeFormatter on DateTime {
      String format(String format) {
        return DateFormat(format).format(this);
      }
    }

    步骤 5:在 lib/features/trip/ui/trips_list 文件夹中新建一个 dart 文件并将其命名为 add_trip_bottomsheet.dart

    步骤 6:打开 add_trip_bottomsheet.dart 文件并使用以下代码更新该文件。这样,我们就可以向用户提供一张表单,供用户提交创建新行程所需的详细信息。

    现在,我们在表单中使用 BottomSheetTextFormField 窗口部件和 DateTimeFormatter 扩展。

    import 'package:amplify_trips_planner/common/ui/bottomsheet_text_form_field.dart';
    import 'package:amplify_trips_planner/common/utils/date_time_formatter.dart';
    import 'package:amplify_trips_planner/features/trip/controller/trips_list_controller.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    import 'package:go_router/go_router.dart';
    
    class AddTripBottomSheet extends ConsumerStatefulWidget {
      const AddTripBottomSheet({
        super.key,
      });
    
      @override
      AddTripBottomSheetState createState() => AddTripBottomSheetState();
    }
    
    class AddTripBottomSheetState extends ConsumerState<AddTripBottomSheet> {
      final formGlobalKey = GlobalKey<FormState>();
    
      final tripNameController = TextEditingController();
      final destinationController = TextEditingController();
      final startDateController = TextEditingController();
      final endDateController = TextEditingController();
    
      @override
      Widget build(BuildContext context) {
        return Form(
          key: formGlobalKey,
          child: Container(
            padding: EdgeInsets.only(
              top: 15,
              left: 15,
              right: 15,
              bottom: MediaQuery.of(context).viewInsets.bottom + 15,
            ),
            width: double.infinity,
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                BottomSheetTextFormField(
                  labelText: 'Trip Name',
                  controller: tripNameController,
                  keyboardType: TextInputType.name,
                ),
                const SizedBox(
                  height: 20,
                ),
                BottomSheetTextFormField(
                  labelText: 'Trip Destination',
                  controller: destinationController,
                  keyboardType: TextInputType.name,
                ),
                const SizedBox(
                  height: 20,
                ),
                BottomSheetTextFormField(
                  labelText: 'Start Date',
                  controller: startDateController,
                  keyboardType: TextInputType.datetime,
                  onTap: () async {
                    final pickedDate = await showDatePicker(
                      context: context,
                      initialDate: DateTime.now(),
                      firstDate: DateTime(2000),
                      lastDate: DateTime(2101),
                    );
    
                    if (pickedDate != null) {
                      print(pickedDate.format('yyyy-MM-dd'));
                      startDateController.text = pickedDate.format('yyyy-MM-dd');
                      print(startDateController.text);
                    }
                  },
                ),
                const SizedBox(
                  height: 20,
                ),
                BottomSheetTextFormField(
                  labelText: 'End Date',
                  controller: endDateController,
                  keyboardType: TextInputType.datetime,
                  onTap: () async {
                    if (startDateController.text.isNotEmpty) {
                      final pickedDate = await showDatePicker(
                        context: context,
                        initialDate: DateTime.parse(startDateController.text),
                        firstDate: DateTime.parse(startDateController.text),
                        lastDate: DateTime(2101),
                      );
    
                      if (pickedDate != null) {
                        print(pickedDate.format('yyyy-MM-dd'));
                        endDateController.text = pickedDate.format('yyyy-MM-dd');
                      }
                    }
                  },
                ),
                const SizedBox(
                  height: 20,
                ),
                TextButton(
                  child: const Text('OK'),
                  onPressed: () async {
                    final currentState = formGlobalKey.currentState;
                    if (currentState == null) {
                      return;
                    }
                    if (currentState.validate()) {
                      await ref.watch(tripsListControllerProvider.notifier).addTrip(
                            name: tripNameController.text,
                            destination: destinationController.text,
                            startDate: startDateController.text,
                            endDate: endDateController.text,
                          );
    
                      if (context.mounted) {
                        context.pop();
                      }
                    }
                  }, //,
                ),
              ],
            ),
          ),
        );
      }
    }

    步骤 7:lib/features/trip/ui 文件夹中新建一个文件夹并将其命名为 trips_gridview,然后在这个文件夹中创建 trip_gridview_item_card.dart 文件。

    步骤 8:打开 trip_gridview_item_card.dart 文件并使用以下代码更新该文件。这将创建一个 Card 窗口部件,用于在行程列表页面上显示行程详细信息。

    import 'package:amplify_trips_planner/common/utils/colors.dart' as constants;
    import 'package:amplify_trips_planner/common/utils/date_time_formatter.dart';
    import 'package:amplify_trips_planner/models/ModelProvider.dart';
    import 'package:cached_network_image/cached_network_image.dart';
    import 'package:flutter/material.dart';
    
    class TripGridViewItemCard extends StatelessWidget {
      const TripGridViewItemCard({
        required this.trip,
        super.key,
      });
    
      final Trip trip;
    
      @override
      Widget build(BuildContext context) {
        return Card(
          clipBehavior: Clip.antiAlias,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(15),
          ),
          elevation: 5,
          child: Column(
            children: [
              Expanded(
                child: Container(
                  height: 500,
                  alignment: Alignment.center,
                  color: const Color(constants.primaryColorDark),
                  child: Stack(
                    children: [
                      Positioned.fill(
                        child: trip.tripImageUrl != null
                            ? Stack(
                                children: [
                                  const Center(child: CircularProgressIndicator()),
                                  CachedNetworkImage(
                                    errorWidget: (context, url, dynamic error) =>
                                        const Icon(Icons.error_outline_outlined),
                                    imageUrl: trip.tripImageUrl!,
                                    cacheKey: trip.tripImageKey,
                                    width: double.maxFinite,
                                    height: 500,
                                    alignment: Alignment.topCenter,
                                    fit: BoxFit.fill,
                                  ),
                                ],
                              )
                            : Image.asset(
                                'images/amplify.png',
                                fit: BoxFit.contain,
                              ),
                      ),
                      Positioned(
                        bottom: 16,
                        left: 16,
                        right: 16,
                        child: FittedBox(
                          fit: BoxFit.scaleDown,
                          alignment: Alignment.centerLeft,
                          child: Text(
                            trip.destination,
                            style: Theme.of(context)
                                .textTheme
                                .headlineSmall!
                                .copyWith(color: Colors.white),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              Padding(
                padding: const EdgeInsets.fromLTRB(2, 8, 8, 4),
                child: DefaultTextStyle(
                  softWrap: false,
                  overflow: TextOverflow.ellipsis,
                  style: Theme.of(context).textTheme.titleMedium!,
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Padding(
                        padding: const EdgeInsets.only(bottom: 8),
                        child: Text(
                          trip.tripName,
                          style: Theme.of(context)
                              .textTheme
                              .titleMedium!
                              .copyWith(color: Colors.black54),
                        ),
                      ),
                      Text(
                        trip.startDate.getDateTime().format('MMMM dd, yyyy'),
                        style: const TextStyle(fontSize: 12),
                      ),
                      Text(
                        trip.endDate.getDateTime().format('MMMM dd, yyyy'),
                        style: const TextStyle(fontSize: 12),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        );
      }
    }

    步骤 9:在 lib/features/trip/ui/trips_gridview 文件夹中创建 trip_gridview_item.dart 文件。

    步骤 10:打开 trip_gridview_item.dart 文件并使用以下代码更新该文件。该应用程序将使用该文件来显示行程列表网格。

    import 'package:amplify_trips_planner/common/navigation/router/routes.dart';
    import 'package:amplify_trips_planner/features/trip/ui/trips_gridview/trip_gridview_item_card.dart';
    import 'package:amplify_trips_planner/models/ModelProvider.dart';
    import 'package:flutter/material.dart';
    import 'package:go_router/go_router.dart';
    
    class TripGridViewItem extends StatelessWidget {
      const TripGridViewItem({
        required this.trip,
        super.key,
      });
    
      final Trip trip;
    
      @override
      Widget build(BuildContext context) {
        return InkWell(
          splashColor: Theme.of(context).primaryColor,
          borderRadius: BorderRadius.circular(15),
          onTap: () {
            context.goNamed(
              AppRoute.trip.name,
              pathParameters: {'id': trip.id},
              extra: trip,
            );
          },
          child: TripGridViewItemCard(
            trip: trip,
          ),
        );
      }
    }

    步骤 11:在 lib/features/trip/ui/trips_gridview 文件夹中创建 trips_list_gridview.dart 文件。 

    步骤 12:打开 trips_list_gridview.dart 文件并使用以下代码更新该文件。这样网格视图组件会显示行程列表。

    import 'package:amplify_trips_planner/features/trip/ui/trips_gridview/trip_gridview_item.dart';
    import 'package:amplify_trips_planner/models/ModelProvider.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    
    class TripsListGridView extends StatelessWidget {
      const TripsListGridView({
        required this.tripsList,
        super.key,
      });
    
      final AsyncValue<List<Trip>> tripsList;
    
      @override
      Widget build(BuildContext context) {
        switch (tripsList) {
          case AsyncData(:final value):
            return value.isEmpty
                ? const Center(
                    child: Text('No Trips'),
                  )
                : OrientationBuilder(
                    builder: (context, orientation) {
                      return GridView.count(
                        crossAxisCount:
                            (orientation == Orientation.portrait) ? 2 : 3,
                        mainAxisSpacing: 4,
                        crossAxisSpacing: 4,
                        padding: const EdgeInsets.all(4),
                        childAspectRatio:
                            (orientation == Orientation.portrait) ? 0.9 : 1.4,
                        children: value.map((tripData) {
                          return TripGridViewItem(
                            trip: tripData,
                          );
                        }).toList(growable: false),
                      );
                    },
                  );
    
          case AsyncError():
            return const Center(
              child: Text('Error'),
            );
          case AsyncLoading():
            return const Center(
              child: CircularProgressIndicator(),
            );
    
          case _:
            return const Center(
              child: Text('Error'),
            );
        }
      }
    }

    步骤 13:打开 trips_list_page.dart 文件并使用以下代码更新该文件,以使用上面创建的 TripsListGridView 窗口部件显示行程。

    import 'package:amplify_trips_planner/common/utils/colors.dart' as constants;
    import 'package:amplify_trips_planner/features/trip/controller/trips_list_controller.dart';
    import 'package:amplify_trips_planner/features/trip/ui/trips_gridview/trips_list_gridview.dart';
    import 'package:amplify_trips_planner/features/trip/ui/trips_list/add_trip_bottomsheet.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    
    class TripsListPage extends ConsumerWidget {
      const TripsListPage({
        super.key,
      });
    
      Future<void> showAddTripDialog(BuildContext context) =>
          showModalBottomSheet<void>(
            isScrollControlled: true,
            elevation: 5,
            context: context,
            builder: (sheetContext) {
              return const AddTripBottomSheet();
            },
          );
    
      @override
      Widget build(BuildContext context, WidgetRef ref) {
        final tripsListValue = ref.watch(tripsListControllerProvider);
        return Scaffold(
          appBar: AppBar(
            centerTitle: true,
            title: const Text(
              'Amplify Trips Planner',
            ),
            backgroundColor: const Color(constants.primaryColorDark),
          ),
          floatingActionButton: FloatingActionButton(
            onPressed: () {
              showAddTripDialog(context);
            },
            backgroundColor: const Color(constants.primaryColorDark),
            child: const Icon(Icons.add),
          ),
          body: TripsListGridView(
            tripsList: tripsListValue,
          ),
        );
      }
    }

    步骤 14:在模拟器中运行该应用程序并创建一个行程。下面是一个使用 iPhone 模拟器的示例。 

    结果

    在本模块中,您已经使用 Amplify 添加了一个用于行程的 GraphQL API,并在您的应用程序中配置了用来创建、读取、更新和删除行程的功能。

    添加 Amplify 存储

    此页内容对您是否有帮助?