Using Google Cloud Platform (GCP) Services Like Cloud Firestore and Cloud Functions in Flutter

Integrating your Flutter app with Google Cloud Platform (GCP) services allows you to leverage scalable backend solutions, robust data storage, and serverless computing. In this comprehensive guide, we will walk you through using Cloud Firestore and Cloud Functions with your Flutter application, enhancing its capabilities with real-time data and backend logic.

Introduction to GCP Services

Google Cloud Platform (GCP) provides a range of services that can significantly enhance the functionality and scalability of your Flutter applications:

  • Cloud Firestore: A NoSQL document database that enables you to easily store, sync, and query data for your mobile, web, and IoT apps.
  • Cloud Functions: A serverless execution environment that lets you create event-driven, scalable backend logic without managing servers.

Setting Up Your GCP Project

Before integrating these services with Flutter, you need to set up a GCP project:

Step 1: Create a GCP Project

  1. Go to the Google Cloud Console.
  2. Click on the project dropdown at the top and select "New Project."
  3. Enter a project name and click "Create."

Step 2: Enable Billing

Ensure billing is enabled for your project to use GCP services.

Step 3: Enable Required APIs

Enable the Cloud Firestore and Cloud Functions APIs:

  1. Navigate to "APIs & Services" in the Cloud Console.
  2. Click "Enable APIs and Services."
  3. Search for "Cloud Firestore API" and enable it.
  4. Search for "Cloud Functions API" and enable it.

Setting Up Flutter Project

Next, create or navigate to your Flutter project and add the necessary dependencies.

Step 1: Create a New Flutter Project (if needed)

flutter create flutter_gcp_app

Step 2: Add Firebase Dependencies

Cloud Firestore and Cloud Functions rely on Firebase SDKs, so add the following dependencies to your pubspec.yaml file:

dependencies:
  firebase_core: ^2.15.0
  cloud_firestore: ^4.9.2
  cloud_functions: ^4.3.0

Run flutter pub get to install the dependencies.

Step 3: Initialize Firebase

Initialize Firebase in your Flutter application:

import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter GCP App',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter GCP Example'),
        ),
        body: Center(
          child: Text('Hello GCP!'),
        ),
      ),
    );
  }
}

Using Cloud Firestore in Flutter

Now, let’s use Cloud Firestore to read and write data.

Step 1: Writing Data to Firestore

import 'package:cloud_firestore/cloud_firestore.dart';

class FirestoreService {
  final FirebaseFirestore _db = FirebaseFirestore.instance;

  Future addData(String collection, Map data) async {
    await _db.collection(collection).add(data);
  }
}

Usage in your Flutter app:

ElevatedButton(
  onPressed: () async {
    await FirestoreService().addData('users', {
      'name': 'John Doe',
      'age': 30,
    });
    print('Data added to Firestore!');
  },
  child: Text('Add Data to Firestore'),
)

Step 2: Reading Data from Firestore

Future>> getData(String collection) async {
    QuerySnapshot querySnapshot = await _db.collection(collection).get();
    return querySnapshot.docs.map((doc) => doc.data() as Map).toList();
  }
}

Usage in your Flutter app:

FutureBuilder>>(
  future: FirestoreService().getData('users'),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return CircularProgressIndicator();
    }
    if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    }
    if (!snapshot.hasData || snapshot.data!.isEmpty) {
      return Text('No data available');
    }
    List> users = snapshot.data!;
    return ListView.builder(
      itemCount: users.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(users[index]['name']),
          subtitle: Text('Age: ${users[index]['age']}'),
        );
      },
    );
  },
)

Step 3: Listening to Real-time Updates

Stream>> streamData(String collection) {
    return _db.collection(collection).snapshots().map((snapshot) {
      return snapshot.docs.map((doc) => doc.data() as Map).toList();
    });
  }
}

Usage in Flutter:

StreamBuilder>>(
  stream: FirestoreService().streamData('users'),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return CircularProgressIndicator();
    }
    if (snapshot.hasError) {
      return Text('Error: ${snapshot.error}');
    }
    if (!snapshot.hasData || snapshot.data!.isEmpty) {
      return Text('No data available');
    }
    List> users = snapshot.data!;
    return ListView.builder(
      itemCount: users.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(users[index]['name']),
          subtitle: Text('Age: ${users[index]['age']}'),
        );
      },
    );
  },
)

Using Cloud Functions in Flutter

Let’s move on to using Cloud Functions for your backend logic.

Step 1: Deploy a Cloud Function

First, create and deploy a simple HTTP Cloud Function.

// functions/index.js
const functions = require('firebase-functions');

exports.helloWorld = functions.https.onRequest((request, response) => {
  functions.logger.info("Hello logs!", {structuredData: true});
  response.send("Hello from Firebase!");
});

Deploy the function using Firebase CLI:

firebase deploy --only functions

Step 2: Call Cloud Function from Flutter

import 'package:cloud_functions/cloud_functions.dart';

class CloudFunctionsService {
  final FirebaseFunctions _functions = FirebaseFunctions.instance;

  Future callHelloWorld() async {
    try {
      final HttpsCallable callable = _functions.httpsCallable('helloWorld');
      final HttpsCallableResult result = await callable.call();
      return result.data.toString();
    } catch (e) {
      return 'Error: ${e.toString()}';
    }
  }
}

Call the function from your Flutter app:

ElevatedButton(
  onPressed: () async {
    String result = await CloudFunctionsService().callHelloWorld();
    print('Cloud Function Result: $result');
  },
  child: Text('Call Cloud Function'),
)

Step 3: Pass Data to Cloud Function

You can also pass data to your Cloud Functions:

// functions/index.js
exports.addNumbers = functions.https.onCall((data, context) => {
  const num1 = data.num1;
  const num2 = data.num2;
  return {
    result: num1 + num2,
  };
});

Flutter Code:

Future addNumbers(int num1, int num2) async {
    try {
      final HttpsCallable callable = _functions.httpsCallable('addNumbers');
      final HttpsCallableResult result = await callable.call({
        'num1': num1,
        'num2': num2,
      });
      return result.data['result'];
    } catch (e) {
      print('Error calling function: $e');
      return -1;
    }
  }
}

Usage in Flutter:

ElevatedButton(
  onPressed: () async {
    int result = await CloudFunctionsService().addNumbers(5, 3);
    print('Addition Result: $result');
  },
  child: Text('Call Add Numbers Function'),
)

Conclusion

Integrating GCP services such as Cloud Firestore and Cloud Functions into your Flutter applications opens up a realm of possibilities, from real-time data management to serverless backend processing. By following this comprehensive guide, you can build robust and scalable Flutter apps powered by Google Cloud Platform.