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鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Discussion] Maintaining Provider Dependency #712

Closed
felangel opened this issue Dec 8, 2019 · 27 comments 路 Fixed by #1880
Closed

[Discussion] Maintaining Provider Dependency #712

felangel opened this issue Dec 8, 2019 · 27 comments 路 Fixed by #1880
Assignees
Labels
dependency This issue has an external dependency discussion Open discussion for a specific topic feedback wanted Looking for feedback from the community
Projects

Comments

@felangel
Copy link
Owner

felangel commented Dec 8, 2019

Hey everyone! 馃憢

I am becoming concerned about the dependency on provider due to the increase in complexity. After each upgrade I find myself questioning whether it makes sense for the bloc library to continue to maintain the dependency on provider and wanted to start this discussion in order to see what everyone's thoughts are.

A few specific examples that come to mind include:

  1. https://github.com/rrousselGit/provider/blob/00d0f8f8020dcfe876bd7826f176e1ca2feeaac7/packages/provider/lib/src/provider.dart#L288 - changes were made to prevent Provider from being used to provide Streams. Before bloc v1.0.0 BlocProvider was able to simply extend Provider which made the implementation extremely straightforward but after v1.0.0 (when bloc extended Stream) BlocProvider had to be refactored to extends ValueDelegateWidget<T> and implement SingleChildCloneableWidget with a manual build override to use an InheritedProvider. In this case, the implementation went from:
import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
import 'package:provider/provider.dart';
import 'package:bloc/bloc.dart';

class BlocProvider<T extends Bloc<dynamic, dynamic>> extends Provider<T> {
  BlocProvider({
    Key key,
    @required ValueBuilder<T> builder,
    Widget child,
  }) : super(
          key: key,
          builder: builder,
          dispose: (_, bloc) => bloc?.dispose(),
          child: child,
        );

  BlocProvider.value({
    Key key,
    @required T value,
    Widget child,
  }) : super.value(
          key: key,
          value: value,
          child: child,
        );

  static T of<T extends Bloc<dynamic, dynamic>>(BuildContext context) {
    try {
      return Provider.of<T>(context, listen: false);
    } catch (_) {...}
  }
}

to

import 'package:flutter/widgets.dart';
import 'package:meta/meta.dart';
import 'package:provider/provider.dart';
import 'package:bloc/bloc.dart';

class BlocProvider<T extends Bloc<dynamic, dynamic>>
    extends ValueDelegateWidget<T> implements SingleChildCloneableWidget {
  final Widget child;

  BlocProvider({
    Key key,
    ValueBuilder<T> builder,
    Widget child,
  }) : this._(
          key: key,
          delegate: BuilderStateDelegate<T>(
            builder,
            dispose: (_, bloc) => bloc?.dispose(),
          ),
          child: child,
        );

  BlocProvider.value({
    Key key,
    @required T value,
    Widget child,
  }) : this._(
          key: key,
          delegate: SingleValueDelegate<T>(value),
          child: child,
        );

  BlocProvider._({
    Key key,
    @required ValueStateDelegate<T> delegate,
    this.child,
  }) : super(key: key, delegate: delegate);

  static T of<T extends Bloc<dynamic, dynamic>>(BuildContext context) {
    try {
      return Provider.of<T>(context, listen: false);
    } catch (_) { ... }
  }

  @override
  Widget build(BuildContext context) {
    return InheritedProvider<T>(
      value: delegate.value,
      child: Builder(
        builder: (context) => child,
      ),
    );
  }

  @override
  BlocProvider<T> cloneWithChild(Widget child) {
    return BlocProvider<T>._(
      key: key,
      delegate: delegate,
      child: child,
    );
  }
}

You might argue that we didn't add that much code but it wasn't immediately obvious to me what ValueDelegateWidget was for so I had to spent a significant amount of time understanding the implementation details of provider in order for me to feel comfortable refactoring the code.

  1. buildWithChild introduced along with SingleChildStatelessWidget, etc... with the dependency on nested in v4.0.0-dev. While I like the idea, in practice I'm not happy with the implementation and there are several major limitations including not being able to use it as a mixin and also having to deal with a potentially null child when buildWithChild is called (unless I'm misunderstanding how to use it). I personally prefer the previous, simpler implementation because it was easy to understand and didn't involve custom Widget and Element implementations.

  2. flutter_bloc only requires a small subset of the functionality provider offers and I feel it would be much easier/less time-consuming to maintain the flutter_bloc library if I didn't have to do a deep-dive of the provider implementation after every major change in order to understand how it will impact the bloc library.

The goal of this issue was just to start a discussion and see what everyone thinks. I'm more than happy to continue to maintain the dependency, but I just want to make sure that the cost of maintenance is justified by the benefit to the community.

cc @rrousselGit, @brianegan, @larssn, @timrijckaert, @jorgecoca

@felangel felangel added discussion Open discussion for a specific topic feedback wanted Looking for feedback from the community labels Dec 8, 2019
@felangel felangel self-assigned this Dec 8, 2019
@felangel felangel added this to To do in bloc via automation Dec 8, 2019
@felangel felangel added the dependency This issue has an external dependency label Dec 8, 2019
@bigworld12
Copy link

extra dependencies are always a problem, I think it's better to just use InheritedWidget (which provider extends) instead of having extra overhead

@felangel felangel pinned this issue Dec 8, 2019
@rrousselGit
Copy link

rrousselGit commented Dec 8, 2019

The v4 of provider will have lazy-loading. That's likely something flutter_bloc wants too, but it's not a one-liner feature.

In the end, it's your call though.

For what it's worth, if your worry is about:

class BlocProvider<T extends Bloc<dynamic, dynamic>> extends Provider<T> {
...
}

vs:

class BlocProvider<T extends Bloc<dynamic, dynamic>>
    extends ValueDelegateWidget<T> implements SingleChildCloneableWidget {
...
}

then it's a relatively easy fix. One of the goals of the 4.0.0 of provider is to simplify custom providers.

As such, if that's what you need, I can make InheritedProvider subclassable, such that you'd write:

class BlocProvider<T extends Bloc<dynamic, dynamic>> extends InheritedProvider<T> {
  BlocProvider({
    Key key,
    @required Create<T> create,
    Widget child,
  }) : super(
          key: key,
          create: create,
          dispose: (_, bloc) => bloc?.dispose(),
          child: child,
        );

  BlocProvider.value({
    Key key,
    @required T value,
    Widget child,
  }) : super.value(
          key: key,
          value: value,
          child: child,
        );

  static T of<T extends Bloc<dynamic, dynamic>>(BuildContext context) {
    try {
      return Provider.of<T>(context, listen: false);
    } catch (_) {...}
  }
}

@narcodico
Copy link
Contributor

I'm sure there's plenty of people using both packages in their apps, myself being one of those. I think being able to pass Providers alongside BlocProviders into a MultiProvider makes it worth keeping the dependency. Not to mention the incoming lazy-loading which is something much needed in both packages.

@felangel
Copy link
Owner Author

felangel commented Dec 8, 2019

@RollyPeres can you please elaborate on how lazy loading would benefit flutter_bloc? Also, I鈥檇 love to understand how you use both together. What do you provide with provider aside from blocs when using flutter_bloc?

@narcodico
Copy link
Contributor

@felangel I believe lazy loading would be great for global blocs which does't really need to be instantiated when app starts but later on when they might be needed.

As for your second question, I mostly provide Firebase streams, since I'm not a fan of manually listening to streams unless it's absolutely necessary. And StreamProvider works great with Firebase. A quick example:

class _GlobalProviders extends StatelessWidget {
  const _GlobalProviders({Key key, @required this.child})
      : assert(child != null),
        super(key: key);

  final Widget child;

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        // * Independent services
        Provider<DbService>.value(value: DbService()),
        Provider<AuthService>.value(value: AuthService()),
        Provider<PostsService>(
          builder: (_) => PostsService(),
          dispose: (_, PostsService value) => value.dispose(),
        ),
        // * Dependent services
        ProxyProvider<DbService, SeedService>(
          builder: (_, DbService dbService, SeedService __) =>
              SeedService(dbService: dbService),
        ),
        // * UI providers
        BlocProvider<AppBloc>(builder: (_) => AppBloc()),
        StreamProvider<User>(
          builder: (BuildContext context) =>
              Provider.of<AuthService>(context, listen: false).user,
        ),
      ],
      child: child,
    );
  }
}

@rrousselGit
Copy link

In the end, the main goal of provider is to introduce some conventions on InheritedWidgets.

Aka providers having two constructors, one stateful with a create+dispose method, and another one named .value that is stateless and just passes the value as-is.
With more recently the default constructor being lazy-loaded.

The other part is to help maintainers to respect such convention (as having two constructors with different logics can be painful), so that as many people as possible respect that convention.

So I won't die if flutter_bloc doesn't use provider. As long as the convention is respected, it's a healthy situation I think.

Although I'm fairly certain that folks will want lazy-loading 馃槢

@larssn
Copy link

larssn commented Dec 10, 2019

@RollyPeres can you please elaborate on how lazy loading would benefit flutter_bloc? Also, I鈥檇 love to understand how you use both together. What do you provide with provider aside from blocs when using flutter_bloc?

We provide services that encapsulate non-flutter related business tasks. This could be a service that provides a low level printing layer via TCP, which I might need to inject into other services or BLoCs. We use Provider for this, as it offers a clean way of doing DI without any side effects.

Regarding lazy loading, in my experience it's not the end all be all achievement; there's always a catch. It's good for reducing load times, but can make the app feel sluggish until it is "warm". I hope you make it optional. 馃檪

So I won't die if flutter_bloc doesn't use provider. As long as the convention is respected, it's a healthy situation I think.

I will literally die, if you ditch Provider. 馃槈

@felangel
Copy link
Owner Author

felangel commented Dec 11, 2019

@RollyPeres thanks for the additional information. Regarding lazy loading, I think that can be easily supported regardless of the dependency on provider so it should not be a problem.

@RollyPeres, @larssn
In your code snippet, I'm noticing that you're providing raw data providers directly instead of having a layered architecture where you have the following data flow:

data -> repository -> bloc -> widget

Can you provide a bit more detail regarding why you decided to use this approach rather than the one outlined in the architecture docs? Thanks!

@larssn
Copy link

larssn commented Dec 12, 2019

@felangel
For us there's a few factors that play in:

  1. We don't want all our business logic in BLoCs, as we need a piece of business logic to be accessable by multiple different BLoCs. Injecting BLoCs into other BLoCs would be the alternative, but it's an alternative I don't consider very clean, as suddenly you have a BLoC that is tied to something other than UI, and I want my BLoC to be tied to the presentation layer always.

  2. We don't want all our business logic to be streams. In a previous project, we went overboard with Rx where we had adopted the mindset of "everything is a stream". It allowed us to do clever things, but it also made everything overly complex, and harder to read.

  3. We don't want DDD, meaning our repositories are much slimmer than if we were doing DDD. And if that business logic fits in neither the repo nor the BLoC, the only fitting alternative is a service.

I hope that clarifies how we're doing things a little.

@timrijckaert
Copy link

timrijckaert commented Dec 12, 2019

My two cents.

We too use the Provider package in conjunction with the flutter_bloc package.
The fact that they are interchangeable is essential to our use case of DI inside our app with MultiProvider.

About the proposed architecture data -> repo -> bloc -> widget we felt it was too limited if you want to share stateless business logic between multiple BLoC's.

Just like @larssn said we also have helper/service classes for this which we inject in multiple BLoC's that purely consists of reusable functions. Easy to unit test in isolation, ...

Maybe a bit of offtopic, but this sometimes looks weird when we need to provide this dependency.
RepositoryProvider(create: (_) => VRTDomainService())

@narcodico
Copy link
Contributor

narcodico commented Dec 12, 2019

@felangel I'm always pro separation of concerns and layering your app and whatnot, but it all depends on your use case. For me doing that would sometimes mean I would have to manually subscribe to streams coming from firebase and that's something I don't want to. I prefer not to break the pipe, if you know what I mean. So sometimes I prefer to transform data from the incoming stream to whatever I need, without involving BLoC. MultiProvider DI with provider and flutter_bloc is essential. Would be a shame to force people into one or other library, or force them to use unnecessary nesting, but that's for you to decide I guess.

@felangel
Copy link
Owner Author

Hey everyone thanks for the awesome feedback! I'm going to try my best to respond to everyone's comments and am looking forward to hearing your thoughts 馃憤

@larssn

  1. We don't want all our business logic in BLoCs, as we need a piece of business logic to be accessible by multiple different BLoCs. Injecting BLoCs into other BLoCs would be the alternative, but it's an alternative I don't consider very clean, as suddenly you have a BLoC that is tied to something other than UI, and I want my BLoC to be tied to the presentation layer always.

In that case I would recommend exposing the business logic to the bloc via a repository and providing that repository using RepositoryProvider. This ensures that you maintain separation between your data layer, your domain layer, your use-case (Bloc) layer, and your presentation layer.

  1. We don't want all our business logic to be streams. In a previous project, we went overboard with Rx where we had adopted the mindset of "everything is a stream". It allowed us to do clever things, but it also made everything overly complex, and harder to read.

I totally understand this concern and it was actually one of the major reasons for creating the bloc library. You shouldn't need to interact directly with Rx very often when using the bloc library because the whole goal was to abstract the Rx while still allowing you to write reactive code. Is there anything in particular that you are concerned about in terms of complexity when using the bloc library? At the end of the day it should just be simple logic that maps events to states (especially if you have separated your architecture into the 4 aforementioned layers).

  1. We don't want DDD, meaning our repositories are much slimmer than if we were doing DDD. And if that business logic fits in neither the repo nor the BLoC, the only fitting alternative is a service.

I'm not sure in this case what the difference between a service and a repository is. Unless I'm misunderstanding, you still want to maintain separation between the presentation and data layers so in this case your services could be parts of repositories. Just out of curiosity, is there a particular reason why you've decided against DDD? Thanks!

@timrijckaert

About the proposed architecture data -> repo -> bloc -> widget we felt it was too limited if you want to share stateless business logic between multiple BLoC's.

It would be very useful if you could elaborate and provide some specific cases where you felt the architecture was too limited? Also, if you are sharing stateless business logic I'm not sure I understand why you need to use provider in the first place. Couldn't you simply have public methods that you inject directly into the bloc when you instantiate it via BlocProvider?

int sum(int x, int y) => x + y;
import 'sum.dart';

BlocProvider<MathBloc>(
  builder: (_) => MathBloc(sum),
)

Maybe a bit of off-topic, but this sometimes looks weird when we need to provide this dependency.
RepositoryProvider(create: (_) => VRTDomainService())

I agree with you and that's intentional because RepositoryProvider was specifically named to discourage providing other types. In this case, if VRTDomainService is stateless business logic, then I don't personally see the need to use provider as I mentioned above. If it is stateful; however, or has dependencies on http clients, dbs, etc... then I would recommend injecting it into the repositories that need it and providing the repositories to the blocs via RepositoryProvider. At the end of the day the goal of the bloc library architecture was:

  • repositories compose data from one or more data providers
  • blocs compose models from one or more repositories
  • UI composes states (view models) from one or more blocs

If that was unclear, I'm more than happy to go back through the docs and update them as needed. Please let me know if it was just a misunderstanding or if you still feel like there are limitations of this approach which require directly providing data providers to the UI.

@RollyPeres

For me doing that would sometimes mean I would have to manually subscribe to streams coming from firebase and that's something I don't want to. I prefer not to break the pipe, if you know what I mean. So sometimes I prefer to transform data from the incoming stream to whatever I need, without involving BLoC.

I'm not sure I understand your reasoning for this. By directly consuming the stream from Firestore you're tightly coupling your UI to your raw entities. If you wanted to share the data across multiple applications it would become very difficult to maintain. Also, is there a particular reason why you're hesitant to subscribe to a Stream within a bloc? It should be pretty straightforward and there are several examples of this in the examples including the Firestore Todos and Timer.

MultiProvider DI with provider and flutter_bloc is essential.

Can you elaborate? I just want to note that MultiProvider actually has nothing to do with provider and can be abstracted into a separate package (which @rrousselGit is already attempting with nested). Also, even in the worst case you could use a MultiProvider to provide whatever you'd like and nest a MultiBlocProvider to provide your blocs. I don't think you should feel like you can't combine the two without this direct dependency.

Would be a shame to force people into one or other library, or force them to use unnecessary nesting, but that's for you to decide I guess.

The goal of this discussion was for me to voice my concerns about the dependency on provider from a library maintenance perspective. Having direct dependencies often means longer wait times for new features/improvements and higher risk for bugs/breaking changes. My goal was to surface these concerns and propose having a consistent API (as @rrousselGit mentioned) without the explicit need to maintain a direct dependency. My goal is to make the bloc library high quality and easy to use while also being able to quickly iterate and add new features/functionality as requested by the community. I definitely don't want anyone to feel like I'm forcing anything on them and regardless of the outcome you should feel free to import whatever dependencies and use them however you see fit. From my perspective, provider offers much more functionality that is not applicable in flutter_bloc (all we really need is InheritedProvider) and that can easily be implemented with an InheritedWidget. We could maintain the same functionality/feature-set while simplifying things from a library maintenance standpoint and that was my main point/reason for bringing this up.

Hope that clarifies things and again I really appreciate the feedback and am looking forward to your responses. Thanks everyone! 馃檹

@narcodico
Copy link
Contributor

@felangel

I'm not sure I understand your reasoning for this. By directly consuming the stream from Firestore you're tightly coupling your UI to your raw entities. If you wanted to share the data across multiple applications it would become very difficult to maintain. Also, is there a particular reason why you're hesitant to subscribe to a Stream within a bloc? It should be pretty straightforward and there are several examples of this in the examples including the Firestore Todos and Timer.

Not really, you can map your entity to a different model if needed. If you replace your incoming firebase stream to something else, you still need to deal with backend specific implementations. I'm well aware of how to manually listen to streams, I just prefer not to do something that would only add noise and no real gain to my app. Why do you think sharing some stream source would be difficult ? At the end of the day, if someone goes with your repository suggestion it means you will manually get the data from the firebase stream then convert it to some model. How is that different from exposing a stream that you map it to some model using a stream operator ? I'm totally up for repo layer when you deal with futures and standard API's. But when the data source is a stream, I think you only lose the goodies streams have to offer by manually subscribing instead of taking advantage of operators. I guess is just a matter of preference. You can easily share a stream and even map it to different models if needed, without really needing another layer. It all depends on what you are trying to achieve I guess.

@larssn
Copy link

larssn commented Dec 18, 2019

is there a particular reason why you've decided against DDD? Thanks!

Yes. It's something many people get wrong, because they don't understand it's many many concepts. There's a lot in DDD I don't understand myself, so I'd rather not force people down that road. I prefer my models to be anaemic 馃榿

I like that BLoC is pretty open ended: it's doesn't force my hand to adopt DDD.

@felangel
Copy link
Owner Author

@RollyPeres in what part of your application do you do the Stream transformation? In my opinion, it's more about abstraction; if you're transforming the raw Firestore stream in your UI layer you no longer have any layer of abstraction which means your UI is tightly coupled to Firestore. If your application blows up in terms of users, for example, and you decide you want to switch to a different DB to save on cost you will end up having to rewrite a lot of code.

@larssn can you please provide any specific examples of concepts that are unclear or difficult to understand in your experience?

Ultimately, even though you're both using bloc in a way which diverges from the recommended architecture the decoupling of provider from bloc shouldn't have a large impact since the API will remain the same. You will just need to add provider as a dependency to you pubspec.yaml (which you should already be doing since it's not good to directly import transitive dependencies) and worst case you might need to refactor

    return MultiProvider(
      providers: [
        // * Independent services
        Provider<DbService>.value(value: DbService()),
        Provider<AuthService>.value(value: AuthService()),
        Provider<PostsService>(
          builder: (_) => PostsService(),
          dispose: (_, PostsService value) => value.dispose(),
        ),
        // * Dependent services
        ProxyProvider<DbService, SeedService>(
          builder: (_, DbService dbService, SeedService __) =>
              SeedService(dbService: dbService),
        ),
        // * UI providers
        BlocProvider<AppBloc>(builder: (_) => AppBloc()),
        StreamProvider<User>(
          builder: (BuildContext context) =>
              Provider.of<AuthService>(context, listen: false).user,
        ),
      ],
      child: child,
    );

into

    return MultiProvider(
      providers: [
        // * Independent services
        Provider<DbService>.value(value: DbService()),
        Provider<AuthService>.value(value: AuthService()),
        Provider<PostsService>(
          builder: (_) => PostsService(),
          dispose: (_, PostsService value) => value.dispose(),
        ),
        // * Dependent services
        ProxyProvider<DbService, SeedService>(
          builder: (_, DbService dbService, SeedService __) =>
              SeedService(dbService: dbService),
        ),
        // * UI providers
        StreamProvider<User>(
          builder: (BuildContext context) =>
              Provider.of<AuthService>(context, listen: false).user,
        ),
      ],
      child: MultiBlocProvider(
        providers: [
          BlocProvider<AppBloc>(builder: (_) => AppBloc()),
          ...
       ],
       child: child,
      ),
    );

Even the latter won't be necessary if the MultiProvider concept is removed from provider because as I mentioned earlier it has no dependency on provider. Both libraries can depend on nested or a different common implementation to maintain compatibility. Thoughts?

@larssn
Copy link

larssn commented Dec 18, 2019

@larssn can you please provide any specific examples of concepts that are unclear or difficult to understand in your experience?

Well I'm talking about DDD, I assume you're referring to the concepts in BLoC?

@felangel
Copy link
Owner Author

@larssn I'd be very interested to understand why you prefer not to follow the architecture outline in the documentation when using bloc so that I may potentially make improvements.

@larssn
Copy link

larssn commented Dec 18, 2019

@felangel
The architecture outline is a great starting point. But I'm not a fan of the Bloc to Bloc approach, as I hinted earlier. When put to practise we fairly quickly ran into scenarios where it was hard to figure out in which order events fed into Blocs depending on other Blocs, caused output to trigger. We decided fairly early that this was a recipe for a lot of potential avoidable complexity, and opted for an approach that did not have an over-reliance on streams. This is where services came in, which fills the niche of exposing some piece of business logic (such as constructing an invoice), that had to be shared between several Blocs. But, it can also be, as @timrijckaert suggested, to simply expose simple stuff like date formatting functions, that might exclusively be used by widgets.

As I mentioned earlier, in a previous project, we went overboard with Rx (JS), and while we're all fricking Rx experts at this point from flatmapping our concatmaps, that product ended up being overly complex, and very hard to maintain. So this time, we use streams pretty sparsely.

@narcodico
Copy link
Contributor

narcodico commented Dec 19, 2019

@larssn But I'm not a fan of the Bloc to Bloc approach, as I hinted earlier

I also try to avoid that at all costs. If you need bloc to bloc then you might actually need that logic in a separate bloc provided on top of the others. At the end of the day, you could even combineLatest multiple blocs :)

@felangel in what part of your application do you do the Stream transformation? In my opinion, it's more about abstraction; if you're transforming the raw Firestore stream in your UI layer you no longer have any layer of abstraction which means your UI is tightly coupled to Firestore. If your application blows up in terms of users, for example, and you decide you want to switch to a different DB to save on cost you will end up having to rewrite a lot of code.

Stream transformation happens in the service, which is nothing but a glorified repository at the end of the day really, which instead of Futures exposes Streams. I could very well suffix them with Repository and use RepositoryProviders to make them available in the tree, but since I will end up needing Streams from them for StreamProviders, I decided not to mix the two.
Switching to a different back-end is rarely the case. When it comes to flutter, a Stream is a Stream, no matter where it comes from. I would personally only switch to a different real-time BaaS.

@felangel
Copy link
Owner Author

@larssn it would be great if you could provide some examples of the scenarios you are describing. Depending on the case, I would argue that logic that is shared between several blocs could be part of the repository layer so you would not need bloc to bloc communication. Also date formatting is a UI specific thing so I would recommend creating a DateFormatter widget like:

DateFormatter(
  date: date,
  builder: (formattedDate) { ... }
),

We had the same fear coming from RxSwift so I totally understand your perspective. The bloc library was our way of using Streams in a safe/scalable way. If you are able to provide some scenarios as I mentioned above I will try my best to improve those areas/pain-points. Thanks!

@felangel
Copy link
Owner Author

@RollyPeres thanks for clarifying! So just for my understanding, you avoid subscribing to streams in blocs and instead prefer to directly transform the stream in a service which you directly consume in your widgets via a StreamBuilder? All this wouldn't be an issue for you if there was a better way to transform/manipulate a stream within a bloc without having to subscribe?

@narcodico
Copy link
Contributor

@felangel no problem at all! I do avoid manually subscribing to streams and I do transform streams inside a service, but I don't actually use a StreamBuilder but instead I pass my streams to provider's StreamProvider which makes use of StreamBuilder under the hood. And yes, I would totally consider using bloc with real time streams if it wouldn't force me to subscribe.

@larssn
Copy link

larssn commented Dec 23, 2019

Broadcast stream support wouldn't be bad. I made an issue about that recently, since I thought it was supported.

@felangel I think we're mostly debating semantics. Our current approach doesn't really display any pain-points, it's just a slightly different way of doing things. :-) One could argue it's just a different way of naming things. It's just that in my view, a repository should be fairly basic, and only handle data access, and not complex business logic. Our repos transforms a Firestore document into Dart object instances (built_value/built_collection for immutability), handles save operations and thats it.

@felangel
Copy link
Owner Author

felangel commented Jan 7, 2020

@rrousselGit, @RollyPeres, @larssn, @timrijckaert, @bigworld12 would love you feedback/opinions regarding #765

@netantho
Copy link
Sponsor Contributor

netantho commented Feb 7, 2020

@felangel Should this issue be closed given that #821 got merged?

@felangel
Copy link
Owner Author

felangel commented Feb 7, 2020

@netantho I don't think so because this PR is related to provider as a dependency not rxdart 馃憤

@manyopensource
Copy link

Hopefully you'll find some inspiration here: https://youtu.be/_ISAA_Jt9kI

@felangel felangel pinned this issue Nov 3, 2020
bloc automation moved this from To do to Done Nov 4, 2020
@felangel felangel unpinned this issue Nov 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
dependency This issue has an external dependency discussion Open discussion for a specific topic feedback wanted Looking for feedback from the community
Projects
bloc
  
Done
Development

Successfully merging a pull request may close this issue.

8 participants