Olá! Este documento contém minhas recomendações pessoais para políticas, procedimentos e padrões sobre como o desenvolvimento deve ser realizado.
O código do projeto deve ser estruturado com uma mistura de pasta a recurso e pasta por tipo .
Significado no nível da raiz, você tem pastas definindo recursos. Por exemplo:
|-- common/
|-- authentication/
|-- settings/
O recurso comum/núcleo contém classes, funções ou widgets que, por padrão, devem estar disponíveis em qualquer recurso.
Portanto, a idéia é ter os recursos do seu aplicativo divididos por pastas de nível superior.
Pense em todas as pastas de nível superior como um pacote de dardo/flutter separado, dependências ou limites de acesso do seu aplicativo.
Então, se você adicionar algo à pasta comum, você deve se perguntar: 99.% Recursos do nosso aplicativo precisam disso?
Se você pensa nisso como uma dependência, a dependência comum é sempre declarada em seu pubSpec.yaml.
Portanto, antes de adicionar uma aula, função ou um widget à pasta comum, pergunte a si mesmo:
Dentro de cada pasta de recursos, você possui pastas definidas por tipo.
Aqui estão os tipos de pastas que fariam parte de uma pasta de recursos.
|-- ui/
|-- cubits/
|-- domain_objects/
|-- exceptions/
|-- use_cases
|-- services/
|-- repositories/
|-- data_sources/
|-- dtos/
Se houver mais de um arquivo relacionado a um único arquivo, você poderá agrupá -los em uma pasta, por exemplo, código gerado para uma classe em um arquivo. Por exemplo:
|-- authentication/
|---- cubits/
|------ login/
|-------- login_cubit.dart
|-------- login_cubit.freezed.dart
|-------- login_cubit.g.dart
Código relacionado à interface do dispositivo do usuário, por exemplo: compradores de página da interface do usuário, telas da interface do usuário, exibição da interface do usuário, componentes da interface do usuário.
Uma tela é um componente de interface do usuário que preenche toda a exibição do dispositivo e é o contêiner da visualização ou componente da interface do usuário (botão, caixas de seleção, imagens e ECT).
Além disso, uma tela é um destino de navegação de aplicativos específico.
Diretriz:
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) {}
}Uma visualização é um componente de interface do usuário que não preenche toda a exibição do dispositivo e é o contêiner da visualização ou componente da interface do usuário (botão, caixas de seleção, imagens e ECT).
Além disso, uma visualização não é um destino de navegação de aplicativos.
Diretriz:
Os côvados são classes que contêm lógica de negócios para a sua interface do usuário, o cúbito está vinculado a um estado, que representam o estado da interface do usuário.
Recomendações de diretrizes:
Future < String ?> bookAppointment ( BookingData booking);
Future < bool > login ( String username, String password);Uma classe estadual de cúbitos representa o estado da interface do usuário limitado a ele um determinado tempo.
Recomendações de diretrizes:
class LoginState {
bool logginSuceeded // avoid this.
}Classes usadas para fazer uma única operação comercial/meta/trabalho/tarefa em seu domínio.
Diretrizes:
class GetCurrentUserUseCase , class SignInUseCase ;UseCase de sufixo ;call() , onde o tipo de retorno é o resultado da execução do caso de uso;Repositories , Services ou qualquer outro high level coordinators ;DataSource ou Cubit ou interagir com DTOs ou qualquer outro objeto de baixo nível;Cubit ou em outras UseCases .Exemplos:
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,
);
});
}
}Classes que fornecem acesso a dados usando apenas operações CRUD para um recurso específico ou um escopo de um recurso, por exemplo:
abstract class BookingRepository {
Futute < Booking > getBookingById ( String id);
Future < List < Booking >> getBookings ();
Future < void > deleteBookingById ( String id);
Future < void > saveBooking ( Booking booking);
}Recomendações de diretrizes:
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...
}Interface
abstract class BookingRepository {}Implementações
class DefaultBookingRepository implements BookingRepository {}
class AnonymousBookingRepository implements BookingRepository {}
class PremiumBookingRepository implements BookingRepository {}Classes que fornecem acesso a funcionalidades para um recurso específico ou um escopo de um recurso, por exemplo:
abstract class AuthenticationService {
Future < void > authenticate ( String username, String password);
//...other functionalities...
}
abstract class AppointmentService {
Future < void > register ( Appointment appointment);
//...other functionalities...
}Recomendações de diretrizes.
Classes que implementam o acesso a dados localizados localmente ou remotamente. Exemplo
abstract class LocalBookingDataSource {
Futture < void > saveBooking ( Booking booking);
//...other functionalities...
}
abstract class RemoteBookingDataSource {
Future <booking> saveBooking ();
}Recomendações de diretrizes:
class SQLiteBookingDataSource implements LocalBookingDataSource {
//...............Implementation................
}
class RestApiBookingDataSource implements RemoteBookingDataSource {
//...............Implementation................
}
class MemoryBookingDataSource implements LocalBookingDataSource {
//...............Implementation................
}Objetos de transferência de dados - usados para transferir dados de um sistema para outro. Exemplo
class ApiRequest {
//...fields...
}
class ApiResponse {
//...fields...
} class SQLiteTableDefinition {
//...fields...
}Diretrizes:
class ApiRequest {
//...fields...
factory ApiRequest . fromDO ( DOObject doObject) {
//...mapper code...
}
DOObject toDO () {
//...mapper code...
}
}O objeto de domínio são as classes de dados representação de uma parte do negócio ou um item dentro dele, elas são usadas para executar a lógica de negócios independentemente da plataforma, biblioteca ou ferramentas.
Um objeto de domínio pode representar, por exemplo, uma pessoa, local, evento, processo de negócios ou conceito e fatura, um produto, uma transação ou até detalhes de uma pessoa .
Diretriz:
O mesmo que para a estrutura do código do projeto.
Primeiro, o nome do recurso, depois a pasta assets que contêm arquivos.
Exemplo:
|-- authentication/
|---- assets/
|------ password_hidden_icon.svg
|------ forget_password_icon.svg
|------ background.png