Привет всем, добро пожаловать обратно в мой очередной учебник. В этом руководстве я покажу вам, как реализовать аутентификацию пользователя с помощью Supabase с Flutter (Riverpod как управление состоянием). К вашему сведению, Supabase — это альтернатива Firebase с открытым исходным кодом. Другими словами, Supabase спроектирована как Firebase, чтобы каждый разработчик мобильного или внешнего интерфейса мог создавать свои внутренние службы. Firebase и Supabase имеют одну и ту же цель, но все же есть какие-то различия. Один из них, Firebase, использует реляционные базы данных, точнее, Supabase использует PostgreSQL.

Хорошо, давайте перейдем к нашему уроку! Во-первых, вам нужно создать свою учетную запись на https://supabase.com/. Большой! Если у вас есть учетная запись, давайте создадим проект. Ваши проекты будут отображаться на панели инструментов https://app.supabase.com/projects.

Следующий шаг, давайте настроим наш проект Supabse на наш проект Flutter. В main.dart вам нужно подключить свой проект Supabase, добавив URL-адрес и ключ anon, например:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Supabase.initialize(
      // TODO CHANGE WITH YOURS
      /*
    YOU CAN READ THIS DOCUMENTATION
    https://supabase.com/docs/guides/api
    */
      url: 'YOUR URL',
      anonKey: 'YOUR ANONKEY');
  runApp(const MyApp());
}

Если у вас возникли проблемы с поиском вашего URL-адреса и анонимного проекта, вы можете перейти по ссылке https://supabase.com/docs/guides/api. Ах да, во-первых, вам нужно добавить пакет supabase_flutter в ваш файл pubspec.yaml.

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class MediumSignUpScreen extends ConsumerStatefulWidget {
  const MediumSignUpScreen({super.key});

  @override
  ConsumerState<ConsumerStatefulWidget> createState() =>
      _MediumSignUpScreenState();
}

class _MediumSignUpScreenState extends ConsumerState<MediumSignUpScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Supabase x Riverpod'),
        centerTitle: true,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(24),
        child: Column(
          children: [
            TextFormField(
              decoration: InputDecoration(
                filled: true,
                contentPadding: const EdgeInsets.all(18),
                hintText: 'Email',
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(
                    8,
                  ),
                  borderSide: const BorderSide(
                    color: Colors.grey,
                  ),
                ),
              ),
            ),
            const SizedBox(
              height: 10,
            ),
            TextFormField(
              decoration: InputDecoration(
                filled: true,
                hintText: 'Password',
                contentPadding: const EdgeInsets.all(18),
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(
                    8,
                  ),
                  borderSide: const BorderSide(
                    color: Color.fromARGB(255, 222, 222, 222),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Выше я создаю простой экран регистрации. Следующий шаг, давайте создадим репозиторий. Я заставлю нашу функцию работать с простыми структурными файлами.

import 'dart:developer';
import 'package:auth_supabase/model/auth_model.dart';
import 'package:supabase_flutter/supabase_flutter.dart' as supabase;
import 'package:flutter_riverpod/flutter_riverpod.dart';

final mediumAuthRepositoryProvider = Provider((ref) {
  return MediumAuthRepository(supabase: supabase.Supabase.instance);
});

// PROVIDER REPOSITORY
class MediumAuthRepository {
  final supabase.Supabase _supabase;
  MediumAuthRepository({
    required supabase.Supabase supabase,
  }) : _supabase = supabase;

  // SIGN UP
  Future<AuthModel> signUp(
      {required String email, required String password}) async {
    log('EMAIL $email password $password');
    try {
      AuthModel authModel =
          AuthModel(uid: '', email: '', password: '', imgUrl: '');
      final response =
          await _supabase.client.auth.signUp(email: email, password: password);
      authModel = AuthModel(
          uid: response.user!.id, email: email, password: password, imgUrl: '');
      log('AUTH MODEL $authModel');
      return authModel;
    } catch (e) {
      log('ERROR $e');
      throw 'Error';
    }
  }

Хорошо, я обновлю свой файл medium_auth_scree.dart, потому что нам нужно реализовать нашу функцию, которую мы создали. Давайте сделаем это быстро.

import 'package:auth_supabase/repository/medium_auth_repository.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class MediumSignUpScreen extends ConsumerStatefulWidget {
  const MediumSignUpScreen({super.key});

  @override
  ConsumerState<ConsumerStatefulWidget> createState() =>
      _MediumSignUpScreenState();
}

class _MediumSignUpScreenState extends ConsumerState<MediumSignUpScreen> {
  final _emailC = TextEditingController();
  final _passwordC = TextEditingController();

  void signUp() {
    ref
        .read(mediumAuthRepositoryProvider)
        .signUp(email: _emailC.text, password: _passwordC.text);
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Supabase x Riverpod'),
        centerTitle: true,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(24),
        child: Column(
          children: [
            TextFormField(
              controller: _emailC,
              decoration: InputDecoration(
                filled: true,
                contentPadding: const EdgeInsets.all(18),
                hintText: 'Email',
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(
                    8,
                  ),
                  borderSide: const BorderSide(
                    color: Colors.grey,
                  ),
                ),
              ),
            ),
            const SizedBox(
              height: 10,
            ),
            TextFormField(
              controller: _passwordC,
              decoration: InputDecoration(
                filled: true,
                hintText: 'Password',
                contentPadding: const EdgeInsets.all(18),
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(
                    8,
                  ),
                  borderSide: const BorderSide(
                    color: Color.fromARGB(255, 222, 222, 222),
                  ),
                ),
              ),
            ),
            const SizedBox(
              height: 50,
            ),
            ElevatedButton(
              style: ButtonStyle(
                fixedSize: MaterialStateProperty.all(
                  Size(MediaQuery.of(context).size.width, 50),
                ),
                padding: MaterialStateProperty.all(
                  const EdgeInsets.symmetric(vertical: 10),
                ),
                shape: MaterialStateProperty.all(
                  RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(10),
                  ),
                ),
              ),
              onPressed: () => signUp(),
              child: const Text(
                'Login',
                style: TextStyle(
                  fontSize: 20,
                  fontWeight: FontWeight.w700,
                  color: Colors.white,
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Если вы запустите код выше, вы получите ответ на консоли отладки.

Потрясающий! Давайте посмотрим на нашу панель управления Supabase.

Прохладный! Мы успешно создали пользователя с Riverpod. Хорошо, следующим шагом мы войдем в систему с пользователем данных (электронная почта и пароль), который уже зарегистрирован в нашей базе Supabase. Давайте создадим функцию входа в наш файл medium_auth_repository.dart.

Войти в Supabase очень просто, нам просто нужно вызвать метод signInWithPassword из Supabase. Довольно просто, правда? Итак, наш метод входа в medium_auth_repository должен выглядеть следующим образом:

Future<AuthModel> signIn(
      {required String email, required String password}) async {
    try {
      AuthModel authModel =
          AuthModel(uid: '', email: '', password: '', imgUrl: '');
      final response = await _supabase.client.auth
          .signInWithPassword(email: email, password: password);
      log('RESPONSE ${response.user}');
      authModel = AuthModel(
          uid: response.user!.id, email: email, password: password, imgUrl: '');
      log('SIGN IN $authModel');
      return authModel;
    } catch (e) {
      log('ERROR SIGN IN $e');
      throw 'Error';
    }
  }
void signIn() {
    ref
        .read(mediumAuthRepositoryProvider)
        .signIn(email: _emailC.text, password: _passwordC.text);
  }

Я добавил метод входа в файл medium_auth_screen.dart, и я реализую функцию onPressed.

Вуаля! Мы успешно вошли в систему с учетной записью, которую мы создали. Последняя часть этого руководства — выход. Давайте сделаем это быстро. Давайте создадим функцию выхода в нашем репозитории.

Как мы узнаем, хорошо ли работает наша функция выхода? Просто, если вы вошли в систему, перезапустите приложение, вы можете увидеть информацию о пользователе в консоли отладки. Если наша функция выхода работает хорошо, всякий раз, когда мы перезапускаем наше приложение, мы не видим никакой информации о пользователе в нашей консоли отладки.

  Future<void> signOut() async {
    try {
      _supabase.client.auth.signOut();
    } catch (e) {
      log('ERROR SIGN OUT $e');
      throw 'Error';
    }
  }

Я добавил функцию signOut в свой репозиторий. Давайте вызовем эту функцию на наш экран:

void signOut() {
    ref.read(mediumAuthRepositoryProvider).signOut();
  }

Очень просто. Давайте реализуем нашу функцию выхода для нашей кнопки onPressed.

ElevatedButton(
                onPressed: () => signOut(), child: const Text('Sign Out')),

Ладно, можешь попробовать со своим. Если наша функция выхода работает хорошо, как я уже сказал, всякий раз, когда вы перезапускаете приложение, в нашей консоли отладки нет информации ни об одном пользователе. Прохладный!

Ладно, ребята, это так. Наш урок по аутентификации с использованием Supabase и Riverpod завершен. Я думаю, вы можете понять, как работает Riverpod. Если вас это смущает, вы можете прочитать мою серию Riverpod. Спасибо, увидимся на следующем уроке, пока!