Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Leandro Forte - Flutter Coding Test #75

Open
wants to merge 26 commits into
base: master
Choose a base branch
from

Conversation

leandromichelforte
Copy link

Hey guys,

Hope this message finds you well
The documentation is the app-documentation.md file in the project root folder

Thank you for the opportunity
See you later

Leandro Forte and others added 26 commits February 6, 2024 15:25
[FEATURE][Flutter] QR Code Scan
[FEATURE] Add NodeJS backend and connect Flutter into it
Copy link

@diegog-sf diegog-sf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work!

I think you have a solid understanding of domain layers, their responsibilities, and how they interact with each other. It is great to see that you tested all layers as well.

On the presentation side, I saw attention to detail and code organization with the cubit responsibilities.

cupertino_icons: ^1.0.2
bloc: ^8.1.2
flutter_bloc: ^8.1.3
bloc_test: ^9.1.5

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bloc_test should be a dev dependency

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I'll pay more attention to that. Thanks.

bloc_test: ^9.1.5
dartz: ^0.10.1
equatable: ^2.0.5
mocktail: ^1.0.3

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mocktail should be a dev dependency

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I'll pay more attention to that. Thanks.


extension BuildContextExtension on BuildContext {
double get screenWidth => MediaQuery.of(this).size.width;
TextTheme get textTheme => Theme.of(this).textTheme;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kudos: nice!


sealed class DioInject {
static Future<void> inject() async {
getIt.registerFactory<Dio>(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: why a factory and not a singleton?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I should have used singleton for that knowing the singleton will create the instance only when called.
I'll pay more attention to that. Thanks.


@override
void dispose() {
_timerCubit.disposeTimer();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: great catch disposing the timer if disposing the screen, butyou can also dispose the timer on the cubit's dispose method.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, of course. Thanks.

final double dimension;

@override
Widget build(BuildContext context) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kudos: nice laoding animation

BlocBuilder<TimerCubit, TimerState>(
bloc: _timerCubit,
builder: (context, state) {
return switch (state) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kudos: dart 3 ussage


test(
'should get QR code from the repository and check if it was called once',
() async {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

kudos: for testing all layers

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Based on the path (lib/features/qr_code_scan/domain/repositories), do you consider that repositories belong to the domain layer?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the Clean Architecture concept, I know the repository belongs to the data layer, but I like to create only the abstract class in the domain folder and the implementation class in the data folder just to represent the connection between the domain and data layers.

Thanks.

import 'package:superformula_leandro/core/errors/failures/failure.dart';
import 'package:superformula_leandro/features/qr_code_scan/domain/entities/qr_code_entity.dart';

abstract interface class GetQrCodeRepository {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Could you explain why you named the repository GetQrCodeRepository and not just QrCodeRepository?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right. I forgot to rename it. I'll pay more attention to that. Thanks.

import 'package:superformula_leandro/features/qr_code_scan/presenter/cubits/scan/scan_cubit.dart';
import 'package:superformula_leandro/features/qr_code_scan/presenter/pages/scan/widgets/scanned_data_body_widget.dart';

class ScanPage extends StatefulWidget {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Could you explain why you preferred using a StatefulWidget instead of an StatelessWidget?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to initialize the ScanCubt from the context.read and after call the startScan method only in the beginning of the page lifecycle.

Theres another way to do that, using StatelessWidget and calling the startScan method when the state is the ScanInitialState.

Thanks.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about doing it when instantiating the cubit? Do you see any inconvenient with this approach?

BlocProvider<ScanCubit>(
  create: (context) => ScanCubit()..startScan(),
  child: const ScanPage(),
),

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No inconvenient at all. That's a really good way to do that as well. Thanks.

padding: const EdgeInsets.all(16),
child: Center(
child: BlocBuilder<ScanCubit, ScanState>(
bloc: _scanCubit,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Do you think it's necessary to pass bloc as parameter?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like to use this parameter to make the understanding of the code easier. For example, if a page has a lot of cubits, this parameter I'll helps who is reading to understand what that widget is responsible for.

Thanks.

),
),
bottomNavigationBar: BlocBuilder<ScanCubit, ScanState>(
bloc: _scanCubit,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Maybe you could have used buildWhen to avoid unnecessary rebuilds.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's necessary because in this page, I need to rebuild this bottomsheet every time cubit emits something.

I have the emitted states: ScanningState, ScanErrorState and ScannedDataState and I need to only show when It's ScannedDataState.

For example, If I've had the buildWhen: (_, current) => current is ScannedDataState, when emits another state the bottomsheet will not disappear and I don't want it.

Don't you think so?

Anyway, thanks for noticing that.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could use the following condition, what do you think?

buildWhen: (previous, current) {
  return (previous is ScannedDataState) ^ (current is ScannedDataState);
},

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yess. This should work, you're right. Thanks.

@leandromichelforte
Copy link
Author

Hi guys. I replied to all your comments.

I really appreciate all your feedbacks and I'm looking forward for the next steps.

Feel free to reach me out to anything. Thanks one more time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants