مرحبًا! تحتوي هذه الوثيقة على توصياتي الشخصية للسياسات والإجراءات والمعايير المتعلقة بكيفية إجراء التنمية.
يجب تنظيم رمز المشروع بمزيج من المجلد على أساس الجسد ونوع المجلد .
معنى على مستوى الجذر لديك مجلدات تحدد الميزات. على سبيل المثال:
|-- common/
|-- authentication/
|-- settings/
تحتوي الميزة المشتركة/الأساسية على فئات أو وظائف أو عناصر واجهة المستخدم التي يجب أن تتوفر افتراضيًا في أي ميزات.
لذا فإن الفكرة هي أن يكون لديك ميزات لتطبيقك مقسومًا على المجلدات العليا.
فكر في كل مجلدات المستوى الأعلى كحزمة DART/FFLUTRE المنفصلة أو التبعيات أو حدود الوصول إلى التطبيق الخاص بك.
لذا ، إذا قمت بإضافة شيء ما إلى المجلد المشترك ، فيجب عليك أن تسأل نفسك ، هل ستحتاج ميزات تطبيقنا 99. ٪ إلى ذلك؟
إذا كنت تفكر في الأمر باعتباره تبعية ، فسيتم الإعلان دائمًا عن التبعية الشائعة في PubSpec.yaml.
لذلك قبل إضافة فئة أو وظيفة أو عنصر واجهة مستخدم إلى المجلد المشترك ، اسأل نفسك:
داخل كل مجلد ميزة لديك مجلدات محددة حسب النوع.
فيما يلي أنواع المجلدات التي ستكون جزءًا من مجلد الميزة.
|-- ui/
|-- cubits/
|-- domain_objects/
|-- exceptions/
|-- use_cases
|-- services/
|-- repositories/
|-- data_sources/
|-- dtos/
إذا كان هناك أكثر من ملف مرتبط بملف واحد ، فيمكنك تجميعه في مجلد ، على سبيل المثال رمز تم إنشاؤه لفئة في ملف. على سبيل المثال:
|-- authentication/
|---- cubits/
|------ login/
|-------- login_cubit.dart
|-------- login_cubit.freezed.dart
|-------- login_cubit.g.dart
الكود المرتبط بواجهة جهاز المستخدم ، على سبيل المثال: لاعبي PageBileds UI ، وشاشات واجهة المستخدم ، وعرض واجهة المستخدم ، ومكونات واجهة المستخدم.
الشاشة عبارة عن مكون واجهة المستخدم يملأ عرض الجهاز بأكمله وهو حاوية عرض واجهة المستخدم أو مكون (الزر ، مربعات الاختيار ، الصور ، ECT).
أيضا ، الشاشة هي وجهة تنقل تطبيق محددة.
المبدأ التوجيهي:
class LoginScreen extends StatelessWidget {
//...fields and constructor...
@override
Widget build ( BuildContext context) {
return Column (
children : < Widget > [
_LoginLogo ,
_LoginForm ,
_LoginActions ,
_LoginFooter
],
);
}
}
// extracted by use case.
class _LoginLogo extends StatelessWidget {}
// extracted by update area.
class _LoginForm extends StatefullWidget {}
// extracted by update area.
class _LoginActions extends StatelesssWidget {}
// extracted by update area.
class _LoginFooter extends StatelessWidget {} login_screen.dart
part 'login_screen_components.dart' ;
class LoginScreen extends StatelessWidget {
//...fields and constructor...
@override
Widget build ( BuildContext context) {
return Column (
children : < Widget > [
_LoginLogo ,
_LoginForm ,
_LoginActions ,
_LoginFooter
],
);
}
}
// Is kept here because it's does not break the 400 max line rule.
class _LoginLogo extends StatelessWidget {} login_screen_components.dart
part of 'login_screen.dart' ;
/// [LoginScreen] 's fields.
class _LoginForm extends StatelessWidget {}
// ************************ Footer ************************
/// [LoginScreen] 's footer.
class _LoginFooter extends StatelessWidget {}
// ************************* ACTIONS *********************************
/// [LoginScreen] 's actions.
class _LoginActions extends StatelessWidget {} class LoginScreen extends StatelessWidget {
@override
Widget build ( BuildContext context) {
return BlocBuilder < LoginCubit , LoginCubitState >(
// Here cubit is not specified either.
builder : ( BuildContext context, LoginCubitState state) {},
);
}
} class SomeWidget extends StatelessWidget {
@override
Widget build ( BuildContext context) {
//...
}
}
class _SubTree1 extends StatelessWidget {}
class _SubTree2 extends StatelessWidget {}
class _SubTree3 extends StatelessWidget {}
class _SubTree4 extends StatelessWidget {} class LoginScreen extends StatelessWidget {
@override
Widget build ( BuildContext context) {
return Column (
childreen : < Widget > [
Button1 (onClick : _openRegisterUser),
Button2 (onClick : _openLogin),
Field (onTextChanged : _onUserNameTextChanged),
],
);
}
void _openRegisterUser () {}
void _openLogin () {}
void _onUserNameTextChanged ( String newText) {}
}العرض هو مكون واجهة المستخدم لا يملأ عرض الجهاز بأكمله وهو حاوية عرض واجهة المستخدم أو مكون (الزر ، مربعات الاختيار ، الصور ، إلخ).
أيضا ، فإن العرض ليس وجهة تنقل التطبيق.
المبدأ التوجيهي:
Cubits عبارة عن فصول تحتوي على منطق عمل لواجهة واجهة المستخدم الخاصة بك ، فإن Cubit مرتبط بالدولة ، التي تمثل حالة واجهة المستخدم.
توصيات التوجيهية:
Future < String ?> bookAppointment ( BookingData booking);
Future < bool > login ( String username, String password);تمثل فئة حالة Cubit حالة واجهة المستخدم التي تحدها وقتًا معينًا.
توصيات التوجيهية:
class LoginState {
bool logginSuceeded // avoid this.
}الفصول الدراسية المستخدمة في إجراء عملية عمل/هدف/مهمة/مهمة واحدة في مجالك.
الإرشادات:
class GetCurrentUserUseCase ، class SignInUseCase .UseCase ؛call() ، حيث يكون نوع الإرجاع نتيجة لتنفيذ حالة الاستخدام ؛Repositories أو Services أو أي high level coordinators ؛DataSource أو Cubit أو التفاعل مع DTOs أو أي كائن آخر منخفض المستوى ؛Cubit أو في UseCases الأخرى.أمثلة:
class GetCurrentUserUseCase {
final UserRepository _repository;
const GetCurrentUserUseCase ( this ._repository);
Future < User ?> call () async {
await _repository. getCurrentUser ();
}
} class AuthenticateMemberUseCase {
/// Create a [AuthenticateMemberUseCase] .
const AuthenticateMemberUseCase (
/* constructor arguments */
);
/* ... fields ...*/
/// Execute the use case.
Future < TegTask < void >> call ( MemberAuthenticationCredentials credentials) {
return runTaskSafelyAsync < void >(() async {
final bool isEmailValid = credentials.email.isEmail;
if ( ! isEmailValid) {
throw const TegEmailInvalidException ();
}
final bool isConnected = await _hostDeviceInternetService. isConnected ();
if ( ! isConnected) {
throw const TegInternetUnavailableException ();
}
final String ? deviceId = await _hostDeviceInfoRepository. getDeviceId ();
if (deviceId == null ) {
throw const TegDeviceIdUnavailableException ();
}
final PublicPrivateKeyPair keyPair = await _keyGenerator. generate ();
final Member member = await _authService. signIn (
email : credentials.email,
password : credentials.password,
deviceId : deviceId,
publicKey : keyPair.publicKey,
);
await _updateCurrentMemberUseCase (
member : member,
memberPrivateKey : keyPair.privateKey,
deviceId : deviceId,
);
await _saveMemberAuthenticationCredentialsUseCase (credentials);
final TegTask < List < Account >> memberAccountsTask = await _getMemberAccountsUseCase ();
if (memberAccountsTask.failed) {
throw memberAccountsTask.exception;
}
await _updateMemberCurrentAccountUseCase (
member : member,
deviceId : deviceId,
memberPrivateKey : keyPair.privateKey,
account : memberAccountsTask.result.first,
);
});
}
}الفئات التي توفر الوصول إلى البيانات باستخدام عمليات CRUD فقط لميزة معينة أو نطاق من الميزة ، على سبيل المثال:
abstract class BookingRepository {
Futute < Booking > getBookingById ( String id);
Future < List < Booking >> getBookings ();
Future < void > deleteBookingById ( String id);
Future < void > saveBooking ( Booking booking);
}توصيات التوجيهية:
class DefaultBookingRepository implements BookingRepository {
final LocalBookingDataSource local;
final RemoteBookingDataSource remote;
Future < Booking ?> getBookingById ( String id) async {
Booking ? savedBooking = await local. getBookingById (id);
if (savedBooking == null ) {
Booking ? remoteBooking = await remote. getBookingById (id);
if (remoteBooking != null ) {
await local. saveBooking (remoteBooking);
}
return remoteBooking;
}
return savedBooking;
}
//...other operations...
}واجهة
abstract class BookingRepository {}التطبيقات
class DefaultBookingRepository implements BookingRepository {}
class AnonymousBookingRepository implements BookingRepository {}
class PremiumBookingRepository implements BookingRepository {}الفصول التي توفر الوصول إلى وظائف لميزة معينة أو نطاق من الميزة ، على سبيل المثال:
abstract class AuthenticationService {
Future < void > authenticate ( String username, String password);
//...other functionalities...
}
abstract class AppointmentService {
Future < void > register ( Appointment appointment);
//...other functionalities...
}توصيات التوجيهية.
الفئات التي تنفذ الوصول إلى البيانات الموجودة محليًا أو عن بُعد. مثال
abstract class LocalBookingDataSource {
Futture < void > saveBooking ( Booking booking);
//...other functionalities...
}
abstract class RemoteBookingDataSource {
Future <booking> saveBooking ();
}توصيات التوجيهية:
class SQLiteBookingDataSource implements LocalBookingDataSource {
//...............Implementation................
}
class RestApiBookingDataSource implements RemoteBookingDataSource {
//...............Implementation................
}
class MemoryBookingDataSource implements LocalBookingDataSource {
//...............Implementation................
}كائنات نقل البيانات - تستخدم لنقل البيانات من نظام إلى آخر. مثال
class ApiRequest {
//...fields...
}
class ApiResponse {
//...fields...
} class SQLiteTableDefinition {
//...fields...
}الإرشادات:
class ApiRequest {
//...fields...
factory ApiRequest . fromDO ( DOObject doObject) {
//...mapper code...
}
DOObject toDO () {
//...mapper code...
}
}كائن المجال هو فئات البيانات تمثيل جزء من العمل أو عنصر داخله ، يتم استخدامها لتنفيذ منطق الأعمال بشكل مستقل عن النظام الأساسي أو المكتبة أو الأدوات.
قد يمثل كائن المجال ، على سبيل المثال ، شخص أو مكان أو حدث أو عملية تجارية أو مفهوم وفاتورة أو منتج أو معاملة أو حتى تفاصيل الشخص .
المبدأ التوجيهي:
نفس الشيء بالنسبة لهيكل رمز المشروع.
أولا اسم الميزة ، ثم مجلد assets يحتوي على ملفات.
مثال:
|-- authentication/
|---- assets/
|------ password_hidden_icon.svg
|------ forget_password_icon.svg
|------ background.png