This is a follow-up question to
How does the const constructor actually work?,
So far from what I've read about const constructors in Dart, it ensures that only one object of the class in question is allocated. In theory, this can save allocation space and execution time. It's even recommended to apply it wherever possible when following Effective Dart design.
Now, say we have a stateless widget Foo:
import 'package:flutter/material.dart';
class Foo extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Text("Hello world");
}
}
Without defining an explicit const constructor, this widget cannot be used in a const context. In other words, the following snippet
import 'package:flutter/material.dart';
class Bar extends StatelessWidget {
#override
Widget build(BuildContext context) {
return const Foo();
}
}
is illegal. But is there even a benefit to adding a const Foo(); to the Foo class if it doesn't (and won't have) any fields in the foreseeable future?
Yes, there is a benefit. You should declare as const if possible.
According to the Flutter doc:
Use const widgets where possible, and provide a const constructor for the widget so that users of the widget can also do so.
The benefits are the same as having a default vs const constructor:
Performance (see)
Your code is consistent and sound
Are used as annotations
Can use in compile switch
You can enable const by default using Lint
Related
I have this little class:
class WidgetToImage extends StatefulWidget {
final Function(GlobalKey key) builder;
const WidgetToImage({Key? key, #required this.builder}) : super(key: key);
#override
_WidgetToImageState createState() => _WidgetToImageState();
}
This chunk of code won't compile because anybody can pass a null value for the builder parameter when constructing the WidgetToImage widget. I know I could make the builder nullable but this is not what I want because later I must be checking if it is null, etc. and semantically it doesnt make any sense. A valid builder must be always passed.
Is there any way to annotate this in dart to avoid converting builder property to a nullable type?
If you use Dart version 2.12, you get null safety as a language feature.
It seems you are already using that since your code contains Key?, which is the null-safety way of writing "nullable".
On the other hand, your this.builder parameter should have been marked as required (a keyword in null safe code) instead of the older #required annotation, so it doesn't look like valid null safe code.
The code should read:
class WidgetToImage extends StatefulWidget {
final Function(GlobalKey key) builder;
const WidgetToImage({Key? key, required this.builder}) : super(key: key);
#override
_WidgetToImageState createState() => _WidgetToImageState();
}
and then it's a compile-time error for null-safe code to pass null as an argument to builder.
(Older non-null-safe code can still get away with passing null, but they're asking for trouble then.)
You can add an assertion:
const WidgetToImage({Key? key, required this.builder})
: assert(builder as dynamic != null),
super(key: key);
that will tell people developing against your library to not pass in null from non-null-sound code, but only while developing with assertions enabled.
I have this method
void doSomething<T>() { ... }
and a variable of type T
T someVar = ...;
how can I use doSomething method to perform an action on someVar, something like this?
doSomething<someVar.Type>();
in fact, I want to access Type T form variable.
here is the example
class BlocManagerProvider extends StatefulWidget {
const BlocManagerProvider({
#required this.child,
#required this.blocs,
Key key,
}) : super(key: key);
final Widget child;
final List<Cubit<Object>> blocs;
#override
_BlocManagerProviderState createState() => _BlocManagerProviderState();
}
class _BlocManagerProviderState extends State<BlocManagerProvider> {
#override
Widget build(BuildContext context) => widget.child;
#override
void dispose() {
for (final Cubit<Object> bloc in widget.blocs) {
BlocManager.instance.dispose<type>();
}
super.dispose();
}
}
Future<void> dispose<T extends Cubit<Object>>() async {
final String objectKey = _getKey<T>(key);
if (_repository.containsKey(objectKey)) {
await _repository[objectKey].close();
_repository.remove(objectKey);
await removeListener<T>(key);
}
}
Dart does not provide a way to go from an object of type X to a type variable bound to X. There are good technical reasons for not allowing that (it allows the web compilers to know at compile-time which types can ever be bound to a type variable, which allows it to reduce the compiled code).
The dispose method is treating the type argument as its only argument and acting on the value of that type argument.
It makes me think you're trying to do something that the language is not designed for.
You're passing in a type argument, and then the code inspects that type of argument and behaves differently depending on the value. That's not what's usually meant by being "generic" - to act the in the same (generic) way independently of the types, so the only real effect of passing a type is to make the return type match the argument type.
(That's why Java can erase type arguments at run-time).
So, if you need to know a type for some object, either that object must provide it for you, or you have to store it from earlier (perhaps when the object was created).
So, if you really need to access the type argument that the cubit is implementing Cubit<X> of, the Cubit class needs to make it available to you. That will usually be with a method with a callback (like a visitor), something like:
abstract class Cubit<T> ... {
...
R visit<R>(R Function<C extends Cubit<T>, T>(C value) action);
}
class SomeCubit<T> extends Cubit<T> {
...
R visit<R>(R Function<C extends Cubit<T>, T>(C value) action) =>
action<SomeCubit<T>, T>(this);
}
If something like that's available, then you can do what you want as:
bloc.visit(<C extends Cubit<T>, T>(_) => BlocManager.instance.dispose<C>());
If something like that is not available, then you are in trouble.
You can detect a number of known types, with a bunch of if statements, but that's unlikely to be sufficient.
That means you need to remember the type from earlier, but since it looks like you just get a List<Cubit<Object>> that has already been created, that doesn't seem practical either.
If the BlocManager is your own class, consider changing it to use Type objects instead of type arguments (which is contrary to everything I usually say you should do), because then you can call ..dispose(bloc.runtimeType). I'd prefer to avoid that, but if other constraints make what you do impossible, then it might be the lesser evil.
I have defined enum like:
enum Status {
done,
current,
future
}
I want to include this enum inside the class which is defined below.
Both the enum and class are in same file.
The enum is defined outside of the class because multiple classes should be able to use it.
When using the enum vscode is giving me an error Status not defined
class WeekDay extends StatelessWidget {
final int day;
final Status status;
const WeekDay({this.day, this.status});
#override
Widget build(BuildContext context) {
return Container();
}
}
How do we import Enum in class in Dart?
You need to import the files your are depending on like the following (if e.g. status and weekday are in the same folder):
Sorry, for the silly question.
It Was VSCODE problem,
The error was gone after restarting the vscode.
The above code works perfectly fine.
I've created a small app to add items in list, however when i delete something from list, it gets deleted successfully but ListView.builder doesn't show correct values. i know its something related to keys given to class but i'm pretty new in flutter so don't know how to do that.
Gist: https://gist.github.com/OculusMode/213052325ec725aad3ab92c73599b187
Thanks in advance.!
Add this to constructor of your Widget:
:super(key:new ObjectKey(_data))
Example:
class TodoTile extends StatefulWidget {
String _data;
int _index;
ValueChanged<int> onDelete;
TodoTile(this._data,this._index,{ #required this.onDelete , Key key}):super(key:new ObjectKey(_data));
TodoTileState createState() {return new TodoTileState(_data, _index,this.onDelete);}
}
Not sure if this would cause problems too but I've also changed widget.onDelete to onDelete (passing the function pointer to the state too)
Source:
https://flutter.io/widgets-intro/#keys
I'm trying to obtain the top-level state of my app using a .of()-method, similar to the Scaffold.of() function. This is the (stripped down) code:
class IApp extends StatefulWidget {
#override
IAppState createState() => new IAppState();
static IAppState of(BuildContext context) =>
context.ancestorStateOfType(const TypeMatcher<IAppState>());
}
The app is started using runApp(new IApp)
This Widget creates a HomePage:
#override
Widget build(BuildContext context) {
return new MaterialApp(
// ommitted: some localization and theming details
home: new HomePage(),
);
}
Then, I try to access the State from the HomePage (a StatefulWidget itself):
#override
Widget build(BuildContext context) {
return new Scaffold(
// ommited: some Scaffold properties such as AppBar
// runtimeType not actual goal, but just for demonstration purposes
body: new Text(IApp.of(context).runtimeType.toString()),
);
}
The strange this is, the code works when I place the code for HomePage in the same file as the IApp, but just as an extra class. However, when I place HomePage in a separate file (main.dart and homepage.dart importing each other), the return value of IApp.of(context) is null.
What causes this? And how can I fix it?
TDLR: imports file only using
import 'package:myApp/path/myFile.dart';
Never with
import './myFile.dart';
This is due to how dart resolves imports.
You may have a single source file, but during builds, there is some kind of duplicates.
Let's say you're working on 'myApp'. To import a file, you could do both :
import 'relativePath/myFile.dart'
import 'package:myApp/path2/myFile.dart'
You'd think that they point to the same file right?
But no. One of them will point to the original source. While the other one will point to a temporary file used for the build.
The problem comes when you start to mix both solutions. Because for the compiler, these two files are different. Which means that IApp imported from package:myApp/IApp is not equal to the same IApp imported from relativePath/myApp/IApp
In your case, you inserted in your widget tree an IApp from pakage:path but your IApp.of(context) use IAppState resolved locally.
They both have a different runtimeType. Therefore const TypeMatcher<IAppState>() won't match. And your function will return null.
There's an extremely easy way to test this behavior.
Create a test.dart file containing only
class Test {
}
then in your main.dart add the following imports :
import 'package:myApp/test.dart' as Absolute;
import './test.dart' as Relative;
You can finally test this by doing :
new Relative.Test().runtimeType == new Absolute.Test().runtimeType
Spoiler: the result is false
Now you can use the relative path.
You can verify this, as Remy suggested two years ago:
Relative.Test().runtimeType == Absolute.Test().runtimeType
Spoiler: the result is true