When building an app, it's often a requirement to make sure your users are real and you want to know if they own the email or phone number that they signed up with so you can communicate via those channels.

In this tutorial, we'll build a flutter app that allows users to sign up and sign in with their email or phone number.

In this post:

Create a Flutter App

If this is your first time making a Flutter app, check out their installation guide. To create a new flutter app, run:

flutter create flutter_passwordless
cd flutter_passwordless

// Run your app
flutter run

Sign in with Email

Make a Welcome Page

We'll edit lib/main.dart to make our welcome page. Replace the contents with the following code:

Sign Up New Users or Login Existing Users

Notice that we only have a "Sign in" button. We've combined the sign up and login functionality. After verifying the user's email:

  • If it's a new user, the SDK will automatically create a new user in Cotter, and return the new user to you.
  • If it's a user that already exists in Cotter, the SDK will return the existing user information.

Step 1: Add Cotter to your project

Add Cotter to your pubspec.yaml , then run flutter pub get.

dependencies:
  cotter:
pubspec.yaml

Check the latest releases in pub.dev.

You may need to restart your flutter for it to run pod install (stop flutter run and re-run it).

Initialize Cotter

Import Cotter in your lib/main.dart, then initialize it inside HomePageState.

import 'package:cotter/cotter.dart'; // Import Cotter

class HomePageState extends State {
  ...

  // 1️⃣ Initialize Cotter
  Cotter cotter = new Cotter(apiKeyID: API_KEY_ID); // 👈 Specify your API KEY ID here

  // 2️⃣ TODO: Make Sign Up & Login Function
  
  _showResponse(BuildContext context, String title, String content) {...}
  
  @override
  Widget build(BuildContext context) { ... }
}
lib/main.dart

You can create a free account at Cotter to get your API_KEY_ID.

Step 2: Setup Deep-Linking

To sign in using email verification, we will open a secure in-app browser where the user can enter the OTP sent to their email. We'll need to set a redirect URL that points to your app to tell the in-app browser what to do after it's finished authenticating. Let's set the redirect URL as myexample://auth_callback.

Setup in iOS

Add the following to your ios/Runner/Info.plist.

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>Editor</string>
    <key>CFBundleURLName</key>
    <string>myexample</string> <!-- 👈 Setting scheme to myexample:// -->
    <key>CFBundleURLSchemes</key>
    <array>
      <string>myexample</string> <!-- 👈 Setting scheme to myexample://  -->
    </array>
  </dict>
</array>
ios/Runner/Info.plist

Setup in Android

Add the following to your android/app/src/main/AndroidManifest.xml.

<manifest ...>
    <application ...> 
    ...

    <!-- Add the lines from here -->
    <activity android:name=".CallbackActivity" >
      <intent-filter android:label="flutter_web_auth">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- 👇 This is for myexample://auth_callback -->
        <data android:scheme="myexample" android:host="auth_callback"/>
      </intent-filter>
    </activity>
    <!-- Until here -->

  </application>
</manifest>
android/app/src/main/AndroidManifest.xml
Now stop flutter run and re-run it.

Step 3: Make a Sign Up/Login Function

To sign in using email verification, call cotter.signInWithEmailOTP.

// 2️⃣  Make Sign Up & Login Function
  void signUpOrLogin(BuildContext context) async {
    try {
      // 🚀 Verify email, then create new user or login
      var user = await cotter.signInWithEmailOTP(
        redirectURL: "myexample://auth_callback",
        email: inputController.text,
      );

      // Show the response
      _showResponse(context, "User Created",
          "id: ${user.id}\nidentifier: ${user.identifier}");
      print(user);
    } catch (e) {
      _showResponse(context, "Error", e.toString());
    }
  }
lib/main.dart

Call the function from our button

class HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
		  ...
              // Sign In Button
              MaterialButton(
                onPressed: () {
+                  signUpOrLogin(context);
                },
                color: Colors.deepPurpleAccent,
                textColor: Colors.white,
                child: Text("Sign in With Email"),
              ),
          ...
  }
}
lib/main.dart

That's It for Email Login

Now you should be able to sign up and log in with email.

Log in with Email using Cotter's Flutter SDK

Sign In with Phone Number

Let's add another page to try signing up with a phone number. Make a new file called phoneSignIn.dart inside the lib folder:

touch lib/phoneSignIn.dart

Then, copy the code below:

import 'package:flutter/material.dart';

void main() {
  runApp(PhoneSignInPage());
}

class PhoneSignInPage extends StatefulWidget {
  @override
  PhoneSignInPageState createState() => PhoneSignInPageState();
}

// Our Home Page
class PhoneSignInPageState extends State<PhoneSignInPage> {
  final inputController = TextEditingController();

  // 1️⃣ TODO: Initialize Cotter
  // 2️⃣ TODO: Make Sign Up & Login Function

  // This is a helper function that shows the response
  // from our Sign Up and Login functions.
  _showResponse(BuildContext context, String title, String content) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text(title),
        content: Text(content),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        padding: EdgeInsets.all(20),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text("Sign in with Phone Number.",
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20)),
            Text("Open pre-built phone input form.",
                style: TextStyle(color: Colors.grey)),

            // Button to open a pre-built phone input form
            Container(
              margin: EdgeInsets.symmetric(vertical: 30),
              child: MaterialButton(
                onPressed: () {},
                color: Colors.deepPurpleAccent,
                textColor: Colors.white,
                child: Text("Sign In with Phone"),
              ),
            ),
          ],
        ),
      ),
    );
  }
}
lib/phoneSignIn.dart

Go to Phone Sign In Page from the Home Page

Let's make a button in our home page to go to this new PhoneSignInPage. Add a button in lib/main.dart.

import 'package:flutter_passwordless/phoneSignIn.dart';

class HomePageState extends State {
  @override
  Widget build(BuildContext context) {
    ...
        // Sign In Button
        MaterialButton(
          onPressed: () { signUpOrLogin(context); },
          color: Colors.deepPurpleAccent,
          textColor: Colors.white,
          child: Text("Sign In With Email"),
        ),

        // Button to go to Phone sign in
        OutlineButton(
          onPressed: () {
            Navigator.push(
              context,
              MaterialPageRoute(builder: (context) => PhoneSignInPage()),
            );
          },
          color: Colors.deepPurpleAccent,
          textColor: Colors.deepPurpleAccent,
          child: Text("Go to Phone Sign In \u2192"),
        ),
    ...
  }
}

lib/main.dart

Initialize Cotter

Just like before, import Cotter in your lib/phoneSignIn.dart, then initialize it inside HomePageState.

import 'package:cotter/cotter.dart'; // Import Cotter

class PhoneSignInPageState extends State {
  ...

  // 1️⃣ Initialize Cotter
  Cotter cotter = new Cotter(apiKeyID: API_KEY_ID); // 👈 Specify your API KEY ID here
  // 2️⃣ TODO: Make Sign Up & Login Function

  _showResponse(BuildContext context, String title, String content) {...}

  @override
  Widget build(BuildContext context) {...}
lib/phoneSignIn.dart

Open your dashboard to get your API_KEY_ID. To send SMS or WhatsApp messages, you need to add a balance to your account.

Make a Sign Up/Login Function

To sign in using phone verification, call cotter.signInWithPhoneOTP. This will open a pre-built phone number input inside the in-app browser. You can also use your own input form if you want.

  // 2️⃣ Make Sign Up & Login Function
  void signUpOrLogin(BuildContext context) async {
    try {
      // 🚀 Verify phone number, then create new user or login
      var user = await cotter.signInWithPhoneOTP(
        redirectURL: "myexample://auth_callback",
        channels: [PhoneChannel.SMS, PhoneChannel.WHATSAPP], // show SMS and WhatsApp options
      );

      // Show the response
      _showResponse(context, "Sign In Success",
          "User id: ${user.id}\nidentifier: ${user.identifier}");
      print(user);
    } catch (e) {
      _showResponse(context, "Error", e.toString());
    }
  }
lib/phoneSignIn.dart

Call the function from our button

class PhoneSignInPageState extends State<PhoneSignInPage> {
  @override
  Widget build(BuildContext context) {
  		...
            // Button to open a pre-built phone input form
            Container(
              margin: EdgeInsets.symmetric(vertical: 30),
              child: MaterialButton(
                onPressed: () {
+                  signUpOrLogin(context);
                },
                color: Colors.deepPurpleAccent,
                textColor: Colors.white,
                child: Text("Sign In with Phone"),
              ),
            ),
        ...
  }
}
lib/phoneSignIn.dart

That's It

Now you should be able to login using your phone number via SMS or WhatsApp.

Phone Login via SMS or WhatsApp using Cotter's Flutter SDK

What's Next?

The signUp and login functions that you implemented automatically create access tokens for you and store it securely within the device. It also saves the currently logged-in user and automatically refreshes your access token when it's expired. Learn how to:

  1. Implement Sign in with Device so you don't have to send an email or SMS on every login.
  2. Getting the Logged-in User
  3. Getting Access Token, ID Token, and Refresh Token

Questions & Feedback

If you have any questions or feedback, feel free to join Cotter's Slack Channel and chat us there.

Ready to use Cotter?

If you enjoyed this tutorial and want to integrate Cotter into your website or app, you can create a free account and check out our documentation.