language
language copied to clipboard
allow const modifier in function parameter and return type for creating const Widget with the parameter
I have an extension function on List<Widget>
which inserts SizedBox
between every child to set spacing, instead of manually putting SizedBox
between widget. What if there were 100 widgets, i don't want to put 99 SizedBox
manually.
Here's the extension:
extension ChildSpace on List<Widget> {
/// returns children with SizedBox between for spacing
List<Widget> setSpace({
final double? height,
final double? width,
}) {
if (isEmpty) return this;
// add the first child
final spacedChildren = [
this[0],
];
for (final child in sublist(1)) {
spacedChildren.addAll([
// add space between every child
SizedBox(
height: height,
width: width,
),
child,
]);
}
return spacedChildren;
}
}
use:
Column(
children: [
const Text("A"),
Text(aVariable),
const Text("C"),
].setSpace(height: 10),
)
Here in the Column
the inserted SizedBox
are not const
, so will be rebuild which is bad. Since both the height
and width
parameter of setSpace
function is not const
, I cannot use
const SizedBox(
height: height,
width: width,
)
So is there any way you can let us use const
modifier in function parameter and return type?
List<Widget> setSpace({const double? height, const double? width})
like C/C++?
const
in return type is useful when doing some math with the const
parameters
Every answer is I get is "const
in dart is not like other languages", yes so let us use the optimization dart wants us to do in every widget "prefer const with const constructor"
Or is there any reason or difficulties for not allowing const
in parameters and return types?
If we add a const
modifier to parameter and return types of a function declaration, with the meaning that the corresponding argument and return expressions must be constant expressions, which is definitely possible, then it a affects function typing too. We'd have to allow const
on the parameter and return types of function types too.
That complicates the type system. And parsing.
We'd have to define the subtype relation. Likely const Foo
is a subtype of Foo
, since it accepts a subset of the values.
That means that
const Foo Function(Foo) <: Foo Function(const Foo)
And
const int Function(int) f = ...;
is ambiguous: Are we declaring a const
variable, or a mutable variable with a function type returning a const type?
So, we may have to allow int foo(covariant const Foo foo) ...
to override int foo(Foo foo) ...
.
Slightly surprisingly, a const
constructor will not have a const
return type. If called as a function, it can return a new object.
(Also because the syntax const Foo() ...
might suggest that it returns a const Foo
. People make that mistake today, and we don't even have const
return types.)
I'm also worried about putting too much emphasis on objects being constant. The Dart language doesn't have any concept of "constant objects", only "constant expressions". At runtime, every value is just a value, you can't easily distinguish const Foo(1)
from new Foo(1)
without already having a copy of const Foo(1)
to compare for identity. Being constant is not part of the object, and a final instance = Foo(1);
will be just as immutable as const instance = Foo(1)
. There is no good reason to disallow using the first instance
where "use the same value each time" is required. It's immutable, and it's the same value each time. You can't tell the difference.
For the problem at hand, you cannot use const
for that. Since the height is not constant. It's a parameter, and even a const
parameter is not a constant expression because it doesn't have the same value every time.
Just do:
List<Widget> setSpace({
final double? height,
final double? width,
}) {
if (length < 2) return this;
var box = SizedBox(
height: height,
width: width,
);
var result = <Widget>[
first,
];
for (var i = 1; i < length; i++) {
result..add(box)..add(this[i]);
}
return result;
}
That at least reuses the same box for every occurrence in the same list.
If you want to reuse boxes between calls, you need to cache the objects.
First of all thank you for your time and the suggestion of reusing the same SizedBox
, I didn't think of that.
Are we declaring a
const
variable, or a mutable variable with a function type returning aconst
type?
const int Function(int) f = ...;
this is the only problem I see, since it is hard to choose whether it is a const int
return type or a const f
variable.
But still having const
in paramter is not difficult right?
And definitely
int add(int a, int b)
is different than
int add(const int a, const int b)
first can be called with any int
variable or literal where second is only callable with a const
variable or literal.
Of course without const
return type it is useless to call the add
function with the const
argument, since return type of that add
function will not be const
and we lose the parameter's const
type.
So what can we do, but we should do something right?, I means since Flutter expects const
for non changing Widget
s, we need those const
everywhere we build a Widget
.
For now i can only think of wrapping the const
with the return type.
const (const int) Function(const int, const int) f = ...;
although it is a little confusing, well we can learn right? i mean all those null-safety symbols with different languages using different symbols for same concept, we still learn it.
But in the end, i think the const
everywhere is a must as long as Flutter expects const
for Widget
building optimization.
Leave no non-changing Widget
s behind without const
why not do like async
, allow const
modifier, which mean that all arguments and return type is const:
int get zero const {
return 0;
}
int add(int a, int b) const {
return a + b;
}
Also constant functions can't be async.
PS: Like @immutable
, you can use this function as usual with non constant values.
If you want int add(const int a, const int b) ...
to return a constant, we're closer to #2222 or generalized constant evaluation (like what @munificent was working on before macros).
The obvious generalization from allowing const
on parameter types is to allow const
on any type, so you can have a List<const Foo>
or a function with type parameter <T extends const Object>
which only accepts const types.
Might seem a little too far fetched, but experience tells us that any type that can occur in a function type, someone also wants to abstract over it using generics. Meaning that this probably means const SomeType
becomes a valid type in general, just like SomeType?
(it's just a subtype of SomeType
instead of a supertype). And yes, you will want Future<const Foo> compute() async ...
too.
That's definitely a lot of complication for the type system. (Grammar notwithstanding, we can do hacks like const<Foo>
if we can't find something better.)
The real gist here is that you don't need to pass width
or height
-- what you're really looking for is a SizedBox
(or any other widget). By making your parameters height
and width
instead of a SizedBox
, you're signaling that you may do something else with those values, like print them. Instead, you can just accept a Widget
directly.
import "package:flutter/material.dart";
class MyList extends StatelessWidget {
@override
Widget build(BuildContext context) => ListView(
children: const [
Text("Row 1"),
Text("Row 2"),
].separate(const SizedBox(height: 10, width: 10)),
// ^ by passing in a `const` SizedBox, the same instance is always reused.
);
}
extension ChildSpace on List<Widget> {
/// Returns itself but with [separator] in between each element.
///
/// You could also make this parameter a [SizedBox], but Flutter likes to be Widget-agnostic.
List<Widget> separate(Widget separator) => [
for (final Widget child in this) ...[
child,
separator,
]
]..removeLast();
}
Regarding generalization, you have to ask yourself, "Is it wrong to call ChildSpace.separate
with a non-constant value?" The answer, to me, is no because height
and width
are parameters and therefore not known in advance. You could, for example, compute these values with, say, a LayoutBuilder
first and then pass them to the function. If they were truly known constants, you could use a const SizedBox
directly in the for loop and have no parameters.
Because you want to use const objects, you can build the SizedBox
yourself and pass it directly to the non-const function. But that doesn't (and shouldn't) make const usage incompatible with non-const usage.
@Levi-Lesches Thank you for your time.
Your solution is ok for the code I used in the issue. But it was only part of the code I really use. In the extension I use, the setSpace function is actually setSpace({double? height, double? width, double? start, double? end})
, the start
and end
is used to add SizedBox
as the first or last child for spaces at the beginning and end.
And also I use two custom widgets:
-
ChildSpace(double space)
to adjust the defaultheight
orwidth
used insetSpace()
. -
NoSpace()
to not insertSizedBox
in the list.
[
const Text("A"),
Text(b),
ChildSpace(-3), // decrease 3 from default height
const Text("C"),
ChildSpace(3), // increase 3 to default height
Text(d),
NoSpace(), // don't insert SizedBox here, so no space
Text("E"),
].setSpace(height: 10, start: 20, end: 30),
So I cannot use SizedBox
as the argument here.
And also with the ChildSpace
widget, I add or subtract the height
or width
with the ChildSpace()
's space
argument, and this is why I requested for const
return type, so that I can also use the value of a mathematical function as a const
parameter.
extension _ChildrenSpaceSum on double {
/// returns 0.0 if addition results to negative
double positiveOrZero(double space) {
final result = add(space);
return result >= 0.0 ? result : 0.0;
}
double add(double other) => this + other;
}
SizedBox(
height: height?.add(start),
width: width?.add(start),
)
And the whole point of asking for const
is not for this function only, but for every function where we build non-changeable Widget
s.
But instead of using const
for build optimization if flutter used some new keyword I think this problems won't be a problem, like norebuild or something.
for (final child in sublist(1)) {
spacedChildren.addAll([
// add space between every child
norebuild SizedBox(
height: height,
width: width,
),
child,
]);
}
I don't know how flutter keeps the const Widget
and resuses, But since the SizedBox
already has the height
and width
, it can rebuild the child correctly right? so the height
and width
doesn't need to be const
if a new keyword is used for the build optimization instead of const
.