Привет! Этот документ содержит мои личные рекомендации по политике, процедурам и стандартам относительно того, как следует проводить развитие.
Код проекта должен быть структурирован с сочетанием папок за поступлением и папкой за типом .
Значение на корневом уровне у вас есть папки, определяющие функции. Например:
|-- common/
|-- authentication/
|-- settings/
Общая/основная функция содержит классы, функции или виджеты, которые по умолчанию должны быть доступны в любых функциях.
Таким образом, идея состоит в том, чтобы функции вашего приложения разделены на папки верхнего уровня.
Думайте о каждом верхнем уровне папков как о отдельном пакете DART/Flutter, зависимостях или границе доступа вашего приложения.
Так что, если вы добавите что -то в общую папку, вы должны спросить себя, нуждаются ли 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
Код, который связан с интерфейсом устройства пользователя, например: PageBuilders 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) {}
}Видение - это компонент пользовательского интерфейса, который не заполняет весь дисплей устройства и является контейнером представления пользовательского интерфейса или компонента (кнопка, флажки, изображения и ECT).
Кроме того, представление не является местом навигации.
Руководство:
Кубиты - это классы, которые содержат бизнес -логику для вашего пользовательского интерфейса, Cubit связан с государством, которое представляет состояние пользовательского интерфейса.
Рекомендации по руководству:
Future < String ?> bookAppointment ( BookingData booking);
Future < bool > login ( String username, String password);Класс штата Кубит представляет состояние пользовательского интерфейса, ограниченное ему в заданное время.
Рекомендации по руководству:
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