Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/05-build-your-first-app/01-creating-endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ Create `magic_recipe_flutter/lib/screens/recipe_screen.dart`:
```dart
import 'package:flutter/material.dart';

import '../main.dart';
import '../client.dart';
import 'greetings_screen.dart';

class RecipeScreen extends StatefulWidget {
Expand Down Expand Up @@ -206,7 +206,7 @@ class _RecipeScreenState extends State<RecipeScreen> {
}
```

`client` comes from `main.dart`, where the template already wired it to talk to your server, and `ResultDisplay` is reused from `greetings_screen.dart`.
`client` comes from `client.dart`, where the template already wired it to talk to your server, and `ResultDisplay` is reused from `greetings_screen.dart`.

Now show the recipe screen instead of the greeting demo. In `magic_recipe_flutter/lib/main.dart`, add the import:

Expand All @@ -220,7 +220,7 @@ Then, in the `MyHomePage` widget, change the body from `GreetingsScreen` to `Rec
body: const RecipeScreen(),
```

Save. UI edits like this would normally hot reload, but adding the endpoint also changed the generated client. The app's `client` is created once in `main()`, which only re-runs on a restart, so the app needs a hot restart to pick up the new `client.recipe` endpoint.
Save. UI edits like this would normally hot reload, but adding the endpoint also changed the generated client. The app's `client` is created once by `initializeClient()` in `main()`, which only re-runs on a restart, so the app needs a hot restart to pick up the new `client.recipe` endpoint.

In the `serverpod start` terminal:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Update `recipe_screen.dart` to load past recipes when it opens and list them nex
import 'package:flutter/material.dart';
import 'package:magic_recipe_client/magic_recipe_client.dart';

import '../main.dart';
import '../client.dart';
import 'greetings_screen.dart';

class RecipeScreen extends StatefulWidget {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ myproject/
├── myproject_client/ # Generated client package
│ └── lib/src/protocol/ # Generated calls and models (do not edit)
└── myproject_flutter/ # Your Flutter app
├── lib/main.dart # App code: creates the global Client
├── lib/main.dart # App entry point: initializes the client, then runs the app
├── lib/client.dart # Creates the global Client that talks to the server
├── lib/screens/ # Scaffolded sign-in and greetings screens
├── lib/driver.dart # Entry point serverpod start uses to launch the app
└── assets/config.json # The server URL the app reads at startup
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,21 @@ Your app calls the method through the generated client:
var result = await client.example.hello('World');
```

The scaffolded Flutter app already creates that client in `lib/main.dart`, connected to your development server:
The scaffolded Flutter app already creates that client in `lib/client.dart`, connected to your development server, and `main()` initializes it before running the app:

```dart
// lib/client.dart
late final Client client;

void main() async {
WidgetsFlutterBinding.ensureInitialized();

final serverUrl = await getServerUrl();

client = Client(serverUrl)
Future<void> initializeClient() async {
client = Client(await getServerUrl())
..connectivityMonitor = FlutterConnectivityMonitor();
}

// lib/main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeClient();
runApp(const MyApp());
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,10 @@ fields:
A call from the client can fail in three ways, and you usually handle each one differently:

- A **serializable exception you defined** (`MyException` above): a known, app-level failure. Catch it by its type and show the user what happened. (It is sent as an HTTP 400 response with a typed payload.)
- A **`ServerpodClientException`**: something went wrong in the communication or on the server. Its typed subclasses map to HTTP status codes: `ServerpodClientBadRequest` (400), `ServerpodClientUnauthorized` (401), `ServerpodClientForbidden` (403), `ServerpodClientNotFound` (404), and `ServerpodClientInternalServerError` (500).
- A **connection failure**: when the app cannot reach the server (offline, wrong URL, or a timeout), it throws a `ServerpodClientException` with a `statusCode` of `-1`. A call that exceeds the [request size limit](../endpoints-and-apis#pass-and-return-data) fails with a generic `ServerpodClientException` with status code 413.
- A **`ServerpodClientHttpException`**: the server answered with an error status, available as `statusCode`. Its subclasses map to HTTP status codes: `ServerpodClientBadRequest` (400), `ServerpodClientUnauthorized` (401), `ServerpodClientForbidden` (403), `ServerpodClientNotFound` (404), and `ServerpodClientInternalServerError` (500). Any other status is a `ServerpodClientUnknownHttpException`, such as the 413 returned when a call exceeds the [request size limit](../endpoints-and-apis#pass-and-return-data).
- A **`ServerpodClientNetworkException`**: the app cannot reach the server (offline, wrong URL, or a timeout). There is no status code.

Both extend the sealed `ServerpodClientException`, so `on ServerpodClientException` still catches every client-side failure at once.

Calls to [streaming methods](./streaming) fail with their own connection-level exception family; see [error handling in streams](./streaming#error-handling).

Expand All @@ -213,14 +215,12 @@ try {
} on ServerpodClientUnauthorized catch (_) {
// The call requires the user to sign in.
redirectToSignIn();
} on ServerpodClientException catch (e) {
if (e.statusCode == -1) {
// Could not reach the server.
showError('Cannot reach the server. Check your connection and try again.');
} else {
// The server returned an error, for example a 500.
showError('Something went wrong. Please try again.');
}
} on ServerpodClientNetworkException catch (_) {
// Could not reach the server.
showError('Cannot reach the server. Check your connection and try again.');
} on ServerpodClientHttpException catch (e) {
// The server returned an error, for example a 500.
showError('Something went wrong (${e.statusCode}). Please try again.');
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,10 +303,10 @@ The examples below use port `8082` (Serverpod's default from `config/development

### Initialize the Google sign-in service

In your Flutter app's `main.dart`, add `initializeGoogleSignIn()` right after the existing `client.auth.initialize()` call:
In your Flutter app's `lib/client.dart`, the template creates the `Client` and calls `client.auth.initialize()` inside `initializeClient()`. Add `initializeGoogleSignIn()` right after that call:

```dart
client.auth.initialize();
unawaited(client.auth.initialize());
client.auth.initializeGoogleSignIn();
```

Expand All @@ -328,7 +328,7 @@ if (kIsWeb) {
Swap the redirect URI for your production URL when deploying. See [Configuring the web redirect URI](./customizations#configuring-the-web-redirect-uri) to avoid hard-coding it per environment.

:::warning
On web, the app Serverpod serves is the build you created in [Web setup](#web). After changing `main.dart` (for example the `redirectUri`), run the build command again and hard-reload the browser. A stale build keeps sending the old values, and sign-in fails with [redirect_uri_mismatch](./troubleshooting#sign-in-fails-with-redirect_uri_mismatch).
On web, the app Serverpod serves is the build you created in [Web setup](#web). After changing `client.dart` (for example the `redirectUri`), run the build command again and hard-reload the browser. A stale build keeps sending the old values, and sign-in fails with [redirect_uri_mismatch](./troubleshooting#sign-in-fails-with-redirect_uri_mismatch).
:::

### Show the Google sign-in button
Expand Down Expand Up @@ -406,7 +406,7 @@ body: SignInScreen(
```

:::warning
The `initializeGoogleSignIn` call lives in `main()`, and hot reload does not re-run `main()`. After making these changes, hot restart the app: press **R** in the `serverpod start` terminal, or rerun `flutter run`. Until then, the Google button stays hidden.
The `initializeGoogleSignIn` call lives in `initializeClient()`, which `main()` runs once, and hot reload does not re-run it. After making these changes, hot restart the app: press **R** in the `serverpod start` terminal, or rerun `flutter run`. Until then, the Google button stays hidden.
:::

The `SignInWidget` renders the standard Google sign-in button:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ You can also set these environment variables in your IDE's run configuration or

### Configuring the web redirect URI

You can pass the web redirect URI to `initializeGoogleSignIn` via `--dart-define`. This is useful when building for different environments (development, staging, production) without changing `main.dart`:
You can pass the web redirect URI to `initializeGoogleSignIn` via `--dart-define`. This is useful when building for different environments (development, staging, production) without changing `client.dart`:

```dart
if (kIsWeb) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ For Firebase-based projects using the Gradle plugin, make sure a Web application

## Endpoint calls fail on Android with connection refused

**Problem:** Sign-in completes at Google, but the app then fails with `ServerpodClientException: ... Connection refused ... uri=http://localhost:8080/...`.
**Problem:** Sign-in completes at Google, but the app then fails with `ServerpodClientNetworkException: ... Connection refused ... uri=http://localhost:8080/...`.

**Cause:** On Android, `localhost` is the emulator or device itself, not the machine running your server. The project template's `assets/config.json` sets `apiUrl` to `http://localhost:8080`, and that value takes precedence over the framework's platform-aware default (see [server URL resolution](../../../endpoints-and-apis)).

Expand All @@ -197,7 +197,7 @@ On the Android emulator, `10.0.2.2` maps to the host machine. On a physical devi

**Cause:** The `SignInWidget` shows the Google button when the client has a registered `GoogleIdpEndpoint` and the Google sign-in service is initialized. The common misses:

- The app was hot reloaded after adding `initializeGoogleSignIn` to `main.dart`. Hot reload does not re-run `main()`, so the service is never initialized.
- The app was hot reloaded after adding `initializeGoogleSignIn` to `client.dart`. Hot reload does not re-run `initializeClient()`, so the service is never initialized.
- `GoogleIdpEndpoint` is missing on the server, or the client was not regenerated after adding it.
- On web, `initializeGoogleSignIn` was called without `clientId` and `redirectUri`. The widget renders nothing without them.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,10 +249,10 @@ The redirect URI and `appleWebRedirectUri` were already configured in the [Store

### Initialize the Sign in with Apple service

In your Flutter app's `main.dart` file (e.g., `my_project_flutter/lib/main.dart`), the template already sets up the `Client` and calls `client.auth.initialize()`. Add `client.auth.initializeAppleSignIn()` right after it:
In your Flutter app's `lib/client.dart`, the template already sets up the `Client` and calls `client.auth.initialize()` inside `initializeClient()`. Add `client.auth.initializeAppleSignIn()` right after it:

```dart
client.auth.initialize();
unawaited(client.auth.initialize());
client.auth.initializeAppleSignIn();
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,18 +456,17 @@ For more detailed macOS setup instructions, refer to the [flutter_facebook_auth

### Initialize the Facebook sign-in service

Initialize the service in your app's `main()` function using the `initializeFacebookSignIn()` extension method on `FlutterAuthSessionManager`, on the line after `client.auth.initialize()`.
Initialize the service in your app's `lib/client.dart` using the `initializeFacebookSignIn()` extension method on `FlutterAuthSessionManager`, on the line after the existing `client.auth.initialize()` call in `initializeClient()`.

```dart
import 'package:serverpod_auth_idp_flutter/serverpod_auth_idp_flutter.dart';
import 'package:serverpod_auth_idp_flutter_facebook/serverpod_auth_idp_flutter_facebook.dart';
import 'package:your_client/your_client.dart';

final client = Client('http://localhost:8080/')
..authSessionManager = FlutterAuthSessionManager();
Future<void> initializeClient() async {
client = Client(await serverUrl)
..connectivityMonitor = FlutterConnectivityMonitor()
..authSessionManager = FlutterAuthSessionManager();

void main() {
client.auth.initialize();
unawaited(client.auth.initialize());
client.auth.initializeFacebookSignIn();
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,18 +193,14 @@ If your Flutter project folder name contains an underscore (or any character tha

### 3. Initialize Firebase and Serverpod

In your Flutter app's `main.dart` file (e.g., `my_project_flutter/lib/main.dart`), the template already sets up the `Client`. Initialize both Firebase and the Serverpod auth services:
The template creates the `Client` in `lib/client.dart` and initializes it from `main()`. Initialize Firebase before the client, in `lib/main.dart`:

```dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:serverpod_flutter/serverpod_flutter.dart';
import 'package:serverpod_auth_idp_flutter/serverpod_auth_idp_flutter.dart';
import 'package:serverpod_auth_idp_flutter_firebase/serverpod_auth_idp_flutter_firebase.dart';
import 'package:your_client/your_client.dart';
import 'firebase_options.dart';

late Client client;
import 'client.dart';
import 'firebase_options.dart';

void main() async {
WidgetsFlutterBinding.ensureInitialized();
Expand All @@ -213,15 +209,23 @@ void main() async {
options: DefaultFirebaseOptions.currentPlatform,
);

client = Client('http://localhost:8080/')
await initializeClient();
runApp(const MyApp());
}
```

Then, in `lib/client.dart`, add `initializeFirebaseSignIn()` right after the existing `client.auth.initialize()` call in `initializeClient()`:

```dart
import 'package:serverpod_auth_idp_flutter_firebase/serverpod_auth_idp_flutter_firebase.dart';

Future<void> initializeClient() async {
client = Client(await serverUrl)
..connectivityMonitor = FlutterConnectivityMonitor()
..authSessionManager = FlutterAuthSessionManager();

await client.auth.initialize();

unawaited(client.auth.initialize());
client.auth.initializeFirebaseSignIn();

runApp(const MyApp());
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,34 +244,28 @@ The examples below use port `8082` (Serverpod's default from `config/development

### Initialize the GitHub sign-in service

Open your Flutter app's `main.dart` (e.g., `my_project_flutter/lib/main.dart`). The Serverpod template already creates the `Client` and calls `client.auth.initialize()` inside `main()`. Add `client.auth.initializeGitHubSignIn(...)` on the line immediately after it.
Open your Flutter app's `lib/client.dart`. The Serverpod template already creates the `Client` and calls `client.auth.initialize()` inside `initializeClient()`. Add `client.auth.initializeGitHubSignIn(...)` on the line immediately after it.

The GitHub provider requires `clientId` and `redirectUri` on every platform because GitHub does not have native platform-specific clients (unlike Google or Apple):

```dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();

final serverUrl = await getServerUrl();

client = Client(serverUrl)
Future<void> initializeClient() async {
client = Client(await serverUrl)
..connectivityMonitor = FlutterConnectivityMonitor()
..authSessionManager = FlutterAuthSessionManager();

await client.auth.initialize();
unawaited(client.auth.initialize());
await client.auth.initializeGitHubSignIn(
clientId: 'your-github-client-id',
redirectUri: 'com.example.yourapp://auth',
);

runApp(const MyApp());
}
```

Replace `your-github-client-id` with the **Client ID** from your GitHub App, and `redirectUri` with the matching callback URL you registered: a reverse-DNS custom scheme for mobile, or the route URL from [Web](#web) for Flutter web. Swap the redirect URI for your production URL when deploying.

:::tip
To keep these values out of `main.dart` and vary them per build, read them from `--dart-define`. See [Configuring client IDs on the app](./customizations#configuring-client-ids-on-the-app) for the pattern.
To keep these values out of `client.dart` and vary them per build, read them from `--dart-define`. See [Configuring client IDs on the app](./customizations#configuring-client-ids-on-the-app) for the pattern.
:::

### Show the GitHub sign-in button
Expand Down
2 changes: 1 addition & 1 deletion docs/06-concepts/cli/commands/create/_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

`serverpod create` scaffolds a new Serverpod project. By default it generates a full server project with a database, a server package, a client package, and a Flutter app.

Pass `--template mini` for a minimal project without a database, or `--template module` to create a shareable module. To set up the prerequisites first, see [Installation](../../../../04-get-started/01-installation.md).
Pass `--template server` for a server without a Flutter app, `--template module` to create a shareable module, or `--no-database` for a project without a database. To set up the prerequisites first, see [Installation](../../../../04-get-started/01-installation.md).
Loading
Loading