Skip to content

Commit 61c3bcf

Browse files
authored
Dart callables (#1258)
* callable sample * add streaming callable sample
1 parent 57617f1 commit 61c3bcf

12 files changed

Lines changed: 327 additions & 11 deletions

File tree

.github/workflows/test_dart.yml

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,15 @@ jobs:
4343
with:
4444
sdk: ${{ matrix.dart-version }}
4545

46-
- name: HTTPS Time Server - Install dependencies
47-
working-directory: ./Dart/quickstarts/https-time-server
48-
run: dart pub get
49-
50-
- name: HTTPS Time Server - Format
51-
working-directory: ./Dart/quickstarts/https-time-server
52-
run: dart format --set-exit-if-changed .
53-
54-
- name: HTTPS Time Server - Analyze
55-
working-directory: ./Dart/quickstarts/https-time-server
56-
run: dart analyze .
46+
- name: Test Dart Samples
47+
run: |
48+
set -e
49+
for dir in $(find Dart -name pubspec.yaml -exec dirname {} \;); do
50+
echo "::group::Testing $dir"
51+
cd "$dir"
52+
dart pub get
53+
dart format --set-exit-if-changed .
54+
dart analyze .
55+
cd - > /dev/null
56+
echo "::endgroup::"
57+
done
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.dart_tool/
2+
.packages
3+
build/
4+
*.dart_tool
5+
pubspec.lock
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Firebase SDK for Cloud Functions Quickstart - Callable Functions Streaming
2+
3+
This quickstart demonstrates how to send requests to a server-side function and _stream_ a response to a client SDK using Dart.
4+
5+
[Read more about Cloud Functions for Firebase](https://firebase.google.com/docs/functions/)
6+
7+
8+
## Frontend Client
9+
10+
You can find the frontend code in [`../../../Node/quickstarts/callable-functions-streaming/website`](../../../Node/quickstarts/callable-functions-streaming/website).
11+
12+
**IMPORTANT NOTE**: Due to limitations during the experimental release of Dart support in Cloud Functions for Firebase, the code in the website must be modified. Instead of using `httpsCallable`, you must use [`httpsCallableFromURL`](https://firebase.google.com/docs/reference/js/functions.md#httpscallablefromurl_7af6987) and pass the full function url (`https://<function-name-and-hash>.run.app` or the emulator equivalent), which can be found in the Cloud console or the emulator logs.
13+
14+
15+
## Deploy the app to prod
16+
17+
First you need to get the `dart` dependencies of the functions:
18+
19+
```bash
20+
dart pub get
21+
```
22+
23+
Deploy to Firebase using the following command:
24+
25+
```bash
26+
firebase deploy --only functions
27+
```
28+
29+
## License
30+
31+
© Google, 2026. Licensed under an [Apache-2](../../../LICENSE) license.
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// [START full-sample]
16+
// Dependencies for callable functions.
17+
import 'dart:convert';
18+
import 'dart:math';
19+
20+
import 'package:firebase_functions/firebase_functions.dart';
21+
import 'package:http/http.dart' as http;
22+
23+
/// Gets the weather from the national weather service
24+
/// https://www.weather.gov/documentation/services-web-api
25+
Future<dynamic> weatherForecastApi(num lat, num lng) async {
26+
final resp = await http.get(
27+
Uri.parse('https://api.weather.gov/points/$lat,$lng'),
28+
);
29+
30+
if (resp.statusCode >= 400) {
31+
return 'error: ${resp.statusCode}';
32+
}
33+
34+
final pointData = jsonDecode(resp.body) as Map<String, dynamic>;
35+
final forecastUrl = pointData['properties']['forecast'] as String;
36+
final forecastResp = await http.get(Uri.parse(forecastUrl));
37+
38+
if (forecastResp.statusCode >= 400) {
39+
return 'error: ${forecastResp.statusCode}';
40+
}
41+
42+
// add an artificial wait to emphasize stream-iness
43+
final randomWait = Random().nextDouble() * 1500;
44+
await Future<void>.delayed(Duration(milliseconds: randomWait.toInt()));
45+
46+
return jsonDecode(forecastResp.body);
47+
}
48+
49+
void main(List<String> args) async {
50+
await fireUp(args, (firebase) {
51+
// [START streaming-callable]
52+
firebase.https.onCall(name: 'getForecast', (request, response) async {
53+
final data = request.data as Map<String, Object?>?;
54+
final locations = data?['locations'] as List<dynamic>?;
55+
56+
if (locations == null || locations.isEmpty) {
57+
throw InvalidArgumentError('Missing locations to forecast');
58+
}
59+
60+
// fetch forecast data for all requested locations
61+
final allRequests = locations.map((location) async {
62+
if (location case {
63+
'latitude': num latitude,
64+
'longitude': num longitude,
65+
}) {
66+
final forecast = await weatherForecastApi(latitude, longitude);
67+
final result = {
68+
'latitude': latitude,
69+
'longitude': longitude,
70+
'forecast': forecast,
71+
};
72+
73+
// clients that support streaming will have each
74+
// forecast streamed to them as they complete
75+
if (request.acceptsStreaming) {
76+
await response.sendChunk(result);
77+
}
78+
79+
return result;
80+
}
81+
82+
throw InvalidArgumentError('Invalid location format');
83+
});
84+
85+
// Return the full set of data to all clients
86+
return CallableResult(await Future.wait(allRequests));
87+
});
88+
// [END streaming-callable]
89+
});
90+
}
91+
92+
// [END full-sample]
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"functions": {
3+
"source": ".",
4+
"codebase": "dart-quickstarts-callable-functions-streaming"
5+
}
6+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: callable_functions_streaming
2+
description: Streaming callable examples for Firebase Functions for Dart
3+
publish_to: none
4+
5+
environment:
6+
sdk: ^3.11.0
7+
8+
dependencies:
9+
firebase_functions:
10+
git:
11+
url: https://github.com/firebase/firebase-functions-dart
12+
ref: main
13+
http: ^1.2.0
14+
15+
dev_dependencies:
16+
build_runner: ^2.10.5
17+
lints: ^6.0.0
18+
19+
dependency_overrides:
20+
firebase_functions:
21+
git:
22+
url: https://github.com/firebase/firebase-functions-dart
23+
ref: main
24+
dart_firebase_admin:
25+
git:
26+
url: https://github.com/firebase/firebase-admin-dart
27+
path: packages/dart_firebase_admin
28+
ref: main
29+
google_cloud_firestore:
30+
git:
31+
url: https://github.com/firebase/firebase-admin-dart
32+
path: packages/google_cloud_firestore
33+
ref: main
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
.dart_tool/
2+
.packages
3+
build/
4+
*.dart_tool
5+
pubspec.lock
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Firebase SDK for Cloud Functions Quickstart - Callable Functions
2+
3+
This quickstart demonstrates how to send requests to a server-side function and get a response back using one of the Client SDKs.
4+
5+
## Introduction
6+
7+
The function `addNumbers` Returns the sum of two numbers.
8+
9+
Further reading:
10+
11+
- [Read more about Cloud Functions for Firebase](https://firebase.google.com/docs/functions/)
12+
13+
## Deploy the app to prod
14+
15+
First you need to get the `dart` dependencies of the functions:
16+
17+
```bash
18+
dart pub get
19+
```
20+
21+
Deploy to Firebase using the following command:
22+
23+
```bash
24+
firebase deploy --only functions
25+
```
26+
27+
## License
28+
29+
© Google, 2026. Licensed under an [Apache-2](../../../LICENSE) license.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright 2026 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// [START imports]
16+
// Dependencies for callable functions.
17+
import 'package:firebase_functions/firebase_functions.dart';
18+
// [END imports]
19+
20+
void main(List<String> args) async {
21+
await fireUp(args, (firebase) {
22+
// [START allAdd]
23+
// [START addFunctionTrigger]
24+
// Adds two numbers to each other.
25+
firebase.https.onCall(name: 'addNumbers', (request, response) async {
26+
// [END addFunctionTrigger]
27+
// [START readAddData]
28+
// Numbers passed from the client.
29+
final data = request.data as Map<String, Object?>?;
30+
final firstNumber = data?['firstNumber'];
31+
final secondNumber = data?['secondNumber'];
32+
// [END readAddData]
33+
34+
// [START addHttpsError]
35+
// Checking that attributes are present and are numbers.
36+
if (firstNumber is! num || secondNumber is! num) {
37+
// Throwing an HttpsError so that the client gets the error details.
38+
throw InvalidArgumentError(
39+
'The function must be called with two arguments "firstNumber" and "secondNumber" which must both be numbers.',
40+
);
41+
}
42+
// [END addHttpsError]
43+
44+
// [START authIntegration]
45+
// Authentication / user information is automatically added to the request.
46+
final uid = request.auth?.uid;
47+
final token = request.auth?.token;
48+
final name = token?['name'];
49+
final picture = token?['picture'];
50+
final email = token?['email'];
51+
52+
// Use variables to suppress 'unused' lint warnings
53+
print(
54+
'User details: uid=$uid, name=$name, picture=$picture, email=$email',
55+
);
56+
// [END authIntegration]
57+
58+
// [START returnAddData]
59+
// returning result.
60+
return CallableResult({
61+
'firstNumber': firstNumber,
62+
'secondNumber': secondNumber,
63+
'operator': '+',
64+
'operationResult': firstNumber + secondNumber,
65+
});
66+
// [END returnAddData]
67+
});
68+
// [END allAdd]
69+
});
70+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"functions": {
3+
"source": ".",
4+
"codebase": "dart-quickstarts-callable-functions"
5+
}
6+
}

0 commit comments

Comments
 (0)