Your Flutter app needs data that lives in a user’s Google account. Their calendar, their contacts, their files. Google’s APIs will hand it over, but not to just anyone: the user has to consent in your app, and your backend needs an access token that lets it act on their behalf.
That second part is where most Flutter developers get stuck, because it means OAuth credentials, token verification, authenticated sessions, and a backend to hold them.
With Serverpod, that backend comes with Google sign-in built in. When a user signs in, your server gets their verified identity and receives an access token. Reading their calendar is an HTTP call away.
In this article, we’ll wire it end-to-end: Google sign-in for a Flutter app, and a backend that reads the user’s upcoming calendar events the moment they sign in. We will demonstrate it with the calendar, but the pattern is the same for any Google API that the user can grant you access to.
Understanding the flow
Before configuring anything, it’s worth understanding the whole journey.
When a user signs in, your app asks Google for two things: proof of who the user is, and permission to read their calendar. Google shows the consent screen, and what comes back reaches your Serverpod backend.
The server verifies that the identity token was issued by Google, creates an authenticated session, and receives the access token that carries the user’s consent. That token is what lets your backend call Google’s APIs for this user.
The user grants access in your app. API access occurs on your server.
Before we start
We’ll begin with a new Serverpod project created using:
serverpod create my_project
During project creation, keep the recommended defaults and make sure Authentication remains enabled when prompted.
The command gives you a Flutter app (my_project_flutter), the backend it talks to (my_project_server), and a generated client package that connects the two, so your app calls the server using plain Dart methods.
With the project in place, we’re ready to connect it to Google.
Configuring Google
Every Google integration begins in Google Cloud, regardless of which backend you’re using.
You’ll need to:
- Create a Google Cloud project.
- Configure the Google Auth Platform.
- Enable the required APIs: the People API, which Serverpod uses for basic profile data, and the Calendar API, which is used in this article.
- Under Data access, add the
userinfo.email,userinfo.profile, andcalendar.readonlyscopes. - Create a Web OAuth client for your backend, and any platform-specific clients your app needs.
The Calendar scope is sensitive, so Google may ask you to verify your app before you publish it. While it’s in testing mode, any account you add as a test user can sign in.
The complete walkthrough, including platform-specific configuration for Android, iOS, and the web, is covered in the Serverpod documentation.
Once those credentials are ready, it’s time to connect them to Serverpod.
Connecting Google to Serverpod
The first step in connecting your backend to Google is making your Google credentials available to Serverpod.
Copy your Web OAuth credentials into config/passwords.yaml.
development:
googleClientSecret: |
{
"web": {
"client_id": "your-client-id.apps.googleusercontent.com",
"client_secret": "your-client-secret",
"redirect_uris": []
}
}
Serverpod reads these credentials at startup and uses them to verify Google identity tokens.
The credential block is a YAML block scalar, so an indentation error silently produces malformed JSON and surfaces later as an authentication failure rather than an error at startup.
The complete credential structure, along with production configuration options, is covered in the documentation.
Register the Google provider
Next, register Google as one of your application’s identity providers.
pod.initializeAuthServices(
tokenManagerBuilders: [
JwtConfigFromPasswords(),
],
identityProviderBuilders: [
GoogleIdpConfigFromPasswords(),
],
);
GoogleIdpConfigFromPasswords() reads the credentials from passwords.yaml and configures Google as an available authentication provider.
The final server-side step is exposing that provider through an endpoint.
import 'package:serverpod_auth_idp_server/providers/google.dart';
class GoogleIdpEndpoint extends GoogleIdpBaseEndpoint {}
That’s the entire endpoint.
Serverpod already provides the implementation. Your application exposes it, making the Google authentication flow available to your Flutter client.
Reading the calendar from your server
When a user signs in, Serverpod passes your code the access token via a callback in the provider config. Whatever you do with it runs on your server, with the user’s consent.
Add the http package to your server, then extend the provider registration:
dart pub add http # from my_project_server
// my_project_server/lib/server.dart
import 'dart:convert';
import 'package:http/http.dart' as http;
pod.initializeAuthServices(
// ... tokenManagerBuilders stay as they are ...
identityProviderBuilders: [
GoogleIdpConfigFromPasswords(
getExtraGoogleInfoCallback: (
session, {
required accountDetails,
required accessToken,
required transaction,
}) async {
try {
final response = await http.get(
Uri.https('www.googleapis.com', '/calendar/v3/calendars/primary/events', {
'maxResults': '5',
'singleEvents': 'true',
'orderBy': 'startTime',
'timeMin': DateTime.now().toUtc().toIso8601String(),
}),
headers: {'Authorization': 'Bearer $accessToken'},
);
if (response.statusCode != 200) {
session.log('Calendar request failed: ${response.statusCode}',
level: LogLevel.warning);
return;
}
final events = (jsonDecode(response.body)['items'] as List?) ?? [];
for (final event in events) {
session.log('Upcoming: ${event['summary'] ?? '(no title)'}');
}
} catch (e) {
session.log('Calendar read skipped: $e', level: LogLevel.warning);
}
},
),
],
);
That’s a real Google API call, made by your backend, for the user who just signed in. This example logs their next five events. Yours might store them instead.
There are two things to know about this callback.
First, it runs as part of the sign-in flow. If the calendar request fails and the error goes uncaught, the entire sign-in fails too. Treat the calendar read as optional: log the error and let sign-in continue.
Second, the access token passed to the callback is short-lived. It works well for reading calendar data immediately during sign-in. If you need to sync calendar data later, such as in a scheduled or background job, you’ll need to store the credentials and handle token refresh separately.
The same token works against any Google API covered by the scopes the user granted. The documentation covers how to request additional scopes and use the token in your own code.
Running the application
With the backend configured, it’s time to start the project.
serverpod start
That’s the only command it takes.
Besides launching your backend, Flutter application, and development services, serverpod start automatically generates any required code before starting the project.
If your authentication setup introduces new database tables, there’s no need to stop the server. While serverpod start is running, press M to create and apply the migration. Once it has been applied, development continues without interrupting your workflow.
With the backend running, the remaining setup happens in Flutter.
Connecting Flutter
The template already initializes authentication when the app starts. Adding Google is one more call in main.dart, right after client.auth.initialize():
client.auth.initializeGoogleSignIn(
serverClientId: '<web-client-id>.apps.googleusercontent.com',
);
The serverClientId is the Web application client ID from earlier, not one of the platform-specific ones. That’s deliberate: the sign-in happens in your app, but the tokens are for your server, and this value tells Google which server that is. On the web, the same value is used as clientId, along with a redirect URI. The documentation includes the exact call for each platform.
Then ask for the calendar scope when the user signs in. The sign-in screen that ships with the template uses SignInWidget, which accepts a customized Google button:
SignInWidget(
client: client,
googleSignInWidget: GoogleSignInWidget(
client: client,
scopes: [
...GoogleAuthController.defaultScopes,
'https://www.googleapis.com/auth/calendar.readonly',
],
),
)
The scopes here are what Google puts on the consent screen and what your server’s access token is allowed to do.
Trying it out
Run the app and tap the Google button.
The consent screen now asks for calendar access alongside the usual profile permissions.
Approve it, and watch your server’s logs.
Your backend just called Google on the user’s behalf. Sign-in, verification, session, and a working API call, and the only authentication code you wrote was a callback.
A pattern you’ll reuse
The calendar was just an example. Change the scopes you request, and the endpoint you call, and the same flow reads Google Drive files, Contacts, or YouTube data. Add another identity provider, and the same four steps repeat: configure the provider, register it with Serverpod, expose its endpoint, and initialize it in Flutter.
The provider-specific configuration changes, but the authentication architecture stays the same. That consistency is one of the advantages of Serverpod’s authentication system. As your application grows, you don’t have to rethink your authentication flow. You just plug in another provider, or another API, and keep building.
Conclusion
Accessing Google APIs from a Flutter app has a reputation for being complicated, and most of that reputation stems from the requirements Google imposes, no matter what backend you use: credentials, consent, and verification. Once those are in place, Serverpod’s side is a config entry, an empty endpoint class, and a callback that receives a ready-to-use access token.
From there, Serverpod handles verifying identity tokens, creating authenticated sessions, and maintaining consistent authentication across your application. What you do with the APIs is up to you.
Happy coding!





Top comments (0)