Using a singleton for Bloc - dart

I know that some of my BLOCs will be instantiated only once throughout the app lifecycle.
Is it bad practice or anti-pattern to create these BLOC classes as a singleton.
Singleton can help in accessing the BLOC anywhere and also makes sure that the class is instantiated only once.
I can also ensure to be safe when using one BLOC inside another BLOC and passing around the singleton as if it were "just another" implementation.
What might be the effects of doing so?
Edit
I have two Blocs
AuthenTicationBloc - this is a global bloc
UserBloc - should exist only once (singleton)
The AuthenTicationBloc has to use the UserBloc inside it. If I made UserBloc a singleton , it would make it easy to just use it as UserBloc().
Othewise I had to create UserBloc as an instance variable in AuthenTicationBloc as follows:
class AuthenTicationBloc {
UserBloc _userBloc;
AuthenTicationBloc({#required UserBloc userBloc}) : _userBloc = userBloc;
myMethod(){
//use the userBloc
_userBloc.setDetails();
}
}
runApp(
UserBloc userBloc = UserBloc();
Provider(
value: AuthenTicationBloc(userBloc: userBloc),
child: MyApp(),
),
);
If I had to use the UserBloc down the widgetchain somewhere else I had to make the UserBloc also be provided globally as follows right?
UserBloc userBloc = UserBloc();
MultiProvider(
providers: [
Provider<AuthenTicationBloc>(
value: AuthenTicationBloc(userBloc:userBloc),
),
Provider<UserBloc>(
value: userBloc,
),
],
child: MyApp(),
);

The behavior stays the same. A singleton vs InheritedWidget doesn't change much
But it has a few architectural consequences, as you should use an InheritedWidget for BLoCs that aren't "instantiated only once throughout the app lifecycle."
Which means that:
you'll mix InheriedWidgets and Singletons in your app. Reducing consistency as you have two different way to create/use a BLoC
it becomes harder to adapt when the specs change. It is very possible that in the future, you may have to instantiate it more than once. That means you'd have to edit the whole application
On another note, singletons do not prevent some mistakes that InheritedWidgets do.
For example, it is impossible to have a circular dependency using InheritedWidgets, when it is definitely possible using singletons.
You're not winning much by using a Singleton vs InheritedWidget in term of code either.
There's a good chunk of libraries on Pub to make your job easier. Including provider:
runApp(
Provider(
value: MyBloc(),
child: MyApp(),
),
);

Related

Problem with local variable - Global variable flutter dart

I want to pass dynamic data through different pages, when using a BottomAppBar. I currently switch between pages/widgets my storing them like this:
#override
void initState() {
super.initState();
final _pageOptions = [
SwipeScreen(currentUserId: currentUserId),
Requests(currentUserId: currentUserId),
Messages(currentUserId: currentUserId),
Settings(),
];}
I then use _pageOptions in my Scaffold:
body: _pageOptions[_selectedPage],
bottomNavigationBar: BottomNavigationBar(
currentIndex: _selectedPage,
onTap: (int index) {
setState(() {
_selectedPage = index;
});
},
As you may have guessed, I can't use _pageOptions as my body in my Scaffold, as it is a local variable when wrapped in an initState(). I have to use this initState though, as without it, I can only pass static members in my initializers.
I don't know how to fix this, as removing one just gives me a different error. I have looked for ways to make a variable global, for example having _pageOptions in a different file, but then it was still local, and therefore not defined when used in my Scaffold.
I hope my problem is clear.
Thanks in advance.
This isn't trying to be mean, just honest so please don't take offence, but from the sounds of things you need to start over with some of the basic tutorials on how flutter works and how to develop things in flutter. I'd recommend working through a couple of the tutorials & codelabs on the flutter website from start to finish (without looking at the final code until you're) and then seeing what you've done differently from them. And maybe even the getting started with Dart documentation...
The simplest answer to your problem is to simply make _pageOptions a member variable of your class as that will allow you to access it from wherever else you need. But since you're running into the issue of only being able to "pass static members in my initializers", that probably means that you aren't initializing things in the right place.
Here's a few things to keep in mind when developing with flutter:
You shouldn't be creating widgets anywhere except the build function (unless you make a helper function called from the build function - but if you're doing that, it's probably because the widget is getting big and you should split it out into its own Stateful or Stateless widget)
You shouldn't inherit widgets unless you know what you're doing. Most of the time you'll only inherit StatelessWidget and StatefulWidget, and everything else is done through encapsulation (i.e. using them in the build function)
Simply switching pages by changing what you build using an array isn't a great way of doing it - you don't get things like animations when navigating, you potentially build things sooner than needed, and lose out on things like the state of the page unless you're careful about how you do it.
Building widgets is cheap in flutter and you should generally be building most of them for each and every page. There are situations where this isn't true but for a basic app, build the navigation bar for each screen (but with the actual logic that builds it split out into a Stateless or Stateful widget!)
The solution for #3 is to use a flutter's navigation and either define your pages in MaterialApp(routes: ...) or MaterialApp(onGenerateRoute: ...). So you'd be setting up routes for SwipeScreen, Requests, Messages, and Settings. You'd use Navigator.push or Navigator.pushNamed, or Navigator.pop to move between them.
And so that you're not copying and pasting the bottom navigation bar everywhere, split it out into its own widget i.e. MyBottomNavigationBar extends StatelessWidget. And then in each of the pages you'd have a Scaffold(body: ..., bottomNavigationBar: MyBottomNavigationBar()). If your scaffold gets really complicated you could even move it to its own widget too.
Oh and I've just read what might be an important part of your question: I want to pass dynamic data through different pages. Using the navigator as I described changes this slightly as you don't want to be passing it down through each layer of build.
There's various ways of getting around this - the most basic is using an InheritedWidget, but as it needs a lot of boilerplate to make it work I recommend using a ScopedModel. That way, you simply have to make a class inherited from Model (i.e. UserModel), and then change the information within the model and notify listeners (i.e. the userId) when the user is chosen/logged in. Something like this:
class UserModel extends Model {
String _userId;
String get userId => _userId;
void setUser(String userId) {
_userId = userId;
notifyListeners();
}
static CounterModel of(BuildContext context, {bool rebuildOnChange = false}) =>
ScopedModel.of<CounterModel>(context, rebuildOnChange = rebuildOnChange);
}
You'd need to put that somewhere high in your widget tree (probably above the MaterialApp or in the MaterialApp(builder: ...)). And you could then add name, profile, color, etc to that model, and use all of that from wherever you need in your app.
Do you want to use _pageOptions in the same class ? if it is this should solve your problem.
var _pageOptions;
// declare a variable inside your class/outside of your initState to reach it from anywhere inside in your class
#override
void initState() {
super.initState();
_pageOptions = [
SwipeScreen(currentUserId: currentUserId),
Requests(currentUserId: currentUserId),
Messages(currentUserId: currentUserId),
Settings(),
];
}

Is it possible to use InheritedWidget as a mixin?

I'm trying to see if I can add some generic navigation behavior passed around my app, and I see that InheritedWidget is a great candidate to avoid passing a specific callback around the widget tree, however I'm noticing more and more that I can only have InheritedWidget of specific class type in order to make the InheritedWidget pattern work, and I was wondering if there's a way to use InheritedWidget as a mixin or if there's a better alternative.
My app looks like this, it passes a callback down the tree
I have 3 navigators right now that I need to present the same method but to act on it themselves right now I need to create 3 InheritedWidget navigators, but the problem is that any widget under the tree would either have to do
NavigatorA.of(context).pushWidget()
But I'd much rather prefer if it was a generic GenericNavigator.of(context).pushWidget() so that I the leaf widgets don't even need to know the right value of the navigator object, I'm afraid to achieve this I'd need to be able to use InheritedWidget as a mixin
This is the desired flow, no callbacks passed
Is this the correct strategy or is there a better way?
How can I use InheritedWidget as a mixin?
No, you cannot make InheritedWidget as a mixin.
However, you can make a generic InheritedWidget:
class Provider<T> extends InheritedWidget {
Provider({Key key, this.value, Widget child}) : super(key: key, child: child);
static T of<T>(BuildContext context) {
Provider<T> provider = context.dependOnInheritedWidgetOfExactType<T>();
return provider?.value;
}
final T value;
#override
bool updateShouldNotify(Provider<T> oldWidget) {
return value != oldWidget.value;
}
}
Adding to #RémiRousselet's answer
Working with flutter 1.19+, where some inheritedWidget APIs are deprecated, the of should look like this now:
static T of<T>(BuildContext context) {
return context.dependOnInheritedWidgetOfExactType<Provider<T>>().value;
}
Since the api is based on generics, you no longer need the _typeof tricks.

Where is context getting passed in?

Consider the following code:
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Welcome to Flutter',
home: new Scaffold(
appBar: new AppBar(
title: new Text('Welcome to Flutter'),
),
body: new Center(
child: new Text('Hello World'),
),
),
);
}
}
My understanding of Buildcontext is that it is the "context" of the widget (please correct me if I'm wrong or elaborate further). What I don't understand is how is the argument "context" passed into the method. Does the runApp function supply the "context"?
TLDR: This is controlled by the framework.
For those who like reading:
Starting from the very beginning. The runApp method takes your app widget and inserts it into the tree, as per comment on the method (binding.dart):
Inflate the given widget and attach it to the screen.
When that's being done, the app StatelessWidget (which obviously is a Widget) is being inflated into an Element (Widget class comments in framework.dart file)
Each time a widget is placed in the tree, it is inflated into an
[Element], which means a widget that is incorporated into the tree
multiple times will be inflated multiple times.
If you then have a look at the abstract class Element, in the same GitHub repo file (framework.dart), you will see the comments above it, saying:
Elements have the following lifecycle:
The framework creates an element by calling [Widget.createElement]
on the widget that will be used as the element's initial
configuration.
The framework calls [mount] to add the newly created
element to the tree at a given slot in a given parent. The [mount]
method is responsible for inflating any child widgets and calling
[attachRenderObject] as necessary to attach any associated render
objects to the render tree.
These two methods createElement and mount are the ones that are responsible for calling the build method.
If you have a look at the the StatelessWidget class, you will see that it has an override for createElement method (framework.dart). Which creates StatelessElement object and passes itself (this) as a constructor parameter. Notice, how the StatelessElement class overrides the build method and calls widget.buildmethod (in this case, the widget is your app widget - i.e. MyApp). This still doesn't tell us how the build method is called though. If you drill down a bit deeper, into the ComponentElement (class that StatelessElement derives from - framework.dart), you can see the mount method being overridden. In this method (which is invoked by the framework), there's a call to _firstBuild method, which then calls rebuild, this method then calls performRebuild method, which finally ends up invoking the build method.
Easy, right?
DISCLAIMER: This is just me connecting the dots. I'm not an expert on Flutter - I literally started using Flutter a week ago. It would be good if some more experienced devs could confirm or not, if my understanding of the mechanisms behind widgets is correct.
EDIT: Answering comment questions
Inflating (in my mind) is creating the object in the memory (so that framework has a reference of it) and rendering it on the screen.
Yes, the BuildContextis going to be the StatelessElement, which contains a reference to the app itself

How do you create Services/Providers in Flutter?

Let's say I need a user service which has all my user variables and functions, and another item service with all item variables and functions, etc etc. I want all this data and functions available throughout the project, passing the user data where it's needed, for example.
I suspect it has something to do with inherited widgets, but how does that work? Like, I see how I could use one inherited widget at the root level, but am I supposed to build a bunch of inherited widgets at the root-level for each service? Or just put ALL the data in the one top inherited widget? Seems like that may get messy. I have yet to see an example of this.
... or should I just be using a class with static variables and call on that where I need it?
See the link provided by Günter above. If you have thirty minutes watch Brian Egan's DartConf 18 presentation: Keep it Simple, State. Also see Brian's example code here. He shows the same app coded using InheritedWidget with a Controller, and with Flutter Redux.
Here's a example of a single InheritedWidget from a simple app of mine...
class StateContainer extends InheritedWidget {
final List<Membership> memberships;
final IntFunction getNextIndex;
final VoidMembershipFunction updateMembership;
final VoidIntFunction deleteMembership;
const StateContainer({
this.memberships,
this.getNextIndex,
this.updateMembership,
this.deleteMembership,
Widget child,
})
: super(child: child);
static StateContainer of(BuildContext context) {
return context.inheritFromWidgetOfExactType(StateContainer);
}
#override
bool updateShouldNotify(StateContainer oldWidget) {
return true;
}
}
I pass in my MaterialApp as the child. A high proportion of the tree then becomes stateless as it has access to the data and methods if the StateContainer. You could think of these as your model and controller in one. Build methods in the tree often start with...
#override
Widget build(BuildContext context) {
final StateContainer container = StateContainer.of(context);
to get access to them.
You are right that a single InheritedWidget becomes unwieldy quickly - which is where you might invest in exploring Redux - see the last 10 minutes of the talk. In my experience, the area that gets messiest quickest is the updateShouldNotify method when ends up comparing all the member variables. (In this simple example there is only one member variable, and setState only gets called when it changes, so it's trivially always true.)

What is the best way to organize Flutter widgets?

I'm writing a Flutter app, and I'd like to know what is the best way to make my code more readable.
Right now, I know this two methods:
Create a final variable and store the customized widget on it.
Create a new widget as a template.
So any recommended methods?
As for:
create a final variable and store the customized widget on it
Using a class variable to hold your widget such as
final Widget foo = new Foo();
may be unnecessary or may point to some other issues. I think you are better off creating a function that returns your widget such as
Widget _buildFoo() {
return new Foo();
}
And then you can use this in, for example, your build() method such as (I'm adding MaterialApp Scaffold to provide more context):
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text("My App"),
),
body: _buildFoo(),
);
}
Take a look at other example apps on the web such as the Flutter Examples for ideas on structuring your widgets.
As for:
create a new widget as a template
I am going to interpret this as creating another file to hold your widget class e.g. foo.dart. This may not be what you are asking, but creating new dart files for each widget can become a good idea as your app grows. For simple apps, especially most example apps you see, leaving the classes in the same dart file (e.g. main.dart) works just fine. Again, I may not be addressing your question.
Getting back to:
best way to make my code more readable
So, in summary, using private functions that return different sections of your widget tree (i.e functions that just return a widget and any sub widgets that it may include) is generally better than storing widgets as class instance variables. You may want to use a class variable if you need to later reference or interact with the widget; however, this is probably a bad practice and may point to a better way to handle the changing of states and the structure of your widget tree (for example, Flutter's Stateful and Inherited widgets are great for handling state change with needing to make an imperative call on an object reference).

Resources