diff --git a/packages/wyatt_bloc_helper/analysis_options.yaml b/packages/wyatt_bloc_helper/analysis_options.yaml index 8c9daa4e..b0c6aced 100644 --- a/packages/wyatt_bloc_helper/analysis_options.yaml +++ b/packages/wyatt_bloc_helper/analysis_options.yaml @@ -1 +1,4 @@ include: package:wyatt_analysis/analysis_options.flutter.yaml + +analyzer: + exclude: "!example/**" \ No newline at end of file diff --git a/packages/wyatt_bloc_helper/example/.metadata b/packages/wyatt_bloc_helper/example/.metadata index 8bb66d20..2112298c 100644 --- a/packages/wyatt_bloc_helper/example/.metadata +++ b/packages/wyatt_bloc_helper/example/.metadata @@ -4,7 +4,7 @@ # This file should be version controlled. version: - revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + revision: f1875d570e39de09040c8f79aa13cc56baab8db1 channel: stable project_type: app @@ -13,11 +13,11 @@ project_type: app migration: platforms: - platform: root - create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 - base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 - - platform: android - create_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 - base_revision: ee4e09cce01d6f2d7f4baebd247fde02e5008851 + create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + - platform: web + create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 + base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 # User provided section diff --git a/packages/wyatt_bloc_helper/example/lib/counter/bloc/counter_bloc.dart b/packages/wyatt_bloc_helper/example/lib/counter/bloc/counter_bloc.dart index 0a99298b..5bbd3d8e 100644 --- a/packages/wyatt_bloc_helper/example/lib/counter/bloc/counter_bloc.dart +++ b/packages/wyatt_bloc_helper/example/lib/counter/bloc/counter_bloc.dart @@ -14,6 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +import 'package:bloc_helper_example/counter/repository/counter_repository.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -21,14 +22,16 @@ part 'counter_event.dart'; part 'counter_state.dart'; class CounterBloc extends Bloc { - CounterBloc() : super(const CounterInitial()) { + final CounterRepository counterRepository; + + CounterBloc(this.counterRepository) : super(const CounterInitial()) { on((event, emit) { - emit(CounterModified(state.count + 1)); + emit(CounterModified(counterRepository.increment(state.count))); }); on((event, emit) { - emit(CounterModified(state.count - 1)); + emit(CounterModified(counterRepository.decrement(state.count))); }); } } diff --git a/packages/wyatt_bloc_helper/example/lib/counter/counter_bloc_page.dart b/packages/wyatt_bloc_helper/example/lib/counter/counter_bloc_page.dart index 62dd1fe1..2cb6f1d5 100644 --- a/packages/wyatt_bloc_helper/example/lib/counter/counter_bloc_page.dart +++ b/packages/wyatt_bloc_helper/example/lib/counter/counter_bloc_page.dart @@ -15,6 +15,7 @@ // along with this program. If not, see . import 'package:bloc_helper_example/counter/bloc/counter_bloc.dart'; +import 'package:bloc_helper_example/counter/repository/counter_repository.dart'; import 'package:flutter/material.dart'; import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; @@ -23,7 +24,7 @@ class CounterBlocPage const CounterBlocPage({super.key}); @override - CounterBloc create(BuildContext context) => CounterBloc(); + CounterBloc create(BuildContext context) => CounterBloc(repo(context)); @override Widget onBuild(BuildContext context, CounterState state) { diff --git a/packages/wyatt_bloc_helper/example/lib/counter/counter_consumer_page.dart b/packages/wyatt_bloc_helper/example/lib/counter/counter_consumer_page.dart index b5c138b2..9a4a3005 100644 --- a/packages/wyatt_bloc_helper/example/lib/counter/counter_consumer_page.dart +++ b/packages/wyatt_bloc_helper/example/lib/counter/counter_consumer_page.dart @@ -35,14 +35,16 @@ class CounterConsumerPage children: [ FloatingActionButton( heroTag: null, - key: const Key('counterConsumerView_increment_floatingActionButton'), + key: + const Key('counterConsumerView_increment_floatingActionButton'), child: const Icon(Icons.add), onPressed: () => bloc(context).increment(), ), const SizedBox(height: 8), FloatingActionButton( heroTag: null, - key: const Key('counterConsumerView_decrement_floatingActionButton'), + key: + const Key('counterConsumerView_decrement_floatingActionButton'), child: const Icon(Icons.remove), onPressed: () => bloc(context).decrement(), ), diff --git a/packages/wyatt_bloc_helper/example/lib/counter/counter_cubit_page.dart b/packages/wyatt_bloc_helper/example/lib/counter/counter_cubit_page.dart index 1776139d..5e04d72a 100644 --- a/packages/wyatt_bloc_helper/example/lib/counter/counter_cubit_page.dart +++ b/packages/wyatt_bloc_helper/example/lib/counter/counter_cubit_page.dart @@ -15,6 +15,7 @@ // along with this program. If not, see . import 'package:bloc_helper_example/counter/cubit/counter_cubit.dart'; +import 'package:bloc_helper_example/counter/repository/counter_repository.dart'; import 'package:flutter/material.dart'; import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; @@ -23,7 +24,7 @@ class CounterCubitPage const CounterCubitPage({super.key}); @override - CounterCubit create(BuildContext context) => CounterCubit(); + CounterCubit create(BuildContext context) => CounterCubit(repo(context)); @override Widget onBuild(BuildContext context, CounterState state) { diff --git a/packages/wyatt_bloc_helper/example/lib/counter/counter_provider_page.dart b/packages/wyatt_bloc_helper/example/lib/counter/counter_provider_page.dart index beac5f1a..7a6c3d8c 100644 --- a/packages/wyatt_bloc_helper/example/lib/counter/counter_provider_page.dart +++ b/packages/wyatt_bloc_helper/example/lib/counter/counter_provider_page.dart @@ -15,6 +15,7 @@ // along with this program. If not, see . import 'package:bloc_helper_example/counter/bloc/counter_bloc.dart'; +import 'package:bloc_helper_example/counter/repository/counter_repository.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; @@ -24,7 +25,11 @@ class CounterProviderPage const CounterProviderPage({super.key}); @override - Widget buildChild(BuildContext context) { + CounterBloc create(BuildContext context) => + CounterBloc(repo(context)); + + @override + Widget builder(BuildContext context) { final textTheme = Theme.of(context).textTheme; return Scaffold( appBar: AppBar(title: const Text('Counter with Provider')), @@ -39,14 +44,16 @@ class CounterProviderPage children: [ FloatingActionButton( heroTag: null, - key: const Key('counterProviderView_increment_floatingActionButton'), + key: + const Key('counterProviderView_increment_floatingActionButton'), child: const Icon(Icons.add), onPressed: () => add(context, CounterIncrement()), ), const SizedBox(height: 8), FloatingActionButton( heroTag: null, - key: const Key('counterProviderView_decrement_floatingActionButton'), + key: + const Key('counterProviderView_decrement_floatingActionButton'), child: const Icon(Icons.remove), onPressed: () => add(context, CounterDecrement()), ), @@ -54,7 +61,4 @@ class CounterProviderPage ), ); } - - @override - CounterBloc create(BuildContext context) => CounterBloc(); } diff --git a/packages/wyatt_bloc_helper/example/lib/counter/cubit/counter_cubit.dart b/packages/wyatt_bloc_helper/example/lib/counter/cubit/counter_cubit.dart index 12759e12..13ee8b4d 100644 --- a/packages/wyatt_bloc_helper/example/lib/counter/cubit/counter_cubit.dart +++ b/packages/wyatt_bloc_helper/example/lib/counter/cubit/counter_cubit.dart @@ -1,32 +1,35 @@ // Copyright (C) 2022 WYATT GROUP // Please see the AUTHORS file for details. -// +// // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. -// +// // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with this program. If not, see . +import 'package:bloc_helper_example/counter/repository/counter_repository.dart'; import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; part 'counter_state.dart'; class CounterCubit extends Cubit { - CounterCubit() : super(const CounterInitial()); + final CounterRepository counterRepository; + + CounterCubit(this.counterRepository) : super(const CounterInitial()); void increment() { - emit(CounterModified(state.count + 1)); + emit(CounterModified(counterRepository.increment(state.count))); } void decrement() { - emit(CounterModified(state.count - 1)); + emit(CounterModified(counterRepository.decrement(state.count))); } } diff --git a/packages/wyatt_bloc_helper/example/lib/counter/repository/counter_repository.dart b/packages/wyatt_bloc_helper/example/lib/counter/repository/counter_repository.dart new file mode 100644 index 00000000..132414e9 --- /dev/null +++ b/packages/wyatt_bloc_helper/example/lib/counter/repository/counter_repository.dart @@ -0,0 +1,27 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +class CounterRepository { + int increment(int before) { + final after = before + 1; + return after; + } + + int decrement(int before) { + final after = before - 1; + return after; + } +} diff --git a/packages/wyatt_bloc_helper/example/lib/counter_repository_provider_page.dart b/packages/wyatt_bloc_helper/example/lib/counter_repository_provider_page.dart new file mode 100644 index 00000000..06b69b4f --- /dev/null +++ b/packages/wyatt_bloc_helper/example/lib/counter_repository_provider_page.dart @@ -0,0 +1,37 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:bloc_helper_example/counter/repository/counter_repository.dart'; +import 'package:bloc_helper_example/main_page.dart'; +import 'package:flutter/material.dart'; +import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart'; + +class CounterRepositoryProviderPage + extends RepositoryProviderScreen { + const CounterRepositoryProviderPage({super.key}); + + @override + CounterRepository create(BuildContext context) => CounterRepository(); + + @override + Widget builder(BuildContext context) => MaterialApp( + title: 'Flutter Demo', + theme: ThemeData( + primarySwatch: Colors.blue, + ), + home: const MainPage(), + ); +} diff --git a/packages/wyatt_bloc_helper/example/lib/main.dart b/packages/wyatt_bloc_helper/example/lib/main.dart index 14d1b2a9..a707077c 100644 --- a/packages/wyatt_bloc_helper/example/lib/main.dart +++ b/packages/wyatt_bloc_helper/example/lib/main.dart @@ -14,77 +14,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import 'package:bloc_helper_example/counter/counter_bloc_page.dart'; -import 'package:bloc_helper_example/counter/counter_consumer_page.dart'; -import 'package:bloc_helper_example/counter/counter_cubit_page.dart'; -import 'package:bloc_helper_example/counter/counter_provider_page.dart'; -import 'package:bloc_helper_example/counter/cubit/counter_cubit.dart'; import 'package:bloc_helper_example/counter_observer.dart'; +import 'package:bloc_helper_example/counter_repository_provider_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; void main() { - BlocOverrides.runZoned(() => runApp(const MyApp()), + BlocOverrides.runZoned(() => runApp(const CounterRepositoryProviderPage()), blocObserver: CounterObserver()); } - -class MyApp extends StatelessWidget { - const MyApp({Key? key}) : super(key: key); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - primarySwatch: Colors.blue, - ), - home: const MainPage(), - ); - } -} - -class MainPage extends StatelessWidget { - const MainPage({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: AppBar( - title: const Text('Main Page'), - ), - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - ElevatedButton( - child: const Text('Counter with BlocScreen'), - onPressed: () => Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const CounterBlocPage())), - ), - ElevatedButton( - child: const Text('Counter with CubitScreen'), - onPressed: () => Navigator.of(context).push( - MaterialPageRoute(builder: (_) => const CounterCubitPage())), - ), - ElevatedButton( - child: const Text('Counter with BlocProviderScreen'), - onPressed: () => Navigator.of(context).push(MaterialPageRoute( - builder: (_) => const CounterProviderPage())), - ), - ElevatedButton( - child: const Text('Counter with BlocConsumerScreen'), - onPressed: () => Navigator.of(context) - .push(MaterialPageRoute(builder: (context) { - return BlocProvider( - create: (context) => CounterCubit(), - child: const CounterConsumerPage(), - ); - })), - ), - ], - ), - ), - ); - } -} diff --git a/packages/wyatt_bloc_helper/example/lib/main_page.dart b/packages/wyatt_bloc_helper/example/lib/main_page.dart new file mode 100644 index 00000000..2a82d039 --- /dev/null +++ b/packages/wyatt_bloc_helper/example/lib/main_page.dart @@ -0,0 +1,69 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:bloc_helper_example/counter/counter_bloc_page.dart'; +import 'package:bloc_helper_example/counter/counter_consumer_page.dart'; +import 'package:bloc_helper_example/counter/counter_cubit_page.dart'; +import 'package:bloc_helper_example/counter/counter_provider_page.dart'; +import 'package:bloc_helper_example/counter/cubit/counter_cubit.dart'; +import 'package:bloc_helper_example/counter/repository/counter_repository.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +class MainPage extends StatelessWidget { + const MainPage({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Main Page'), + ), + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + child: const Text('Counter with BlocScreen'), + onPressed: () => Navigator.of(context).push( + MaterialPageRoute(builder: (_) => const CounterBlocPage())), + ), + ElevatedButton( + child: const Text('Counter with CubitScreen'), + onPressed: () => Navigator.of(context).push( + MaterialPageRoute(builder: (_) => const CounterCubitPage())), + ), + ElevatedButton( + child: const Text('Counter with BlocProviderScreen'), + onPressed: () => Navigator.of(context).push(MaterialPageRoute( + builder: (_) => const CounterProviderPage())), + ), + ElevatedButton( + child: const Text('Counter with BlocConsumerScreen'), + onPressed: () => Navigator.of(context) + .push(MaterialPageRoute(builder: (context) { + return BlocProvider( + create: (context) => CounterCubit(context.read()), + child: const CounterConsumerPage(), + ); + })), + ), + ], + ), + ), + ); + } +} diff --git a/packages/wyatt_bloc_helper/example/test/widget_test.dart b/packages/wyatt_bloc_helper/example/test/widget_test.dart index 9cd92fe5..7349f1b3 100644 --- a/packages/wyatt_bloc_helper/example/test/widget_test.dart +++ b/packages/wyatt_bloc_helper/example/test/widget_test.dart @@ -5,15 +5,14 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. +import 'package:bloc_helper_example/counter_repository_provider_page.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:bloc_helper_example/main.dart'; - void main() { testWidgets('Counter increments smoke test', (WidgetTester tester) async { // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); + await tester.pumpWidget(const CounterRepositoryProviderPage()); // Verify that our counter starts at 0. expect(find.text('0'), findsOneWidget); diff --git a/packages/wyatt_bloc_helper/example/web/favicon.png b/packages/wyatt_bloc_helper/example/web/favicon.png new file mode 100644 index 00000000..8aaa46ac Binary files /dev/null and b/packages/wyatt_bloc_helper/example/web/favicon.png differ diff --git a/packages/wyatt_bloc_helper/example/web/icons/Icon-192.png b/packages/wyatt_bloc_helper/example/web/icons/Icon-192.png new file mode 100644 index 00000000..b749bfef Binary files /dev/null and b/packages/wyatt_bloc_helper/example/web/icons/Icon-192.png differ diff --git a/packages/wyatt_bloc_helper/example/web/icons/Icon-512.png b/packages/wyatt_bloc_helper/example/web/icons/Icon-512.png new file mode 100644 index 00000000..88cfd48d Binary files /dev/null and b/packages/wyatt_bloc_helper/example/web/icons/Icon-512.png differ diff --git a/packages/wyatt_bloc_helper/example/web/icons/Icon-maskable-192.png b/packages/wyatt_bloc_helper/example/web/icons/Icon-maskable-192.png new file mode 100644 index 00000000..eb9b4d76 Binary files /dev/null and b/packages/wyatt_bloc_helper/example/web/icons/Icon-maskable-192.png differ diff --git a/packages/wyatt_bloc_helper/example/web/icons/Icon-maskable-512.png b/packages/wyatt_bloc_helper/example/web/icons/Icon-maskable-512.png new file mode 100644 index 00000000..d69c5669 Binary files /dev/null and b/packages/wyatt_bloc_helper/example/web/icons/Icon-maskable-512.png differ diff --git a/packages/wyatt_bloc_helper/example/web/index.html b/packages/wyatt_bloc_helper/example/web/index.html new file mode 100644 index 00000000..41b3bc33 --- /dev/null +++ b/packages/wyatt_bloc_helper/example/web/index.html @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + example + + + + + + + + + + diff --git a/packages/wyatt_bloc_helper/example/web/manifest.json b/packages/wyatt_bloc_helper/example/web/manifest.json new file mode 100644 index 00000000..096edf8f --- /dev/null +++ b/packages/wyatt_bloc_helper/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/packages/wyatt_bloc_helper/lib/src/bloc.dart b/packages/wyatt_bloc_helper/lib/src/bloc.dart index 2ef9c3c1..7113c132 100644 --- a/packages/wyatt_bloc_helper/lib/src/bloc.dart +++ b/packages/wyatt_bloc_helper/lib/src/bloc.dart @@ -14,27 +14,79 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_bloc/flutter_bloc.dart' as blocbase; import 'package:wyatt_bloc_helper/src/bloc_base/bloc_base_consumer_screen.dart'; import 'package:wyatt_bloc_helper/src/bloc_base/bloc_base_provider_screen.dart'; import 'package:wyatt_bloc_helper/src/bloc_base/bloc_base_screen.dart'; import 'package:wyatt_bloc_helper/src/mixins/bloc_base_provider_mixin.dart'; import 'package:wyatt_bloc_helper/src/mixins/bloc_provider_mixin.dart'; +import 'package:wyatt_bloc_helper/src/mixins/repository_base_provider_mixin.dart'; -abstract class BlocProviderScreen, E, S extends Object> - extends BlocBaseProviderScreen - with BlocBaseProviderMixin, BlocProviderMixin { - const BlocProviderScreen({super.key}); +/// {@template bloc_provider} +/// Need to implement a [create] function that is responsible for +/// creating the [Bloc] and a [builder] which will return a child +/// that have access to the instance via `context.read()`. +/// It is used as a dependency injection (DI) widget so that a single instance +/// of a [Bloc] can be provided to multiple widgets within a subtree. +/// +/// It automatically handles closing the instance when used with [create]. +/// By default, [create] is called only when the instance is accessed. +/// To override this behavior, set [lazy] to `false`. +/// +/// By default, it provide already provided instance found in the tree. +/// To override this behavior, set [smart] to `false`. +/// {@endtemplate} +abstract class BlocProviderScreen, + Event, State extends Object> extends BlocBaseProviderScreen + with + BlocBaseProviderMixin, + RepositoryProviderMixin, + BlocProviderMixin { + /// {@macro bloc_provider} + const BlocProviderScreen({super.key, super.lazy = true, super.smart = true}); } -abstract class BlocConsumerScreen, E, S extends Object> - extends BlocBaseConsumerScreen - with BlocBaseProviderMixin, BlocProviderMixin { +/// {@template bloc_consumer} +/// [BlocConsumerScreen] exposes [onBuild] and [onListen] in order react +/// to new states. +/// +/// An optional [shouldBuildWhen] and [shouldListenWhen] can be implemented +/// for more granular control over when [onListen] and [onBuild] are called. +/// The [shouldListenWhen] and [shouldBuildWhen] will be invoked on +/// each [Bloc] or `state` change. +/// They each take the previous `state` and current `state` and must return +/// a [bool] which determines whether or not the [onBuild] and/or [onListen] +/// function will be invoked. +/// The previous `state` will be initialized to the `state` of the [Bloc] when +/// the BlocConsumer is initialized. +/// [shouldListenWhen] and [shouldBuildWhen] are optional and if they +/// aren't implemented, they will default to `true`. +/// +/// An optional [onWrap] can also be implemented. This build a wrapper arround +/// the built BlocConsumer that is **not** rebuild on each state. +/// {@endtemplate} +abstract class BlocConsumerScreen, + Event, State extends Object> extends BlocBaseConsumerScreen + with + BlocBaseProviderMixin, + RepositoryProviderMixin, + BlocProviderMixin { + /// {@macro bloc_consumer} const BlocConsumerScreen({super.key}); } -abstract class BlocScreen, E, S extends Object> - extends BlocBaseScreen - with BlocBaseProviderMixin, BlocProviderMixin { - const BlocScreen({super.key}); +/// {@template bloc_screen} +/// Provide AND access to a [Bloc]. +/// +/// This extends [BlocConsumerScreen] with the methods +/// of [BlocBaseScreen]. +/// {@endtemplate} +abstract class BlocScreen, Event, + State extends Object> extends BlocBaseScreen + with + BlocBaseProviderMixin, + RepositoryProviderMixin, + BlocProviderMixin { + /// {@macro bloc_screen} + const BlocScreen({super.key, super.lazy = true, super.smart = true}); } diff --git a/packages/wyatt_bloc_helper/lib/src/bloc_base/bloc_base_consumer_screen.dart b/packages/wyatt_bloc_helper/lib/src/bloc_base/bloc_base_consumer_screen.dart index 3fc8e6e2..13c25367 100644 --- a/packages/wyatt_bloc_helper/lib/src/bloc_base/bloc_base_consumer_screen.dart +++ b/packages/wyatt_bloc_helper/lib/src/bloc_base/bloc_base_consumer_screen.dart @@ -17,39 +17,62 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -abstract class BlocBaseConsumerScreen, S extends Object> - extends StatelessWidget { +/// {@template bloc_base_consumer} +/// [BlocBaseConsumerScreen] exposes [onBuild] and [onListen] in order react +/// to new states. +/// +/// An optional [shouldBuildWhen] and [shouldListenWhen] can be implemented +/// for more granular control over when [onListen] and [onBuild] are called. +/// The [shouldListenWhen] and [shouldBuildWhen] will be invoked on +/// each [Bloc] or `state` change. +/// They each take the previous `state` and current `state` and must return +/// a [bool] which determines whether or not the [onBuild] and/or [onListen] +/// function will be invoked. +/// The previous `state` will be initialized to the `state` of the [Bloc] when +/// the [BlocConsumer] is initialized. +/// [shouldListenWhen] and [shouldBuildWhen] are optional and if they +/// aren't implemented, they will default to `true`. +/// +/// An optional [onWrap] can also be implemented. This build a wrapper arround +/// the built [BlocConsumer] that is **not** rebuild on each state. +/// {@endtemplate} +abstract class BlocBaseConsumerScreen, + State extends Object> extends StatelessWidget { + /// {@macro bloc_base_consumer} const BlocBaseConsumerScreen({super.key}); /// Takes the previous `state` and the current `state` and is responsible for /// returning a [bool] which determines whether or not to trigger /// [onBuild] with the current `state`. - bool shouldBuildWhen(S previous, S current) => true; + bool shouldBuildWhen(State previous, State current) => true; /// Takes the previous `state` and the current `state` and is responsible for /// returning a [bool] which determines whether or not to trigger /// [onListen] with the current `state`. - bool shouldListenWhen(S previous, S current) => true; + bool shouldListenWhen(State previous, State current) => true; + + /// The [onWrap] function which will be invoked on build. + /// The [onWrap] takes a `BuildContext` that **doesn't have** access + /// to the [Bloc] or [Cubit]. + Widget onWrap(BuildContext context, Widget child) => child; /// The [onBuild] function which will be invoked on each widget build. /// The [onBuild] takes the `BuildContext` and current `state` and /// must return a widget. - Widget onBuild(BuildContext context, S state); - - /// The [onWrap] function which will be invoked on each widget build. - /// The [onWrap] takes the `BuildContext` - /// Used to wrap which depends on the state. - Widget onWrap(BuildContext context, Widget child) => child; + Widget onBuild(BuildContext context, State state); /// Takes the `BuildContext` along with the `state` /// and is responsible for executing in response to `state` changes. - void onListen(BuildContext context, S state) {} + void onListen(BuildContext context, State state) {} @override - Widget build(BuildContext context) => BlocConsumer( - listenWhen: shouldListenWhen, - listener: onListen, - buildWhen: shouldBuildWhen, - builder: (context, state) => onWrap(context, onBuild(context, state)), + Widget build(BuildContext context) => onWrap( + context, + BlocConsumer( + listenWhen: shouldListenWhen, + listener: onListen, + buildWhen: shouldBuildWhen, + builder: onBuild, + ), ); } diff --git a/packages/wyatt_bloc_helper/lib/src/bloc_base/bloc_base_provider_screen.dart b/packages/wyatt_bloc_helper/lib/src/bloc_base/bloc_base_provider_screen.dart index 3a9bae5f..26c656e2 100644 --- a/packages/wyatt_bloc_helper/lib/src/bloc_base/bloc_base_provider_screen.dart +++ b/packages/wyatt_bloc_helper/lib/src/bloc_base/bloc_base_provider_screen.dart @@ -16,20 +16,53 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:wyatt_bloc_helper/src/utils/smart_provider.dart'; -abstract class BlocBaseProviderScreen, S extends Object> - extends StatelessWidget { - const BlocBaseProviderScreen({super.key}); +/// {@template bloc_base_provider} +/// Need to implement a [create] function that is responsible for +/// creating the [Bloc] or [Cubit] and a [builder] which will return a child +/// that have access to the instance via `context.read()`. +/// It is used as a dependency injection (DI) widget so that a single instance +/// of a [Bloc] or [Cubit] can be provided to multiple widgets within a subtree. +/// +/// It automatically handles closing the instance when used with [create]. +/// By default, [create] is called only when the instance is accessed. +/// To override this behavior, set [lazy] to `false`. +/// +/// By default, it provide already provided instance found in the tree. +/// To override this behavior, set [smart] to `false`. +/// {@endtemplate} +abstract class BlocBaseProviderScreen, + State extends Object> extends StatelessWidget { + /// {@macro bloc_base_provider} + const BlocBaseProviderScreen({ + super.key, + this.lazy = true, + this.smart = true, + }); - /// Creates the [Cubit] or [Bloc] to be used. - B create(BuildContext context); + /// Whether the [Bloc] or [Cubit] should be created lazily. + /// Defaults to `true` which means the [Bloc] or [Cubit] is created only when + /// accessed the first time, not when provided. + final bool lazy; + + /// Whether this uses [SmartProvider]. + /// Defaults to `true`. But if you want to provide new [Bloc] or [Cubit] + /// of a same type in a sub-tree you may have to disable this. + final bool smart; + + /// Creates the [Bloc] or [Cubit] to be used. + Bloc create(BuildContext context); /// Creates the child [Widget] to be used. - Widget buildChild(BuildContext context); + Widget builder(BuildContext context); @override - Widget build(BuildContext context) => BlocProvider( + Widget build(BuildContext context) => SmartProvider.bloc( + context, + lazy: lazy, + enable: smart, create: (_) => create(context), - child: Builder(builder: buildChild), + child: Builder(builder: builder), ); } diff --git a/packages/wyatt_bloc_helper/lib/src/bloc_base/bloc_base_screen.dart b/packages/wyatt_bloc_helper/lib/src/bloc_base/bloc_base_screen.dart index 4916b725..4c853af6 100644 --- a/packages/wyatt_bloc_helper/lib/src/bloc_base/bloc_base_screen.dart +++ b/packages/wyatt_bloc_helper/lib/src/bloc_base/bloc_base_screen.dart @@ -17,17 +17,44 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:wyatt_bloc_helper/src/bloc_base/bloc_base_consumer_screen.dart'; +import 'package:wyatt_bloc_helper/src/utils/smart_provider.dart'; -abstract class BlocBaseScreen, S extends Object> - extends BlocBaseConsumerScreen { - const BlocBaseScreen({super.key}); +/// {@template bloc_base_screen} +/// Provide AND access to a [Bloc] or [Cubit]. +/// +/// This extends [BlocBaseConsumerScreen] with the methods +// ignore: comment_references +/// of [BlocBaseProviderScreen]. +/// {@endtemplate} +abstract class BlocBaseScreen, + State extends Object> extends BlocBaseConsumerScreen { + /// {@macro bloc_base_screen} + const BlocBaseScreen({ + super.key, + this.lazy = true, + this.smart = true, + }); + + /// Whether the [Bloc] or [Cubit] should be created lazily. + /// Defaults to `true` which means the [Bloc] is created only when + /// accessed the first time, not when provided. + final bool lazy; + + + /// Whether this uses [SmartProvider]. + /// Defaults to `true`. But if you want to provide new Bloc of a same type, + /// in a sub-tree you may have to disable this. + final bool smart; /// Creates the [Cubit] or [Bloc] to be used. - B create(BuildContext context); + Bloc create(BuildContext context); @override - Widget build(BuildContext context) => BlocProvider( - create: (_) => create(context), - child: super.build(context), - ); + Widget build(BuildContext context) => SmartProvider.bloc( + context, + lazy: lazy, + enable: smart, + create: (_) => create(context), + child: super.build(context), + ); } diff --git a/packages/wyatt_bloc_helper/lib/src/cubit.dart b/packages/wyatt_bloc_helper/lib/src/cubit.dart index f3abacf0..8f740a6e 100644 --- a/packages/wyatt_bloc_helper/lib/src/cubit.dart +++ b/packages/wyatt_bloc_helper/lib/src/cubit.dart @@ -14,23 +14,70 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_bloc/flutter_bloc.dart' as blocbase; import 'package:wyatt_bloc_helper/src/bloc_base/bloc_base_consumer_screen.dart'; import 'package:wyatt_bloc_helper/src/bloc_base/bloc_base_provider_screen.dart'; import 'package:wyatt_bloc_helper/src/bloc_base/bloc_base_screen.dart'; import 'package:wyatt_bloc_helper/src/mixins/bloc_base_provider_mixin.dart'; +import 'package:wyatt_bloc_helper/src/mixins/repository_base_provider_mixin.dart'; -abstract class CubitProviderScreen, S extends Object> - extends BlocBaseProviderScreen with BlocBaseProviderMixin { - const CubitProviderScreen({super.key}); +/// {@template cubit_provider} +/// Need to implement a [create] function that is responsible for +/// creating the [Cubit] and a [builder] which will return a child +/// that have access to the instance via `context.read()`. +/// It is used as a dependency injection (DI) widget so that a single instance +/// of a [Cubit] can be provided to multiple widgets within a subtree. +/// +/// It automatically handles closing the instance when used with [create]. +/// By default, [create] is called only when the instance is accessed. +/// To override this behavior, set [lazy] to `false`. +/// +/// By default, it provide already provided instance in found in the tree. +/// To override this behavior, set [smart] to `false`. +/// {@endtemplate} +abstract class CubitProviderScreen, + State extends Object> extends BlocBaseProviderScreen + with BlocBaseProviderMixin, RepositoryProviderMixin { + /// {@macro cubit_provider} + const CubitProviderScreen({super.key, super.lazy = true, super.smart = true}); } -abstract class CubitConsumerScreen, S extends Object> - extends BlocBaseConsumerScreen with BlocBaseProviderMixin { +/// {@template cubit_consumer} +/// [CubitConsumerScreen] exposes [onBuild] and [onListen] in order react +/// to new states. +/// +/// An optional [shouldBuildWhen] and [shouldListenWhen] can be implemented +/// for more granular control over when [onListen] and [onBuild] are called. +/// The [shouldListenWhen] and [shouldBuildWhen] will be invoked on +/// each [Cubit] or `state` change. +/// They each take the previous `state` and current `state` and must return +/// a [bool] which determines whether or not the [onBuild] and/or [onListen] +/// function will be invoked. +/// The previous `state` will be initialized to the `state` of the [Cubit] when +/// the BlocConsumer is initialized. +/// [shouldListenWhen] and [shouldBuildWhen] are optional and if they +/// aren't implemented, they will default to `true`. +/// +/// An optional [onWrap] can also be implemented. This build a wrapper arround +/// the built BlocConsumer that is **not** rebuild on each state. +/// {@endtemplate} +abstract class CubitConsumerScreen, + State extends Object> extends BlocBaseConsumerScreen + with BlocBaseProviderMixin, RepositoryProviderMixin { + /// {@macro cubit_consumer} const CubitConsumerScreen({super.key}); } -abstract class CubitScreen, S extends Object> - extends BlocBaseScreen with BlocBaseProviderMixin { - const CubitScreen({super.key}); +/// {@template cubit_screen} +/// Provide AND access to a [Cubit]. +/// +/// This extends [CubitConsumerScreen] with the methods +// ignore: comment_references +/// of [CubitProviderScreen]. +/// {@endtemplate} +abstract class CubitScreen, + State extends Object> extends BlocBaseScreen + with BlocBaseProviderMixin, RepositoryProviderMixin { + /// {@macro cubit_screen} + const CubitScreen({super.key, super.lazy = true, super.smart = true}); } diff --git a/packages/wyatt_bloc_helper/lib/src/mixins/bloc_base_provider_mixin.dart b/packages/wyatt_bloc_helper/lib/src/mixins/bloc_base_provider_mixin.dart index 3ae13e26..6c2cf006 100644 --- a/packages/wyatt_bloc_helper/lib/src/mixins/bloc_base_provider_mixin.dart +++ b/packages/wyatt_bloc_helper/lib/src/mixins/bloc_base_provider_mixin.dart @@ -19,10 +19,14 @@ import 'package:flutter_bloc/flutter_bloc.dart'; /// A mixin that provides implementation of helper methods for /// [Bloc] and [Cubit] widgets. -mixin BlocBaseProviderMixin> { +mixin BlocBaseProviderMixin> { /// Returns the [BlocBase] used by this [BlocBaseProviderMixin]. - B bloc(BuildContext context) => context.read(); + Bloc bloc(BuildContext context) => context.read(); - /// Returns the [BlocBase] used by this [BlocBaseProviderMixin]. - R repo(BuildContext context) => context.read(); + /// Returns another [BlocBase] **not** used by this [BlocBaseProviderMixin]. + /// Short hand for `context.read();` + /// + /// To get [BlocBase] used by by this [BlocBaseProviderMixin] see `bloc()` + AnotherBloc anotherBloc(BuildContext context) => + context.read(); } diff --git a/packages/wyatt_bloc_helper/lib/src/mixins/bloc_provider_mixin.dart b/packages/wyatt_bloc_helper/lib/src/mixins/bloc_provider_mixin.dart index 547dd6ca..0680a048 100644 --- a/packages/wyatt_bloc_helper/lib/src/mixins/bloc_provider_mixin.dart +++ b/packages/wyatt_bloc_helper/lib/src/mixins/bloc_provider_mixin.dart @@ -15,10 +15,14 @@ // along with this program. If not, see . import 'package:flutter/widgets.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_bloc/flutter_bloc.dart' as blocbase; /// [Bloc] specific mixin that provides implementation /// of helper methods for events. -mixin BlocProviderMixin, E> { - void add(BuildContext context, E event) => context.read().add(event); +mixin BlocProviderMixin, Event> { + /// Add an event to the [Bloc]. + /// + /// Short hand for `context.read().add(event)`. + void add(BuildContext context, Event event) => + context.read().add(event); } diff --git a/packages/wyatt_bloc_helper/lib/src/mixins/repository_base_provider_mixin.dart b/packages/wyatt_bloc_helper/lib/src/mixins/repository_base_provider_mixin.dart new file mode 100644 index 00000000..06f08afc --- /dev/null +++ b/packages/wyatt_bloc_helper/lib/src/mixins/repository_base_provider_mixin.dart @@ -0,0 +1,26 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +/// A mixin that provides implementation of helper methods for +/// Repository widgets. +mixin RepositoryProviderMixin { + /// Returns the [Repository] used by this [Widget]. + Repository repo(BuildContext context) => + context.read(); +} diff --git a/packages/wyatt_bloc_helper/lib/src/repo.dart b/packages/wyatt_bloc_helper/lib/src/repo.dart new file mode 100644 index 00000000..c6e32540 --- /dev/null +++ b/packages/wyatt_bloc_helper/lib/src/repo.dart @@ -0,0 +1,39 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:wyatt_bloc_helper/src/mixins/repository_base_provider_mixin.dart'; +import 'package:wyatt_bloc_helper/src/repository/repository_provider_screen.dart'; + +/// {@template repository_provider} +/// Need to implement a [create] function that is responsible for +/// creating the [Repository] and a [builder] which will return a child +/// that have access to the instance via `context.read()`. +/// It is used as a dependency injection (DI) widget so that a single instance +/// of a [Repository] can be provided to multiple widgets within a subtree. +/// +/// It automatically handles closing the instance when used with [create]. +/// By default, [create] is called only when the instance is accessed. +/// To override this behavior, set [lazy] to `false`. +/// +/// By default, it provide already provided instance found in the tree. +/// To override this behavior, set [smart] to `false`. +/// {@endtemplate} +abstract class RepositoryProviderScreen + extends RepositoryBaseProviderScreen + with RepositoryProviderMixin { + /// {@macro repository_provider} + const RepositoryProviderScreen({super.key}); +} diff --git a/packages/wyatt_bloc_helper/lib/src/repository/repository_provider_screen.dart b/packages/wyatt_bloc_helper/lib/src/repository/repository_provider_screen.dart new file mode 100644 index 00000000..e7a8c5ce --- /dev/null +++ b/packages/wyatt_bloc_helper/lib/src/repository/repository_provider_screen.dart @@ -0,0 +1,67 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/material.dart'; +import 'package:wyatt_bloc_helper/src/utils/smart_provider.dart'; + +/// {@template repository_base_provider} +/// Need to implement a [create] function that is responsible for +/// creating the [Repository] and a [builder] which will return a child +/// that have access to the instance via `context.read()`. +/// It is used as a dependency injection (DI) widget so that a single instance +/// of a [Repository] can be provided to multiple widgets within a subtree. +/// +/// It automatically handles closing the instance when used with [create]. +/// By default, [create] is called only when the instance is accessed. +/// To override this behavior, set [lazy] to `false`. +/// +/// By default, it provide already provided instance found in the tree. +/// To override this behavior, set [smart] to `false`. +/// {@endtemplate} +abstract class RepositoryBaseProviderScreen + extends StatelessWidget { + /// {@macro repository_base_provider} + const RepositoryBaseProviderScreen({ + super.key, + this.lazy = true, + this.smart = true, + }); + + /// Whether the [Repository] should be created lazily. + /// Defaults to `true` which means the [Repository] is created only when + /// accessed the first time, not when provided. + final bool lazy; + + /// Whether this uses [SmartProvider]. + /// Defaults to `true`. But if you want to provide new [Repository] of a + /// same type in a sub-tree you may have to disable this. + final bool smart; + + /// Creates the [Repository] to be used. + Repository create(BuildContext context); + + /// Creates the child [Widget] to be used. + Widget builder(BuildContext context); + + @override + Widget build(BuildContext context) => SmartProvider.repo( + context, + lazy: lazy, + enable: smart, + create: (_) => create(context), + child: Builder(builder: builder), + ); +} diff --git a/packages/wyatt_bloc_helper/lib/src/utils/smart_provider.dart b/packages/wyatt_bloc_helper/lib/src/utils/smart_provider.dart new file mode 100644 index 00000000..5fdb959a --- /dev/null +++ b/packages/wyatt_bloc_helper/lib/src/utils/smart_provider.dart @@ -0,0 +1,69 @@ +// Copyright (C) 2022 WYATT GROUP +// Please see the AUTHORS file for details. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +import 'package:flutter/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +abstract class SmartProvider { + static BlocProvider + bloc, State extends Object>( + BuildContext context, { + required Bloc Function(BuildContext) create, + Widget? child, + bool lazy = true, + bool enable = true, + }) { + if (enable) { + final bloc = context.read(); + if (bloc != null) { + return BlocProvider.value( + value: bloc, + child: child, + ); + } + } + + return BlocProvider( + lazy: lazy, + create: (_) => create(context), + child: child, + ); + } + + static RepositoryProvider repo( + BuildContext context, { + required Repository Function(BuildContext) create, + Widget? child, + bool lazy = true, + bool enable = true, + }) { + if (enable) { + final repo = context.read(); + if (repo != null) { + return RepositoryProvider.value( + value: repo, + child: child, + ); + } + } + + return RepositoryProvider( + lazy: lazy, + create: (_) => create(context), + child: child, + ); + } +} diff --git a/packages/wyatt_bloc_helper/lib/wyatt_bloc_helper.dart b/packages/wyatt_bloc_helper/lib/wyatt_bloc_helper.dart index 83be0be2..cb9a6bcf 100644 --- a/packages/wyatt_bloc_helper/lib/wyatt_bloc_helper.dart +++ b/packages/wyatt_bloc_helper/lib/wyatt_bloc_helper.dart @@ -18,3 +18,4 @@ library wyatt_bloc_helper; export 'src/bloc.dart'; export 'src/cubit.dart'; +export 'src/repo.dart';