Google フォトと Flutter で写真共有アプリを作成する

作業内容

この Codelab では、ユーザーが写真を共有できる見学旅行アプリ「Field Trippa」を作成します。

Google Photos Library API を使用して独自のアプリでメディア共有機能をサポートする方法について学習します。

この Codelab のアプリは、Flutter を使用して作成されています。Flutter は、1 つのコードベースからネイティブにコンパイルして、モバイル、ウェブ、デスクトップの美しいアプリケーションを作成できる Google の UI ツールキットです。詳細については https://flutter.dev をご覧ください。

6571e359f222ccf6.png

学習内容

  • Google Photos Library API を使用して、メディアをアップロードし、アルバムを共有する方法
  • Flutter で Google ログインを使用する方法
  • Flutter から Google API 呼び出しを行う方法

必要なもの

  • Flutter 開発環境
  • Google フォトにアクセスできる種々のエミュレータまたはデバイスに設定された 2 つの Google ユーザー アカウント(これによりユーザー間の共有をテストします)
  • Android デバイス、エミュレータ、または物理 iOS デバイス(カメラ ハードウェアがないため、iOS シミュレータはサポートされていません)

この Codelab では、Google Photos Library API を使用して、遠足や見学旅行の写真を共有するアプリを作成します。

ユーザーは Google ログインを使用してログインし、アプリに対し Google Photos Library API の使用を許可します。

これで、ユーザーは説明付きの写真をアップロードするための trip を作成できます。各 trip はアプリの他のメンバーと共有できます。また、写真の投稿もできます。

146953eced1f4f92.png

具体的には、各 trip は Google フォト内で共有アルバムとして保存されます。アプリがこのアルバムの共有とアップロードを処理しますが、Google フォトの URL を通じて、アプリを持っていない他の人とアルバムを直接共有することもできます。

c4af82aa4bf8cc31.png

この Codelab で学びたいことは次のどれですか?

このトピックは初めてなので、簡単に概要を知りたい。このトピックについてある程度は知っているが、復習したい。プロジェクトで使用するサンプルコードを探している。特定の項目に関する説明を確認したい。

b2f84ff91b0e1396.png この Codelab のソースコードをダウンロードします。

ソースコードをダウンロード GitHub を表示

(開始用アプリコードは、リポジトリmain ブランチにあります)

ダウンロードした zip ファイルを解凍すると、ルートフォルダ photos-sharing-main が作成されます。これには、開始に必要なすべてのコードとリソースが含まれています。

解凍したフォルダを Flutter IDE(Dart プラグインと Flutter プラグインがインストールされた VSCode や Android Studio など)で開きます。

最終版の実装コード

次のリンクは、完全に実装されたアプリの最終版を指しています。行き詰まった場合や実装を比較する場合にご使用ください。

最終版のソースコードをダウンロード GitHub で最終版のソースコードを表示

(最終版のアプリコードは、リポジトリfinal ブランチにあります)

Flutter で開発をしたことがない場合は、こちらの手順に沿って開発環境を設定してください。

「Field Trippa」アプリを実行するには、開発 IDE で「run」ボタンをクリックするか、ソースコードのルート ディレクトリから次のコマンドを使用します。

flutter run

[Connect with Google Photos] 画面が表示されます。

6bfc7e3fab746b8d.png

Google Photos Library API では、OAuth 2.0 を使用してユーザーを認証する必要があります。ユーザーはアプリにログインし、アプリがユーザーに代わって API を操作することを許可します。

この手順の最後に、その他のトラブルシューティングのヒントを記載しています。

新しい Firebase プロジェクトを作成してアプリを登録する

b2f84ff91b0e1396.png Firebase コンソールに移動し、[+ Add Project] を選択します。プロジェクト名を入力し、[Create Project] を選択して次に進みます。Firebase コンソールで他の手順を行わないでください。代わりに、この Codelab に戻り、下の「Android」または「iOS」の部分に進んでください。

Android のみ: Android でアプリを実行している場合は、Android アプリを登録します。

b2f84ff91b0e1396.png Android アイコンをクリックして Android アプリの登録画面を開きます。

b2f84ff91b0e1396.png [package] に「com.google.codelab.photos.sharing」と入力します。

b2f84ff91b0e1396.png マシンから署名証明書 SHA-1 を取得します。

Windows では、次のコマンドを実行します。

keytool -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore -list -v -storepass android

Mac または Linux では、次のコマンドを実行します。

keytool -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v -storepass android

b2f84ff91b0e1396.png [register app] をクリックして次に進みます。

b2f84ff91b0e1396.png google-service.json ファイルをパソコンにダウンロードし、「android/app/」ディレクトリに移動します(ヒント: Android Studio では、ダウンロードしたファイルをプロジェクト サイドパネルの適切な場所に直接ドラッグできます)。

このファイルには、先ほど設定した Firebase と Google Developers プロジェクトのプロジェクト構成が含まれています。

(詳細についてはパッケージ google_sign_in のドキュメントをご覧ください)

Firebase コンソールで他の手順を行う必要はありません。アプリにはすでに Firebase SDK が追加されています。

iOS のみ: iOS でアプリを実行している場合は、Firebase に iOS アプリを登録します。

b2f84ff91b0e1396.png iOS アイコンをクリックして iOS アプリの登録画面を開きます。

b2f84ff91b0e1396.png [iOS bundle ID] に「com.google.codelab.photos.sharing」と入力します。

b2f84ff91b0e1396.png [next] をクリックして次に進みます。

b2f84ff91b0e1396.png GoogleService-Info.plist ファイルをパソコンにダウンロードします。

b2f84ff91b0e1396.png Xcode で Flutter プロジェクトを開きます。

b2f84ff91b0e1396.png [Runner] ディレクトリを右クリックして [Add Files to Runner] を選択し、ダウンロードした GoogleService-Info.plist ファイルを選択して Runner モジュールに追加します。

b2f84ff91b0e1396.png ios/Runner/Info.plist ファイルのソースコードを編集し、GoogleService-Info.plist ファイルの REVERSED_CLIENT_ID プロパティの値を追加します。ファイルの下部にあるエントリを置き換えます。

ios/Runner/Info.plist

<!-- Google Sign-in Section -->
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>Editor</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>COPY_REVERSED_CLIENT_ID_HERE</string>
    </array>
  </dict>
</array>
<!-- End of the Google Sign-in Section -->

(詳細についてはパッケージ google_sign_in のドキュメントをご覧ください)

Google Photos Library API を有効にする

b2f84ff91b0e1396.png Google Developers Console の API 画面を開き、[Google Photos Library API] を有効にします([有効にする] ボタンが無効になっている場合は、最初に画面上部で Firebase プロジェクトを選択する必要があります)。

b2f84ff91b0e1396.png Google Developers コンソールの OAuth 同意画面の設定を開いて、Google Photos Library API のスコープとメールアドレスを追加します(この設定は、Google Photos Library API で使用されるスコープの OAuth 検証レビューに必要です)。検証のために送信する必要はありませんが、フォームに入力して回答を保存する必要があります。これにより、開発とテストのためのスコープが有効になります。

  • [Application Name] に入力します(Field Trippa Codelab など)。
  • [Support email address] を選択します。
  • [Add scope]、次に [manually add paste scopes] を選択して、次のスコープを入力します。
https://www.googleapis.com/auth/photoslibrary
https://www.googleapis.com/auth/photoslibrary.sharing
  • [Save] を選択します。
  • この Codelab を続行するために、検証用の送信をする必要はありません。これはアプリをリリースする場合にのみ必要です。個人的なテストでは必要ありません。

アプリを実行してログインする

Google ログインは、すでに google_sign_in Flutter パッケージを使用して実装されています。このパッケージには、プロジェクトにコピー済みの google-services.json または GoogleService-Info.plist ファイルが必要です。

b2f84ff91b0e1396.png アプリをもう一度実行し、[Connect to Google Photos] を選択します。ユーザー アカウントを選択し、認証スコープを受け入れるよう求めるプロンプトが表示されます。

すべてが正常に設定されると、次の画面に空のリストが表示されます。

9f3bcae1f8e7cd0d.png

ログインのトラブルシューティング

アプリへのログインに問題がある場合は、次の点をご確認ください。

b2f84ff91b0e1396.png デバイスがインターネットに接続されていることを確認します。

b2f84ff91b0e1396.png PlatformException(sign_in_failed, com.google.android.gms.common.api.ApiException: 10: , null) というエラーが表示された場合は、「Google Photos Library API を有効にする」の手順をすべて行っていることを確認します。Google Photos Library API のスコープを追加し、サポート用メールアドレスを入力して [save] を選択する必要があります。

b2f84ff91b0e1396.png r PlatformException(sign_in_failed, com.google.android.gms.common.api.ApiException: 12500: , null) というエラーが表示された場合は、Firebase コンソールでサポート用メールアドレスを追加したことを確認します。Firebase コンソールを開き、プロジェクト タイトルの横にある歯車アイコンを選択して [プロジェクトの設定] に移動します。[全般設定] 画面の [サポートメール] でメールアドレスを選択します。

b2f84ff91b0e1396.png Firebase コンソールで設定した署名証明書 SHA-1 を確認します。最初のステップの、keytool コマンドの出力と一致しますか?android/ プロジェクトで実行したときのコマンド ./gradlew signingReport の出力と一致しますか?コンソールで、署名証明書 SHA-256 も含める必要があります。

b2f84ff91b0e1396.png Firebase コンソールで設定したパッケージ名iOS バンドル ID を確認します。com.google.codelab.photos.sharing に設定する必要があります。

b2f84ff91b0e1396.png Firebase コンソールからダウンロードした構成ファイルの場所を確認します。Android の場合、ファイルを android/app/google-service.json にコピーする必要があります。iOS の場合、Runner モジュールに追加する必要があります。

b2f84ff91b0e1396.png Firebase プロジェクトのログイン プロバイダとして Google を有効にする必要があります。Firebase コンソールを開き、[Authentication]、[Sign-in method] に移動します。Google が有効になっていることを確認します。

Google Photos Library API への最初の API 呼び出しを実装する前に、「Field Trippa」アプリが使用するデータ アーキテクチャについて説明します。

アプリ アーキテクチャ

  • 各画面は個別のページとして実装されます。71b9a588fb1bbb41.png
  • PhotosLibraryApiModel はアプリのデータモデルを記述し、Google Photos Library API の呼び出しを抽象化します。
  • Library API への HTTPS REST 呼び出しは、PhotosLibraryApiClient に実装されています。このクラスで提供される各呼び出しは、呼び出しのパラメータとオプションを指定する *Request オブジェクトを取ります。
  • Library API には、OAuth2 によるユーザー認証が必要です。すべての API 呼び出しに含める必要があるアクセス トークンは、PhotosLibraryApiClient の google_sign_in パッケージによって直接設定されます。

アルバム作成の API 呼び出しを実装する

各旅行は Google フォトにアルバムとして保存されます。[CREATE A TRIP ALBUM] ボタンを選択した場合、旅行の名前をユーザーに尋ね、その名前をタイトルにして新しいアルバムを作成する必要があります。

b2f84ff91b0e1396.png create_trip_page.dart で、アルバムを作成するために Library API に対するリクエストを行うロジックを記述します。ファイルの末尾に _createTrip(...) メソッドを実装し、ユーザーが入力した旅行の名前を使用して PhotosLibraryApiModel を呼び出します。

lib/pages/create_trip_page.dart

Future<void> _createTrip(BuildContext context) async {
  // Display the loading indicator.
  setState(() => _isLoading = true);

  await ScopedModel.of<PhotosLibraryApiModel>(context)
      .createAlbum(tripNameFormController.text);

  // Hide the loading indicator.
  setState(() => _isLoading = false);
  Navigator.pop(context);
}

b2f84ff91b0e1396.png アルバムを作成する Library API の呼び出しを実装します。API モデルで、アルバムのタイトルをパラメータとして取る createAlbum(...) メソッドを実装します。実際の REST 呼び出しが行われる PhotosLibraryApiClient を呼び出します。

lib/model/photos_library_api_model.dart

Future<Album> createAlbum(String title) async {
  final album = await client.createAlbum(CreateAlbumRequest.fromTitle(title));
  updateAlbums();
  return album;
}

b2f84ff91b0e1396.png photos_library_api_client.dart で、REST 呼び出しを実装してアルバムを作成します。CreateAlbumRequest には、この呼び出しに必要な title プロパティがすでに含まれていることに注意してください。

以下は、JSON としてエンコードし、認証ヘッダーを追加してリクエストを承認します。最後に、API によって作成されたアルバムを返します。

lib/photos_library_api/photos_library_api_client.dart

Future<Album> createAlbum(CreateAlbumRequest request) async {
  final response = await http.post(
    Uri.parse('https://photoslibrary.googleapis.com/v1/albums'),
    body: jsonEncode(request),
    headers: await _authHeaders,
  );

  printError(response);

  return Album.fromJson(jsonDecode(response.body));
}

試してみる

b2f84ff91b0e1396.png アプリをデプロイし、[+ Create Trip] を選択します。

2f0bec785bec1710.gif

旅行リストに、アプリで作成したものではない、Google フォトの他のアルバムが表示されることにお気づきでしょうか(Google フォトに他のアルバムがない場合、この動作を確認するには、Google フォト アプリを開いてアルバムを作成します。ただし、この Codelab を続けるために必要なわけではありません)。

各旅行は Google フォトにアルバムとして保存される点にご注意ください。ただし、他の方法で作成された Google フォトのアルバムを表示する意味はありません - Field Trippa にはアプリが作成した旅行のみが表示されます。

API を使用して、表示される旅行のリストを、アプリで作成されたもののみを表示するように制限できます。

b2f84ff91b0e1396.png photos_library_api_client.dartlistAlbums() メソッド(listSharedAlbums() ではありません)を変更します。このメソッドは、アルバムのリストを取得する REST 呼び出しを行います。パラメータ excludeNonAppCreatedData=true を追加して、このアプリで作成したものではないアルバムを除外するよう、返されるデータを制限します。

lib/photos_library_api/photos_library_api_client.dart

Future<ListAlbumsResponse> listAlbums() async {
  final response = await http.get(
        Uri.parse('https://photoslibrary.googleapis.com/v1/albums?'
            'pageSize=50&excludeNonAppCreatedData=true'),
        headers: await _authHeaders);

       ...
}

試してみる

最初のページに、アプリが作成した旅行のみが表示されるようになりました。

c7c20b76dcbfbfea.png

次のステップとして、旅行に写真をアップロードします。データはユーザーの Google フォト アカウントに保存されるため、自分でデータの保存や処理を行う必要はありません。

Flutter で写真を撮る

b2f84ff91b0e1396.png まず、写真投稿ダイアログにメソッド _getImage(...) を実装します。このメソッドは、ユーザーが [+ADD PHOTO] ボタンをクリックすると呼び出されます。

次のコードでは、image_picker パッケージを使用して写真を撮影し、UI を更新し、API モデルを呼び出して画像をアップロードします(次のステップで実装します)。_getImage(...) 呼び出しでは、後ほど Google フォトで写真を作成するために必要となるアップロード トークンが格納されます。

lib/components/contribute_photo_dialog.dart

Future _getImage(BuildContext context) async {
  // Use the image_picker package to prompt the user for a photo from their
  // device.
  final pickedImage = await _imagePicker
      .getImage(
        source: ImageSource.camera,
      );
  final pickedFile = File(pickedImage.path);

  // Store the image that was selected.
  setState(() {
    _image = pickedFile;
    _isUploading = true;
  });

  // Make a request to upload the image to Google Photos once it was selected.
  final uploadToken = await ScopedModel.of<PhotosLibraryApiModel>(context)
      .uploadMediaItem(pickedFile);

  setState(() {
    // Once the upload process has completed, store the upload token.
    // This token is used together with the description to create the media
    // item later.
    _uploadToken = uploadToken;
    _isUploading = false;
  });
}

画像をアップロードする Library API 呼び出しを実装し、アップロード トークンを取得する

Library API への写真と動画のアップロードは、次の 2 つのステップで行います。

  1. メディアバイトをアップロードしてアップロード トークンを受け取ります
  2. アップロード トークンからユーザーのライブラリにメディア アイテムを作成します

b2f84ff91b0e1396.png メディアをアップロードする REST リクエストを実装します。アップロード リクエストのタイプとファイル名を指定するために、ヘッダーをいくつか設定する必要があります。photos_library_api_client.dart ファイルで、ファイルがアップロードされるメソッド uploadMediaItem(...) を実装し、HTTP 呼び出しが返すアップロード トークンを返します。

lib/photos_library_api/photos_library_api_client.dart

Future<String> uploadMediaItem(File image) async {
  // Get the filename of the image
  final filename = path.basename(image.path);

  // Set up the headers required for this request.
  final headers = <String, String>{};
  headers.addAll(await _authHeaders);
  headers['Content-type'] = 'application/octet-stream';
  headers['X-Goog-Upload-Protocol'] = 'raw';
  headers['X-Goog-Upload-File-Name'] = filename;

  // Make the HTTP request to upload the image. The file is sent in the body.
  final response = await http.post(
    Uri.parse('https://photoslibrary.googleapis.com/v1/uploads'),
    body: image.readAsBytesSync(),
    headers: await _authHeaders,
  );

  printError(response);

  return response.body;
}

アップロード トークンからメディア アイテムを作成する

次に、アップロード トークンからユーザーのライブラリにメディア アイテムを作成するよう実装します。

メディア アイテムを作成するには、アップロード トークン、任意の説明(写真や動画のキャプションなど、省略可)、アルバムの任意の ID が必要です。Field Trippa は常に、アップロードされた写真を旅行アルバムに直接追加します。

b2f84ff91b0e1396.png アップロード トークン、説明、アルバム ID を使用して、mediaItems.batchCreate を呼び出す photos_library_api_client の呼び出しを実装します。API モデルで、Library API を呼び出すメソッド createMediaItem(...) を実装します。このメソッドはメディア アイテムを返します。

(この呼び出しの photos_library_client は実装済みです)

lib/model/photos_library_api_model.dart

Future<BatchCreateMediaItemsResponse> createMediaItem(
    String uploadToken, String albumId, String description) async {
  // Construct the request with the token, albumId and description.
  final request =
      BatchCreateMediaItemsRequest.inAlbum(uploadToken, albumId, description);

  // Make the API call to create the media item. The response contains a
  // media item.
  final response = await client.batchCreateMediaItems(request);

  // Print and return the response.
  print(response.newMediaItemResults[0].toJson());
  return response;
}

試してみる

b2f84ff91b0e1396.png アプリを開き、旅行を選択します。[contribute] をクリックし、以前に撮影した写真を選択します。説明を入力して [upload] を選択します。画像は数秒で旅行に表示されます。

b2f84ff91b0e1396.png Google フォト アプリでアルバムを開くと、この旅行のアルバムに新しい画像が表示されます。

526ede994fcd5d8d.gif

これまでに、旅行を作成し、説明付きで写真をアップロードする機能を実装しました。バックエンドでは、各旅行が Google フォトのアルバムとして保存されます。

次に、アプリを使用していない他の人と旅行を共有します。

各旅行は Google フォトのアルバムに基づいているため、URL でアルバムを「共有」すると、その URL を知っている人であれば誰でも利用できるようになります。

アルバムを共有する呼び出しを実装する

アルバムの上部にある共有ボタンを押すと、旅行ページからアルバムを共有できます。

b2f84ff91b0e1396.pngモデルを呼び出してアルバムを共有し、表示されたアルバムを再読み込みする、非同期呼び出し _shareAlbum(...) を実装します。アルバムを再読み込みすると、shareInfo プロパティが反映されます。これには、後でダイアログに表示する shareableUrl が含まれます。

lib/pages/trip_page.dart

Future<void> _shareAlbum(BuildContext context) async {
  // Show the loading indicator
  setState(() => _inSharingApiCall = true);

  const snackBar = SnackBar(
    duration: Duration(seconds: 3),
    content: Text('Sharing Album...'),
  );
  Scaffold.of(context).showSnackBar(snackBar);

  // Share the album and update the local model
  await ScopedModel.of<PhotosLibraryApiModel>(context).shareAlbum(album.id);
  final updatedAlbum =
      await ScopedModel.of<PhotosLibraryApiModel>(context).getAlbum(album.id);

  print('Album has been shared.');
  setState(() {
    album = updatedAlbum;
    // Hide the loading indicator
    _inSharingApiCall = false;
  });
}

b2f84ff91b0e1396.png ユーザーがページ上部の [SHARE WITH ANYONE] ボタンをクリックしたときに呼び出されるメソッド _showShareableUrl(...) を実装します。まず、アルバムが共有済みであるかどうかを確認します。共有されていたら、_showUrlDialog(...) メソッドを呼び出します。

lib/pages/trip_page.dart

Future<void> _showShareableUrl(BuildContext context) async {
  if (album.shareInfo == null || album.shareInfo.shareableUrl == null) {
    print('Not shared, sharing album first.');
    // Album is not shared yet, share it first, then display dialog
    await _shareAlbum(context);
    _showUrlDialog(context);
  } else {
    // Album is already shared, display dialog with URL
    _showUrlDialog(context);
  }
}

b2f84ff91b0e1396.png ダイアログに shareableUrl を表示するメソッド _showUrlDialog(...) を実装します。

lib/pages/trip_page.dart

void _showUrlDialog(BuildContext context) {
  print('This is the shareableUrl:\n${album.shareInfo.shareableUrl}');

  _showShareDialog(
      context,
      'Share this URL with anyone. '
      'Anyone with this URL can access all items.',
      album.shareInfo.shareableUrl);
}

試してみる

アプリは、まだ共有されていない旅行のみをメイン画面に表示します。これは次のステップで実装します。現時点では、この画面から移動すると新しい旅行を簡単に作成できます。

b2f84ff91b0e1396.png アプリを開き、旅行を選択します。画面上部の [SHARE WITH ANYONE] を選択し、返された URL をブラウザで開きます(ヒント: URL はログにも出力されるため、パソコンに簡単にコピーできます。Android Studio では、[Run] タブにログが表示されます)。

1d1a40c1078e4221.gif

Google フォトでは、URL にアクセスできる人なら誰でもアクセスできる URL で、アルバムを共有できます。Library API を使用すると、共有トークンを介してアルバムを共有することもできます。共有トークンは、API を介してユーザーを共有アルバムに参加させるためにアプリ内で使用される文字列です。

Library API を介してアプリでアルバムを共有するプロセスは次のようになります。

  1. ユーザー A がアプリにログインし、Library API を承認します
  2. アルバムを作成します
  3. アルバムの ID を使用してアルバムを共有します
  4. 共有トークンを別のユーザーに転送します

参加プロセスも同様です。

  1. ユーザー B がアプリにログインし、Library API を承認します
  2. ユーザーが参加するアルバムの共有トークンを取得します
  3. 共有トークンを使用してアルバムに参加します

共有アルバムは Google フォトの [共有] タブに表示されます。

共有トークンを表示する

前のステップで、アルバムを共有するメソッド _shareAlbum(...) をすでに実装しました。shareInfo プロパティには、画面に表示される共有トークンも含まれます。

b2f84ff91b0e1396.png 旅行ページでユーザーが [SHARE WITH FIELD TRIPPA] ボタンを押したときに呼び出される _showShareToken(...) メソッドを実装します。

lib/pages/trip_page.dart

Future<void> _showShareToken(BuildContext context) async {
  if (album.shareInfo == null) {
    print('Not shared, sharing album first.');
    // Album is not shared yet, share it first, then display dialog
    await _shareAlbum(context);
    _showTokenDialog(context);
  } else {
    // Album is already shared, display dialog with token
    _showTokenDialog(context);
  }
}

次に、メソッド _showTokenDialog(...) に「共有トークン」の表示を実装します。共有トークンはアルバムの shareInfo プロパティの一部です。

lib/pages/trip_page.dart

void _showTokenDialog(BuildContext context) {
  print('This is the shareToken:\n${album.shareInfo.shareToken}');
  _showShareDialog(
      context, 'Use this token to share', album.shareInfo.shareToken);
}

共有アルバムをリストする

現在のところ、アプリにはユーザーが所有するアルバムのみがリストされ、共有アルバムはリストされません。

Google フォト アプリ内の [アルバム] 画面には、ユーザーが作成したか、明示的に Google フォト ライブラリに追加したアルバムのみが表示されます。Library API で albums.list を呼び出すと、これらのアルバムのみが返されます。しかし、このアプリでは、ユーザーは他のユーザーの共有アルバムに参加できます(共有アルバムをリストする呼び出しでのみ返されます)。所有アルバムと共有アルバムの両方が含まれるように、Library API から旅行(アルバム)のリストを取得する方法を変更する必要があります。

b2f84ff91b0e1396.png アルバムは API モデルで読み込まれ、キャッシュに保存されます。アルバムと共有アルバムを読み込んでから 1 つのリストとして保存するように、モデル内の updateAlbums() の実装を変更します。

この実装では、複数の Future を使用してアルバムを非同期にリストし、キャッシュに保存されたアルバムのリストにまとめます。古い実装を削除し、新しいコードをコメントアウトします。

lib/model/photos_library_api_model.dart

void updateAlbums() async {
  // Reset the flag before loading new albums
  hasAlbums = false;
  // Clear all albums
  _albums.clear();
  // Skip if not signed in
  if (!isLoggedIn()) {
    return;
  }
  // Add albums from the user's Google Photos account
  // var ownedAlbums = await _loadAlbums();
  // if (ownedAlbums != null) {
  //   _albums.addAll(ownedAlbums);
  // }

  // Load albums from owned and shared albums
  final list = await Future.wait([_loadSharedAlbums(), _loadAlbums()]);

  _albums.addAll(list.expand((a) => a ?? []));

  notifyListeners();
  hasAlbums = true;
}

共有アルバムに参加する

共有トークンを使用して、アプリのユーザーをアルバムに参加させることができます。この Codelab では、シンプルなテキスト ダイアログで行います。

b2f84ff91b0e1396.png ユーザーが入力した共有トークンを使用して API モデルを呼び出す旅行参加ページに _joinTrip メソッドを実装します。まず読み込みインジケーターを表示し、次にテキスト フォームの入力を使用して共有アルバムに参加します。その後、読み込みインジケーターを非表示にして、前の画面に戻ります。

lib/pages/join_trip_page.dart

Future<void> _joinTrip(BuildContext context) async {
  // Show loading indicator
  setState(() => _isLoading = true);

  // Call the API to join an album with the entered share token
  await ScopedModel.of<PhotosLibraryApiModel>(context)
      .joinSharedAlbum(shareTokenFormController.text);

  // Hide loading indicator
  setState(() => _isLoading = false);

  // Return to the previous screen
  Navigator.pop(context);
}

試してみる

Codelab のこの部分を試すには、別のユーザー アカウントによる 2 つ目のデバイスまたはエミュレータが必要です。

b2f84ff91b0e1396.png あるユーザーで旅行を作成して共有したら、[SHARE IN FIELD TRIPPA] オプションを選択して共有トークンを取得します。この共有トークンを他のデバイスまたはエミュレータにコピーし、ホームページの [JOIN A TRIP ALBUM] オプションを介して入力します(ヒント: エミュレータとホスト コンピュータの間でクリップボードが共有されます)。

8043086cc00eaa16.gif 55c1e75014d4d2a4.gif

実際の実装のヒント

Codelab ではなく実際のアプリに共有を実装する場合は、共有トークンを使用してユーザーをアルバムに参加させる方法を慎重に検討する必要があります。安全なバックエンドに保存し、ユーザー間の関係を使用してアルバムを作成し、参加することをご検討ください。

たとえばサッカークラブ交流アプリは、予定された特定のイベントの参加者を追跡し、参加者に促した後にのみアルバムに参加させることができます。

ユーザーの Google フォト アカウントに変更を加える前に、ユーザーに通知し、同意を求めることが重要です。詳細については、Google Photos Library API の UX ガイドラインをご覧ください。

作業した内容

  • Google フォトに基づく共有機能をアプリに実装
  • Google Photos Library API を使用した、インフラやストレージを気にせず写真や動画を共有できる独自の機能の作成
  • 興味深い斬新な方法で API の一部である共有機能を使用し、コンテンツをユーザーに直接共有
  • Library API の主な機能の一部を使用
  • 新しいアルバムの作成と新しい写真のアップロード
  • 共有アルバムのリスト(アプリで作成されたアルバムに限定)

次のステップ

メディアの共有や Library API のその他の部分について詳しくは、Google フォト API に関するデベロッパー向けドキュメント(https://developers.google.com/photos)をご覧ください。たとえば、機械学習によるスマート コンテンツ フィルタでは、適切な写真と動画を見つけることができます。

統合をリリースする準備ができたら、Google フォト パートナー プログラムに参加してください。

UX ガイドライン技術的なベスト プラクティスを必ずご確認ください。一部の言語では、便利なクライアント ライブラリもご利用いただけます。