language
language copied to clipboard
Dot syntax for static access
Proposal
If a type T
can be inferred, then interpret .foo
as T.foo
.
This is similar to #357, but with a broader use case: it applies to enum
s, static
members, and also factories & constructors.
Example
// enums
Brightness brightness = .dark;
final brightness = .dark; // error: no inferred type
// static members
bool isArrowKey(LogicalKeyboardKey key) => switch (key) {
.arrowDown || .arrowLeft || .arrowRight || .arrowUp => true, // fits on a single line!
_ => false,
};
if (key == .arrowUp) {} // error, since == operator doesn't infer type
// factories & constructors
HSLColor favoriteColor = .fromColor(Color(0xFF00FFFF)); // factory
HSLColor favoriteHue = .fromAHSL(1, 180, 1, 0.5); // named constructor
// you don't write a dot for the unnamed constructor, so it doesn't change
const Duration twoSeconds = Duration(seconds: 2);
Here's everything put together:
@override
Widget build(BuildContext context) {
return Container(
width: .infinity,
margin: .zero,
padding: .symmetric(vertical: 8.0),
decoration: BoxDecoration(
color: .fromARGB(255, 0, 128, 255),
shape: .circle,
),
child: widget.child,
);
}
Later On
Static Analysis
If/when this is implemented, it should probably come with some additional linter rules:
- prefer class name for enums, constructors, static members
- and the reverse: prefer no class name for enums, constructors, static members
from
keyword
I really liked the keyword idea from #1955 and would love to see it implemented at some point down the line.
In that issue, from
was added after the parameter name. I think that attaching it to the type would be best but would be happy either way.
abstract final class MyColors {
static const chartreuse = Color(0xFF80FF00);
static const spring = Color(0xFF00FF80);
static const vermilion = Color(0xFFFF4000);
static const indigo = Color(0xFF4000FF);
}
typedef MyColor = Color from MyColors;
class AnimatedIcon extends StatelessWidget {
const AnimatedIcon({
required this.icon,
required this.duration,
required this.curve,
required this.color,
});
final IconData from Icons icon;
final Duration from Durations duration;
final Curve from Curves curve;
final MyColor color;
...
}
final icon = AnimatedIcon(
icon: .home,
duration: .medium1,
curve: .easeOut,
color: .spring,
);
It's worth noting that the example from #357 wouldn't work under this proposal.
enum CompassPoint { north, south, east, west }
if (myValue == .north) {} // this should throw an error,
// since the other side of the == operator could have any object
But the additional keyword would make it work:
class Foo {
bool operator ==(Object from Foo other) {
...
}
}
And without a from
keyword, a lack of support for ==
would at least be in accordance with the Flutter style guide:
Avoid using
==
with enum values
In my opinion it brings little value, but greatly decreases code readability. You never know, where the dotted member comes from.
Consider:
SomeWidget(
color: .blue // is that MyAppColor? CupertinoColors? Just Colors? - you never know until you hover over it.
)
In my opinion it brings little value, but greatly decreases code readability. You never know, where the dotted member comes from.
Consider:
SomeWidget( color: .blue // is that MyAppColor? CupertinoColors? Just Colors? - you never know until you hover over it. )
The proposal of this issue would not include this case. It would work for enums, static members, factories & constructors of a given type T
that is inferred by the context.
So, if SomeWidget
receives a parameter Color color
, MyAppColor
, CupertinoColors
and Colors
would never be considered for .blue
. Only Color
would. And as Color
does not have a .blue
static member, an error would be emitted.
@mateusfccp Thank you for the explanation, I had the wrong implementations in my mind.
Yet, the issue is still there.
enum CupertinoColor {
blue("0xXXA");
const CupertinoColor(this.color);
final String color;
}
enum MaterialColor {
blue("0xXXB");
const MaterialColor(this.color);
final String color;
}
enum MyColor {
blue("0xXXC");
const MyColor(this.color);
final String color;
}
// ...
SomeWidget(
color: .blue // You don't know where this `.blue` comes from until you see SomeWidget declaration.
);
Though most of the time you don't have ambiguous declarations, I'm just bringing it up so it could be considered.
Sure, you would have to know whether SomeWidget
accepts MyColor
, MaterialColor
or CupertinoColor
.
Personally, I don't see this as a huge disadvantage, and you can always use the full reference. We would probably also have linters to enforce the full or dot syntax, depending on the preference of the team.
I have added somewhat larger-in-scope design proposal.
It has some ideas for how to allow more constants than just on the type itself, but it still cannot reach Flutter's Colors
class. I don't think any reasonable design can.