Sodapdf
Sodapdf
1. Dependencias en pubspec.yaml
dependencies:
flutter:
sdk: flutter
flutter_bloc: ^8.1.4
http: ^1.2.0
provider: ^6.1.2
internet_connection_checker: ^1.0.0+1
2. Estructura de Carpetas
La estructura de carpetas siguiendo Clean Architecture y Atomic Design
para este ejemplo sería:
/lib
/main.dart
/src
/core
/error
exceptions.dart
failures.dart
/network
network_info.dart
/features
/auth
/data
/datasources
auth_remote_data_source.dart
/models
user_model.dart
/repositories
auth_repository_impl.dart
/domain
/entities
user.dart
/repositories
auth_repository.dart
/usecases
login_usecase.dart
/presentation
/bloc
auth_bloc.dart
/pages
login_page.dart
home_page.dart
/injection_container.dart
Mac
mkdir -p lib/src/core/error lib/src/core/network lib/src/core/theme lib/src/core/utils
lib/src/features/example/data/datasources lib/src/features/example/data/models
lib/src/features/example/data/repositories lib/src/features/example/domain/entities
lib/src/features/example/domain/repositories
lib/src/features/example/domain/usecases
lib/src/features/example/presentation/bloc
lib/src/features/example/presentation/pages
lib/src/features/example/presentation/widgets lib/src/ui/atoms lib/src/ui/molecules
lib/src/ui/organisms lib/src/ui/templates
Windows
mkdir lib\src\core\error lib\src\core\network lib\src\core\theme lib\src\core\utils
lib\src\features\example\data\datasources lib\src\features\example\data\models
lib\src\features\example\data\repositories lib\src\features\example\domain\entities
lib\src\features\example\domain\repositories
lib\src\features\example\domain\usecases
lib\src\features\example\presentation\bloc
lib\src\features\example\presentation\pages
lib\src\features\example\presentation\widgets lib\src\ui\atoms lib\src\ui\molecules
lib\src\ui\organisms lib\src\ui\templates
NetworkInfoImpl
/lib/src/core/network/network_info.dart
import 'package:internet_connection_checker/internet_connection_checker.dart';
NetworkInfoImpl(this.connectionChecker);
@override
Future<bool> get isConnected => connectionChecker.hasConnection;
}
ServerException(this.message);
@override
String toString() => "ServerException: $message";
}
CacheException(this.message);
@override
String toString() => "CacheException: $message";
}
ApiClient
/lib/src/core/network/api_client.dart
class ApiClient {
final http.Client httpClient;
ApiClient({required this.httpClient});
if (response.statusCode == 200) {
return json.decode(response.body);
} else {
throw ServerException(response.body);
}
}
// Aquí podrías agregar más métodos para POST, PUT, DELETE, etc.
}
Failure
/lib/src/core/error/failures.dart
Failure(this.message);
@override
String toString() => message;
}
class User {
final String id;
final String name;
final String email;
import '../repositories/auth_repository.dart';
import '../entities/user.dart';
class LoginUseCase {
final AuthRepository repository;
LoginUseCase(this.repository);
import '../entities/user.dart';
import '../../../../core/network/network_info.dart';
import '../datasources/auth_remote_data_source.dart';
import '../../domain/entities/user.dart';
import '../../domain/repositories/auth_repository.dart';
import '../../../../core/error/exceptions.dart';
import '../../../../core/error/failures.dart';
@override
Future<User> login(String email, String password) async {
if (await networkInfo.isConnected) {
try {
final userModel = await remoteDataSource.login(email, password);
return User(id: userModel.id, name: userModel.name, email: userModel.email);
} on ServerException catch (e) {
throw ServerFailure(e.message);
}
} else {
throw ServerFailure("No Internet connection");
}
}
}
import '../models/user_model.dart';
AuthRemoteDataSourceImpl({required this.apiClient});
@override
Future<UserModel> login(String email, String password) async {
final response = await apiClient.get('endpoint_to_login');// cambiar a url usado
return UserModel.fromJson(response);
}
}
import '../../domain/entities/user.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../domain/usecases/login_usecase.dart';
import '../../domain/entities/user.dart';
LoginRequested(this.email, this.password);
}
Authenticated(this.user);
}
AuthError(this.message);
}
@override
Stream<AuthState> mapEventToState(AuthEvent event) async* {
if (event is LoginRequested) {
yield* _mapLoginRequestedToState(event);
}
}
LoginRequested(this.email, this.password);
}
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../bloc/auth_bloc.dart';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Login')),
body: BlocListener<AuthBloc, AuthState>(
listener: (context, state) {
if (state is Authenticated) {
// Navegar a la pantalla de inicio después de la autenticación
Navigator.of(context).pushReplacementNamed('/home');
} else if (state is AuthError) {
// Mostrar error
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(state.
}
},
child: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
TextFormField(
controller: _passwordController,
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
// Usar AuthBloc para iniciar sesión
BlocProvider.of<AuthBloc>(context).add(
LoginRequested(_emailController.text, _passwordController.te
);
}
},
child: Text('Login'),
),
],
),
),
),
),
);
}
}
9. Implementación
Aquí configurarías tu contenedor de inyección de dependencias. Para
mantenerlo simple, puedes usar el paquete get_it :
/lib/injection_container.dart
import 'package:get_it/get_it.dart';
import 'src/features/auth/data/datasources/auth_remote_data_source.dart';
import 'src/features/auth/data/repositories/auth_repository_impl.dart';
import 'src/features/auth/domain/repositories/auth_repository.dart';
import 'src/features/auth/domain/usecases/login_usecase.dart';
import 'src/features/auth/presentation/bloc/auth_bloc.dart';
final sl = GetIt.instance;
void setup() {
// Bloc
sl.registerFactory(() => AuthBloc(loginUseCase: sl()));
// Use cases
sl.registerLazySingleton(() => LoginUseCase(sl()));
// Repository
sl.registerLazySingleton<AuthRepository>(() => AuthRepositoryImpl(remoteDataSource
// Data sources
sl.registerLazySingleton<AuthRemoteDataSource>(() => AuthRemoteDataSourceImpl());
}
/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'src/features/auth/presentation/bloc/auth_bloc.dart';
import 'src/features/auth/presentation/pages/login_page.dart';
import 'injection_container.dart' as di;
void main() {
di.setup();
runApp(MyApp());
}
Hasta aqui finaliza la guia, una seccion adicional para mas casos de uso a
continuacion
/lib/injection_container.dart
import 'package:get_it/get_it.dart';
import 'package:http/http.dart' as http;
import 'src/core/network/api_client.dart';
import 'src/core/network/network_info.dart';
import 'src/features/auth/data/datasources/auth_remote_data_source.dart';
import 'src/features/auth/data/repositories/auth_repository_impl.dart';
import 'src/features/auth/domain/repositories/auth_repository.dart';
import 'src/features/auth/domain/usecases/login_usecase.dart';
import 'src/features/auth/domain/usecases/logout_usecase.dart';
import 'src/features/auth/domain/usecases/signup_usecase.dart';
import 'src/features/auth/presentation/bloc/auth_bloc.dart';
final sl = GetIt.instance;
void setup() {
// Bloc
sl.registerFactory(
() => AuthBloc(
loginUseCase: sl(),
logoutUseCase: sl(),
signupUseCase: sl(),
),
);
// Use cases
sl.registerLazySingleton(() => LoginUseCase(sl()));
sl.registerLazySingleton(() => LogoutUseCase(sl()));
sl.registerLazySingleton(() => SignupUseCase(sl()));
// Repository
sl.registerLazySingleton<AuthRepository>(
() => AuthRepositoryImpl(
remoteDataSource: sl(),
networkInfo: sl(),
),
);
// Data sources
sl.registerLazySingleton<AuthRemoteDataSource>(
() => AuthRemoteDataSourceImpl(apiClient: sl()),
);
// Core
sl.registerLazySingleton(() => ApiClient(httpClient: http.Client()));
sl.registerLazySingleton<NetworkInfo>(() => NetworkInfoImpl());
// External
sl.registerLazySingleton(() => http.Client());
}
/lib/src/features/auth/domain/usecases/login_usecase.dart
import '../repositories/auth_repository.dart';
import '../entities/user.dart';
class LoginUseCase {
final AuthRepository repository;
LoginUseCase(this.repository);
AuthBloc({
required this.loginUseCase,
required this.logoutUseCase,
required this.signupUseCase,
}) : super(AuthInitial());
Desarrollador Flutter
No responses yet
Respond
Lists
Organize your knowledge with lists and Support writers you read most
highlights.
Earn money for your writing
Sign up to discover human stories that Tell your story. Find your audience.
Listen to audio narrations
deepen your understandingSeeofmorethe world.
recommendations
Read offline with the Medium app