A bit simplified, my app routes have three levels of nesting:
/profile
/course
/quiz
These are represented by named routes, /profile, /profile/course and /profile/course/quiz. These routes map to widgets: ProfileScreen, CourseScreen and QuizScreen. Each of these takes an argument: the Profile, Course and Quiz model objects, respectively.
If I understand correctly, the Navigator will ensure that parent routes will always be on the stack before child routes, so when we're on the /profile/course/quiz route, we'll have /profile/course above that.
Now I'm inside the QuizScreen widget, and I want to access the current Course. How do I do that?
It's somewhere on the route stack, but ModalRoute.of(context) will of course give me the /profile/course/quiz route, not the /profile/course route, so ModalRoute.of(context).settings.arguments will be a Quiz.
I tried wrapping the CourseScreen in a Provider<Course> widget (from the excellent providers package) which provides the Course down to child widgets, but sub-routes actually become sibling widgets, not children.
Of course I could explicitly pass the Course down to the /profile/course/quiz route as a second argument (wrapped together with the Quiz in an array or object), but this seems needlessly convoluted and doesn't scale well.
Is there a way to get the route arguments of a parent route? Or does this actually indicate that I'm doing it wrong and should be designing my app differently?
Of course I could explicitly pass the Course down to the /profile/course/quiz route as a second argument (wrapped together with the Quiz in an array or object), but this seems needlessly convoluted and doesn't scale well.
I found a compelling argument for this approach after all: testability. It means that the QuizScreen can be tested independently of all the other screens and even the routing system.
So I created a simple class to hold the arguments:
class QuizScreenArgs {
final Course course;
final Quiz quiz;
QuizScreenArgs(this.course, this.quiz);
}
Then I realized I can take this one step further and make each route a class:
class QuizRoute extends MaterialRoute<QuizResult> {
QuizRoute(Course course, Quiz quiz) : super
}
This has the advantage that both the route arguments and the route's return type are explicitly typed. No more ModalRoute.of(context).settings.arguments as IHopeThisIsWhatWasPassed or (await Navigator.of(...).pushNamed(...)) as IHopeThisIsWhatWasReturned.
It needs to be a class (rather than just a factory method) so we can use Navigator.popUntil:
Navigator.of(context).popUntil((route) => route is CourseRoute);
All in all, I'm quite happy with this approach and I'm surprised that the docs don't recommend or even hint at this possibility.
Related
Looking through the class that defines BlocObserver, all the methods do not have any functionality defined. The BlocObserver does not inherit anything from any other class. It is only connected to the Bloc class by being created during instantiation of a Bloc.
How do the methods in BlocObserver have functionality when they are empty inside BlocObserver?
Read through the BlocObserver definition, and read through the Bloc definition.
What to do
The way you are expected to use BlocObserver is described pretty well in Core Concepts.
Basically, as BlocObserver is an abstract class, you would extend it in your own class, providing implementations for the handler methods as appropriate for your use-case.
So, roughly:
class CustomObserver extends BlocObserver {
#override
void onChange(BlocBase bloc, Change change) {
super.onChange(bloc, change);
// Perform logic based on the change
}
}
Then, you would assign an instance of this class as the static observer on Bloc, for example:
Bloc.observer = CustomObserver();
After this point, you would expect any changes that propagate through Bloc to call your CustomObserver.onChange() method.
How this works
The pattern of providing some framework object a definition of the code you'd like to run when certain events happen is a pretty common one, so it's useful to come to grips with it. Usually (and also in this case) it's way simpler than it appears.
As discussed above, you provide a BlocObserver to Bloc by setting a static member. This means both that:
you can only have one observer in the system at a time, and
any code can access it directly by calling Bloc.observer
Then, when making state changes, you ensure you do so via an instance of BlocBase (such as Cubit), which takes care of calling the appropriate method on our observer.
So, once again using Core Concepts as a base, when calling CounterCubit().increment(), the call stack looks like this:
CounterCubit.increment
CounterCubit.emit/Cubit.emit/BlocBase.emit (through inheritance)
CounterCubit.onChange
BlocBase.onChange
SimpleBlocObserver.onChange
At this point, you're back in your own code, and you can see that SimpleBlocObserver.onChange(...) calls super.onChange(...). No magic, just function calls.
In Flutter, I feel a bit lost with how I create my params for classes, and knowing what is the best way to create those params. Usually, the params are just classes to inject into another class, to perform tasks. It doesn't really seem to matter how those params are created functionality-wise, since the code works with all manner of creation methods. I see online people talking about service locators, singletons, dependency injection. Riverpod states on the website "Providers are a complete replacement for patterns like Singletons, Service Locators, Dependency Injection or InheritedWidgets." So I guess I don't need a service locator, since I use Riverpod. However I can't find anything online on how I can inject a service with Riverpod providers. I can see you can read a provider with no context with ProviderContainer().read but is this for use as service injection? Is this a singleton so pretty much a service locator? In the Riverpod example, it states that you don't need ProviderContainer().read for Flutter, which kind of sounds like it isn't a replacement for anything like a service locator then:
// Where the state of our providers will be stored.
// Avoid making this a global variable, for testability purposes.
// If you are using Flutter, you do not need this.
final container = ProviderContainer();
Here is a code example, a field in a class which is a ViewModel which takes some use cases as params. Use cases here are domain layer actions classes which call repositories to do external stuff like API requests or local storage manipulations.
final CreateUserViewModel createUserViewModel =
CreateUserViewModel(SavePasswordLocallyUseCase(), CreateUserUseCase());
...
So I just literally created them in-line like SavePasswordLocallyUseCase(), in order to inject them. This is defnitely the easiest approach, with the least code. I guess it might be less efficient since it is creating a new one every time, though i don't see that usually making a visible difference. Will these params that are created in this manner be cleaned up by the garbage collector? What is the repercussion of doing this?
If I had to inject a type AuthenticationService, should I be using a service locator or creating them inline like AuthenticationService(), or using Riverpod's ProviderContainer.read()?
After looking around I can see that dependency injection in Flutter is mostly done by creating a new instance of the param to inject into the constructor like in my question:
final CreateUserViewModel createUserViewModel =
CreateUserViewModel(SavePasswordLocallyUseCase(), CreateUserUseCase());
And that this becomes very messy very fast if the injected classes also need their own params injected. Dependency injection packages mostly aim to solve this by setting up the classes with their required params once, and then you can just request the instance from the dependency injection package without ever needing to create its constructor params again. I believe the Riverpod version of this is indeed to use ProviderContainer.read() if not in the UI layer. Lets say I want to instantiate a class which takes a repository in its constructor:
class SavePasswordLocallyUseCase implements ISavePasswordLocallyUseCase {
const SavePasswordLocallyUseCase(this._userRepository);
final IRegistrationRepository _userRepository;
String invoke(String password, String confirmPassword) {
return _userRepository.putPasswords(password, confirmPassword);
}
And the injected repository itself needs 2 constructor params:
class RegistrationRepository implements IRegistrationRepository {
const RegistrationRepository(
this._authenticationRemoteDataSource, this._registrationLocalDataSource);
}
final AuthenticationRemoteDataSource _authenticationRemoteDataSource;
final IRegistrationLocalDataSource _registrationLocalDataSource;
Instead of instantiating the class like this:
new RegistrationRepository(AuthenticationRemoteDataSource(X()), RegistrationLocalDataSource(Y()))
By creating a stock standard Provider in Riverpod which instantiates the params once:
final registrationRepositoryProvider =
Provider.autoDispose<RegistrationRepository>((ref) {
ref.onDispose(() {
print('disposing');
});
return RegistrationRepository(
AuthenticationRemoteDataSource(X()), RegistrationLocalDataSource(Y()));
});
I can then allow classes to access the RegistrationRepository like so:
ref.container.read(registrationRepositoryProvider);
Or without a ProviderReference:
ProviderContainer().read(registrationRepositoryProvider);
I'm still not sure about lazy loading and singletons in riverpod and if those options are possible. It might be more configurable to use Injectable for DI that is not in the View, which I am considering.
What does BuildContext do, and what information do we get out of it?
https://docs.flutter.dev/flutter/widgets/BuildContext-class.html is just not clear.
https://flutter.dev/widgets-intro/#basic-widgets on the 9th instance of the term BuildContext there is an example, but it's not clear how it is being used. It's part of a much larger set of code that loses me, and so I am having a hard time understanding just what BuildContext is.
Can someone explain this in simple/very basic terms?
BuildContext is, like it's name is implying, the context in which a specific widget is built.
If you've ever done some React before, that context is kind of similar to React's context (but much smoother to use) ; with a few bonuses.
Generally speaking, there are 2 use cases for context :
Interact with your parents (get/post data mostly)
Once rendered on screen, get your screen size and position
The second point is kinda rare. On the other hand, the first point is used nearly everywhere.
For example, when you want to push a new route, you'll do Navigator.of(context).pushNamed('myRoute').
Notice the context here. It'll be used to get the closest instance of NavigatorState widget above in the tree. Then call the method pushNamed on that instance.
Cool, but when do I want to use it ?
BuildContext is really useful when you want to pass data downward without having to manually assign it to every widgets' configurations for example ; you'll want to access them everywhere. But you don't want to pass it on every single constructor.
You could potentially make a global or a singleton ; but then when confs change your widgets won't automatically rebuild.
In this case, you use InheritedWidget. With it you could potentially write the following :
class Configuration extends InheritedWidget {
final String myConf;
const Configuration({this.myConf, Widget child}): super(child: child);
#override
bool updateShouldNotify(Configuration oldWidget) {
return myConf != oldWidget.myConf;
}
}
And then, use it this way :
void main() {
runApp(
new Configuration(
myConf: "Hello world",
child: new MaterialApp(
// usual stuff here
),
),
);
}
Thanks to that, now everywhere inside your app, you can access these configs using the BuildContext. By doing
final configuration = context.inheritFromWidgetOfExactType(Configuration);
And even cooler is that all widgets who call inheritFromWidgetOfExactType(Configuration) will automatically rebuild when the configurations change.
Awesome right ?
what is the BuildContext object/context?
Before we knowing about BuildCotext, We have to know about the Element object.
What is Element object
(note: As a flutter developer we never worked with Element object, but we worked with an object(known as BuildContext object) that's similar to Element object)
The Element object is the build location of the current widget.
What's really mean by "build location" ?
when the framework builds a widget object by calling its constructor will correspondingly need to create an element object for that widget object.
And this element object represents the build location of that widget.
This element object has many useful instance methods.
Who uses the Element object and its methods ?
They are 02 parties that use the Element object and its methods.
Framework (To create RenderObject tree etc)
Developers (Like us)
What is BuildContext object ?
BuildContext objects are actually Element objects. The BuildContext interface is used to discourage direct manipulation of Element objects.
So BuildContext object = discouraged element object (That contains less number of instance methods compared to the original Element object)
Why framework discouraged the Element object and pass it to us ?
Because Element object has instance methods that must only be needed by the framework itself.
but what happens when we access these methods by us, It's something that should not be done.
So that the reason why framework discouraged the Element object and pass it to us
Ok Now let's talk about the topic
What does BuildContext object do in Flutter ?
BuildContext object has several useful methods to easily perform certain tasks that need to be done in the widget tree.
findAncestorWidgetOfExactType().
Returns the nearest ancestor widget of the given type T.
findAncestorStateOfType().
Returns the State object of the nearest ancestor StatefulWidget.
dependOnInheritedWidgetOfExactType().
Obtains the nearest widget of the given type T, which must be the type of a concrete InheritedWidget subclass, and registers this build context with that widget such that when that widget changes.
[Used by Provider package]
The above methods are mostly used instance methods of BuildContext object if you want to see all the methods of that BuildContext object visit this LINK + see #remi Rousselot's answer.
I've been refactoring my app to make more components stateless/pure components; i.e., they're just functions. However, I noticed that some components will need to connect with the redux store via mapStateToProps. Which causes me to do something like this:
const someComp = (props) => {
const {
funcFromReduxStore,
} = props;
return (
...
<SomeComponent
func={ funcFromReduxStore(myArgs) }
...
);
};
This will not work because I am executing funcFromReduxStore. An easy solution is to wrap the prop in an arrow function. However, this causes many unnecessary re-renders b/c the function won't be bound.
The question then becomes: How do I bind a function in a stateless component?
Is it still stateless if I make it a class, without a constructor, and create a class instance field as so:
class someComp extends React.Component {
const {
funcFromReduxStore,
} = this.props,
wrapper = (x) => funcFromReduxStore(x) // equivalent way to bind w/ ES8+
render() {
...
<SomeCompnent
func={ wrapper(myArgs) }/>
...
}
}
I don't have a constructor, nor state. I want to keep the comopnent stateless, but I also want to bind the function to avoid unncessary re-renders. I also want to continue to keep it stateless b/c React has stated there will be performance benefits for stateless comopnents. Does this qualify as a workaround?
Short answer, no. Stateless functional components need to be simple functions.
You should take a look at the Recompose library for some really cool helpers that allow you to beef up your SFCs.
If you're trying to prevent unnecessary re-renders, you could look into onlyUpdateForKeys() or pure().
EDIT: So, I've been thinking about this a bit more and found this really great article on React component rendering performance. One of the key points in that article that pertains to your question:
Stateless components are internally wrapped in a class without any optimizations currently applied, according to Dan Abramov.
From a tweet in July 2016
So it appears that I was wrong. "Stateless Functional Components" are classes...for now. The confusing thing is that there have been performance improvements theorized:
In the future, we’ll also be able to make performance optimizations specific to these components by avoiding unnecessary checks and memory allocations.
At this point, I think the answer to your question becomes largely subjective. When you make a class that extends a React Component, any instances of your class get the setStateprototype method. Meaning you have the ability to set state. So does that mean it's stateful even if you're not using state? Thanks to #Jordan for the link to the code. SFCs only get a render method on the prototype when they are wrapped in a class by React.
To your point about wanting to bind functions, there's only two reasons I can think of that you'd want to bind the function:
To give the function access to this (the instance of the component). From your example, it doesn't seem like you need that.
To ensure that the function passed as a prop to a child component always retains the same identity. The wrapper function in your example seems unnecessary. The identity of the function is determined by the parent component (or mapStateToProps, or whatever HOC).
You should also take a look at React's PureComponent which does the same kind of shallow checking that the pure() HOC from recompose does.
I've been reading up a bit about coffeescript's inheritance model and I have the feeling I'm on the fringes of an ideological debate which I really don't understand. So, I would be perfectly happy to find out that I'm just doing things in the wrong way.
Basically what I am doing is writing a set of widgets which, among other things, need to handle events on their DOM elements. I thought a good way to go about this would be to have a class method which would be called once, to delegate all the events which the widget might need. The base widget class might have some simple click handlers, while the subclass might add to that some mouseover handlers or extra click handlers.
However, it appears that I'm not supposed to try and do the equivalent of calling super() inside a static method. There is a workaround which exists, (this.__super__.constructor.METHODNAME() but I've seen a lot of suggestions that this isn't the best way to do what I'm trying to do. Has anyone got any insights on how I should structure this code? Keep using the workaround, or put all the delegation into a totally different place? I can't really just stick it in the prototype, since I won't necessarily have an instance to call the method on (or can I essentially still call a method on the prototype from a static context, like putting SwatchableWidget.prototype.delegateEvents() into an onload function or something?
Here's a bit of code to illustrate what I'm talking about:
class Widget
#testProp: "ThemeWidget"
#delegateEvents: ->
console.log "delegate some generic events"
class SwatchableWidget extends Widget
#testProp2 = "SwatchWidget"
#delegateEvents: ->
console.log "delegate some specific swatchable widget events"
this.__super__.constructor.delegateEvents()
Widget.delegateEvents()
SwatchableWidget.delegateEvents()
Thanks for any help.
I suggest replacing
this.__super__.constructor.delegateEvents()
with
Widget.delegateEvents()
trying to use super to call static methods is not required (and doesn't make much sense)
I don't understand why delegateEvents would be a class-level method, or why Widget.delegateEvents have to be called again from SwatchableWidget.delegateEvents. If it's just class initialization code, you should put it in the class body directly:
class Widget
console.log "delegate some generic events"
...
#testProp: "ThemeWidget"
class SwatchableWidget extends Widget
console.log "delegate some specific swatchable widget events"
...
#testProp2 = "SwatchWidget"
I take it you're waiting for a specific DOM state before running this initialization code? Maybe I could suggest another approach if you told me a little bit more about the preconditions for delegateEvents.
It sounds like you want a different type of inheritance model where each inherited function of a certain type ("parent calling") will walk the inheritance tree and call all its parents with the same name.
You could call any direct parent functions in each child manually as you've written. Then it will float up the inheritance chain anywhere you specify such a relationship.
I would bind the parents delegate call in the constructor to a current class function
delegateparents =>
#call any parent class methods