Lifecycle Owner
The primary role of Lifecycle Owner is to manage the lifecycle of the dependencies like State-Holder, which is responsible for holding the screen’s business logic and state. The State-holder is typically marked with @LazySingleton(scope: 'scopeName'), ensuring that it is scoped to a specific feature or flow in your application.With Lifecycle Owner, you control the dependencies lifecycle by automatically creating an instance when the screen initializes (initState) and cleaning it from memory when the screen is disposed (dispose).This approach optimizes resource usage and ensures that only the necessary objects are kept alive as long as they are needed.The integration with injectable allows you to define the scope of these singletons, ensuring they are automatically cleaned up when no longer required, preventing memory leaks and unnecessary resource usage.Furthermore, Lifecycle Owner works with Dependency Injection, enabling various components within the same screen to share a single instance of the ViewModel as mentioned in Login Example.
Implementing Lifecycle Owner:
Lifecycle Owner depends on injectable scoped singleton to manage State holder (ViewModel) lifecycle.1. Add Scope to ViewModel
Add @LazySingleton with your scope name to ViewModel, this will make injectable generate the necessary code for access your viewModel with get_it through scope name.import 'package:injectable/injectable.dart';
import 'package:flutterx_live_data/flutterx_live_data.dart'
@LazySingleton(scope: 'login')
class LoginViewModel {
final loading = MutableLiveData<bool>(value: false);
}
dart run build_runner build --delete-conflicting-outputs
2. Including Lifecycle Owner Mixin:
As mentioned before Lifecycle Owner manage screen lifecycle with state-holder, so Lifecycle Owner needs to main information:- Screen State class name
- State Holder class name
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
LoginViewModel get viewModel = GetIt.instance<LoginViewModel>();
@override
void initState() {
// ... register required scopes such as 'authentication' repository or data-source scopes
GetIt.instance.initLoginScope();
}
@override
Widget build(BuildContext context) {
return PlaceHolder();
}
}
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@override
State<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> with LifecycleOwner<LoginScreen, LoginViewModel>, ObserverMixin {}
3. Adding Scope:
Lifecycle Owner requires adding ViewModel DiScope to register it when screen initialized and remove it when screen disposed:@override
DiScope get diScope => DiScope(
name: 'login', // same of @LazySingleton scope name
factory: () => GetIt.instance.initLoginScope(), // 'initLoginScope()' generated by injectable generator
dependencies: [
// add 'login' scope required dependencies (list of DiScope) if exist, such as 'authentication' repository or data-source scopes
],
);
4. Observing LiveData Changes:
You can notice ObserverMixin is required for including Lifecycle Owner, This help you to observe LiveData changes:class _LoginScreenState extends State<LoginScreen> with LifecycleOwner<LoginScreen, LoginViewModel>, ObserverMixin {
@override
void observeChanges(ObserverMixin observer) {
viewModel.loading.observe(observer, (bool value) {
// called every time 'loading' value changed
}
}
}