From 69406fbba03e50a842bc7e01bdfc16ccec13136e Mon Sep 17 00:00:00 2001 From: Alexander Bangert Date: Tue, 19 Nov 2024 00:29:12 +0500 Subject: [PATCH] dispose --- .pubignore | 3 +- CHANGELOG.md | 4 + EXAMPLE.md | 246 ++++++++++++++++++++++++++++++++ README.md | 13 +- coverage/lcov.info | 52 ++++--- example/.pubignore | 7 + example/lib/main.dart | 17 ++- lib/src/dependencies.dart | 44 +++++- lib/src/dependency_library.dart | 25 +++- pubspec.yaml | 2 +- 10 files changed, 381 insertions(+), 32 deletions(-) create mode 100644 EXAMPLE.md create mode 100644 example/.pubignore diff --git a/.pubignore b/.pubignore index 8c6b475..145f126 100644 --- a/.pubignore +++ b/.pubignore @@ -47,4 +47,5 @@ reports/ analysis_options.yaml Makefile -doc/ \ No newline at end of file +doc/ +example/ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d7fa6f..2eede4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.1 + +* Add dispose for DependencyLibrary + ## 2.0.0 * Fix bugs, Add new feature, parent dependency diff --git a/EXAMPLE.md b/EXAMPLE.md new file mode 100644 index 0000000..875e4da --- /dev/null +++ b/EXAMPLE.md @@ -0,0 +1,246 @@ + +# depend + +![Pub Version](https://img.shields.io/pub/v/depend) +![License](https://img.shields.io/github/license/AlexHCJP/depend) +![Issues](https://img.shields.io/github/issues/AlexHCJP/depend) +![Coverage](https://img.shields.io/codecov/c/github/contributors-company/depend) +![Stars](https://img.shields.io/github/stars/AlexHCJP/depend) +![Contributors](https://img.shields.io/github/contributors/AlexHCJP/depend) +![Watchers](https://img.shields.io/github/watchers/AlexHCJP/depend) +![Forks](https://img.shields.io/github/forks/AlexHCJP/depend) + +`depend` is a library for managing dependencies in Flutter applications. It provides a convenient way to initialize and access services or repositories via an `InheritedWidget`. + +--- + +## Why it Rocks 🚀 + +- Initialize dependencies before launching the app +- Access dependencies from anywhere in the widget tree +- Log initialization times for each dependency +- Clean and extensible way to manage dependencies +- Easy to use and integrate with existing codebases + +--- + +- [dependencies](#dependencies) + - [Why it Rocks 🚀](#why-it-rocks-) + - [Installation](#installation) + - [Example Usage](#example-usage) + - [Example 1: Define Dependencies](#example-1-define-dependencies) + - [Step 2: Initialize Dependencies](#step-2-initialize-dependencies) + - [Step 3: Access Dependencies with `InheritedWidget`](#step-3-access-dependencies-with-inheritedwidget) + - [Example 2: Use Parent Dependencies](#example-2-use-parent-dependencies) + - [Step 1: Define Parent Dependencies](#step-1-define-parent-dependencies) + - [Logging and Debugging](#logging-and-debugging) + +## Installation + +Add the package to your `pubspec.yaml`: + +```yaml +dependencies: + depend: ^0.0.1 +``` + +Then run: + +```bash +$ flutter pub get +``` +--- + +## Example Usage + +### Example 1: Define Dependencies + +#### Step 1: Extends `DependenciesLibrary` + +Create a `DependenciesLibrary` that extends `DependenciesLibrary` and initializes your dependencies: + +```dart +class RootLibrary extends DependenciesLibrary { + late final ApiService apiService; + + @override + Future init() async { + await log(() async => apiService = await ApiService().init()); + } +} +``` + +#### Step 2: Initialize Dependencies + +Use `DependenciesInit` to initialize your dependencies before launching the app: + +```dart +void main() { + runApp( + Dependencies( + library: RootLibrary(), + placeholder: const ColoredBox( + color: Colors.white, + child: Center(child: CircularProgressIndicator()), + ), + child: const MyApp(), + ), + ); +} +``` + +#### Step 3: Access Dependencies with `InheritedWidget` + +Once initialized, dependencies can be accessed from anywhere in the widget tree using `Dependencies.of(context).authRepository`: + +```dart + +/// The repository for the example +final class AuthRepository { + final AuthDataSource dataSource; + + AuthRepository({required this.dataSource}); + + Future login() => dataSource.login(); + + void dispose() { + // stream.close(); + } +} + +class MyApp extends StatelessWidget { + const MyApp({super.key}); + + @override + Widget build(BuildContext context) { + return MaterialApp( + title: 'Flutter Demo', + home: Dependencies( + library: ModuleLibrary( + parent: Dependencies.of(context), + ), + child: BlocProvider( + create: (context) => DefaultBloc( + Dependencies.of(context).authRepository, + ), + child: const MyHomePage(), + ), + ), + ); + } +} + +class MyHomePage extends StatefulWidget { + const MyHomePage({super.key}); + + @override + State createState() => _MyHomePageState(); +} + +class _MyHomePageState extends State { + void _login() { + context.read().add(DefaultEvent()); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SafeArea( + child: SingleChildScrollView( + child: Column( + children: [ + BlocBuilder( + builder: (context, state) { + return Text('Login: ${state.authorized}'); + }, + ), + Builder( + builder: (context) { + return ElevatedButton( + onPressed: _login, + child: const Text('Login'), + ); + }, + ) + ], + ), + ), + ), + ); + } +} + +``` + +### Example 2: Use Parent Dependencies + +#### Step 1: Define Parent Dependencies + +```dart + +class RootLibrary extends DependenciesLibrary { + late final ApiService apiService; + + @override + Future init() async { + apiService = await ApiService().init(); + } +} + +class ModuleLibrary extends DependenciesLibrary { + late final AuthRepository authRepository; + + ModuleLibrary({required super.parent}); + + @override + Future init() async { + // initialize dependencies + authRepository = AuthRepository( + dataSource: AuthDataSource( + apiService: parent.apiService, // parent - RootLibrary + ), + ); + } + + @override + void dispose() { + authRepository.dispose(); + } +} + + + +``` + +## Logging and Debugging + +During initialization, each dependency logs the time it took to initialize: + +```dart +class ModuleLibrary extends DependenciesLibrary { + late final AuthRepository authRepository; + + ModuleLibrary({required super.parent}); + + @override + Future init() async { + await log(() async => authRepository = AuthRepository( + dataSource: AuthDataSource( + apiService: parent.apiService, + ), + ), + ); + } +} +``` + +```dart +💡 ApiService: initialized successfully for 10 ms +💡 AuthRepository: initialized successfully for 0 ms +``` + +This is useful for tracking performance and initialization times. + +## Codecov + +![Codecov](https://codecov.io/gh/contributors-company/depend/graphs/sunburst.svg?token=DITZJ9E9OM) diff --git a/README.md b/README.md index 3ee6516..875e4da 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,10 @@ final class AuthRepository { AuthRepository({required this.dataSource}); Future login() => dataSource.login(); + + void dispose() { + // stream.close(); + } } class MyApp extends StatelessWidget { @@ -191,13 +195,17 @@ class ModuleLibrary extends DependenciesLibrary { @override Future init() async { // initialize dependencies - authRepository = AuthRepository( dataSource: AuthDataSource( apiService: parent.apiService, // parent - RootLibrary ), ); } + + @override + void dispose() { + authRepository.dispose(); + } } @@ -220,7 +228,8 @@ class ModuleLibrary extends DependenciesLibrary { dataSource: AuthDataSource( apiService: parent.apiService, ), - )); + ), + ); } } ``` diff --git a/coverage/lcov.info b/coverage/lcov.info index 2c93e0e..6249e47 100644 --- a/coverage/lcov.info +++ b/coverage/lcov.info @@ -9,35 +9,47 @@ LH:5 end_of_record SF:lib/src/dependencies.dart DA:49,1 -DA:56,4 -DA:57,3 -DA:58,2 -DA:59,3 -DA:63,0 -DA:93,1 -DA:99,0 -DA:100,0 -DA:101,2 -DA:119,1 -DA:123,2 +DA:55,4 +DA:56,3 +DA:57,2 +DA:58,3 +DA:89,1 +DA:95,0 +DA:96,0 +DA:97,2 +DA:115,1 +DA:119,2 +DA:121,1 +DA:122,1 +DA:123,1 DA:125,1 DA:126,1 -DA:127,1 +DA:127,2 +DA:128,1 DA:129,1 -DA:130,1 -DA:131,2 +DA:130,2 DA:132,1 DA:133,1 -DA:134,2 +DA:134,1 +DA:135,1 DA:136,1 DA:137,1 DA:138,1 DA:139,1 -DA:140,2 DA:141,1 DA:142,1 -DA:147,0 -DA:149,0 -LF:30 -LH:25 +DA:143,1 +DA:144,1 +DA:150,0 +DA:152,0 +DA:157,1 +DA:163,1 +DA:164,1 +DA:169,1 +DA:171,1 +DA:172,3 +DA:175,1 +DA:176,2 +LF:42 +LH:38 end_of_record diff --git a/example/.pubignore b/example/.pubignore new file mode 100644 index 0000000..b9a13bc --- /dev/null +++ b/example/.pubignore @@ -0,0 +1,7 @@ +ios/ +linux/ +windows/ +web/ +macos/ +android/ +lib/src/ \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart index 18d6525..f47c491 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -10,6 +10,11 @@ class RootLibrary extends DependenciesLibrary { Future init() async { await log(() async => apiService = await ApiService().init()); } + + @override + dispose() { + + } } class ModuleLibrary extends DependenciesLibrary { @@ -23,7 +28,13 @@ class ModuleLibrary extends DependenciesLibrary { dataSource: AuthDataSource( apiService: parent.apiService, ), - )); + ), + ); + } + + @override + void dispose() { + authRepository.dispose(); } } @@ -65,6 +76,10 @@ final class AuthRepository { AuthRepository({required this.dataSource}); Future login() => dataSource.login(); + + void dispose() { + // stream.close(); + } } class MyApp extends StatelessWidget { diff --git a/lib/src/dependencies.dart b/lib/src/dependencies.dart index e24b010..d0b05e9 100644 --- a/lib/src/dependencies.dart +++ b/lib/src/dependencies.dart @@ -53,13 +53,14 @@ class Dependencies> this.placeholder, }) { library.init().then((val) { - _completer.complete(library); + completer.complete(library); }).catchError((Object error) { - _completer.completeError(error, StackTrace.current); + completer.completeError(error, StackTrace.current); }); } - final Completer _completer = Completer(); + /// A [Completer] to handle the asynchronous initialization of the [library]. + final Completer completer = Completer(); /// The instance of [DependenciesLibrary] to provide to the widget tree. final T library; @@ -123,7 +124,7 @@ class Dependencies> @override Widget get child => FutureBuilder( - future: _completer.future, + future: completer.future, builder: (context, snapshot) { if (snapshot.hasError) { return ErrorWidget(snapshot.error!); @@ -132,9 +133,16 @@ class Dependencies> case ConnectionState.none: case ConnectionState.waiting: case ConnectionState.active: - return placeholder ?? super.child; + return placeholder ?? + _DisposeDependency( + library: library, + child: super.child, + ); case ConnectionState.done: - return super.child; + return _DisposeDependency( + library: library, + child: super.child, + ); } }, ); @@ -143,3 +151,27 @@ class Dependencies> bool updateShouldNotify(Dependencies oldWidget) => library != oldWidget.library; } + +class _DisposeDependency> + extends StatefulWidget { + const _DisposeDependency( + {required this.child, required this.library, super.key}); + + final T library; + final Widget child; + + @override + State<_DisposeDependency> createState() => _DisposeDependencyState(); +} + +class _DisposeDependencyState> + extends State<_DisposeDependency> { + @override + void dispose() { + super.dispose(); + widget.library.dispose(); + } + + @override + Widget build(BuildContext context) => widget.child; +} diff --git a/lib/src/dependency_library.dart b/lib/src/dependency_library.dart index 24a88ff..242fdcb 100644 --- a/lib/src/dependency_library.dart +++ b/lib/src/dependency_library.dart @@ -65,9 +65,32 @@ abstract class DependenciesLibrary { /// ``` @mustCallSuper Future init(); - // coverage:ignore-start + /// Cleans up resources used by the dependencies. + /// + /// This method can be overridden to release any resources, close streams, or dispose + /// of objects that were initialized in the `init` method or elsewhere in the library. + /// + /// The base implementation is empty, so calling `super.dispose()` is optional unless + /// overridden by subclasses to include specific cleanup logic. + /// + /// ### Example + /// + /// ```dart + /// @override + /// void dispose() { + /// // Close a stream controller + /// myStreamController.close(); + /// + /// // Dispose of other resources + /// someDependency.dispose(); + /// + /// super.dispose(); + /// } + /// ``` + void dispose() {} + /// Logs the initialization process of a dependency. /// /// This method executes the provided [callback] and logs the time taken to complete it. diff --git a/pubspec.yaml b/pubspec.yaml index fe0243c..0f40dc6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: depend description: "Dependencies is a library for managing dependencies in Flutter applications. It provides a convenient way to initialize and access services or repositories via an InheritedWidget." -version: 2.0.0 +version: 2.0.1 homepage: https://www.contributors.info/repository/depend