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

Compiled first then eval widget #3

Closed
aloisdeniel opened this issue Aug 23, 2022 · 5 comments
Closed

Compiled first then eval widget #3

aloisdeniel opened this issue Aug 23, 2022 · 5 comments

Comments

@aloisdeniel
Copy link

aloisdeniel commented Aug 23, 2022

Thanks a lot for the package, it is clearly a game changer!

I know that this is pretty easy to implement on top of EvalWidget, but I think it would be great to have it built in since it is a great pattern: I would love to have regular widgets, that have the possibility to be evaluated later on if needed. The eval allows to have a packaged EVC, but why not simply use a natively compiled widget instead?

So to summarize a widget like this :

At the root of the widget tree we would have a library manager inherited widget (loading, caching libraries):

@override 
Widget build(BuildContext context) {
    return EvalLibrary(
         storage: DefaultEvalStorage(), // All libraries are cached and could be cleared after an app update for example
         load: FutureOr<LibraryUpdate> (BuildContext context, LibraryUpdate? lastUpdate, String id) async {
             // The `lastUpdate` can be used to add basic update checking logic like here :
             // If last updates is less than one day ago we don't try to update widgets.
             if(lastUpdate != null && lastUpdate.date.isBefore(DateTime.now() + const Duration(days: 1)) {
                     return lastUpdate;
             }
              
              // We could also use http protocol logic to track changes.
              final eTag = await sharedPrefs.getEtag(id);
              final result = await http.get('https://code.push/widgets/$id', headers: {
                  if(eTag != null) 'etag': eTag,
              } );
              if(result.statusCode == 304 ) {
                 // The server indicates that there's no update.
                 // The manager will consider it updated.
                 return lastUpdate.copyWithDate(DateTime.now());
              }
      
              if(result.statusCode == 200) {
                  final evc =  await result.body.asBytes();
                  return LibraryUpdate(
                      library: evc,
                  );
              }
              
              return LibraryUpdate.failed('Reason');
         },
         child: MaterialApp(...),
    );
}

And then we could declare CodePushWidgets that would have a default implementation and a potential code pushed update by using the inherited EvalLibrary under the hood.

class MyWidget extends CodePushWidget {
   const MyWidget({ 
       super.key, 
       this.title 
   });
   
   final String title;

   @override
   String get evalLibrary => 'my-widgets';

   @override
   String get evalFunction => 'MyWidget';

   @override
   List<dynamic> get evalArgs => [title];

  // Until the `EvalLibrary` provides an eval library, the regular build method is used
  @override
   Widget build(BuildContext context) {
       return Column(...);
   }
}
@ethanblake4
Copy link
Owner

Yeah, this is certainly something I've thought about. dart_eval actually has the concept of runtime overrides which let you implement the hot wrapper pattern, pretty similar to your idea but without the flutter dependency.

Before I add hot constructors throughout flutter_eval though, dart_eval needs to have a file format that specifies what IDs are overriden and by what, and there needs to be an easy way to generate that (maybe annotations?). I do also like your idea of a root Flutter widget that loads the updates, although I'm not sure hot-swapping them when they're already in use is the best idea. Honestly if you want to work on this and puzzle it out I'd be very grateful and happy to help you along :)

@aloisdeniel
Copy link
Author

Ah really cool for hot wrappers!

Regarding hot-swapping, I don't know which is the best strategy :

  • Increasing load time while checking for updates
  • Triggering updates but waiting for the next launch for evaluation (so quick initial load time)
  • Hot swapping which means quick initial load time but it may be confusing while using the app.

I could definitively help with that loading part as soon as you have a clearer vision of the incremental updates based on IDs.

@krishaajtak
Copy link

@ethanblake4 & @aloisdeniel looking forward for this interesting idea.

@ethanblake4
Copy link
Owner

@aloisdeniel are you still interested in this? I finished the dart_eval implementation of runtime overrides, so all that's left now is Flutter. I'll be working on adding hot constructors to flutter_eval, let me know if you're interested in working on a PR for the loading. Documentation here, tests here and the code is in the in-progress v0.6 branches of both dart_eval and flutter_eval.

@ethanblake4
Copy link
Owner

Completed in v0.6.0. There are new HotSwap and HotSwapLoader widgets to facilitate this.

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