If you're building an app, starting with the increasingly popular passwordless login is the way forward – it's secure, easy, and frictionless for the user.

In this tutorial, we'll create a Flutter app from scratch that allows users to sign in with their device.

How it works.

When a user first signs up, we'll automatically trust the current device. This means that the user can automatically log in when accessing your app from this device. When the user wants to access the app from another device, the user can simply tap a prompt in your app to approve the login request.

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 new app:

flutter run

Make a Login Page

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

We combined the Sign-Up and Login page into one because they both only require the user's email to identify the user.

Sign Up New Users

When a new user presses sign up, we'll register the user and set the current device as trusted to allow automatic logins from this device.

Add Dependencies

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 Function
  // 3️⃣ TODO: Make Login Function
  
  _showResponse(BuildContext context, String title, String content) {...}
  
  @override
  Widget build(BuildContext context) { ... }
}

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

Make a Sign-Up Function

To sign up, call cotter.signUpWithDevice and input the user's email. This will create a new user in Cotter and trust the current device to allow logins.

  // 2️⃣  Make Sign Up Function
  void signUp(BuildContext context) async {
    try {
      // 🚀 One-line Sign Up
      var user = await cotter.signUpWithDevice(identifier: inputController.text);
      // This will create a new user in Cotter and trust the current device
          
      // 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

Make a Login Function

To sign in, call cotter.signInWithDevice . If the user is logging in from a device that they trust, they'll automatically be approved.

  // 3️⃣  Make Login Function
  void login(BuildContext context) async {
    try {
      // 🚀 One-line Login
      var event = await cotter.signInWithDevice(identifier: inputController.text, context: context);
      
      // Show the response
      _showResponse(context, event.approved ? "Login Success" : "Login Failed", "User id: ${event.userID}");
      print(event);
    } catch (e) {
      _showResponse(context, "Error", e.toString());
    }
  }
lib/main.dart

If the current device is not trusted, this will open a prompt asking the user to approve the request. Check out how to log in from a non-trusted device in our documentation.

Call the Sign-Up and Login Functions from our buttons

class HomePageState extends State<HomePage> {
  @override
  Widget build(BuildContext context) {
  		...
            MaterialButton(
              onPressed: () {
+                signUp(context);
              },
              color: Colors.deepPurpleAccent,
              textColor: Colors.white,
              child: Text("Sign Up"),
            ),
            OutlineButton(
              onPressed: () {
+                login(context);
              },
              child: Text("Sign In With Device"),
              highlightedBorderColor: Colors.deepPurpleAccent,
              textColor: Colors.deepPurpleAccent,
            ),
  		...
  }
}
lib/main.dart

That's it

You should be able to sign up and log in with an email address.

You can also check your terminal to see the returned User and Event objects from the signup and login functions.

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 refresh your access token when it's expired. Learn how to:

  1. Approve login requests from a non-trusted device
  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.