Fliin
On this page

Blocks / auth

Provider sign-in

Lets people sign in with Apple, Google or another identity provider: one outlined button per provider, in a column or a row of logos, under an "or" divider.

16 of 16 variants pass
Provider sign-in, column state
Platform
Brightness
Text scale
Direction

Import

Provider sign-in is part of Fliin Pro. With a license key, sign in once with fliin login, then add it in a Flutter app where fliin init has run:

fliin login
fliin add social-sign-in

Also installs: core, button, separator

Examples

Column

One full-width button per provider, then a divider and its label before the form. Put each provider's official logo in icon.

Provider sign-in, column state
import 'package:your_app/ui/fliin/components/social_sign_in/fl_social_sign_in.dart';
import 'package:your_app/ui/fliin/core/fliin.dart';
import 'package:flutter/widgets.dart';

class SocialSignInColumnExample extends StatelessWidget {
  const SocialSignInColumnExample({super.key});

  @override
  Widget build(BuildContext context) {
    return FlSocialSignIn(
      dividerLabel: 'or',
      providers: [
        FlSocialProvider(label: 'Continue with Acme', icon: const _Monogram('Acme'), onPressed: () {}),
        FlSocialProvider(label: 'Continue with Globex', icon: const _Monogram('Globex'), onPressed: () {}),
      ],
    );
  }
}

/// Stands in for a provider's logo until the app puts in the official one:
/// its initial in a square, at the size and color of the icon.
class _Monogram extends StatelessWidget {
  const _Monogram(this.name);

  final String name;

  @override
  Widget build(BuildContext context) {
    final icon = IconTheme.of(context);
    final theme = FlScope.of(context);
    final size = icon.size!;
    return ExcludeSemantics(
      child: SizedBox.square(
        dimension: size,
        child: DecoratedBox(
          decoration: ShapeDecoration(
            shape: FlContinuousBorder(
              radius: theme.shape.radiusXs,
              side: BorderSide(color: icon.color!, width: theme.size.border),
            ),
          ),
          child: Center(
            child: Text(
              name[0].toUpperCase(),
              textScaler: TextScaler.noScaling,
              style: TextStyle(
                color: icon.color,
                fontSize: size * 0.6,
                height: 1,
                fontWeight: theme.tokens.typography.weight.semibold,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Row

FlSocialSignInLayout.row sets three or more providers side by side, each named by its logo; the label stays for the screen reader.

Provider sign-in, row state
import 'package:your_app/ui/fliin/components/social_sign_in/fl_social_sign_in.dart';
import 'package:your_app/ui/fliin/core/fliin.dart';
import 'package:flutter/widgets.dart';

class SocialSignInRowExample extends StatelessWidget {
  const SocialSignInRowExample({super.key});

  @override
  Widget build(BuildContext context) {
    return FlSocialSignIn(
      layout: FlSocialSignInLayout.row,
      dividerLabel: 'or',
      providers: [
        FlSocialProvider(label: 'Continue with Acme', icon: const _Monogram('Acme'), onPressed: () {}),
        FlSocialProvider(label: 'Continue with Globex', icon: const _Monogram('Globex'), onPressed: () {}),
        FlSocialProvider(label: 'Continue with Initech', icon: const _Monogram('Initech'), onPressed: () {}),
      ],
    );
  }
}

/// Stands in for a provider's logo until the app puts in the official one:
/// its initial in a square, at the size and color of the icon.
class _Monogram extends StatelessWidget {
  const _Monogram(this.name);

  final String name;

  @override
  Widget build(BuildContext context) {
    final icon = IconTheme.of(context);
    final theme = FlScope.of(context);
    final size = icon.size!;
    return ExcludeSemantics(
      child: SizedBox.square(
        dimension: size,
        child: DecoratedBox(
          decoration: ShapeDecoration(
            shape: FlContinuousBorder(
              radius: theme.shape.radiusXs,
              side: BorderSide(color: icon.color!, width: theme.size.border),
            ),
          ),
          child: Center(
            child: Text(
              name[0].toUpperCase(),
              textScaler: TextScaler.noScaling,
              style: TextStyle(
                color: icon.color,
                fontSize: size * 0.6,
                height: 1,
                fontWeight: theme.tokens.typography.weight.semibold,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Loading

While a provider signs in, its button shows a spinner and the others are disabled with onPressed: null.

Provider sign-in, loading state
import 'package:your_app/ui/fliin/components/social_sign_in/fl_social_sign_in.dart';
import 'package:your_app/ui/fliin/core/fliin.dart';
import 'package:flutter/widgets.dart';

class SocialSignInLoadingExample extends StatelessWidget {
  const SocialSignInLoadingExample({super.key});

  @override
  Widget build(BuildContext context) {
    return FlSocialSignIn(
      providers: [
        const FlSocialProvider(label: 'Continue with Acme', icon: _Monogram('Acme'), loading: true, onPressed: null),
        const FlSocialProvider(label: 'Continue with Globex', icon: _Monogram('Globex'), onPressed: null),
      ],
    );
  }
}

/// Stands in for a provider's logo until the app puts in the official one:
/// its initial in a square, at the size and color of the icon.
class _Monogram extends StatelessWidget {
  const _Monogram(this.name);

  final String name;

  @override
  Widget build(BuildContext context) {
    final icon = IconTheme.of(context);
    final theme = FlScope.of(context);
    final size = icon.size!;
    return ExcludeSemantics(
      child: SizedBox.square(
        dimension: size,
        child: DecoratedBox(
          decoration: ShapeDecoration(
            shape: FlContinuousBorder(
              radius: theme.shape.radiusXs,
              side: BorderSide(color: icon.color!, width: theme.size.border),
            ),
          ),
          child: Center(
            child: Text(
              name[0].toUpperCase(),
              textScaler: TextScaler.noScaling,
              style: TextStyle(
                color: icon.color,
                fontSize: size * 0.6,
                height: 1,
                fontWeight: theme.tokens.typography.weight.semibold,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Tertiary

Filled buttons with variant: FlButtonVariant.tertiary, at the size of the main button with size, above the form: the divider goes after.

Provider sign-in, tertiary state
import 'package:your_app/ui/fliin/components/button/fl_button.dart';
import 'package:your_app/ui/fliin/components/social_sign_in/fl_social_sign_in.dart';
import 'package:your_app/ui/fliin/core/fliin.dart';
import 'package:flutter/widgets.dart';

class SocialSignInTertiaryExample extends StatelessWidget {
  const SocialSignInTertiaryExample({super.key});

  @override
  Widget build(BuildContext context) {
    return FlSocialSignIn(
      variant: FlButtonVariant.tertiary,
      size: FlButtonSize.lg,
      dividerLabel: 'or',
      divider: FlSocialSignInDivider.after,
      providers: [
        FlSocialProvider(label: 'Continue with Acme', icon: const _Monogram('Acme'), onPressed: () {}),
        FlSocialProvider(label: 'Continue with Globex', icon: const _Monogram('Globex'), onPressed: () {}),
      ],
    );
  }
}

/// Stands in for a provider's logo until the app puts in the official one:
/// its initial in a square, at the size and color of the icon.
class _Monogram extends StatelessWidget {
  const _Monogram(this.name);

  final String name;

  @override
  Widget build(BuildContext context) {
    final icon = IconTheme.of(context);
    final theme = FlScope.of(context);
    final size = icon.size!;
    return ExcludeSemantics(
      child: SizedBox.square(
        dimension: size,
        child: DecoratedBox(
          decoration: ShapeDecoration(
            shape: FlContinuousBorder(
              radius: theme.shape.radiusXs,
              side: BorderSide(color: icon.color!, width: theme.size.border),
            ),
          ),
          child: Center(
            child: Text(
              name[0].toUpperCase(),
              textScaler: TextScaler.noScaling,
              style: TextStyle(
                color: icon.color,
                fontSize: size * 0.6,
                height: 1,
                fontWeight: theme.tokens.typography.weight.semibold,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Usage

The code the reference images render:

final example = ColoredBox(
  color: FlScope.of(context).colors.surface,
  child: SingleChildScrollView(
    padding: EdgeInsetsDirectional.all(FlScope.of(context).space.lg),
    child: Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      spacing: FlScope.of(context).space.xxl,
      children: [
        FlSocialSignIn(
          dividerLabel: 'or',
          providers: [
            FlSocialProvider(
              label: 'Continue with Acme',
              icon: const FlLogoSlot(),
              onPressed: () {},
            ),
            FlSocialProvider(
              label: 'Continue with Globex',
              icon: const FlLogoSlot(),
              onPressed: () {},
            ),
          ],
        ),
        FlSocialSignIn(
          layout: FlSocialSignInLayout.row,
          dividerLabel: 'or',
          providers: [
            FlSocialProvider(
              label: 'Continue with Acme',
              icon: const FlLogoSlot(),
              onPressed: () {},
            ),
            FlSocialProvider(
              label: 'Continue with Globex',
              icon: const FlLogoSlot(),
              onPressed: () {},
            ),
            FlSocialProvider(
              label: 'Continue with Initech',
              icon: const FlLogoSlot(),
              onPressed: () {},
            ),
          ],
        ),
        FlSocialSignIn(
          providers: [
            FlSocialProvider(
              label: 'Continue with Acme',
              icon: const FlLogoSlot(),
              onPressed: null,
              loading: true,
            ),
            FlSocialProvider(
              label: 'Continue with Globex',
              icon: const FlLogoSlot(),
              onPressed: null,
              loading: false,
            ),
          ],
        ),
      ],
    ),
  ),
);

API

FlSocialProvider: final class
FlSocialProvider.new: const new({required String label, required Widget icon, required VoidCallback? onPressed, bool loading, String? shortLabel})
FlSocialProvider.label: get String
FlSocialProvider.icon: get Widget
FlSocialProvider.onPressed: get VoidCallback?
FlSocialProvider.loading: get bool
FlSocialProvider.shortLabel: get String?
FlSocialSignInLayout: enum {column, row}
FlSocialSignInLayout.column: static final FlSocialSignInLayout
FlSocialSignInLayout.row: static final FlSocialSignInLayout
FlSocialSignInDivider: enum {before, after}
FlSocialSignInDivider.before: static final FlSocialSignInDivider
FlSocialSignInDivider.after: static final FlSocialSignInDivider
FlSocialSignIn: class extends StatelessWidget
FlSocialSignIn.new: const new({super.key, required List<FlSocialProvider> providers, String? dividerLabel, FlSocialSignInDivider divider, FlSocialSignInLayout layout, FlButtonVariant variant, FlButtonSize? size})
FlSocialSignIn.providers: get List<FlSocialProvider>
FlSocialSignIn.dividerLabel: get String?
FlSocialSignIn.divider: get FlSocialSignInDivider
FlSocialSignIn.layout: get FlSocialSignInLayout
FlSocialSignIn.variant: get FlButtonVariant
FlSocialSignIn.size: get FlButtonSize?
FlSocialSignIn.build: Widget(BuildContext context)
FlLogoSlot: class extends StatelessWidget
FlLogoSlot.new: const new({super.key})
FlLogoSlot.build: Widget(BuildContext context)

Reference renders

The 16 images published with version 0.2.4, rendered by the shared harness on macOS with Flutter 3.47.7. fliin verify social-sign-in compares your copy to them.

iOS · Light
Provider sign-in, iOS, Light, text 1.0×, Left to right
Text 1.0× · Left to right
Provider sign-in, iOS, Light, text 1.0×, Right to left
Text 1.0× · Right to left
Provider sign-in, iOS, Light, text 2.0×, Left to right
Text 2.0× · Left to right
Provider sign-in, iOS, Light, text 2.0×, Right to left
Text 2.0× · Right to left
iOS · Dark
Provider sign-in, iOS, Dark, text 1.0×, Left to right
Text 1.0× · Left to right
Provider sign-in, iOS, Dark, text 1.0×, Right to left
Text 1.0× · Right to left
Provider sign-in, iOS, Dark, text 2.0×, Left to right
Text 2.0× · Left to right
Provider sign-in, iOS, Dark, text 2.0×, Right to left
Text 2.0× · Right to left
Android · Light
Provider sign-in, Android, Light, text 1.0×, Left to right
Text 1.0× · Left to right
Provider sign-in, Android, Light, text 1.0×, Right to left
Text 1.0× · Right to left
Provider sign-in, Android, Light, text 2.0×, Left to right
Text 2.0× · Left to right
Provider sign-in, Android, Light, text 2.0×, Right to left
Text 2.0× · Right to left
Android · Dark
Provider sign-in, Android, Dark, text 1.0×, Left to right
Text 1.0× · Left to right
Provider sign-in, Android, Dark, text 1.0×, Right to left
Text 1.0× · Right to left
Provider sign-in, Android, Dark, text 2.0×, Left to right
Text 2.0× · Left to right
Provider sign-in, Android, Dark, text 2.0×, Right to left
Text 2.0× · Right to left

Use it to

  • offer sign in with Apple or Google below a form
  • separate the form from identity providers with an or divider
  • show provider logos side by side
  • show that a provider sign in is in progress
  • let users sign up with an existing provider account
Not for
  • an ordinary action button: use a button (button)
  • a line between two pieces of content: use a separator (separator)
States
  • Column
  • Row
  • Loading

Arrows to move, Enter to open, Escape to close.