# Google Classroom

- Tool: FlutterFlow
- Difficulty: Advanced
- Time required: 60 minutes
- Last updated: July 2026

## TL;DR

Connect FlutterFlow to Google Classroom using a Custom Action that wraps the `google_sign_in` Dart package to obtain OAuth 2.0 education scopes on-device, then calls the Classroom REST API via FlutterFlow's API Calls panel. Warning: OAuth redirects break inside FlutterFlow's web preview iframe — test exclusively on a real device or published build.

## Building a Google Classroom Companion App in FlutterFlow

Google Classroom is a free product bundled with Google Workspace for Education — there is no per-call fee for using the API. But unlike tools that give you a static API key, Classroom demands full OAuth 2.0 with education-specific permission scopes. The user must actively consent to let your app read their courses, class roster, and coursework. This consent flow opens a Google sign-in browser window on the device — which is exactly why it cannot be tested inside FlutterFlow's web preview iframe: the OAuth redirect URL gets blocked. Every meaningful test must happen on a real iOS or Android device, or on a published web build with an authorized origin registered in Google Cloud Console.

On the technical side, the `google_sign_in` pub.dev package handles the OAuth dance. Your Dart custom action requests the three core Classroom scopes, receives the access token after the user consents, and stores it in FlutterFlow's app state. From that point, standard FlutterFlow API Calls to `https://classroom.googleapis.com/v1` with `Authorization: Bearer {{ accessToken }}` work like any other REST API.

Before going public, be aware: if your app will be used by anyone outside your own Google Workspace domain, you must submit your OAuth consent screen and the Classroom scopes for Google's app verification. Classroom scopes are classified as restricted, meaning the review process is more involved than typical OAuth apps and can take several weeks. Plan for this before your launch deadline.

## Before you start

- A Google Cloud project with the Google Classroom API enabled in the API Library
- An OAuth 2.0 consent screen configured in Google Cloud Console with the required Classroom scopes
- Platform-specific OAuth client IDs: an iOS client (for the reversed client ID in Info.plist) and an Android client (with your app's SHA-1 fingerprint)
- A FlutterFlow project — Custom Code (Actions) is available on FlutterFlow paid plans
- Willingness to test exclusively on a real device or published build (NOT in FlutterFlow's web preview)

## Step-by-step guide

### 1. Set up Google Cloud project, enable Classroom API, and configure OAuth consent

Open the Google Cloud Console at console.cloud.google.com. Create a new project or select an existing one, then go to APIs & Services → Library, search for 'Google Classroom API', and click Enable. Next, go to APIs & Services → OAuth consent screen. Select 'External' (for apps used outside your Google Workspace domain) or 'Internal' (for apps restricted to your own domain's users — much faster to configure and does not require Google's verification review for restricted scopes).

Fill in the app name, support email, and developer contact email. On the Scopes screen, click 'Add or Remove Scopes' and add the three Classroom scopes: `https://www.googleapis.com/auth/classroom.courses.readonly`, `https://www.googleapis.com/auth/classroom.rosters.readonly`, and `https://www.googleapis.com/auth/classroom.coursework.students`. These are classified as restricted scopes by Google — if your app is External and will be used by more than the test users you explicitly add in the console, you must go through Google's app verification process before publishing. Add test user emails in the 'Test users' section so you can test during development without completing verification.

Finally, go to APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client ID. Create both an iOS client (you will need your iOS bundle ID from FlutterFlow) and an Android client (you will need the SHA-1 fingerprint of your debug keystore, found in FlutterFlow's project settings). Download the `google-services.json` file for Android — you will need it in the next step.

**Expected result:** The Classroom API is enabled, the OAuth consent screen is configured with Classroom scopes, and you have both iOS and Android OAuth client IDs plus the google-services.json file.

### 2. Configure platform OAuth credentials in FlutterFlow

For the `google_sign_in` custom action to work on iOS and Android, each platform needs platform-specific OAuth credential files embedded in the native app build. In FlutterFlow, go to Settings & Integrations → Firebase → connect or configure your Firebase project. Upload your `google-services.json` (Android) via the Firebase integration panel — FlutterFlow places this in the Android project automatically.

For iOS, FlutterFlow's Custom Code → Dependencies section is where you configure the reversed client ID. The reversed client ID looks like `com.googleusercontent.apps.YOUR_CLIENT_ID_NUMBER` and is listed in the Google Cloud Console under your iOS OAuth credential. In FlutterFlow, go to Custom Code → click the gear icon → iOS Custom Info.plist Settings, and add the key `CFBundleURLSchemes` with an array value containing your reversed client ID. This entry tells iOS to handle the OAuth redirect back to your app after the user completes Google sign-in.

For Android, the SHA-1 fingerprint registered in your Google Cloud Android client must match FlutterFlow's debug keystore SHA-1. Find FlutterFlow's SHA-1 in Settings & Integrations → Firebase → SHA Certificate Fingerprints. If there is a mismatch, the sign-in will silently fail on Android with no visible error. Double-check that the SHA-1 in Google Cloud Console exactly matches what FlutterFlow reports.

```
<!-- iOS Info.plist addition (configured via FlutterFlow Custom Code settings) -->
<key>CFBundleURLSchemes</key>
<array>
  <string>com.googleusercontent.apps.YOUR_REVERSED_CLIENT_ID</string>
</array>
```

**Expected result:** The google-services.json is uploaded in FlutterFlow's Firebase settings, and the reversed client ID is added to the iOS Info.plist via FlutterFlow's Custom Code settings.

### 3. Write the google_sign_in custom action in FlutterFlow

In FlutterFlow, go to Custom Code in the left navigation panel. Click + Add → Action. Name it 'SignInWithClassroomScopes'. In the Dependencies field, type `google_sign_in` — FlutterFlow will prompt you to add the pub.dev package. Use a recent stable version compatible with your Flutter SDK (check pub.dev for the latest version number).

In the Dart code editor, write the action. The action initializes `GoogleSignIn` with the three Classroom scopes, calls `signIn()` to trigger the consent flow, retrieves the authentication object, and requests the access token from it. The action should return the access token as a String, which you then store in a Page State or App State variable called `classroomAccessToken`. This token is short-lived (typically 1 hour); you will need to handle expiry.

IMPORTANT: Custom actions do NOT run in FlutterFlow's Run mode (web preview). Do not try to test this in the browser — you will see either a silent failure or a missing plugin error. Build a debug APK and install it on an Android device, or use a TestFlight build for iOS. The OAuth flow will open a full Google sign-in browser window on the device, the user consents, and the token is returned to the action.

```
// FlutterFlow Custom Action: sign_in_with_classroom_scopes.dart
import 'package:google_sign_in/google_sign_in.dart';

Future<String?> signInWithClassroomScopes() async {
  final GoogleSignIn googleSignIn = GoogleSignIn(
    scopes: [
      'https://www.googleapis.com/auth/classroom.courses.readonly',
      'https://www.googleapis.com/auth/classroom.rosters.readonly',
      'https://www.googleapis.com/auth/classroom.coursework.students',
    ],
  );

  try {
    final GoogleSignInAccount? account = await googleSignIn.signIn();
    if (account == null) {
      // User cancelled the sign-in
      return null;
    }

    final GoogleSignInAuthentication auth = await account.authentication;
    // Return the access token to store in app state
    return auth.accessToken;
  } catch (e) {
    // Log error and return null
    debugPrint('Google Classroom sign-in error: $e');
    return null;
  }
}
```

**Expected result:** The SignInWithClassroomScopes custom action appears in FlutterFlow's Custom Code section and compiles without errors. On a real device, calling the action opens the Google sign-in consent screen and returns an access token.

### 4. Create the Classroom API Group and add course/roster calls

Now configure the REST API calls that will use the access token from the custom action. In the left nav, click API Calls → + Add → Create API Group. Name it 'Google Classroom'. Set the Base URL to `https://classroom.googleapis.com/v1`. Add a shared header: `Authorization` = `Bearer {{ accessToken }}`. Create a group-level variable `accessToken` of type String — you will pass the value stored in app state from the custom action.

Add individual API Calls:
- 'GetCourses': Method GET, endpoint `/courses`. Query params: `courseStates=ACTIVE`, `pageSize=20`.
- 'GetCourseStudents': Method GET, endpoint `/courses/{{ courseId }}/students`. Variable: `courseId` (String).
- 'GetCourseWork': Method GET, endpoint `/courses/{{ courseId }}/courseWork`. Variable: `courseId` (String).

In the Response & Test tab for GetCourses, paste a sample Classroom API response (available in Google's API documentation) and generate JSON Paths: `$.courses[*].id`, `$.courses[*].name`, `$.courses[*].section`, `$.courses[*].descriptionHeading`, `$.courses[*].enrollmentCode`.

Wire the Action Flow on your page: when the page loads, call the SignInWithClassroomScopes custom action first (stores token in app state), then call GetCourses with the stored token. Add a condition — if the token is null (user cancelled), show a 'Sign in with Google' button instead of the course list.

```
// GetCourses API Call config
{
  "name": "GetCourses",
  "method": "GET",
  "endpoint": "/courses",
  "headers": {
    "Authorization": "Bearer {{ accessToken }}"
  },
  "query_params": {
    "courseStates": "ACTIVE",
    "pageSize": "20"
  }
}

// JSON Paths after generating from Classroom API response:
// Course ID:      $.courses[*].id
// Course name:    $.courses[*].name
// Section:        $.courses[*].section
// Description:    $.courses[*].descriptionHeading
// Enrollment code: $.courses[*].enrollmentCode
// Next page token: $.nextPageToken
```

**Expected result:** The 'Google Classroom' API Group is configured with GetCourses, GetCourseStudents, and GetCourseWork calls. Testing with a valid token in Response & Test returns real Classroom data.

### 5. Test on a real device and handle token refresh

This step is non-negotiable: FlutterFlow custom actions do not run in Run mode (web preview). Testing the sign-in flow requires a real device or a published build. In FlutterFlow, click the Test/Run mode arrow menu → select 'Build APK' for Android. Download the APK and install it on a physical Android device via Android's 'Allow unknown sources' setting. Open the app, tap your 'Sign In with Google' button, and confirm the Google consent screen appears with your three Classroom scope descriptions.

After granting consent, verify the access token is stored in app state (add a debug Text widget bound to the token app state variable and remove it before release). Confirm the GetCourses call returns your courses list. Test GetCourseStudents by tapping a course.

For token refresh in production: access tokens from `google_sign_in` last about one hour. Implement a refresh mechanism by calling `googleSignIn.signInSilently()` before each API call — if it returns an account, call `account.authentication` to get a fresh token. If `signInSilently()` returns null, the user needs to go through the full consent flow again. Add error handling for 401 responses from the Classroom API — catch them in your Action Flow and trigger a re-authentication action.

```
// Token refresh in a separate custom action: refresh_classroom_token.dart
import 'package:google_sign_in/google_sign_in.dart';

Future<String?> refreshClassroomToken() async {
  final GoogleSignIn googleSignIn = GoogleSignIn(
    scopes: [
      'https://www.googleapis.com/auth/classroom.courses.readonly',
      'https://www.googleapis.com/auth/classroom.rosters.readonly',
      'https://www.googleapis.com/auth/classroom.coursework.students',
    ],
  );

  try {
    // Try silent sign-in first (no UI if already signed in)
    final GoogleSignInAccount? account = await googleSignIn.signInSilently();
    if (account == null) return null; // Needs full sign-in

    final GoogleSignInAuthentication auth = await account.authentication;
    return auth.accessToken;
  } catch (e) {
    debugPrint('Token refresh error: $e');
    return null;
  }
}
```

**Expected result:** On a real Android device, the full OAuth flow works: sign-in consent screen appears, courses load from the Classroom API, and the token refresh action returns a fresh token without showing the consent screen again.

## Best practices

- Never request more Classroom scopes than your app actually uses — each additional restricted scope adds time to Google's verification review.
- Test exclusively on real devices for this integration; FlutterFlow's web preview does not support the google_sign_in custom action's OAuth flow.
- Implement token refresh using `signInSilently()` before each API call to handle the 1-hour access token expiry gracefully.
- Use 'Internal' OAuth consent screen mode for school/district apps to bypass Google's app verification for restricted Classroom scopes.
- Add test users in Google Cloud Console during development so you can test without completing the full app verification process.
- Handle the case where `signIn()` returns null (user cancelled the consent screen) by showing a retry button rather than crashing.
- Cache the courses list in Firestore or local app state after the first load — avoid calling GetCourses on every widget rebuild.
- Plan for Google's app verification timeline (can take several weeks) before committing to a public launch date with Classroom integration.

## Use cases

### Teacher companion app for quick class management

A Flutter app that lets teachers view their active classes, check today's assignment due dates, and quickly see which students have submitted work — without opening the full Classroom web interface. The app reads roster and coursework data using the Classroom API with teacher-scoped OAuth credentials.

Prompt example:

```
Build a teacher companion app that shows all my Google Classroom classes, lets me tap into each class to see pending assignments and which students have submitted, and shows me the class roster with student names and photos.
```

### Student assignment tracker for families

A parent-friendly mobile app that shows a student's enrolled Classroom courses and upcoming assignment due dates in a calendar view. Parents sign in via the student's Google account (with their permission), see the course list and assignment deadlines, and receive a local notification when an assignment is due in 24 hours.

Prompt example:

```
Create a mobile app where students (or their parents) can sign in with Google, see all their Google Classroom courses, view upcoming assignment due dates on a calendar, and get a push notification reminder one day before each deadline.
```

### School admin enrollment dashboard

An admin-facing dashboard that reads enrollment counts across multiple Classroom classes using the `/v1/courses/{id}/students` endpoint, shows class sizes in a bar chart, and flags any class that exceeds a configured maximum enrollment threshold.

Prompt example:

```
Build an admin dashboard app for a school coordinator that shows all active Google Classroom classes, the number of enrolled students in each, and highlights classes where enrollment exceeds 30 students in red.
```

## Troubleshooting

### Google sign-in works on web preview but Classroom scopes are missing / access token is null

Cause: FlutterFlow's web preview Run mode uses a browser-based sign-in that does not fully replicate the native google_sign_in package behavior and does not support custom Dart actions.

Solution: Stop testing in web preview for this integration. Build a debug APK via FlutterFlow → Test/Run → Build APK, install on a physical Android device, and test the full OAuth flow there. Custom actions only execute correctly on native device builds.

### Android sign-in silently fails with no error message — the consent screen never appears

Cause: The SHA-1 fingerprint of FlutterFlow's build keystore does not match the SHA-1 registered for the Android OAuth client in Google Cloud Console.

Solution: In FlutterFlow, go to Settings & Integrations → Firebase → SHA Certificate Fingerprints and copy the SHA-1 value. Open Google Cloud Console → APIs & Services → Credentials → your Android OAuth client → edit → add the SHA-1 under 'SHA-1 certificate fingerprints'. Save and wait a few minutes for propagation before testing again.

### 401 Unauthorized from the Classroom API after the sign-in succeeds

Cause: The access token has expired (tokens last approximately 1 hour), or the token was obtained with incorrect scopes that do not include the specific Classroom resource being accessed.

Solution: Implement the `refreshClassroomToken` action using `googleSignIn.signInSilently()` and call it before each API request. Verify that the GoogleSignIn instance in the custom action is initialized with all three required Classroom scopes. Catch 401 responses in the Action Flow and trigger re-authentication.

### Error: 'This app has not been verified by Google' warning on the consent screen

Cause: Classroom scopes are restricted scopes that require Google's app verification before being shown to users outside the developer's own Google Workspace domain.

Solution: For internal school apps, switch the OAuth consent screen to 'Internal' to restrict access to your domain and bypass the verification requirement. For public apps, submit for Google's app verification via the OAuth consent screen configuration in Google Cloud Console. During development, add specific test users' emails in the 'Test users' section — they bypass the unverified warning.

## Frequently asked questions

### Why can't I test the Google sign-in in FlutterFlow's Run mode (web preview)?

FlutterFlow's Run mode renders your app inside a browser iframe. OAuth redirect URLs cannot complete inside an iframe — the redirect gets blocked by the iframe security policy. Additionally, custom Dart actions like the google_sign_in integration do not execute in Run mode at all. You must build a debug APK or TestFlight build and test on a real device.

### Do I need to pay for the Google Classroom API?

No. The Google Classroom API is free to use as part of Google Workspace for Education. There is no per-call fee. However, the API is subject to per-project rate limits (check your Google Cloud Console quotas) and requires OAuth 2.0 setup in Google Cloud Console, which is a free service.

### Can students and teachers use the same app, or do I need separate apps?

Both can use the same FlutterFlow app. The Classroom API returns data based on the signed-in user's role. A teacher's account returns their classes as the owner, while a student's account returns their enrolled classes. Use conditional UI in FlutterFlow — check `$.courses[*].courseState` or the presence of teacher-only fields — to show different views for teachers versus students.

### What happens when the access token expires during a session?

The Classroom API returns a 401 Unauthorized response. Implement a `refreshClassroomToken` custom action that calls `googleSignIn.signInSilently()`. If the user is still signed into Google on their device, this returns a new token without showing the consent screen again. Catch 401 responses in your Action Flow and call the refresh action automatically before showing an error to the user.

### How long does Google's app verification take for Classroom scopes?

Google's OAuth app verification for restricted scopes (which Classroom scopes are) can take several weeks to several months, depending on Google's review queue and whether your application requires a security assessment. Start the process early. For apps restricted to a specific Google Workspace domain (Internal consent screen), verification is not required — this is the recommended path for school-specific apps.

---

Source: https://www.rapidevelopers.com/flutterflow-integrations/google-classroom
© RapidDev — https://www.rapidevelopers.com/flutterflow-integrations/google-classroom
