My use case is in constructing a WidgetApp in flutter:
new WidgetsApp(
pageRouteBuilder: <Contructor for MaterialPageRoute here>,
...
);
Instead of referencing the constructor, I'm just wrapping it in a function:
PageRoute pageRouteBuilder(RouteSettings settings, WidgetBuilder builder) {
return MaterialPageRoute(settings: settings, builder: builder);
}
And then referencing that function later:
new WidgetsApp(
pageRouteBuilder: pageRoutebuilder,
...
);
It used to be possible at some point in the history of Dart (like spread operator).
It is currently not possible though, but the feature may come back at some point.
In the meantime, you can use refactoring options for them to generate some of the boilerplate.
Constructor tear-off are not supported in Dart (https://github.com/dart-lang/sdk/issues/10659)
But even if it was supported, your code cannot really use it. MaterialPageRoute takes settings and builder as named parameters but PageRouteFactory takes 2 positionals parameters. So it wouldn't match.
You can write it like:
new WidgetsApp(
pageRouteBuilder: (settings, builder) => MaterialPageRoute(settings: settings, builder: builder),
);
The automatic inference, allows you to omit the type in the parameter of the closure.
Related
This is a function in the main.dart file of the just_audio example. I don't understand what's going on with the "ambiguate" line. I understand the bang operator in this context casts to the "underlying type" but in this case there is no underlying type, I don't think. The underlying type is <T?>. I'm only familiar with what that means when I see it in documentation as "a type goes here." If it's in actual code, not sure what it's doing.
void initState() {
super.initState();
ambiguate(WidgetsBinding.instance)!.addObserver(this);
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.black,
));
_init();
}
The ambiguate function from common.dart in the same lib folder:
T? ambiguate<T>(T? value) => value;
https://github.com/ryanheise/just_audio/tree/minor/just_audio/example/lib
The ambiguate function casts a value to its nullable type, so an int is cast to int?.
That allows using the ! (null assert) operator on the value without a warning, because a value of type int cannot be null, so you don't need to assert that it isn't.
The reason for doing so is that in a program which also contains non-null-safe code, the value can actually be null at runtime.
Or because you don't actually know whether the type will be int or int?, because it changed between versions of a library, and you don't want to lock yourself to only the newest version.
Which means that the only reason to use the function is that you expect your code to run against two different and incompatible versions of the same library, one null-safe or non-null-safe where a function can return null, and a newer null-safe version where the function cannot return null and is typed as such.
Then you can do ambiguate(uncertainValue)?.doSomething(), and it does something if the value isn't null, and it compiles against both versions without warning.
If you are not trying to make your code work against two different versions of the same library, which differ in nullability-behavior, then don't use ambiguate.
Even then, consider whether it'd just be easier to require the new version of the library, and lean into null safety.
(This particular use seems unnecessary. Doing ambiguate(something)!.method() will throw an error if the value is null, but so will something.method(), which will also not give any warnings. Well, unless the other version of the library is null safe and returns a nullable value, but then you shouldn't be using ! on it.)
What is the logic here when the programmer initializes _random at once but the _streamController is initialized in the constructor?
Can all the fields be initialized without a constructor then?
RandomStore {
RandomStore() {
_streamController = StreamController<int>();
_timer = Timer.periodic(const Duration(seconds: 1),
(_) => _streamController.add(_random.nextInt(100)));
randomStream = ObservableStream(_streamController.stream);
}
late final Timer _timer;
final _random = Random();
late final StreamController<int> _streamController;
late final ObservableStream<int?> randomStream;
...
Can all the fields be initialized without a constructor ?
Yes, you can initialize all fields without having to declare a constructor, but only if you don't need a reference to the current instance (this) or if they are 'late' fields.
The determining factor in choosing where to initialize fields is whether or not you need to have the reference (even implicit) to this.
In Dart this is only available from the construcor body; this means in particular that this is not usable in the initializer list and inside the inline initializers (except for the late fields).For terminology, see Glossary below.
this is the reference to the current instance, and is required in order to read the instance fields, even if you usually omit it (e.g., in your snippet, randomStream is equivalent to this.randomStream).
For example, in your snippet, to initialize randomStream you need to be able to read the streamController field, so you have to mark it with late; thanks to late you can initialize randomStream in the constructor body or in the inline initializer (in this second case it will actually be initialized only when you try to access it for the first time; which is an advantage if its initialization is expensive and you want to avoid it as long as possible).
As an alternative to late, you could mark the field as nullable and initialize it in the constructor body (in which case the field will first be implicitly initialized with a null value; in fact this approach is not usable if the field is final and therefore cannot be reassigned).
Instead, to initialize the streamController field, you don't use a reference to this, so you could avoid the overhead of marking the field with late and you can initialize it in the initializer list or in the inline initializer (it is the same).
Example:
class RandomStore {
final StreamController<int> _streamController;
RandomStore()
: _streamController = StreamController<int>() {
}
}
Is late always a good choice? (UPDATED)
While from the above it might seem that 'late' is a great solution for most situations, the doc suggests avoiding 'late' if it is not really necessary, because:
It is less secure:
a late field (also if it has a non-nullable type) entails a risk of errors at runtime similar to that which occurred before the introduction of sound null safety, because Dart does not force you to perform any checks before reading its value (unlike nullable fields, for which access to properties requires the use of not null operator or conditional operator). Note that Dart does not offer the possibility to check if a late field has already been initialized (See Issue #324 mentioned below)
It adds overhead:
under the cover will be created a field with the indicated type, a field -presumably boolean- for keep track of whether the initialization has occurred, and a getter that at each access checks if the initialization had occurred.
Useful sources about late:
Doc Guide about null safety and late variables https://dart.dev/null-safety/understanding-null-safety#late-variables
Dart Best practice about late
https://dart.dev/guides/language/effective-dart/usage#dont-use-late-when-a-constructor-initializer-list-will-do
https://dart.dev/guides/language/effective-dart/usage#avoid-late-variables-if-you-need-to-check-whether-they-are-initialized
Dart issue #324 Should we provide a way to query the status of late variables?
a very interesting insight into 'late' (the discussion in which the Dart Team decided not to allow the final developers to check if a late field has been initialized)
Note the differences with Java:
(which personally made it difficult for me to switch from Java to Dart at first)
this in Dart is not available in inline initializers (in Java it is available)
the final fields in Dart must be initialized before the constructor body (in Java they can also be initialized in the constructor body)
Gloassary:
class MyClass {
MyClass(String v1, String v2)
: myField1 = v1, myField2 = v2 //This is the "initializer list"
//Compared to inline initializers, it allows you to initialize fields using the constructor arguments
{
//This is the "constructor body"
myField4 = myField1; //This operation require an implicit reference to `this`; it is equivalent to `myField4 = this.myField1;`
}
String myField1;
String myField2;
String myField3 = '3'; //This is the "inline initialization"
late String myField4;
}
What's the difference between Navigator.of(context).pop and Navigator.pop(context)?
To me both seems to do the same work, what is the actual difference. Is one deprecated?
Navigator.push(context, route) vs Navigator.of(context).push(route)
Navigator is used to manage the app's stack of pages(routes). When push the given route onto the screen(Navigator), We need to get the right Navigator and then push.
Navigator.of(context).push(route) splits .of(context) to get the right Navigator and .push(route). Navigator.of(context) has optional parameters, if rootNavigator is set to true, the NavigatorState from the furthest is given instead.
static NavigatorState of(
BuildContext context, {
bool rootNavigator = false,
bool nullOk = false,
})
Navigator.push(context, route) is a static method and do both at the same time. It internally calls Navigator.of(context).push(route). The navigator is most tightly encloses the given context.
static Future<T> push<T extends Object>(BuildContext context, Route<T> route) {
return Navigator.of(context).push(route);
}
pop() is similar to push().
When multiple Navigators are nested in App. The dialog route created by showDialog(...) method is pushed to the root navigator. If the application has multiple Navigator objects, it may be necessary to call Navigator.of(context, rootNavigator: true).pop(result) to close the dialog rather than just Navigator.pop(context, result).
There is no difference between the two, source code confirms this. Calling
Navigator.pop(context)
actually calls
Navigator.of(context).pop()
Source code:
static bool pop(BuildContext context, [ dynamic result ]) {
return Navigator.of(context).pop(result);
}
A little bit (not really a little) late to this but the main difference I notice between these two is that Navigator.pop(context) calls Navigator.of(context).pop() with the current widget's BuildContext.
Basically Navigator.of(context).pop() gets a NavigatorState from the passed context and pops the top-most route off the navigator. So when you use it directly, it pops the top-most route off the parent's Navigator (which is the route you are currently on). When you do Navigator.pop(context), you are doing Navigator.of(<current widget's context>).pop() which usually does the same thing since the current widget is usually on the top-most route.
To see differences between objects, you can try to check their hash code. For example with the code below, you can see if the function is being called with the same instance of BuildContext.
final navigatorState = Navigator.of(context);
print(navigatorState.context.hashCode); // Prints the parent's context's hash code.
print(context.hashCode); // Prints the current widget's context's hash code.
BUT this can become tricky when for example you call showDialog on the current context and your widget rebuilds, while the Dialog is still showing. In this case, If you try to pop the dialog with a Navigator.pop(context) you may encounter an Exception such as:
The following assertion was thrown while handling a gesture:
Looking up a deactivated widget's ancestor is unsafe.
At this point the state of the widget's element tree is no longer stable.
To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by
calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method.
In this use case, using Navigator.of(context).pop() would be better.
One can use a value, type or factory for registering an object. I have tried to find simple examples how and when to use each of a registering types, but not succeed.
It would be wonderful, if someone could give brief examples and explain the typical use cases.
Here are some links about the subject:
https://stackoverflow.com/a/21245335/2777805
http://victorsavkin.com/post/72452331552/angulardart-for-angularjs-developers-introduction-to
type
// old syntax
type(SomeType); // or
type(SomeInterface, implementedBy: SomType)
// new syntax
bind(SomeType); // or
bind(SomeInterface, toImplementation: SomType)
default, DI creates an instance and all constructor parameters (if any are resolved by DI and provided)
value
// created inline or e.g. passed in from somewhere as a parameter
// old syntax
value(new SomeType('xxx', 123));
// new syntax
bind(SomeType, toValue: new SomeType('xxx', 123));
if you want to to pass a previously instantiated instance.
I usually use this for configuration settings.
factory
// old syntax
factory(NgRoutingUsePushState,
(_) => new NgRoutingUsePushState.value(false));
// or
factory(UsersRepository, (Injector inj) => new UsersRepository(inj.get(Http)));
// new syntax
bind(NgRoutingUsePushState,toFactory:
(_) => new NgRoutingUsePushState.value(false));
bind(UsersRepository, toFactory: (Injector inj) => new UsersRepository(inj.get(Http)));
(from http://victorsavkin.com/post/72452331552/angulardart-for-angularjs-developers-introduction-to)
when you want DI to delegate the instantiation to a factory function
I am new to Android development with Xamarin.Android and I would like to understand how to have the next issue fixed.
Sometimes after restoring my Android application from background I was facing the next error:
Unable to find the default constructor on type MainMenuFragment. The MainMenuFragment is used by the application NavigationDrawerActivity to allow users to switch between different Fragments inside the app.
In order to solve it, I added a default constructor to the MainMenuFragment as described inside the next links:
Xamarin Limitations - 2.1. Missing constructors
Added a default constructor, should fix the issue.
public class MainMenuFragment : DialogFragment
{
readonly NavigationDrawerActivity navigationDrawer;
#region Constructors
public MainMenuFragment () {} // Default constructor...
public MainMenuFragment (NavigationDrawerActivity navigationDrawer, IMenuType launchMenu = null)
{
if (navigationDrawer == null)
throw new ArgumentNullException ("navigationDrawer");
this.navigationDrawer = navigationDrawer;
...
Fragment UpdateTopFragmentForCurrentMenu (Fragment newMenuRootFragment = null)
{
Fragment currentMenuRootFragment = navigationDrawer.CurrentFragment; // issued line.
But now sometime in the future, the MainMenuFragment gets initialized using its default constructor and at the first time it tries to access its navigationDrawer it throws a System.NullReferenceException:
System.NullReferenceException: Object reference not set to an instance of an object
at MainMenuFragment.UpdateTopFragmentForCurrentMenu (Android.App.Fragment) <0x00018>
at MainMenuFragment.OpenMenu (IMenuType,bool) <0x0006b>
at MainMenuFragment.OnCreate (Android.OS.Bundle) <0x00053>
at Android.App.Fragment.n_OnCreate_Landroid_os_Bundle_ (intptr,intptr,intptr) <0x0005b>
at (wrapper dynamic-method) object.3919a6ec-60c1-49fd-b101-86191363dc45 (intptr,intptr,intptr) <0x00043>
How can I have a default constructor implemented without facing this null reference exception?
You're programming like a C# developer, thats what the problem is :) I faced these same hurdles learning monodroid.
Take a look at the examples out there, in java, you'll see almost all the time they initialize using a static method like object.NewInstance() which returns object. This is how they initialize their views/receivers/fragments. At that point they populate the Arguments property and store that in the fragment. You need to remove all your constructors EXCEPT the empty ones and use arguments to pass your data around. If you try to do this using constructors and regular oo concepts you'll be in for a world of hurt. Arguments.putExtra and all those methods are there. It makes things a little verbose but once you get the hang of it you'll start creating some helper methods etc.
Once you get that sorted, you'll need to figure out if you need to recreate your fragments everytime the activity is resumed and if not, mark them as RetainInstance = true as well as get them onto a fragmentmanager which will help you retain all your state.
If you haven't built on android before it's weird and certainly not what I expected. But it's reeaaallly cool, much more awesome than I expected too. And same with Xamarin.
Great similar question: Best practice for instantiating a new Android Fragment