> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crossmint.com/llms.txt
> Use this file to discover all available pages before exploring further.

# CrossmintEmailSignInViewModel

> Flutter Final Class

**Final Class**

Headless view model for the SDK's email + OTP sign-in flow. Owns the state machine (current step, loading flag, error, OTP challenge id, resend cooldown) and the authentication calls against `CrossmintAuthClient`, so the default-UI `CrossmintEmailSignIn` widget can become a thin presentational layer and headless consumers can reuse the same state machine behind their own design system.

```dart theme={null}
final class CrossmintEmailSignInViewModel extends ChangeNotifier
```

The view model does **not** own text controllers — those are
presentation concerns. Consumers pass the trimmed email / OTP strings
into `sendOtp` / `confirmOtp` / `resend` and own their own input widgets.

Lifecycle: consumers must call `dispose` when the view model is no longer
needed so the internal resend-cooldown timer is cancelled.

## Constructors

### CrossmintEmailSignInViewModel

```dart theme={null}
CrossmintEmailSignInViewModel({
  required CrossmintAuthClient auth,
  this.cooldownDuration = const Duration(seconds: 60),
})
```

## Properties

### cooldownDuration

```dart theme={null}
final Duration cooldownDuration
```

Duration of the resend cooldown. Defaults to 60 seconds to match the React Native Crossmint SDK. Lowered in tests to keep them fast.

### step

```dart theme={null}
CrossmintEmailSignInStep get step
```

Current step in the sign-in flow.

### isLoading

```dart theme={null}
bool get isLoading
```

True while a network call (send, confirm, or resend) is in flight.

### error

```dart theme={null}
String? get error
```

Localized error message from the last failed operation, or `null` if the flow is in a clean state.

### emailId

```dart theme={null}
String? get emailId
```

The OTP challenge id returned by `CrossmintAuthClient.sendEmailOtp`, required for the subsequent confirm call. `null` until `sendOtp` succeeds.

### submittedEmail

```dart theme={null}
String get submittedEmail
```

The email address the user successfully submitted. Empty string until `sendOtp` succeeds.

### cooldownRemaining

```dart theme={null}
int get cooldownRemaining
```

Seconds remaining before `resend` becomes available again. Zero when the cooldown is not active.

### canResend

```dart theme={null}
bool get canResend
```

True when `resend` can be invoked: the cooldown has elapsed and no operation is currently in flight.

## Methods

### sendOtp

```dart theme={null}
Future<void> sendOtp(String email)
```

Send an OTP challenge for `email`. On success, transitions to `CrossmintEmailSignInStep.otpInput` and starts the resend cooldown. On failure, stores the error message on `error` and stays on the email-input step.

### confirmOtp

```dart theme={null}
Future<bool> confirmOtp(String code)
```

Confirm the OTP challenge with `code`. Returns `true` if the verification succeeded, `false` if the code was rejected, and rethrows on a non-validation error (after storing the error on `error` and notifying listeners).

### resend

```dart theme={null}
Future<void> resend()
```

Re-send the OTP challenge for the previously-submitted email. No-op when the resend cooldown has not yet elapsed or when an operation is already in flight. On success the cooldown restarts; on failure the error is stored on `error` and the cooldown is not touched.

### goBackToEmail

```dart theme={null}
void goBackToEmail()
```

Return to the email-input step. Cancels the resend cooldown, clears any displayed error, and preserves `submittedEmail` / `emailId` in case the consumer wants to re-use them (e.g. for a "wrong email" confirmation dialog).

### dispose

```dart theme={null}
void dispose()
```
