Giter Club home page Giter Club logo

flutter-tdd-clean-architecture-course's People

Contributors

resodev avatar ricardoebbers avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flutter-tdd-clean-architecture-course's Issues

fixture reader can't open file

i'm trying running test with
flutter test
but it show error

test/features/number_trivia/data/models/number_trivia_model_test.dart:
 fromJson should return a valid model when the JSON number is an integer [E]
 
FileSystemException: Cannot open file, path = 'test/fixtures/trivia.json'
(OS Error: No such file or directory, errno = 2)
 dart:io  File.readAsStringSync 
fixtures/fixture_reader.dart 3:60                                       fixture
features/number_trivia/data/models/number_trivia_model_test.dart 26:25  main.<fn>.<fn>

There is no test widget

I ran the project tdd clean architector method, but I had a problem with the test wiget. Does the test wiget work with your method?
Please leave a tutorial link🙏

Clean architecture with bloc as tree structure(lift state up)

I have an AuthBloc on top of the tree. Beneath are ProfileBloc, PostBlocs...
When an event is launched to ProfileBloc/PostBlocs usecase called, repository. I get back from repository an 401 error, usecase returns Either as you used.
The problem is how do you think is the best way to "tell" this to AuthBloc (because it handles the authentication state)?

  1. Pass the AuthBloc instance to each sub blocs, so they can call authBloc.add(LogoutEvent())
  2. Use a AppEventService using service locator. AuthBloc will listen/filter to AuthExternalEvents from AppEventService. ProfileService will add AuthExternalEvents to AppEventService (seems like an EventBus)
  3. Same as point (2) but add an interceptor to Dio (api calls) that will add the event to AppEventService in case of 401
  4. When creating the repository that will be injected in blocs constructors. Repository(onRevoke: () => authBloc.add(Logout())) (inspired by Felix Angelov)

Any help with this so that still remains in a clean architecture way?

Got Error Expected: Actual

image

i got error
`fromJson should return a valid model when the JSON number is an integer:

ERROR: Expected: NumberTriviaModel:
Actual: NumberTriviaModel:

package:test_api expect
package:flutter_test/src/widget_tester.dart 431:3 expect
test\features\number_trivia\data\models\number_trivia_model_test.dart 30:9 main..`

how to Resolve this ?

Question about architecture and external events

In my app, I have an authentication dataSource designed to retrieve JWT access tokens and refresh tokens from my server. The access token has to be supplied on every call.

However, in my server calls, I may return a 401 and automatically use the refresh token to refresh the access token with a server call and then retry the original call.

When this happens, the contents of the access token will change. That could possible affect other things (like permissions, etc.). In addition, the new tokens will have to be saved to storage.

To do that, the authentication bloc will have a new event of refreshed and that might effect other things. The various other parts of the app can then reflect the new access token values.

So, do I create a singleton authentication bloc and add the refreshed event to it when this happens from inside the API? That sounds right. That's the way I wrote it, but it doesn't seem "clean".

Proposal Discussion

Hey! I stumbled upon your channel by accident, glad I did! Nice stuff!! I do, however have a couple of questions.

  • Usually when you go to Clean you end up placing your Business Logic inside your domain so you can eventually reuse it, this (for me) meant that your BLoCs should be a part of that domain layer, however you place them within the Presentation Layer, what is your main reason? My guess is that since it is updating the UI like a ViewModel would do then you are placing it with the screen/view that it is working with.

  • The way you set up your folder structure also can take a toll whenever developing bigger projects, for example, you have setup domain in each feature but if you need another feature to use the same entities that another feature has, then you would be breaking your own folder proposal. To be honest, that's something I've struggled working with in Flutter. I believe a solution could be to have the presentation layer to be feature grouped and have a more general domain + data layer outside. Going with your app it would look something like:

    • lib:
      • data:
        • datasources
        • repositories (concrete)
      • domain:
        • repositories (edge)
        • entities
        • usecases
      • presentation:
        • feature:
          • logic holder
          • screen/ui/page (or whatever you'd like to call it)

data_connection_checker cannot run with sound null safety

I want to run test, but this error accurs:

Error: Cannot run with sound null safety, because the following dependencies
don't support null safety:

 - package:data_connection_checker

For solutions, see https://dart.dev/go/unsound-null-safety

What package is better to replace data_connection_checker?

Blank White Screen when App is Launched

Thanks for this awesome work Reso
After upgrading my flutter SDK, I get a white screen when the app starts, after reading the logs, all you have to do is add WidgetsFlutterBinding.ensureInitialized(); to the main method in main.dart.

And also consider upgrading the app to androidX

I just thought I should point it out to save others from similar frustration because it is heartbreaking to go through all this work and have a white screen in the end.

Again, thanks for the awesome tutorial, it has taught me a lot

Cheers

Bloc has hard-coded dependency on concrete use case type

You have this:

class NumberTriviaBloc extends Bloc<NumberTriviaEvent, NumberTriviaState> {
  final GetConcreteNumberTrivia getConcreteNumberTrivia;
  final GetRandomNumberTrivia getRandomNumberTrivia;

Which is hardcoding a dependency on GetConcreteNumberTrivia and GetRandomNumberTrivia types.

Shouldn't that really be:

class NumberTriviaBloc extends Bloc<NumberTriviaEvent, NumberTriviaState> {
  final UseCase<NumberTrivia, Params> getConcreteNumberTrivia;
  final UseCase<NumberTrivia, NoParams> getRandomNumberTrivia;

where Params would be a type (probably better named) in entities or better yet just use int instead and make GetConcreteNumberTrivia implement UseCase<NumberTrivia, int>

Add .gitkeep files to empty folders

Hey Matt!

I'm a big fan of your Flutter videos. I've been experimenting with Flutter for some weeks and your guides helped me a lot, thanks!

For this course, as folder structure is really important, I want to ask you if you can add .gitkeep empty files to the empty folders, so we can better follow the fundamentals of the first lesson, or even clone this repository as a template for new projects!

Thanks again and keep the excellent work!

About entity issues

I read your article
Entity: The business objects of the application, they are the least likely to change when external things change
Model: Inherit and extend Domain layer Entity, realize json conversion and more needs

Generally speaking, business requirements have some additional fields, such as isSelected and isComplete, which are related to UI state
I don't know whether these attributes are placed in the Domain layer or in the Presentation layer to create a new model

I am very confused and hope to wait for your reply

Thanks

Domain:

class QuestionEntitie {
  final int id;
  final String category;
  final String question;

 /// Should it be declared here?
  final bool isSelected;
  final bool isComplete;
}

OR Presentation:

class QuestionItem {
  final QuestionEntitie item
  /// or declare here ?
  final bool isSelected;
  final bool isComplete;
}

Data:

class QuestionModel extends QuestionEntitie {
  QuestionModel({
    required super.id,
    required super.category,
    required super.question,
  });

  factory QuestionModel.fromJson(Map<String, dynamic> json) {
    return QuestionModel(
      id: json['id'],
      category: json['category'],
      question: json['question'],
    );
  }
}

你会用TDD吗?

测试代码本身也算另一种形式的业务代码,为了保证测试代码的正确。
你给测试代码写测试吗?
你给测试代码的测试代码写测试吗?
你给测试代码的测试代码的测试代码写测试吗?
你给测试代码的测试代码的测试代码的测试代码写测试吗?
你不写,就代表你不会用TDD,你就写不出高质量代码,你就无法重构代码,你就会被程序界开除人籍

Question - Immutable and Equatable

I am using equatable as you describe in your course.

How do you manage when one class contains another and the contained class gets updated?

All the examples I find use a copyWith approach with non-positional arguments.

Is this the best practices way to do it?

TIA

Null input throws unhandled exception in InputConverter

You should add the following line to InputConverter before trying to parse the input as an int:

  if (str == null) return Left(InvalidInputFailure());

otherwise an unhandled exception is thrown that is not covered in your exception handling.

Should have permission Internet

If I want to put the application on Android phone we need to add a permission for internet in the manifest like so:

<uses-permission android:name="android.permission.INTERNET" />

Btw, very thanks for all this stuff, video, tutorial and this code you rock ;) ! @ResoDev

[Proposal] Simple starter project

So, lo and behold because now I will introduce you to the Reso Coder's Flutter Clean Architecture Proposal - Explanation & Project Structure

I really cannot get this statement out of my head 😄and I propose to take this to the next level by:

  • Offering a skeleton app with the folder structure and dependencies list
  • Include the helper generic code like the fixture file reader
  • Any stubs, examples, helpers that can make TDD easier
  • Maybe offering a simple cli tool to generate a file by type like repo make:entity EntityName

I'm coming from a backend background (Laravel) and really like the simple yet advanced tutorial you offer, and I can grantee that after a small increment changes, you may end with a solid framework that others can contribute on.

Thanks for your effort, it's my first time to be motivated to become a Mobile Developer!

infinity from random endpoint causes crash

Random can return infinity, which has a null value for the number field, see json response below

{
  "text": "-Infinity is negative infinity.",
  "number": null,
  "found": true,
  "type": "trivia"
}

main.dart

Hi and thank you so much for those tutorials, very interesting and well explained.

Can you please update your main.dart? i'm not sure how to initiate the app.

Thanks.

get_it error

I/flutter (30280): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter (30280): The following assertion was thrown building Builder(dirty):
I/flutter (30280): You tried to access an instance of TriviaNumberLocalDataSource that was not ready yet
I/flutter (30280): 'package:get_it/get_it_impl.dart':
I/flutter (30280): Failed assertion: line 272 pos 14: 'instanceFactory.isReady'
I/flutter (30280): The relevant error-causing widget was:
I/flutter (30280): Builder
I/flutter (30280):
package:trivia_number_new/…/pages/trivia_number_page.dart:57
I/flutter (30280): When the exception was thrown, this was the stack

The app shows Cache Failure in release mode.

The app works completely fine in debug and profile modes without any issue. But when trying in release mode it Shows either only a single Number Trivia every time or Cache Failure on both the trivia controls. Please help in fixing this issue.

Params

Hey ResoCoder!

I am currently working on a flutter project, and have applied your architecture. It was hard in the beginning, but atm I have a pretty good understanding of it! I have one struggle though.

When working on the [feature]_bloc file, where everything comes together. I get an error when importing/injecting multiple usecases that there are multiple imports that have a "Params" class. VSCode suggests using prefixes to seperate them. I this the right solution or is there a better/less code solution?

this is my solution.

import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:njoy_shopping_app/constants/error_constants.dart';
import 'package:njoy_shopping_app/core/error/failures.dart';
import 'package:njoy_shopping_app/features/merchants/data/models/merchant_model.dart';
import 'package:njoy_shopping_app/features/merchants/domain/usecases/get_merchant_by_id.dart'
    as _merchantById;
import 'package:njoy_shopping_app/features/merchants/domain/usecases/get_merchants.dart'
    as _merchants;
import 'package:njoy_shopping_app/features/merchants/domain/usecases/get_merchants_by_name.dart'
    as _merchantByName;
import './bloc.dart';
import 'package:meta/meta.dart';

class MerchantBloc extends Bloc<MerchantEvent, MerchantState> {
  final _merchantById.GetMerchantById getMerchantById;
  final _merchantByName.GetMerchantsByName getMerchantsByName;
  final _merchants.GetMerchants getMerchants;

  MerchantBloc({
    @required _merchantById.GetMerchantById getMerchantById,
    @required _merchantByName.GetMerchantsByName getMerchantsByName,
    @required _merchants.GetMerchants getMerchants,
  })  : assert(getMerchantById != null),
        assert(getMerchantsByName != null),
        assert(getMerchants != null),
        getMerchantById = getMerchantById,
        getMerchantsByName = getMerchantsByName,
        getMerchants = getMerchants;

  @override
  MerchantState get initialState => InitialMerchantState();

  @override
  Stream<MerchantState> mapEventToState(
    MerchantEvent event,
  ) async* {
    if (event is SearchByMerchantNameEvent) {
      yield MerchantsLoading();

      final failureOrMerchants =
          await getMerchantsByName(_merchantByName.Params(name: event.name));

      yield* _eitherLoadedMerchantsOrErrorState(failureOrMerchants);
    } else if (event is SearchMerchantsEvent) {
      yield MerchantsLoading();

      final failureOrMerchants = await getMerchants(_merchants.Params(
        lat: event.lat,
        lng: event.lng,
        rad: event.rad,
        cat: event.cat,
      ));

      yield* _eitherLoadedMerchantsOrErrorState(failureOrMerchants);
    } else if (event is GetMerchantByIdEvent) {
      yield MerchantsLoading();

      final failureOrMerchant =
          await getMerchantById(_merchantById.Params(merchantId: event.id));

      yield* _eitherLoadedMerchantOrErrorState(failureOrMerchant);
    }
  }

  Stream<MerchantState> _eitherLoadedMerchantsOrErrorState(
    Either<Failure, List<MerchantModel>> failureOrMerchants,
  ) async* {
    yield failureOrMerchants.fold(
        (failure) => Error(message: _mapFailureToMessage(failure)),
        (merchants) => MerchantsLoaded(merchants: merchants));
  }

  Stream<MerchantState> _eitherLoadedMerchantOrErrorState(
    Either<Failure, MerchantModel> failureOrMerchants,
  ) async* {
    yield failureOrMerchants.fold(
        (failure) => Error(message: _mapFailureToMessage(failure)),
        (merchant) => MerchantLoaded(merchant: merchant));
  }

  String _mapFailureToMessage(Failure failure) {
    switch (failure.runtimeType) {
      case ServerFailure:
        return SERVER_FAILURE_MESSAGE;
      case CacheFailure:
        return CACHE_FAILURE_MESSAGE;
      default:
        return 'Unexpected error';
    }
  }
}

Update code

I have got many fails when testing usecases.
Because right now dart supports null safety

Relations between features

Hi
How can I use widgets or pages of another feature?

Example:
For example, I have an authentication feature, I want to use the login widget of this plugin in the feature of my store, how should I do this?

If I import the widget into my storeframe, the modular structure will be lost, right?

parsing nested json

Hi, I'm new to Flutter.

I facing problem when I try to parsing "complex" json with nested list. How to parsing nested json data in model file, while the entity doesn't contain .fromJson() and .toJson() ?
We know that model class extends entity class.

It's confused me.
Thank you :)

block tests with flutter_bloc 7.0.0 failing

Hello,

BLOC tests are failing after upgrading flutter_bloc to 7.0.0. Tried several options and searched web. Did not find suitable answer. Can you please suggest a fix ?

Here is an example test name:

should emit [Error] when the input is invalid

Fails for this expectLater(bloc, emitsInOrder(expected));

Problem with DataConnectionChecker

Hello everyone and @ResoDev ! The DataConnectionChecker is good lib, but it seems me it doesn't work in some countries, because I compiled app and it didn't work, than I debuged it and changed the line of coude if (await networkInfo.isConnected) to if(true) than it worked. Thats why I recomend everyone don't use DataConnectionChecker in future on your apps, becuase your app will not work in some countries, of course if it's need internet!

Update course to null safety

Hello, I am learning TDD with the course but I see that it is a little out of date, and now with Flutter 2.0 and null safety it would be a great idea to update it, because in addition to teaching TDD it would also help us with the issue of null safety in Flutter, which is so new

[Proposal] Add a license to the project

Many thanks for this project and the video tutorial.

You do a fabulous job on making it clear how to apply these abstract design principles to Flutter.

Could you add a license to the project, maybe the GPL v3 like in the other Flutter course you have (flutter-ddd)?

If it's easier for you, I can make a pull request with a LICENSE file.

Thank you for your effort, you made me take a look at Flutter again!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.