Criar um app de compartilhamento de fotos com o Google Fotos e o Flutter

O que você criará

Neste codelab, você criará um app de excursão, Field Trippa, que possibilita que os usuários compartilhem fotos.

Aprenda a usar a API Google Photos Library para ter uma experiência de compartilhamento de mídia no seu aplicativo.

O app deste codelab foi criado usando o Flutter, kit de ferramentas de IU do Google voltado para a criação de apps nativos incríveis para dispositivos móveis, Web e computadores com uma única base de código. Saiba mais em https://flutter.dev (link em inglês).

6571e359f222ccf6.png

O que você aprenderá

Pré-requisitos

  • Ambiente para desenvolvedores do Flutter.
  • Duas contas de usuário do Google configuradas em diferentes emuladores ou dispositivos com acesso ao Google Fotos para testar o compartilhamento entre usuários.
  • Um dispositivo Android, emulador ou dispositivo iOS físico: o simulador iOS não é compatível devido à ausência de hardware de câmera.

Neste codelab, você criará um app para compartilhar fotos de excursões ou viagens de estudo usando a API Google Photos Library.

O usuário entra usando o Login do Google e autoriza o aplicativo a usar a API Google Photos Library.

Em seguida, o usuário pode criar uma trip para fazer upload de fotos com uma descrição. Cada trip pode ser compartilhada com outros membros do aplicativo, que também podem contribuir com fotos.

146953eced1f4f92.png

Internamente, cada trip é armazenada como um álbum compartilhado no Google Fotos. O app gerencia o compartilhamento e o upload desse álbum. Também é possível compartilhá-lo diretamente com outras pessoas que não tenham acesso ao app usando um URL para o Google Fotos.

c4af82aa4bf8cc31.png

O que você quer aprender neste codelab?

Ainda não conheço bem o assunto e quero ter uma boa visão geral. Conheço um pouco sobre esse assunto, mas quero me atualizar. Estou procurando exemplos de código para usar no meu projeto. Estou procurando uma explicação de algo específico.

b2f84ff91b0e1396.pngFaça o download do código-fonte para este codelab:

Fazer o download do código-fonte Procurar no GitHub

O código inicial do app está disponível na ramificação main do repositório.

Descompacte o arquivo ZIP transferido por download. A pasta raiz photos-sharing-main é criada, com todo o código e os recursos necessários para começar.

Abra a pasta extraída no seu ambiente de desenvolvimento integrado do Flutter preferido, como o VSCode ou o Android Studio, com os plug-ins Dart e Flutter instalados.

Código de implementação final

O link a seguir direciona para a versão final do aplicativo que está totalmente implementada. Use essa opção se tiver problemas ou para compará-la com sua implementação:

Fazer o download do código-fonte final Procurar o código-fonte final no GitHub

O código final do app está disponível na ramificação final do repositório.

Siga estas etapas para configurar seu ambiente de desenvolvimento (link em inglês), se você ainda não desenvolveu com o Flutter.

Para executar o app "Field Trippa", clique no botão "executar" no seu ambiente de desenvolvimento integrado ou use o seguinte comando no diretório raiz do código-fonte:

flutter run

Você verá a tela "Connect with Google Photos":

6bfc7e3fab746b8d.png

A API Google Photos Library exige a autenticação dos usuários com o OAuth 2.0. Eles fazem login no aplicativo, que autoriza a interação com a API em nome deles.

Você pode encontrar mais algumas dicas para solução de problemas no final desta etapa.

Criar um novo projeto do Firebase e registrar seu app

b2f84ff91b0e1396.png Acesse o Console do Firebase e selecione "+ Adicionar projeto". Insira um nome para o projeto e selecione "Criar projeto" para continuar. Não faça mais nada no Console do Firebase. Em vez disso, volte a este codelab e continue para as seções "Android" ou "iOS" abaixo para configurar o aplicativo.

Apenas para Android: registre um app Android se você estiver executando nessa plataforma:

b2f84ff91b0e1396.png Clique no ícone do Android para abrir a tela de registro do app Android.

b2f84ff91b0e1396.png No pacote, insira: com.google.codelab.photos.sharing

b2f84ff91b0e1396.png Recupere o certificado de assinatura SHA-1 da sua máquina:

No Windows, execute o seguinte comando:

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

No Mac ou no Linux, execute o seguinte comando:

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

b2f84ff91b0e1396.png Clique em "registrar app" para continuar.

b2f84ff91b0e1396.png Faça o download do arquivo google-service.json no computador e mova-o para o diretório "android/app/". Dica: no Android Studio, você pode arrastar o arquivo transferido por download diretamente para o local correto no painel lateral Project.

Esse arquivo contém as configurações dos projetos do Firebase e do Google Developers que você acabou de definir.

Consulte a documentação do pacote google_sign_in (link em inglês) para mais detalhes.

Você não precisa realizar outras etapas no Console do Firebase. O SDK do Firebase já foi adicionado ao aplicativo.

Apenas para iOS: registre um app iOS no Firebase se você estiver executando nessa plataforma:

b2f84ff91b0e1396.png Clique no ícone do iOS para abrir a tela de registro do app iOS.

b2f84ff91b0e1396.png Em ID do pacote iOS, insira: com.google.codelab.photos.sharing

b2f84ff91b0e1396.png Clique em "próxima" para continuar.

b2f84ff91b0e1396.png Faça o download do arquivo GoogleService-Info.plist no computador.

b2f84ff91b0e1396.png Abra o projeto do Flutter no Xcode.

b2f84ff91b0e1396.pngClique com o botão direito no diretório Runner selecione "Adicionar arquivos ao Runner" e selecione oGoogleService-Info.plist arquivo transferido por download para adicioná-lo ao módulo Runner.

b2f84ff91b0e1396.png Edite o código-fonte do arquivo ios/Runner/Info.plist e adicione o valor da propriedade REVERSED_CLIENT_ID do arquivo GoogleService-Info.plist. Substitua a entrada na parte inferior do arquivo:

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 -->

Consulte a documentação do pacote google_sign_in (link em inglês) para ter mais detalhes.

Ativar a API Google Photos Library

b2f84ff91b0e1396.png Abra a tela da API no Google Developers Console e ative a API Google Photos Library. Se o botão "Ativar" estiver desativado, talvez você precise selecionar o projeto do Firebase na parte superior da tela.

b2f84ff91b0e1396.png Abra a configuração da tela de consentimento OAuth no Google Developers Console para adicionar os escopos da API Google Photos Library e seu endereço de e-mail. Essa configuração é necessária para a análise de verificação OAuth de todos os escopos usados pela API Google Photos Library. Você não precisa enviar para verificação, mas é necessário preencher o formulário e salvar a resposta. Isso ativará os escopos para desenvolvimento e teste.

  • Insira o "Nome do aplicativo", por exemplo: Field Trippa Codelab.
  • Selecione um "Endereço de e-mail de suporte".
  • Selecione "Adicionar escopo" e "adicionar manualmente escopos por colagem" para inserir os seguintes escopos:
https://www.googleapis.com/auth/photoslibrary
https://www.googleapis.com/auth/photoslibrary.sharing
  • Selecione "Salvar".
  • Não é necessário enviar para verificação para continuar com este codelab. Ela só é necessária para o lançamento do aplicativo, não para testes pessoais.

Executar o app e fazer login

O Login do Google já foi implementado usando o pacote google_sign_in do Flutter (link em inglês). Esse pacote exige os arquivos google-services.json ou GoogleService-Info.plist que você já copiou para o projeto.

b2f84ff91b0e1396.png Abra o aplicativo novamente e selecione "Connect to Google Photos". Você precisará selecionar uma conta de usuário e aceitar os escopos de autenticação.

Se tudo for configurado com sucesso, você verá uma lista vazia na próxima tela.

9f3bcae1f8e7cd0d.png

Solucionar problemas de login

Se você está com problemas para fazer login no aplicativo, há alguns itens que você pode conferir:

b2f84ff91b0e1396.png Confira se o dispositivo tem conectividade com a Internet.

b2f84ff91b0e1396.png Se você receber o erro PlatformException(sign_in_failed, com.google.android.gms.common.api.ApiException: 10: , null), veja se você seguiu todas as etapas da seção Ativar a API Google Photos Library. É necessário adicionar os escopos da API Google Photos Library, inserir um endereço de e-mail de suporte e selecionar salvar.

b2f84ff91b0e1396.png Se você receber o erro r PlatformException(sign_in_failed, com.google.android.gms.common.api.ApiException: 12500: , null), confira se adicionou um endereço de e-mail de suporte no Console do Firebase. Abra o Console do Firebase e navegue até as configurações do projeto selecionando o ícone de engrenagem ao lado do título do projeto. Selecione um endereço de e-mail em e-mail de suporte na tela configurações gerais.

b2f84ff91b0e1396.png Confira o certificado SHA-1 de assinatura que está configurado no Console do Firebase. Ele corresponde à saída do comando keytool da primeira etapa? Ele corresponde à saída do comando ./gradlew signingReport quando executado no projeto android/? Talvez também seja necessário incluir o certificado de assinatura SHA-256 no console.

b2f84ff91b0e1396.png Verifique o nome do pacote e o ID do pacote iOS configurados no Console do Firebase. Eles precisam estar definidos como com.google.codelab.photos.sharing.

b2f84ff91b0e1396.png Consulte o local dos arquivos de configuração transferidos por download do Console do Firebase. No Android, o arquivo precisa ser copiado para android/app/google-service.json. No iOS, ele precisa ser adicionado ao módulo Runner.

b2f84ff91b0e1396.png Talvez seja necessário ativar o Google como provedor de login do seu projeto do Firebase. Abra o Console do Firebase e navegue até Autenticação e Método de login. O Google precisa estar ativado.

Antes de implementar a primeira chamada à API Google Photos Library, vamos analisar a arquitetura de dados que o app "Field Trippa" usa.

Arquitetura do app

  • Cada tela é implementada como uma página separada: 71b9a588fb1bbb41.png
  • O PhotosLibraryApiModel descreve o modelo de dados do aplicativo e abstrai as chamadas da API Google Photos Library.
  • As chamadas HTTPS REST para a API Library são implementadas no PhotosLibraryApiClient. Cada chamada fornecida por essa classe usa um objeto *Request, em que você especifica parâmetros e opções para uma chamada.
  • A API Library exige autenticação do usuário pelo OAuth2. O token de acesso que precisa estar incluso em todas as chamadas de API é definido diretamente pelo pacote google_sign_in no PhotosLibraryApiClient.

Implementar a chamada de API para criação de álbuns

Cada viagem é armazenada como um álbum no Google Fotos. Ao selecionar o botão "CREATE A TRIP ALBUM", é preciso pedir ao usuário o nome da viagem e criar um novo álbum com ele como título.

b2f84ff91b0e1396.png Em create_trip_page.dart, escreva a lógica que faz uma solicitação à API Library para criar o álbum. Implemente o método _createTrip(...) no final do arquivo para chamar o PhotosLibraryApiModel com o nome da viagem inserida pelo usuário.

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 Implemente a chamada para a API Library que cria o álbum. No modelo da API, implemente o método createAlbum(...) que usa o título do álbum como um parâmetro. Ele faz uma chamada para o PhotosLibraryApiClient em que a chamada REST é feita.

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 Implemente a chamada REST para criar o álbum em photos_library_api_client.dart. O CreateAlbumRequest já contém a propriedade title obrigatória para essa chamada.

O código a seguir a codifica como JSON e adiciona os cabeçalhos de autenticação para autorizar a solicitação. Por fim, retorne o álbum criado pela 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));
}

Experimente!

b2f84ff91b0e1396.png Implante o app e selecione "+ Create Trip".

2f0bec785bec1710.gif

Talvez você tenha notado que a lista de viagens mostra outros álbuns do Google Fotos que não foram criados pelo seu app. Se você não tem outros álbuns e quer ver esse comportamento, abra o app Google Fotos e crie um álbum. No entanto, isso não é obrigatório para continuar neste codelab.

Lembre-se de que cada viagem é armazenada como um álbum no Google Fotos. No entanto, não faz sentido mostrar outros álbuns do Google Fotos que foram criados por outros meios. O Field Trippa só mostra as viagens criadas pelo app.

Você pode usar a API para restringir a lista de viagens exibidas para mostrar apenas as criadas pelo app.

b2f84ff91b0e1396.pngModifique o método listAlbums() (NÃO o listSharedAlbums()) em photos_library_api_client.dart. Esse método faz a chamada REST para recuperar uma lista de álbuns. Adicione o parâmetro excludeNonAppCreatedData=true que restringe os dados retornados para excluir os álbuns que não foram criados por este app.

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

       ...
}

Experimente!

Agora, a primeira página só mostra as viagens que foram criadas pelo app.

c7c20b76dcbfbfea.png

A próxima etapa é fazer upload de fotos para uma viagem. Os dados são armazenados na conta do Google Fotos do usuário. Portanto, você não precisa se preocupar com o armazenamento ou o processamento dos dados.

Tirar uma foto no Flutter

b2f84ff91b0e1396.png Primeiro, implemente o método _getImage(...) na caixa de diálogo de contribuição de foto. Esse método é chamado quando o usuário clica no botão "+ADD PHOTO".

O código a seguir usa o pacote image_picker para tirar uma foto, atualizar a IU e chamar o modelo de API para fazer upload da imagem. Isso será implementado na próxima etapa. A chamada _getImage(...) armazena um token de upload necessário mais tarde para criar a foto no Google Fotos.

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

Implementar a chamada da API Library para fazer upload da imagem e receber um token de upload

O upload de fotos e vídeos para a API Library é feito em duas etapas:

  1. Faça upload dos bytes de mídia para receber um token de upload.
  2. Crie um item de mídia na biblioteca do usuário usando o token de upload.

b2f84ff91b0e1396.png Implemente a solicitação REST para fazer upload de mídia. É necessário definir alguns cabeçalhos para especificar o tipo de solicitação de upload e o nome do arquivo. No arquivo photos_library_api_client.dart, implemente o método uploadMediaItem(...) em que o arquivo é enviado, recuperando o token de upload retornado pela chamada 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;
}

Criar um item de mídia usando um token de upload

Em seguida, implemente a criação de um item de mídia na biblioteca do usuário usando o token de upload.

A criação de um item de mídia exige o token de upload, uma descrição opcional (por exemplo, a legenda da foto ou do vídeo) e o identificador opcional de um álbum. O Field Trippa sempre adiciona a foto enviada diretamente ao álbum de uma viagem.

b2f84ff91b0e1396.png Implemente a chamada para o photos_library_api_client que chama mediaItems.batchCreate com o token de upload, a descrição e o ID do álbum. No modelo de API, implemente o método createMediaItem(...) que chama a API Library. Esse método retorna um item de mídia.

O photos_library_client dessa chamada já foi implementado.

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

Experimente!

b2f84ff91b0e1396.pngAbra o app e selecione uma viagem. Clique em contribute e selecione uma foto que você tirou anteriormente. Insira uma descrição e selecione upload. A imagem aparecerá na viagem após alguns segundos.

b2f84ff91b0e1396.png Abra o álbum no app Google Fotos. Você verá a imagem nova no álbum desta excursão.

526ede994fcd5d8d.gif

Até agora, você implementou a funcionalidade para criar uma excursão e fazer upload de fotos com uma descrição. No back-end, cada viagem é armazenada como um álbum no Google Fotos.

Em seguida, você compartilhará uma viagem com outras pessoas que não estão usando seu aplicativo.

Como cada viagem consiste em um álbum no Google Fotos, você pode "compartilhar" um álbum pelo URL e disponibilizá-lo para qualquer pessoa que tiver acesso a ele.

Implementar a chamada para compartilhar um álbum

Os álbuns são compartilhados na página da viagem quando o botão share, na parte superior do álbum, é pressionado.

b2f84ff91b0e1396.png Implemente a chamada assíncrona _shareAlbum(...) que chama o modelo para compartilhar o álbum e atualiza o álbum em exibição. Ao recarregar o álbum, a propriedade shareInfo é propagada. Ela contém o shareableUrl, que você mostrará ao usuário em uma caixa de diálogo mais tarde.

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 Implemente o método _showShareableUrl(...) que é chamado quando o usuário clica no botão "SHARE WITH ANYONE", na parte superior da página. Primeiro, confira se o álbum já foi compartilhado e chame o método _showUrlDialog(...) depois de fazer isso.

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 Implemente o método _showUrlDialog(...) que mostra o shareableUrl em uma caixa de diálogo.

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

Experimente!

O app lista apenas as viagens que ainda não foram compartilhadas na tela principal. Não se preocupe, isso será implementado na próxima etapa. Por enquanto, você pode simplesmente criar uma nova viagem caso saia da tela.

b2f84ff91b0e1396.png Abra o app e selecione uma viagem. Selecione "SHARE WITH ANYONE" na parte superior da tela e abra o URL retornado no navegador. Dica: o URL também é impresso no registro para que você possa copiá-lo facilmente no computador. No Android Studio, o registro é exibido na guia "Run".

1d1a40c1078e4221.gif

No Google Fotos, os álbuns podem ser compartilhados por um URL e podem ser acessados por qualquer pessoa com acesso a ele. Com a API Library, também é possível compartilhar álbuns usando os tokens de compartilhamento. Trata-se de uma string usada dentro do aplicativo para associar usuários a um álbum compartilhado usando a API.

O processo de compartilhamento de um álbum pelo seu aplicativo usando a API Library ocorre da seguinte maneira:

  1. O usuário A faz login no seu aplicativo e autoriza a API Library.
  2. Crie o álbum.
  3. Compartilhe o álbum usando o identificador dele.
  4. Transfira o token de compartilhamento para outro usuário.

O processo de participação é parecido:

  1. O usuário B faz login no aplicativo e autoriza a API Library.
  2. Recupere o token de compartilhamento do álbum a que o usuário será associado.
  3. Use o token de compartilhamento para associar o usuário ao álbum.

Os álbuns compartilhados são mostrados no Google Fotos na guia "Compartilhar".

Exibir o token de compartilhamento

Na etapa anterior, você já implementou o método _shareAlbum(...) que compartilha um álbum. A propriedade shareInfo também contém o "token de compartilhamento" que será mostrado na tela.

b2f84ff91b0e1396.png Na página da viagem, implemente o método _showShareToken(...), que é chamado quando o usuário pressiona o botão "SHARE WITH FIELD TRIPPA" na tela.

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

Em seguida, implemente a exibição do "token de compartilhamento" no método _showTokenDialog(...). O token faz parte da propriedade shareInfo de um álbum.

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

Listar álbuns compartilhados

Atualmente, o app lista apenas os álbuns de propriedade do usuário, e não os álbuns compartilhados.

Somente os álbuns que o usuário criou ou adicionou explicitamente na própria biblioteca do Google Fotos são exibidos na tela "Álbuns" do app Google Fotos. Da mesma forma, só eles são retornados ao chamar albums.list na API Library. Porém, no nosso app, o usuário pode participar dos álbuns compartilhados de outros usuários, que apenas são retornados na chamada para listar os álbuns compartilhados. É necessário mudar a forma como a lista de viagens (álbuns) é recuperada da API Library para incluir os álbuns de propriedade do usuário e compartilhados.

b2f84ff91b0e1396.png Os álbuns são carregados e armazenados em cache no modelo de API. Mude a implementação de updateAlbums() no modelo para álbuns carregados e compartilhados antes de armazená-los em uma lista.

Essa implementação usa várias classes Future para listar os álbuns de forma assíncrona antes de juntá-los na lista de álbuns em cache. Exclua a implementação antiga e comente o novo código.

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

Participar de um álbum compartilhado

Você pode associar os usuários do seu aplicativo a um álbum usando o token de compartilhamento. Neste codelab, isso é feito usando uma caixa de diálogo de texto simples.

b2f84ff91b0e1396.png Implemente o método _joinTrip na página de participação na viagem que chama o modelo de API com o token de compartilhamento que o usuário inseriu. Primeiro, exiba o indicador de carregamento. Em seguida, faça a chamada para participar do álbum compartilhado com a entrada do formulário de texto, antes de ocultar o indicador de carregamento e voltar à tela anterior.

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

Experimente!

Você precisa de um segundo dispositivo ou emulador com uma conta de usuário diferente para testar essa parte do codelab.

b2f84ff91b0e1396.png Crie e compartilhe uma viagem com um usuário, depois selecione a opção "SHARE IN FIELD TRIPPA" para recuperar o token de compartilhamento. Copie-o para outro dispositivo ou emulador e o insira-o usando a opção "JOIN A TRIP ALBUM" da página inicial. Dica: a área de transferência entre os emuladores e o computador host é compartilhada.

8043086cc00eaa16.gif 55c1e75014d4d2a4.gif

Dicas para implementação no mundo real

Ao implementar o compartilhamento em um aplicativo do mundo real (não de um codelab), você precisa pensar com cuidado sobre como usar os tokens de compartilhamento para associar os usuários aos álbuns. Considere armazená-los em seu back-end seguro e usar os relacionamentos entre os usuários para criar álbuns e associá-los a eles.

Por exemplo: um aplicativo de encontro de um clube de futebol pode monitorar os convidados de eventos programados específicos e só associá-los ao álbum depois de fazer uma solicitação a eles.

Antes de fazer qualquer mudança na conta do Google Fotos do usuário, é importante avisar e pedir o consentimento dele. Consulte as diretrizes de UX da API Google Photos Library para ter mais informações.

O que você criou

  • Implementou a funcionalidade de compartilhamento no seu aplicativo, com suporte do Google Fotos.
  • Criou suas próprias experiências de compartilhamento de fotos e vídeos com a API Google Photos Library, sem precisar se preocupar com infraestrutura ou armazenamento.
  • Usou a funcionalidade de compartilhamento que faz parte da API de maneiras interessantes e originais para compartilhar conteúdo diretamente com os usuários.
  • Usou algumas partes importantes da API Library.
  • Criou novos álbuns e fez upload de novas fotos.
  • Listou álbuns compartilhados, limitados aos álbuns criados pelo seu aplicativo.

A seguir

Consulte a documentação para desenvolvedores das APIs do Google Fotos em https://developers.google.com/photos para saber mais sobre o compartilhamento de mídia e outras partes da API Library. Por exemplo, os filtros de conteúdo inteligente com aprendizado de máquina ajudam você a encontrar as fotos e os vídeos certos.

Quando tudo estiver pronto para lançar sua integração, participe do Programa de parceiros do Google Fotos.

Consulte as diretrizes de UX e as práticas técnicas recomendadas. Para ajudar você a começar, as bibliotecas de cliente também estão disponíveis em alguns idiomas.