diff --git a/examples/flutter_login/lib/app.dart b/examples/flutter_login/lib/app.dart index 754f98faebd..522c5031985 100644 --- a/examples/flutter_login/lib/app.dart +++ b/examples/flutter_login/lib/app.dart @@ -7,39 +7,24 @@ import 'package:flutter_login/login/login.dart'; import 'package:flutter_login/splash/splash.dart'; import 'package:user_repository/user_repository.dart'; -class App extends StatefulWidget { +class App extends StatelessWidget { const App({super.key}); - @override - State createState() => _AppState(); -} - -class _AppState extends State { - late final AuthenticationRepository _authenticationRepository; - late final UserRepository _userRepository; - - @override - void initState() { - super.initState(); - _authenticationRepository = AuthenticationRepository(); - _userRepository = UserRepository(); - } - - @override - void dispose() { - _authenticationRepository.dispose(); - super.dispose(); - } - @override Widget build(BuildContext context) { - return RepositoryProvider.value( - value: _authenticationRepository, + return MultiRepositoryProvider( + providers: [ + RepositoryProvider( + create: (_) => AuthenticationRepository(), + dispose: (repository) => repository.dispose(), + ), + RepositoryProvider(create: (_) => UserRepository()), + ], child: BlocProvider( lazy: false, - create: (_) => AuthenticationBloc( - authenticationRepository: _authenticationRepository, - userRepository: _userRepository, + create: (context) => AuthenticationBloc( + authenticationRepository: context.read(), + userRepository: context.read(), )..add(AuthenticationSubscriptionRequested()), child: const AppView(), ), diff --git a/packages/flutter_bloc/CHANGELOG.md b/packages/flutter_bloc/CHANGELOG.md index aae8a53ace7..93512b2d94d 100644 --- a/packages/flutter_bloc/CHANGELOG.md +++ b/packages/flutter_bloc/CHANGELOG.md @@ -1,3 +1,7 @@ +# 9.1.0 + +- feat: expose `dispose` callback on `RepositoryProvider` ([#4356](https://github.com/felangel/bloc/pull/4356)) + # 9.0.0 - fix: ensure widget is mounted before invoking listener ([#4237](https://github.com/felangel/bloc/pull/4237)) diff --git a/packages/flutter_bloc/README.md b/packages/flutter_bloc/README.md index 723a927294d..cc840bbd550 100644 --- a/packages/flutter_bloc/README.md +++ b/packages/flutter_bloc/README.md @@ -426,6 +426,16 @@ context.read(); RepositoryProvider.of(context) ``` +Repositories that manage resources which must be disposed can do so via the `dispose` callback: + +```dart +RepositoryProvider( + create: (context) => RepositoryA(), + dispose: (context, repository) => repository.close(), + child: ChildA(), +); +``` + ### MultiRepositoryProvider **MultiRepositoryProvider** is a Flutter widget that merges multiple `RepositoryProvider` widgets into one. diff --git a/packages/flutter_bloc/lib/src/bloc_provider.dart b/packages/flutter_bloc/lib/src/bloc_provider.dart index 324d88811a1..78950b34d0a 100644 --- a/packages/flutter_bloc/lib/src/bloc_provider.dart +++ b/packages/flutter_bloc/lib/src/bloc_provider.dart @@ -5,7 +5,7 @@ import 'package:provider/provider.dart'; import 'package:provider/single_child_widget.dart'; /// {@template bloc_provider} -/// Takes a [Create] function that is responsible for +/// Takes a `create` function that is responsible for /// creating the [Bloc] or [Cubit] and a [child] which will have access /// to the instance via `BlocProvider.of(context)`. /// It is used as a dependency injection (DI) widget so that a single instance @@ -19,7 +19,7 @@ import 'package:provider/single_child_widget.dart'; /// ``` /// /// It automatically handles closing the instance when used with [Create]. -/// By default, [Create] is called only when the instance is accessed. +/// By default, `create` is called only when the instance is accessed. /// To override this behavior, set [lazy] to `false`. /// /// ```dart @@ -35,7 +35,7 @@ class BlocProvider> extends SingleChildStatelessWidget { /// {@macro bloc_provider} const BlocProvider({ - required Create create, + required T Function(BuildContext context) create, Key? key, this.child, this.lazy = true, @@ -52,7 +52,7 @@ class BlocProvider> /// /// A new [Bloc] or [Cubit] should not be created in `BlocProvider.value`. /// New instances should always be created using the - /// default constructor within the [Create] function. + /// default constructor within the `create` function. /// /// ```dart /// BlocProvider.value( @@ -76,7 +76,7 @@ class BlocProvider> /// Defaults to `true`. final bool lazy; - final Create? _create; + final T Function(BuildContext context)? _create; final T? _value; diff --git a/packages/flutter_bloc/lib/src/repository_provider.dart b/packages/flutter_bloc/lib/src/repository_provider.dart index 0e3f0e3ce94..25b109807cb 100644 --- a/packages/flutter_bloc/lib/src/repository_provider.dart +++ b/packages/flutter_bloc/lib/src/repository_provider.dart @@ -2,7 +2,7 @@ import 'package:flutter/widgets.dart'; import 'package:provider/provider.dart'; /// {@template repository_provider} -/// Takes a [Create] function that is responsible for creating the repository +/// Takes a `create` function that is responsible for creating the repository /// and a `child` which will have access to the repository via /// `RepositoryProvider.of(context)`. /// It is used as a dependency injection (DI) widget so that a single instance @@ -28,14 +28,15 @@ import 'package:provider/provider.dart'; class RepositoryProvider extends Provider { /// {@macro repository_provider} RepositoryProvider({ - required Create create, + required T Function(BuildContext context) create, + void Function(T value)? dispose, Key? key, Widget? child, bool? lazy, }) : super( key: key, create: create, - dispose: (_, __) {}, + dispose: (_, value) => dispose?.call(value), child: child, lazy: lazy, ); diff --git a/packages/flutter_bloc/pubspec.yaml b/packages/flutter_bloc/pubspec.yaml index f69784c312c..a5729757988 100644 --- a/packages/flutter_bloc/pubspec.yaml +++ b/packages/flutter_bloc/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_bloc description: Flutter Widgets that make it easy to implement the BLoC (Business Logic Component) design pattern. Built to be used with the bloc state management package. -version: 9.0.0 +version: 9.1.0 repository: https://github.com/felangel/bloc/tree/master/packages/flutter_bloc issue_tracker: https://github.com/felangel/bloc/issues homepage: https://bloclibrary.dev diff --git a/packages/flutter_bloc/test/repository_provider_test.dart b/packages/flutter_bloc/test/repository_provider_test.dart index 9ef56cf6a24..da7271fcd1f 100644 --- a/packages/flutter_bloc/test/repository_provider_test.dart +++ b/packages/flutter_bloc/test/repository_provider_test.dart @@ -356,5 +356,47 @@ void main() { final counterText = counterFinder.evaluate().first.widget as Text; expect(counterText.data, '0'); }); + + testWidgets('calls dispose callback when disposed', (tester) async { + var disposeCalled = false; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: RepositoryProvider( + create: (_) => const Repository(0), + dispose: (repository) { + disposeCalled = true; + expect(repository.data, equals(0)); + }, + child: Builder( + builder: (context) => Text( + '${context.read().data}', + key: const Key('value_data'), + ), + ), + ), + floatingActionButton: Builder( + builder: (context) => FloatingActionButton( + onPressed: () => Navigator.of(context).pop(), + child: const Icon(Icons.remove), + ), + ), + ), + ), + ); + final repositoryFinder = find.byKey(const Key('value_data')); + expect(repositoryFinder, findsOneWidget); + + final repositoryText = repositoryFinder.evaluate().first.widget as Text; + expect(repositoryText.data, '0'); + + final fabFinder = find.byType(FloatingActionButton); + expect(fabFinder, findsOneWidget); + + expect(disposeCalled, isFalse); + await tester.tap(fabFinder); + await tester.pumpAndSettle(); + expect(disposeCalled, isTrue); + }); }); }