Closed elonzh closed 5 years ago
@earlzo thanks for bringing this up and opening an issue!
I would recommend using InheritedWidget
or InheritedModel
to expose your repositories to the sections of the widget tree that need them. If you want to use a 3rd party package, you can check out the provider package and do something like:
MultiProvider(
providers: [
Provider<LocalRepository>(value: LocalRepository(databaseName: null)),
Provider<AnotherRepository>(value: AnotherRepository()),
Provider<YetAnotherRepository>(value: YetAnotherRepository()),
],
child: MaterialApp(
...
),
)
Then when creating a bloc that has a dependency on a repository, you can use the context to access the repository and inject it into the bloc like:
final ChoiceBloc _choiceBloc = ChoiceBloc(Provider.of<LocalRepository>(context));
Otherwise you can simply extend InheritedWidget
/InheritedModel
and implement your own RepositoryProvider
.
The main advantage of doing it this way as opposed to the way you described is testability. It's much easier test your widgets and blocs with mock repositories using this approach.
Let me know if that helps!
I will try this.
Thanks for your prompt and detailed response.
No problem! π
Closing this for now but feel free to comment with further questions/concerns and I'll gladly reopen this. π
I think this section can be documented in tutorials for beginners.
I'm still actively writing sample apps and adding to the documentation so I'll definitely be sure to keep this in mind π
Also feel free to propose updates to the documentation and create PRs to improve it!
Hello, I try to use Provider Package.
I have 1 Bloc
that have a Respositoty
paramenter
Want to use like this:
class _someScreenState extends State<someScreen> {
Bloc bloc;
@override
void initState() {
bloc = Bloc(Provider.of<Repository>(context));
super.initState();
}
@override
void dispose() {
_bloc.dispose();
super.dispose();
}
}
But I got his error
inheritFromWidgetOfExactType(Provider<Repository>) or inheritFromElement() was called before
_SomeScreenState.initState() completed.
I try fo fix like using the didChangeDependencies() and works... But this method are called a lot of times, and the dispose() only one, And I think I could have memory leaks but i don't know. Can i have memory leaks?
class _someScreenState extends State<someScreen> {
Bloc bloc;
@override
void didChangeDependencies() {
bloc = Bloc(Provider.of<Repository>(context));
super.didChangeDependencies();
}
@override
void dispose() {
_bloc.dispose();
super.dispose();
}
}
Also I can do this
@override
void didChangeDependencies() {
bloc ??= Bloc(Provider.of<UserRepository>(context));
super.didChangeDependencies();
}
But I don't know what is the correct way.
Thanks
Hey @basketball-ico π Can you please share a sample app with the problem you're having and I can take a look?
In general, the approach you've taken is not ideal because, like you said, didChangeDependencies
is called multiple times and the bloc streams will not be disposed which will cause memory leaks.
Have you tried this?
class _someScreenState extends State<someScreen> {
Bloc _bloc;
@override
void initState() {
super.initState(); // call super.initState first
_bloc = Bloc(Provider.of<Repository>(context));
}
@override
void dispose() {
_bloc.dispose();
super.dispose();
}
}
Please let me know if that helps and if not it'd be awesome if you could share a sample application with the problem you're having π
Thanks! π
@basketball-ico looks like you can set listen
to false
when using Provider.of<Repository>(context, listen: false)
to avoid re-triggering builds on changes (since the repository shouldn't change). Check this sample out.
Alternatively, in a simple example like this you can skip using Provider
altogether by injecting the repository directly (sample).
Let me know if that helps! π
Thanks, It Works, I like it. π
But I can't understand, the Repository
can Change in some scenario?
When i need use listen: true
and when listen: false
?
Thanks π€
Awesome! Basically when you say listen: true then any time the Repository changes your widget will rebuild. Since your repository should not change over the course of the application lifecycle you should set listen to false. Hope that helps!
Hey, thanks but sorry i can't understand, When you said "change", that means that the some property of Repository
are changed or when you assign new Object.
Thanks
For example:
class Repository{
String someProperty;
}
void main() {
var r = Repository();
// Change property
r.someProperty = 'sd';
// assign new Object
r = Repository();
}
Hey no problem! When I said change I meant the old value is not the same instance as the new value.
In your example changing the property would not trigger a rebuild but assigning a new object would since it is not longer the same instance of Repository
.
Does that help?
I'm a backend developer and a novice in app development.
State management is an interesting area in frontend development. I read many materials then I found Bloc.
Bloc is a great pattern and your package helps me a lot. I can easily handle input and output in my app, but there is still a problem.
As your examples described, I need passing repository from the root widget to every widget that has a bloc require the repository, It's quite annoying and this behavior breaks the architecture we want.
So, I delegate my repository implementation to an abstract Repository, The repository is hidden for widgets because all inputs and outputs are managed by the bloc. When a bloc needs a repository, just uses the Repository delegation.
Is my solution right for real production developments?
If not, is there a better way to solve this problem?
Here is the sample code:
Repository
Bloc
main.dart