Comunicación entre capas
Cómo implementar la inyección de dependencias para comunicarse entre las capas de MVVM.
Además de definir responsabilidades claras para cada componente de la arquitectura, es importante considerar cómo se comunican dichos componentes. Esto se refiere tanto a las reglas que dictan la comunicación como a la implementación técnica de cómo se comunican. La arquitectura de una aplicación debería responder a las siguientes preguntas:
- ¿Qué componentes tienen permitido comunicarse con qué otros componentes (incluyendo componentes del mismo tipo)?
- ¿Qué exponen estos componentes como salida entre sí?
- ¿Cómo se "conecta" una capa determinada a otra capa?
Utilizando este diagrama como guía, las reglas de participación son las siguientes:
| Componente | Reglas de participación |
|---|---|
| View |
|
| ViewModel |
|
| Repository |
|
| Service |
|
Inyección de dependencias
#Esta guía ha mostrado cómo estos diferentes componentes se comunican
entre sí mediante el uso de entradas y salidas.
En cada caso, la comunicación entre dos capas se facilita al pasar
un componente a los métodos constructores (de los componentes que
consumen sus datos), como un Service a un Repository.
class MyRepository {
MyRepository({required MyService myService})
: _myService = myService;
late final MyService _myService;
}
Sin embargo, algo que falta es la creación de objetos. ¿En qué lugar,
de una aplicación, se crea la instancia de MyService para que pueda ser
pasada a MyRepository?
La respuesta a esta pregunta involucra un
patrón conocido como dependency injection (inyección de dependencias).
En la aplicación Compass, la dependency injection se gestiona utilizando
package:provider. Basándose en su experiencia construyendo aplicaciones en Flutter,
los equipos de Google recomiendan usar package:provider para implementar
la inyección de dependencias.
Los servicios y repositorios se exponen en el nivel superior del árbol de Widget de
la aplicación Flutter como objetos Provider.
runApp(
MultiProvider(
providers: [
Provider(create: (context) => AuthApiClient()),
Provider(create: (context) => ApiClient()),
Provider(create: (context) => SharedPreferencesService()),
ChangeNotifierProvider(
create: (context) => AuthRepositoryRemote(
authApiClient: context.read(),
apiClient: context.read(),
sharedPreferencesService: context.read(),
) as AuthRepository,
),
Provider(create: (context) =>
DestinationRepositoryRemote(
apiClient: context.read(),
) as DestinationRepository,
),
Provider(create: (context) =>
ContinentRepositoryRemote(
apiClient: context.read(),
) as ContinentRepository,
),
// In the Compass app, additional service and repository providers live here.
],
child: const MainApp(),
),
);
Los servicios se exponen únicamente para que puedan ser inyectados
inmediatamente en los repositorios a través del método BuildContext.read de provider,
como se muestra en el fragmento anterior.
Luego, los repositorios se exponen para que puedan ser
inyectados en los view models según sea necesario.
Un poco más abajo en el árbol de Widget, los view models que corresponden a
una pantalla completa se crean en la configuración de package:go_router,
donde se vuelve a utilizar provider para inyectar los repositorios necesarios.
// This code was modified for demo purposes.
GoRouter router(
AuthRepository authRepository,
) =>
GoRouter(
initialLocation: Routes.home,
debugLogDiagnostics: true,
redirect: _redirect,
refreshListenable: authRepository,
routes: [
GoRoute(
path: Routes.login,
builder: (context, state) {
return LoginScreen(
viewModel: LoginViewModel(
authRepository: context.read(),
),
);
},
),
GoRoute(
path: Routes.home,
builder: (context, state) {
final viewModel = HomeViewModel(
bookingRepository: context.read(),
);
return HomeScreen(viewModel: viewModel);
},
routes: [
// ...
],
),
],
);
Dentro del view model o repositorio, el componente inyectado debe ser privado.
Por ejemplo, la clase HomeViewModel se ve así:
class HomeViewModel extends ChangeNotifier {
HomeViewModel({
required BookingRepository bookingRepository,
required UserRepository userRepository,
}) : _bookingRepository = bookingRepository,
_userRepository = userRepository;
final BookingRepository _bookingRepository;
final UserRepository _userRepository;
// ...
}
Los métodos privados evitan que la vista, que tiene acceso al view model, llame a métodos del repositorio directamente.
Esto concluye el recorrido por el código de la aplicación Compass. Esta página solo recorrió el código relacionado con la arquitectura, pero no cuenta toda la historia. La mayor parte del código de utilidades, el código de Widget y el estilo de UI se ignoró. Explora el código en el repositorio de la aplicación Compass para obtener un ejemplo completo de una aplicación robusta en Flutter construida siguiendo estos principios.
Comentarios
#Dado que esta sección del sitio web está evolucionando, ¡agradecemos tus comentarios!
A menos que se indique lo contrario, la documentación de este sitio refleja Flutter 3.44.0. Página actualizada por última vez el 2026-05-05. Ver código fuente oreportar un problema.