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

Building expressions from statements #18

Open
Hixie opened this issue Jul 13, 2021 · 6 comments
Open

Building expressions from statements #18

Hixie opened this issue Jul 13, 2021 · 6 comments

Comments

@Hixie
Copy link
Collaborator

Hixie commented Jul 13, 2021

One of the use cases is simplifying build methods that today require significant nesting.

Here is one example: https://github.com/TimWhiting/local_widget_state_approaches/blob/master/lib/stateful/animated_counter.dart

The desired result is the equivalent of the single expression in the build method in that file. Some developers, however, find the large expression unintutive and would prefer something like:

    var value1a = ValueListenableBuilder<int>(valueListenable: firstCounter);
    var value1b = TweenAnimationBuilder<int>(
      duration: const Duration(seconds: 5),
      curve: Curves.easeOut,
      tween: IntTween(end: value1a),
    );
    var value2a = ValueListenableBuilder<int>(valueListenable: secondCounter);
    var value2b = TweenAnimationBuilder<int>(
      duration: const Duration(seconds: 2),
      curve: Curves.easeInOut,
      tween: IntTween(end: value2a),
    );
    return Scaffold(
      appBar: AppBar(
        title: Text('Stateful animated counter'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('$value1b'),
            RaisedButton(
              onPressed: () => firstCounter.value += 100,
              child: Text('+'),
            ),
            Text('$value2b');
            RaisedButton(
              onPressed: () => secondCounter.value += 100,
              child: Text('+'),
            ),
            const Text('total:'),
            Text('${value1b + value2b}'),
          ],
        ),
      ),
    );

Ideally the translation would not cause more rebuilds than today (so e.g. it's not good if hoisting the TweenAnimationBuilder up to the top causes the Scaffold to rebuild whenever the counters change -- in the original, the whole build method in fact only runs once, only the builders rerun).

The current API doesn't really provide any hooks that I can see for this kind of change.

@rrousselGit
Copy link
Contributor

That is one of my motivations behind the issue #13

I was originally thinking that static metaprogramming would allow us to implement new language features by generating patch code. Like how babel in JS allows us to use async/await but target a version of JS that does not support it yet

For example one thing I had in mind was converting

Future<T> future
Future<T> future2

Widget build(context) {
  print('a');
  AsyncSnapshot<T> snapshot = $useFuture(future);
  AsyncSnapshot<T> snapshot2 = $useFuture(future2);

  return Text('$snapshot $snapshot2');
}

into:

Widget build(context) {
  print('a');

  return FutureBuilder<T>(
    future: future,
    builder: (context, snapshot) {
       FutureBuilder<T>(
         future: future2,
         builder: (context, snapshot2) {
           return Text('$snapshot $snapshot2');
         },
       ),
    },
  );
}

@jakemac53
Copy link
Owner

We have considered a form of macros that aren't ran on declarations, but instead on statements/expressions/elements. And they would likely be able to transform those things arbitrarily.

Likely these would run in a new, 4th phase. But we haven't spent much time yet investigating it.

@jakemac53
Copy link
Owner

@Hixie would you be able to elaborate a bit more on exactly the type of api you would like to be able to expose here?

How would you envision the user applying the macro, and how would it transform the code?

@Hixie
Copy link
Collaborator Author

Hixie commented Jul 19, 2021

I have no idea. I tried writing sample versions of ways to do this a half dozen times and couldn't come up with anything compelling.

@jakemac53
Copy link
Owner

jakemac53 commented Jul 19, 2021

Ok, ya I think I understand what you want to achieve here but was also struggling with coming up with a good solution for macros.

You could use helper methods and/or extensions to reduce the duplication in the original example, without introducing the rebuild issues. For instance https://gist.github.com/jakemac53/5b499747d0a8cc705e2fcf7598c8789c.

@rrousselGit
Copy link
Contributor

rrousselGit commented Jul 20, 2021

You could use helper methods and/or extensions to reduce the duplication in the original example, without introducing the rebuild issues. For instance https://gist.github.com/jakemac53/5b499747d0a8cc705e2fcf7598c8789c.

I don't think this truly solves the issue, which is about flutter/flutter#51752 and variant topics like flutter_hooks.

Maybe it's worth jumping on a call to talk about this?
As this is a complex topic and is difficult to capture in a few words (as proved by the issue mentioned above).

This is a topic close to my heart. I'd love to help coming up with a way to use macros to improve on this area.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants