ایجاد یک فرم لاگین ساده در فلاتر با الگوی BLoC — از صفر تا صد

۴۶۰ بازدید
آخرین به‌روزرسانی: ۰۱ مهر ۱۴۰۲
زمان مطالعه: ۶ دقیقه
ایجاد یک فرم لاگین ساده در فلاتر با الگوی BLoC — از صفر تا صد

فلاتر یک SDK است که امکان ساخت اپلیکیشن‌هایی برای چند پلتفرم شامل پلتفرم‌های موبایل را از کدبیس منفرد فراهم ساخته است. اگر تجربه کدنویسی با این فریمورک را داشته باشید، می‌دانید که امکان ساخت کل اپلیکیشن با استفاده از یک فایل منفرد در فلاتر وجود دارد، گرچه روش ایده‌آلی محسوب نمی‌شود. در این مقاله با روش ایجاد یک فرم لاگین ساده در فلاتر با بهره‌گیری از الگوی BLoC آشنا می‌شویم.

زیبایی یک پروژه در معماری اپلیکیشن نهفته است. هنگامی که پروژه را به بخش‌های منطقی تقسیم‌بندی می‌کنیم که هریک به اندازه بقیه مهم هستند، نه‌تنها به ارتقای قابلیت نگهداری پروژه کمک می‌کنیم، بلکه به درک بهتر پروژه نیز کمک کرده‌ایم.

از میان الگوهای معماری موجود برای فلاتر می‌توان از الگوهای Vanilla ،Scoped Model و BLoC به عنوان سه مورد از رایج‌ترین گزینه‌ها استفاده کرد. استفاده از الگوی BLoC توصیه می‌شود زیرا واکنشی است، منطق تجاری، مدیریت حالت و منطق UI را جدا می‌کند و از سوی جامعه توسعه‌دهندگان گوگل نیز پیشنهاد شده است.

در این راهنما تلاش می‌کنیم به توسعه‌دهندگان موبایل که تاکنون از فلاتر استفاده نکرده‌اند کمک کنیم تا این فریمورک را بهتر درک کنند. بنابراین در ادامه یک اپلیکیشن ساده فلاتر با استفاده از الگوی BLoC ایجاد می‌کنیم. پیش‌فرض ما بر این است که شما با مبانی توسعه با استفاده از اندروید استودیو آشنا هستید.

شروع

کار خود را با ایجاد یک پروژه جدید فلاتر با استفاده از اندروید استودیو و تعیین نام login_demo برای آن آغاز می‌کنیم.

در ادامه همه کدهای تولیدشده خودکار را از فایل main.dart پاک کرده و تک خط زیر و گزاره import را وارد کنید:

import ‘package:flutter/material.dart’;

void main() => runApp(App());

همواره بهتر است که فایل main تا حد امکان تمیز بماند. در ادامه یک ساختار پکیج مناسب برای اپلیکیشن به صورت زیر ارائه شده است:

 فرم لاگین ساده در فلاتر

زمانی که ساختار پوشه پایان یافت، یک فایل به نام app.dart درون src folder ایجاد می‌کنیم و کد زیر را درون آن قرار می‌دهیم:

1class App extends StatelessWidget {
2 @override
3 Widget build(BuildContext context) {
4 return MaterialApp(
5 title: ‘Login Demo’,
6 theme: ThemeData(
7 primarySwatch: Colors.blue,
8 ),
9 home: createContent(),
10 );
11 }
12 createContent() {
13 return Text(“Login Demo app”);
14 }
15}

تا به اینجا صرفاً یک ویجت ریشه اپلیکیشن ایجاد کرده‌ایم. اینک می‌توانیم آن را اجرا کرده و یک صفحه سیاه با متن Login Demo app به رنگ قرمز ببینیم. پیش از نوشتن کد بیشتر، کمی در مورد کاری که می‌خواهیم انجام دهیم و چگونگی آن توضیح می‌دهیم.

ما یک اپلیکیشن ساده فلاتر ایجاد می‌کنیم که از الگوی BLoC پیروی می‌کند و شامل دو صفحه به نام‌های صفحه ورود (Login) و صفحه اصلی (Home) است. صفحه ورود باید به کاربر امکان وارد کردن اطلاعات احراز هویت معتبر (ایمیل و رمز عبور) را بدهد و با شبیه‌سازی شبکه کاربر وارد شود. هر بار که یک نشست باز می‌شود کاربر را به صفحه اصلی هدایت می‌کنیم.

زمانی که از الگوی BLoC استفاده می‌کنیم، هر صفحه یک فایل BLoC مرتبط خواهد داشت. در ادامه یک فایل BLoC برای صفحه اپلیکیشن که در بخش قبلی ایجاد کردیم می‌سازیم. به منوی New->Dart File بروید و نام فایل جدید را authorization_BLoC.dart بگذارید. آن را درون پکیج BLoCs نگه دارید. کد زیر را درون این فایل قرار دهید:

1import ‘package:rxdart/rxdart.dart’;
2import ‘package:shared_preferences/shared_preferences.dart’;
3class AuthorizationBloc {
4 String _tokenString = “”;
5 final PublishSubject _isSessionValid = PublishSubject<bool>();
6 Observable<bool> get isSessionValid => _isSessionValid.stream;
7void dispose() {
8 _isSessionValid.close();
9 }
10void restoreSession() async {
11 SharedPreferences prefs = await SharedPreferences.getInstance();
12 _tokenString = prefs.get(“token”);
13 if (_tokenString != null && _tokenString.length > 0) {
14 _isSessionValid.sink.add(true);
15 } else {
16 _isSessionValid.sink.add(false);
17 }
18 }
19void openSession(String token) async {
20 SharedPreferences prefs = await SharedPreferences.getInstance();
21 await prefs.setString(“token”, token);
22 _tokenString = token;
23 _isSessionValid.sink.add(true);
24 }
25void closeSession() async {
26 SharedPreferences prefs = await SharedPreferences.getInstance();
27 prefs.remove(“token”);
28 _isSessionValid.sink.add(false);
29 }
30}
31final authBloc = AuthorizationBloc();

این BLoC درون اپلیکیشن برای حفظ نشست (Session) اپلیکیشن مورد استفاده قرار می‌گیرد.

این BLoC شامل 3 تابع است که هر یک به صورتی نامگذاری شده که مقصود آن را توضیح می‌دهد:

  1. openSession(String Token) – این تابع مسئول باز کردن نشست کاربر در زمان وارد شدن وی است.
  2. closeSession – این تابع مسئول بستن نشست کاربر در مواردی است که کاربرمی خواهد از اپلیکیشن خارج شود.
  3. restoreSession – این تابع مسئول بازیابی نشست کاربر در مواردی است که اپلیکیشن از حالت خاتمه یافته باز می‌شود.

اکنون به پیکربندی BLoC در فایل app.dart می‌پردازیم و تابع createContent()‎ را با کد زیر به‌روزرسانی می‌کنیم:

1createContent() {
2 return StreamBuilder<bool> (
3 stream: authBloc.isSessionValid,
4 builder: (context, AsyncSnapshot<bool> snapshot){
5 if (snapshot.hasData && snapshot.data) {
6 return HomeScreen();
7 }
8 return LoginScreen();
9 });
10}

خط ;()authBLoC.restoreSession را در ابتدای متد build(BuildContext context) در ویجت اضافه کنید. اکنون متد نهایی به صورت زیر درمی‌آید:

1@override
2Widget build(BuildContext context) {
3authBloc.restoreSession();
4return MaterialApp(
5title: ‘Login Demo’,
6theme: ThemeData(
7primarySwatch: Colors.blue,
8),
9home: createContent(),
10);
11}

با اعمال این تغییرات، مقداری پیشرفت به دست آمده است، هر بار که نشست کاربر به‌روزرسانی می‌شود، UI نیز بر اساس آن به‌روزرسانی خواهد شد. فعلاً خطاها را نادیده بگیرید. سپس دو کلاس برای شبیه‌سازی عملیات شبکه برای ورود کاربر ایجاد می‌کنیم. یک فایل dart درون پکیج resources ایجاد کرده و نام آن را repository.dart می‌گذاریم. کد زیر را در این فایل قرار دهید:

1import ‘auth_provider.dart’;
2class Repository {
3final AuthProvider authProvider = AuthProvider();
4Future<String> login(String email, String password) => authProvider.login(email: email, password: password);
5}

فایل دیگری برای مدیریت همه عملیات احراز هویت شبکه ایجاد کنید. آن را درون همان پکیج حفظ کنید و نام آن را auth_provider.dart گذارده و کد زیر را در آن بنویسید:

1import ‘package:flutter/material.dart’;
2class AuthProvider {
3 Future<String> login({
4 @required String email,
5 @required String password,
6 }) async {
7 await Future.delayed(Duration(seconds: 1));
8 return ‘token-info’;
9 }
10}

در این زمان صرفاً یک توکن نشست را بدون هیچ عملیاتی بازگشت می‌دهیم و سپس پاسخ را به مدت 1 ثانیه به تأخیر می‌اندازیم. اینک فایل Login BLoC را درون پکیج BLoCs با نام login_BLoC.dart ایجاد کرده و کد زیر را در آن می‌نویسیم:

1class LoginBloc extends Validators {
2 Repository repository = Repository();
3 final BehaviorSubject _emailController = BehaviorSubject<String>();
4 final BehaviorSubject _passwordController = BehaviorSubject<String>();
5 final PublishSubject _loadingData = PublishSubject<bool>();
6Function(String) get changeEmail => _emailController.sink.add;
7Function(String) get changePassword => _passwordController.sink.add;
8Stream<String> get email => _emailController.stream.transform(validateEmail);
9 Stream<String> get password => _passwordController.stream.transform(validatePassword);
10 Stream<bool> get submitValid => Observable.combineLatest2(email, password, (email, password) => true);
11 Observable<bool> get loading => _loadingData.stream;
12void submit() {
13 final validEmail = _emailController.value;
14 final validPassword = _passwordController.value;
15 _loadingData.sink.add(true);
16login(validEmail, validPassword);
17 }
18login(String email, String password) async {
19 String token = await repository.login(email, password);
20 _loadingData.sink.add(false);
21 authBloc.openSession(token);
22 }
23void dispose() {
24 _emailController.close();
25 _passwordController.close();
26 _loadingData.close();
27 }
28}

در این کد سه متغیر rxdart به نام‌های emailController ،_passwordController ،_loadingData_ ایجاد کرده‌ایم. دو متغیر نخست مسئول مدیریت اعتبارسنجی فیلدهای ایمیل و رمز عبور هستند و متغیر آخر برای به‌روزرسانی UI جهت نمایش بارگذاری داده‌ها مورد استفاده قرار می‌گیرد.

ایجاد صفحه ورود

اکنون فایل login screen class را درون ui package با نام login_screen.dart ایجاد می‌کنیم. سپس کد زیر را در آن وارد می‌نماییم:

1class LoginScreen extends StatefulWidget {
2@override
3 State<StatefulWidget> createState() => LoginScreenState();
4}
5class LoginScreenState extends State<LoginScreen> {
6 LoginBloc bloc = LoginBloc();
7@override
8 Widget build(BuildContext context) {
9 return Scaffold(
10 appBar: AppBar(
11 title: Text(‘Login’),
12 ),
13 body: Container(
14 margin: EdgeInsets.all(20.0),
15 child: Column(
16 children: <Widget>[
17 emailField(bloc),
18 passwordField(bloc),
19 Container(margin: EdgeInsets.only(top: 25.0)),
20 submitButton(bloc),
21 loadingIndicator(bloc)
22 ],
23 ),
24 ),
25 );
26 }
27}
28Widget loadingIndicator(LoginBloc bloc) => StreamBuilder<bool>(
29 stream: bloc.loading,
30 builder: (context, snap) {
31 return Container(
32 child: (snap.hasData && snap.data)
33 ? CircularProgressIndicator()
34 : null,
35 );
36 },
37);
38Widget emailField(LoginBloc bloc) => StreamBuilder<String>(
39 stream: bloc.email,
40 builder: (context, snap) {
41 return TextField(
42 keyboardType: TextInputType.emailAddress,
43 onChanged: bloc.changeEmail,
44 decoration: InputDecoration(
45 labelText: ‘Email address’,
46 hintText: ‘you@example.com’,
47 errorText: snap.error
48 ),
49 );
50 },
51);
52Widget passwordField(LoginBloc bloc) => StreamBuilder<String>(
53 stream: bloc.password,
54 builder:(context, snap) {
55 return TextField(
56 obscureText: true,
57 onChanged: bloc.changePassword,
58 decoration: InputDecoration(
59 labelText: ‘Password’,
60 hintText: ‘Password’,
61 errorText: snap.error
62 ),
63 );
64 }
65);
66Widget submitButton(LoginBloc bloc) => StreamBuilder<bool>(
67 stream: bloc.submitValid,
68 builder: (context, snap) {
69 return RaisedButton(
70 onPressed: (!snap.hasData) ? null : bloc.submit,
71 child: Text(‘Login’, style: TextStyle(color: Colors.white),),
72 color: Colors.blue,
73 );
74 },
75);

تا به اینجا از 4 ویجت پایه زیر استفاده کرده‌ایم:

  1. emailField – این ویجت ورودی ایمیل را می‌گیرد، آن را به یک BLoC به نام changeEmail وصل می‌کند تا ورودی کاربر را اعتبارسنجی کند و UI را بر همین مبنا به‌روزرسانی می‌کند.
  2. passwordField - مشابه emailField است، اما ورودی رمز عبور می‌گیرد و آن را اعتبارسنجی می‌کند.
  3. loadingIndicator – برای نمایش بارگذاری UI در زمان‌های انتظار برای فراخوانی API مورد استفاده قرار می‌گیرد.
  4. submitButton – این ویجت برای دکمه‌ای استفاده می‌شود که به کاربر امکان ورود با استفاده از اطلاعات احراز هویت را می‌دهد. این دکمه زمانی که ایمیل و رمز عبور اعتبارسنجی شدند فعال می‌شود. به همین جهت از سوی BLoCk به نام submitValid نیز کنترل می‌شود.

اکنون زمانی که کاربر با موفقیت وارد شود، او را به صفحه اصلی اپلیکیشن هدایت می‌کنیم. چنان که پیش‌تر اشاره کردیم، هر صفحه دو فایل دارد که یکی فایل منطق تجاری (BLoC File) و دیگری فایل اینترفیس کاربری (UI File) است.

ایجاد صفحه اصلی

اینک دو فایل جدید برای صفحه اصلی به نام‌های home_BLoC.dart و home_screen.dart ایجاد می‌کنیم.

1home_bloc.dart:
2class HomeBloc {
3logoutUser() {
4 authBloc.closeSession();
5 }
6}
7home_screen.dart:
8class HomeScreen extends StatefulWidget {
9@override
10 State<StatefulWidget> createState() => HomeScreenState();
11}
12class HomeScreenState extends State<HomeScreen> {
13 HomeBloc bloc = HomeBloc();
14 @override
15 Widget build(BuildContext context) {
16 return Scaffold(
17 appBar: AppBar(title: Text(“Home Screen”),),
18 body: Center(
19 child: RaisedButton(
20 onPressed: bloc.logoutUser,
21 child: Text(‘Logout’, style: TextStyle(color: Colors.white),),
22 color: Colors.blue,
23 ),
24 ),
25 );
26 }
27}

این صفحه اصلی شامل تنها یک دکمه است که به کاربر امکان خروج از اپلیکیشن می‌دهد. اینک اپلیکیشن را اجرا می‌کنیم تا ببینیم چه اتفاقی رخ می‌دهد. شما اینک به عنوان کاربر باید یک صفحه ساده ورود با فیلدهای ایمیل و رمز عبور ببینید. در این زمان در صورتی که همه ورودی‌های معتبر را ارائه کنید و روی گزینه ادامه بزنید، به صفحه اصلی اپلیکیشن هدایت می‌شوید. بهترین بخش داستان را برای انتها نگه داشته‌ایم. اگر اپلیکیشن را ببندید و دوباره وارد شوید، به صفحه اصلی هدایت خواهید شد. اگر در این حالت خارج شوید، به صفحه ورود هدایت می‌شوید.

امیدواریم با مطالعه این راهنما، ایده‌ای در مورد الگوی BLoC و چگونگی آن به دست آورده باشید و به شما کمک کرده باشد که کد خود را سازماندهی کرده و بین منطق تجاری و UI تمایز قائل شوید.

اگر این نوشته برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۹ نفر
آیا این مطلب برای شما مفید بود؟
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.
منابع:
swlh
۲ دیدگاه برای «ایجاد یک فرم لاگین ساده در فلاتر با الگوی BLoC — از صفر تا صد»

بهتر بود اشاره ای به کتابخانه های مورد نیاز هم میکردید.

rxdart: ^0.25.0
shared_preferences: ^0.5.12+4
validators: ^2.0.1

با تشکر

این آموزش عملا هیچ کاربردی نداره چون خیلی از مواردی که مربوط به ری اکت میشه توضیح داده نشده مثلا transform چیه به چه دردی میخوره و چرا باید اینجا استفاده کنیم

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *