Skip to content

Commit

Permalink
Merge pull request #752 from Prime-Holding/develop
Browse files Browse the repository at this point in the history
rx_bloc_cli-v3.8.0
  • Loading branch information
StanevPrime authored Jul 19, 2024
2 parents 34364b7 + da125e0 commit 20948a8
Show file tree
Hide file tree
Showing 14 changed files with 193 additions and 97 deletions.
4 changes: 2 additions & 2 deletions examples/reminders/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Each feature represents a separate flow in the app. They can be composed of one
The logic of each page should be placed into its own [BLoC][rx_bloc_lnk]. This is desired especially if the page has to be a **Stateful Widget**. The BLoC is placed inside the `blocs` directory. In order for the BLoC to be more readable, its implementation details can be offloaded to its own extensions file ( `[bloc_name]_extensions.dart`, placed inside the same directory) or one or more usecases.

## Architecture
<img src="https://raw.githubusercontent.com/Prime-Holding/rx_bloc/develop/packages/rx_bloc_cli/mason_templates/bricks/rx_bloc_base/__brick__/docs/app_architecture.jpg" alt="Rx Bloc Architecture"></img>
<img src="https://raw.githubusercontent.com/Prime-Holding/rx_bloc/develop/packages/rx_bloc_cli/mason_templates/bricks/rx_bloc_base/__brick__/docs/app_architecture.png" alt="Rx Bloc Architecture"></img>

<div id="navigation"/>

Expand Down Expand Up @@ -166,4 +166,4 @@ In order to make the notifications work on your target platform, make sure you f
[flutter_local_notifications_lnk]: https://pub.dev/packages/flutter_local_notifications
[shelf_lnk]: https://pub.dev/packages/shelf
[shelf_router_lnk]: https://pub.dev/packages/shelf_router
[shelf_static_lnk]: https://pub.dev/packages/shelf_static
[shelf_static_lnk]: https://pub.dev/packages/shelf_static
2 changes: 2 additions & 0 deletions packages/rx_bloc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ class HomePage extends StatelessWidget {
- [Github search][github_search_example_line_ref] - a feature-rich list view including infinity-scroll and pull-to-refresh functionalities.
- [Counter][counter_bloc_sample_lnk] - an example of how to create a `CounterBloc` to implement an advanced Flutter Counter app.
- [Division][division_sample_lnk] - Division sample
- [Todo App][todo_sample_lnk] - A todo app that solves various tech challenges such as: Various widget_toolkit components, List filtering, Inter-feature communication, Complete error handling and more.



Expand Down Expand Up @@ -228,6 +229,7 @@ class HomePage extends StatelessWidget {
[rx_bloc_provider_lnk]: https://pub.dev/documentation/flutter_rx_bloc/latest/flutter_rx_bloc/RxBlocProvider-class.html
[rx_bloc_builder_lnk]: https://pub.dev/documentation/flutter_rx_bloc/latest/flutter_rx_bloc/RxBlocBuilder-class.html
[booking_app_sample_lnk]: https://github.com/Prime-Holding/rx_bloc/tree/master/examples/booking_app
[todo_sample_lnk]: https://github.com/Prime-Holding/rx_bloc/tree/master/examples/todoapp
[favourites_advanced_sample_lnk]: https://github.com/Prime-Holding/rx_bloc/tree/master/examples/favorites_advanced
[counter_bloc_sample_lnk]: https://github.com/Prime-Holding/rx_bloc/tree/master/examples/counter
[division_sample_lnk]: https://github.com/Prime-Holding/rx_bloc/tree/master/examples/division
Expand Down
3 changes: 3 additions & 0 deletions packages/rx_bloc_cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## [3.8.0]
* Added functionality to exclude smart widgets dependency injection components during golden tests.

## [3.7.0]
Updated environment variable names used in CI/CD functionality:
- `MOBILE_DISTRIBUTION_ENCRYPTION_PASSWORD` -> `CREDENTIAL_ENCRYPTION_PASSWORD`
Expand Down
3 changes: 2 additions & 1 deletion packages/rx_bloc_cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ Out of the box, a Rx Bloc CLI created projects includes:

[Design system][design_system_lnk] - A single place where you can define all your colors, typography, assets and more. Your app's Light and Dark mode are already configured

[Testing][testing_lnk] - With Unit and Golden Tests you know your app is working as intended
[Testing][testing_lnk] - With Unit and Golden Tests you know your app is working as intended. Please read the Rx Bloc CLI specific [Golden Tests documentation][golden_tests_documentation_lnk].

[Static analysis][static_analysis_lnk] - Strict Lint rules which are used to write quality code

Expand Down Expand Up @@ -127,3 +127,4 @@ Rx Bloc Command Line Interface supports [extendability] with the help of mason t
[dio_http_client_lnk]: https://pub.dev/packages/dio
[interceptors_lnk]: https://pub.dev/documentation/dio/latest/dio/Interceptor-class.html
[extendability]: /packages/rx_bloc_cli/mason_templates/README.md
[golden_tests_documentation_lnk]: /packages/rx_bloc_cli/doc/golden_tests.md
3 changes: 2 additions & 1 deletion packages/rx_bloc_cli/bin/compile_test_project.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ cp example/testapp/README.md example/
mkdir example/docs
cp example/testapp/docs/continuous_delivery.md example/docs/
cp example/testapp/docs/app_architecture.png example/docs/
cp example/testapp/docs/cicd_diagram.png example/docs/
cp example/testapp/docs/cicd_diagram.png example/docs/
cp doc/golden_tests.md example/docs/
48 changes: 48 additions & 0 deletions packages/rx_bloc_cli/doc/golden_tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
### Smart Widgets dependencies for Golden Tests

In golden tests we want to mock the BLoCs to be able to test all UI states. By using smart widgets that manage their dependencies, golden tests could fail. To fix this issue we need to build the smart widgets without injecting the required components during the test. This is why we added `isInTestMode` in `app_constants.dart`.

To use it in custom widgets with their own dependency injection you can follow this example:
```
import '../../base/app/config/app_constants.dart';
...
class AppTodoListBulkEditPopupMenuButtonWithDependencies {
@override
Widget build(BuildContext context) {
final current = TodoManagementPage();
if(isInTestMode) {
return current;
}
return MultiProvider(
providers: [
..._services,
..._blocs,
],
child: current,
);
}
}
```
> [!CAUTION]
> You should import `isInTestMode` from [app_constants.dart]!
> Since we don't want to add dependency on `universal_io/io.dart`, because we want to avoid the additional maintenance cost, we check if `dart.library.io` is available to determine if we are running a test or other environment.
*app_constants.dart*
```
export 'is_in_test_mode_io_not_available.dart' if (dart.library.io) 'is_in_test_mode_io_available.dart' if (dart.library.html) 'is_in_test_mode_io_not_available.dart';
```
*is_in_test_mode_io_available.dart*
```
import 'dart:io';
bool isInTestMode = Platform.environment.containsKey('FLUTTER_TEST');
```

*is_in_test_mode_io_not_available.dart*
```
const bool isInTestMode = false;
```
2 changes: 1 addition & 1 deletion packages/rx_bloc_cli/lib/src/rx_bloc_cli_constants.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// Indicates the version of the package
const rxBlocCliPackageVersion = '3.7.0';
const rxBlocCliPackageVersion = '3.8.0';

/// Generated project's Android Compile SDK version
const kAndroidCompileSDKVersion = 34;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import '../repositories/apple_auth_repository.dart';
import '../services/apple_social_login_service.dart';
import '../services/social_login_service.dart';
import 'social_login_button.dart';
import '../../base/app/config/app_constants.dart';

/// [AppleLoginWidget] provides out of the box Log in with Apple
/// functionality along with default view of the button.
Expand All @@ -26,43 +27,51 @@ class AppleLoginWidget extends StatelessWidget {
const AppleLoginWidget({super.key});

@override
Widget build(BuildContext context) => MultiProvider(
Widget build(BuildContext context) {
final current = Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
AppErrorModalWidget<SocialLoginBlocType>(
errorState: (bloc) => bloc.states.errors,
),
RxBlocBuilder<SocialLoginBlocType, bool>(
state: (bloc) => bloc.states.isLoading,
builder: (context, snapshot, bloc) => SocialLoginButton(
isLoading: (snapshot.data ?? false) ? false : true,
textStyle: context.designSystem.typography.appleButtonText,
backgroundColor: context.designSystem.colors.appleBackground,
text: context.l10n.featureLogin.appleLogin,
progressIndicatorColor:
context.designSystem.colors.appleButtonText,
onPressed:
(snapshot.data ?? false) ? null : () => bloc.events.login(),
child: SvgPicture.asset(
context.designSystem.images.appleLogo,
height: context.designSystem.spacing.xl,
colorFilter: ColorFilter.mode(
context.designSystem.colors.appleButtonText,
BlendMode.srcIn,
),
),
),
),
],
);

if (isInTestMode) {
return current;
}

return MultiProvider(
providers: [
..._dataSources,
..._repositories,
..._services,
..._blocs,
],
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
AppErrorModalWidget<SocialLoginBlocType>(
errorState: (bloc) => bloc.states.errors,
),
RxBlocBuilder<SocialLoginBlocType, bool>(
state: (bloc) => bloc.states.isLoading,
builder: (context, snapshot, bloc) => SocialLoginButton(
isLoading: (snapshot.data ?? false) ? false : true,
textStyle: context.designSystem.typography.appleButtonText,
backgroundColor: context.designSystem.colors.appleBackground,
text: context.l10n.featureLogin.appleLogin,
progressIndicatorColor:
context.designSystem.colors.appleButtonText,
onPressed:
(snapshot.data ?? false) ? null : () => bloc.events.login(),
child: SvgPicture.asset(
context.designSystem.images.appleLogo,
height: context.designSystem.spacing.xl,
colorFilter: ColorFilter.mode(
context.designSystem.colors.appleButtonText,
BlendMode.srcIn,
),
),
),
),
],
),
child: current,
);
}

List<Provider> get _dataSources => [
Provider<AppleAuthDataSource>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import '../repositories/facebook_auth_repository.dart';
import '../services/facebook_social_login_service.dart';
import '../services/social_login_service.dart';
import 'social_login_button.dart';
import '../../base/app/config/app_constants.dart';

/// [FacebookLoginWidget] provides out of the box Log in with Apple
/// functionality along with default view of the button.
Expand All @@ -25,43 +26,51 @@ class FacebookLoginWidget extends StatelessWidget {
const FacebookLoginWidget({super.key});

@override
Widget build(BuildContext context) => MultiProvider(
Widget build(BuildContext context) {
final current = Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
AppErrorModalWidget<SocialLoginBlocType>(
errorState: (bloc) => bloc.states.errors,
),
RxBlocBuilder<SocialLoginBlocType, bool>(
state: (bloc) => bloc.states.isLoading,
builder: (context, snapshot, bloc) => SocialLoginButton(
isLoading: (snapshot.data ?? false) ? false : true,
backgroundColor: context.designSystem.colors.facebookBackground,
text: context.l10n.featureLogin.facebookLogin,
textStyle: context.designSystem.typography.facebookButtonText,
progressIndicatorColor:
context.designSystem.colors.facebookTextColor,
onPressed:
(snapshot.data ?? false) ? null : () => bloc.events.login(),
child: SvgPicture.asset(
context.designSystem.images.facebookLogo,
colorFilter: ColorFilter.mode(
context.designSystem.colors.facebookTextColor,
BlendMode.srcIn,
),
height: context.designSystem.spacing.xl,
),
),
),
],
);

if (isInTestMode) {
return current;
}

return MultiProvider(
providers: [
..._dataSources,
..._repositories,
..._services,
..._blocs,
],
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
AppErrorModalWidget<SocialLoginBlocType>(
errorState: (bloc) => bloc.states.errors,
),
RxBlocBuilder<SocialLoginBlocType, bool>(
state: (bloc) => bloc.states.isLoading,
builder: (context, snapshot, bloc) => SocialLoginButton(
isLoading: (snapshot.data ?? false) ? false : true,
backgroundColor: context.designSystem.colors.facebookBackground,
text: context.l10n.featureLogin.facebookLogin,
textStyle: context.designSystem.typography.facebookButtonText,
progressIndicatorColor:
context.designSystem.colors.facebookTextColor,
onPressed:
(snapshot.data ?? false) ? null : () => bloc.events.login(),
child: SvgPicture.asset(
context.designSystem.images.facebookLogo,
colorFilter: ColorFilter.mode(
context.designSystem.colors.facebookTextColor,
BlendMode.srcIn,
),
height: context.designSystem.spacing.xl,
),
),
),
],
),
child: current,
);
}

List<Provider> get _dataSources => [
Provider<FacebookAuthDataSource>(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:flutter_svg/svg.dart';
import 'package:provider/provider.dart';

import '../../app_extensions.dart';
import '../../base/app/config/app_constants.dart';
import '../../base/common_ui_components/app_error_modal_widget.dart';
import '../../base/data_sources/remote/http_clients/api_http_client.dart';
import '../blocs/social_login_bloc.dart';
Expand All @@ -25,44 +26,52 @@ class GoogleLoginWidget extends StatelessWidget {
const GoogleLoginWidget({super.key});

@override
Widget build(BuildContext context) => MultiProvider(
Widget build(BuildContext context) {
final current = Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
AppErrorModalWidget<SocialLoginBlocType>(
errorState: (bloc) => bloc.states.errors,
),
RxBlocBuilder<SocialLoginBlocType, bool>(
state: (bloc) => bloc.states.isLoading,
builder: (context, loadingState, bloc) => SocialLoginButton(
isLoading: (loadingState.data ?? false) ? false : true,
text: context.l10n.featureLogin.googleLogin,
borderSide: BorderSide(
color: context.designSystem.colors.white,
width: 0.3,
),
textStyle: context.designSystem.typography.googleButtonText,
backgroundColor: context.designSystem.colors.googleBackground,
progressIndicatorColor:
context.designSystem.colors.googleButtonText,
onPressed: (loadingState.data ?? false)
? null
: () => bloc.events.login(),
child: SvgPicture.asset(
context.designSystem.images.googleLogo,
height: context.designSystem.spacing.xl,
),
),
),
],
);

if (isInTestMode) {
return current;
}

return MultiProvider(
providers: [
..._dataSources,
..._repositories,
..._services,
..._blocs,
],
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
AppErrorModalWidget<SocialLoginBlocType>(
errorState: (bloc) => bloc.states.errors,
),
RxBlocBuilder<SocialLoginBlocType, bool>(
state: (bloc) => bloc.states.isLoading,
builder: (context, loadingState, bloc) => SocialLoginButton(
isLoading: (loadingState.data ?? false) ? false : true,
text: context.l10n.featureLogin.googleLogin,
borderSide: BorderSide(
color: context.designSystem.colors.white,
width: 0.3,
),
textStyle: context.designSystem.typography.googleButtonText,
backgroundColor: context.designSystem.colors.googleBackground,
progressIndicatorColor:
context.designSystem.colors.googleButtonText,
onPressed: (loadingState.data ?? false)
? null
: () => bloc.events.login(),
child: SvgPicture.asset(
context.designSystem.images.googleLogo,
height: context.designSystem.spacing.xl,
),
),
),
],
),
child: current,
);
}

List<Provider> get _dataSources => [
Provider<GoogleAuthDataSource>(
Expand Down
Loading

0 comments on commit 20948a8

Please sign in to comment.