Привет всем, добро пожаловать обратно в мой очередной учебник. В этом руководстве я покажу вам, как реализовать аутентификацию пользователя с помощью 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. Спасибо, увидимся на следующем уроке, пока!