Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flutter shorthand #19811

Closed
lukepighetti opened this issue Jul 26, 2018 · 10 comments
Closed

Flutter shorthand #19811

lukepighetti opened this issue Jul 26, 2018 · 10 comments
Labels
will need additional triage This issue or PR needs attention during weekly triage

Comments

@lukepighetti
Copy link

lukepighetti commented Jul 26, 2018

This is not meant to be a "JSX or not" question, but more a question of if there is any way to reduce boilerplate in flutter with pure dart. Note: I am naiive to most of Dart's language features

The goal of this issue is not for me to just propose my own form of shorthand, but simply to ask how something like this could work today, and to throw an idea into the wind and see if it goes anywhere.

Is it possible for us to roll our own modifiers? (like async, await)

StatelessWidget today

class MyText extends StatelessWidget {
  final String title;

  MyText(this.title);

  @override
  Widget build(BuildContext context) {
    return Text('title');
  }
}

tomorrow?

MyText (String title) widget {
  build => Text('title');
}

StatefulWidget today

class MyOtherText extends StatefulWidget {
  String title;

  MyOtherText(this.title);

  @override
  _MyOtherTextState createState() => _MyOtherTextState();
}

class _MyOtherTextState extends State<MyOtherText> {
  bool isShown;

  _handlePressed(){
    setState(() {
      isShown = !isShown;
    });
  }

  @override
  void initState() {
    super.initState();
    isShown = false;
  }

  @override
  Widget build(BuildContext context) {

    return isShown ? 
      RaisedButton(
        child: Text("Click me"),
        onPressed: ()=>_handlePressed
      )
    : Container();
  }

  @override
  void dispose() {
    isShown = null;
    super.dispose();
  }
}

tomorrow?

MyOtherText (String title) widget* {
  bool isShown;

  initState => isShown = false;

  build => isShown ? 
    RaisedButton(
      child: Text('Click me'),
      onPressed: ()=> setState=> isShown = !isShown
    )
  : Container();

  dispose => isShown = null;
}
@sir-boformer
Copy link
Contributor

I think that kind of boilerplate is not really the problem. Your code is really hard to understand, and putting props and state into the same logical block is problematic.

Even today, you could do something like this to shorten your first example:

Widget buildMyText(String title) => Text(title);

I think what really causes a lot of boilerplate are the child/children constructor arguments, and in general the composition of widgets with all of those brackets.

Also, when it comes to constructors with named arguments, it would be great to have this kind of shorthand:

final color = Colors.blue;
final padding = EdgeInsets.all(16.0);

return Container(
  color,
  padding,
  child: Text('Hello World'),
);

FutureBuilder and StreamBuilder also generate quite a lot of boilerplate, for example compared to Angular 6:

<child-widget [time]="time$ | async"></child-widget>

vs.

StreamBuilder<DateTime>(
  stream: time$,
  builder: (context, snapshot) {
    if(snapshot.hasData) {
      return ChildWidget(time: snapshot.data);
    }
    else {
      return Container();
    }
  },
);

@lukepighetti
Copy link
Author

lukepighetti commented Jul 26, 2018

Thinking about it further, there is already a lot of room to reduce text

class MyText extends StatelessWidget {
  final String title;

  MyText(this.title);

  @override
  Widget build(BuildContext context) {
    return Text('title');
  }
}

vs ( I believe this works)

class MyText extends StatelessWidget {
  final String title;
  MyText(this.title);

  @override
  build(context) => Text('title');
}

Getting rid of the @override and super would clean the code up a LOT.

class MyOtherText extends StatefulWidget {
  String title;
  MyOtherText(this.title);

  createState() => _MyOtherTextState();
}

class _MyOtherTextState extends State<MyOtherText> {
  bool isShown;

  initState() => isShown = false;

  build(context) =>
    isShown ? 
      RaisedButton(
        child: Text("Click me"),
        onPressed: ()=>setState(()=> isShown = !isShown)
      )
    : Container();

  dispose() => isShown = null;
}

And yes, I believe we desperately need destructuring in Dart.

This is an aside, but having syntax highlighting treat user-defined widgets as different than imported widgets would go a long way to making trees more readable in my opinion.

@lukepighetti
Copy link
Author

lukepighetti commented Jul 26, 2018

Also, you pointed out the functional widget. I use those a lot but (and this may be a misunderstanding on my part on what context really does) it seems like context isn't passed through. If context is passed through then I see no reason to use class based StatelessWidgets

@zoechi
Copy link
Contributor

zoechi commented Jul 26, 2018

You don't need to add @override.
@override is only enforced by a linter rule that can be disabled,
bit @override helps to avoid bugs which is usually much more important than writing less code.

@zoechi
Copy link
Contributor

zoechi commented Jul 26, 2018

dispose() => isShown = null;

is just not the same code as

  void dispose() {
    isShown = null;
    super.dispose();
  }
}
```dart
and such kind of "magic" is terrible because it makes it hard to reason about code.

Explicit
```dart
super.dispose();

makes it clear that there is a super implementation that is also executed.
Special-casing all kinds of Flutter methods is IMHO a bad thing.
One time you want super.xxx() be called before the other time after the code in the method.

@lukepighetti
Copy link
Author

lukepighetti commented Jul 26, 2018

I haven't seen anyone call super.dispose() before, and I've never seen anyone call super.initState() after, but my experience is definitely limited.

Also, these questions come up a lot in the community and I think they will continue to do so. Templating languages like XML/JSX exist to reduce boilerplate and repetition. I'm not saying we should use JSX, but I am saying that I think there is a lot of room for improvement on keeping things pure Dart while reducing boilerplate. At least, I hope there is, and I'm interested in exploring it.

@lukepighetti
Copy link
Author

lukepighetti commented Jul 26, 2018

What about a modifier? Can we make our own custom ones in Dart?

dispose() super => isShown = null;

dispose() super{
  isShown = null;
}

@zoechi
Copy link
Contributor

zoechi commented Jul 26, 2018

I haven't seen anyone call super.dispose() before, and I've never seen anyone call super.initState() after, but my experience is definitely limited.

Special casing everything where a line of code could be saved by some language magic would make a gigantic mess out of Flutter and Dart.
This kind of optimizations buys you nothing but confusion from users who have to memorize and understand all special cases.

dispose() super => isShown = null;

Honestly I hated stenography in school and this is exactly that ;p
Dart has simple to understand mostly unambiguous syntax.
Such stuff would break that and doesn't provide any value whatsoever.

Writing code is the simplest part of the whole software development workflow.
I don't think it's worthwhile to optimize here in such dangerous ways.
Most other areas provide more potential for optimizations.

@Hixie
Copy link
Contributor

Hixie commented Dec 12, 2018

Duplicate of #20865

@Hixie Hixie marked this as a duplicate of #20865 Dec 12, 2018
@Hixie Hixie closed this as completed Dec 12, 2018
@github-actions
Copy link

github-actions bot commented Sep 1, 2021

This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Sep 1, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
will need additional triage This issue or PR needs attention during weekly triage
Projects
None yet
Development

No branches or pull requests

4 participants