flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
162.25k stars 26.66k forks source link

Remote Flutter Widgets (rfw) package feedback thread #90218

Open Hixie opened 2 years ago

Hixie commented 2 years ago

This issue is intended to collect feedback regarding the rfw package.

If you have found a bug with RFW, please file a new issue. This issue is specifically for discussing the package's general applicability to your use cases, to determine if this is a useful direction for us to follow, and to report experiences you may have with writing applications with this package. Thanks in advance.

shuoch-g commented 2 years ago

Hi, Ian,

Very thrilled to see flutter team's effort on the dynamic UI ! We are evaluating on how to leverage rfw in our project.

First of all, may I ask about the compatibility policy we will have in rfw ?

In our project we will send down configuration data from server (DynamicMap analogue in rfw) and our runtime evaluator will translate them to widget constructor calls. As the evaluator code (decoder + WidgetBuilder analogue in rfw) is released with our app's apk, we want the server returned configuration to be always compatible with the evaluator that is released in a client version.

This is how we solve this: For each client release, we will snapshot the "capabilities" of the evaluator by the branch cut. The capabilities include

  1. which set of widgets are supported, and their names.
  2. which set of arguments are supported for each widget, and their name/type.

these snapshot capabilities data will be kept in our database, and keyed by the app release version.

Before the configuration data is sent to client with version A, we will retrieve the capabilities data for version A, and go through all configuration data and validate that all configuration key/value are supported in the capabilities. Our feature is authored in a single place, but when releasing to different client versions we will do tree-shaking or branching and release different configuration data targeting different client apk versions.

Could rfw package provide an interface that returns a structured data object that encodes all widgets / arguments its WidgetBuilders can support ? If it makes sense to you, happy to contribute as well.

Hixie commented 2 years ago

Right now there's no compatibility policy but there could be one. I think it makes sense to have a policy that we never break things.

I don't know how easy it would be to accurately return what the supported API is. You can get the list of widgets easily enough by querying the map of constructors, but getting the arguments is non-trivial.

Hixie commented 2 years ago

@ljmatan Well the good news is that it didn't take much effort to make this package so it didn't take away from those other efforts. :-) That said, your comment really isn't constructive and doesn't really make anyone feel welcome, so please think of other ways to phrase that kind of feedback in the future. You may wish to review our code of conduct, which requires everyone to be actively welcoming. Feedback along those lines can be given in a much more friendly manner.

Hixie commented 2 years ago

@ljmatan This is an open source project. You are welcome to contribute if you believe there are areas that are lacking in investment. Others in the community are welcome to work on what they want to work on. Nobody owes you progress on particular features.

shuoch-g commented 2 years ago

This package is not part of the flutter core repo, but a critical contribution to the flutter community.

Flutter as a cross-platform app development framework lacks a go-to solution to let server dynamically control the runtime behavior. Meanwhile, some important platforms that flutter can support (namely, Android, iOS) have seen such solutions, from early days of PhoneGap, to ReactNative, etc. If I used to enjoy the dynamic server release cycle while working on Android/iOS, but then have to discard if I migrate to Flutter, it would be very under expectation. Go to flutter community and the quest to have dynamic release support is clearly there (see reactions to https://github.com/flutter/flutter/issues/14330#issuecomment-485565194), this package is not solving a non-critical problem at all.

Even today there are quite a lot of solutions in the flutter community to tackle the same problem space, e,g https://github.com/Tencent/mxflutter. Many such solutions can benefit from this package if it can provide an official server payload specifications on widget constructions. https://github.com/flutter/flutter/issues/14330#issuecomment-932692288

fzyzcjy commented 2 years ago

@Hixie As suggested by you a few months ago, we are interested in cooperating rfw with flutter_rust_bridge. Could you please provide some suggestions on the details?

Related: https://github.com/fzyzcjy/flutter_rust_bridge/issues/296

Hixie commented 2 years ago

(responded in the other issue — thanks!)

mhoad commented 2 years ago

@Hixie I only just came across this package yesterday and haven’t had a chance to kick the tires on it yet but I’m incredibly optimistic about this direction especially when it comes to app delivery and I am curious to hear your thoughts about what kinds of ways this could be extended and built upon in the future.

One of the things I find hardest with Flutter web currently is just how much code I have to ship to get that first frame on the screen and the lack of modularity compared to say how I might deliver a regular HTML / JS / CSS based web app.

It also gives me more control it seems over what kind of experience I want to render for what kind of user which I appreciate because again I feel like currently I’m in a position where I need to ship all possible set of experiences not only when the user might only want a tiny fraction of them but I have to do all of this before I even render a frame which is upsetting for everyone.

I would love to see what a Flutter application server could look like.

Hixie commented 2 years ago

This technique doesn't reduce the amount of code you have to send down, really, since you still have to send the whole Flutter engine and the whole framework, but certainly something using this or another solution along this lines could give you more flexibility in terms of what the application does.

dengzq commented 2 years ago

Hi! Thanks for doing this for dynamic ui !
Here we can already map the widgets that we support and render them. But how can we do for dynamic logic ? For example , logic in tap() method or other calculation logic.

Are there any solutions for logic dynamic? Thanks a lot.

hmbenhaim commented 2 years ago

Hi thanks for the package, I always wanted dynamic widget like you have in html, Just wanted to ask will you add like regular imports for consistency, like import "package:flutter/material.dart" it will be a lot easier to manage the code this way,

Hixie commented 2 years ago

@hmbenhaim do you mean from an .rfwtxt file?

0xchase commented 1 year ago

Hey, I just wanted to add that I've found this package useful in designing a (sort of) interactive UI designer for my app. I hope it continues to be developed and maintained.

isAlmogK commented 1 year ago

Hi, I have a couple of questions and hope someone from the team can help; in general, this could be very useful, especially if you need to load custom widgets for enterprise customers, at least in our use case.

My first question is whether there are plans to support this package, as I see there has not been a big use case. The second question would it be possible to load the following into a widget build method from, let's say Firestore

MultiSelect(
                onSaved: (val) {
                  setState(() {
                    for (final element in commandsType) {
                      final contain =
                          commandTypeName.where((item) => item == element.name);
                      if (contain.isEmpty) {
                        commandTypeName.add(element.name as String);
                      }
                    }
                    dynamicTrainingType.commands =
                        commandTypeName.cast<String>();
                  });
                },
                buttonText: 'Select Commands',
                items: _commandItems,
                initialVue: commandsType,
                onConfirm: (dynamic vr) {
                  setState(() {
                    dynamicTrainingType.commands?.clear();
                    commandsType = vr as List<dynamic>;
                  });
                },
              ),
Hixie commented 1 year ago

@isAlmogK Can you elaborate on what you mean by "support" exactly?

For the second question, I'm not sure I understand exactly what you are asking; in general package:rfw does not allow imperative code to be sent from the server, only declarative widgets. You could send code as JavaScript or Wasm or some such though. If you have a clearer description of your use case I could try to advise in more detail.

dkbast commented 1 year ago

Just wanted to give this another try (had it working when it first came out but didn't have a usecase then, now I do), but currently I'm unable to run the example/wasm app, no matter the plattform I get an error that wasm could not be initialized, this happens in the build method of the example widget:

[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: WasmError: Wasm library not found. Did you `dart run wasm:setup`?
#0      _getLibPath (package:wasm/src/runtime.dart:119:3)
#1      new WasmRuntime._init (package:wasm/src/runtime.g.dart:118:52)
#2      runtime (package:wasm/src/runtime.dart:20:29)
#3      runtime (package:wasm/src/runtime.dart)
#4      new WasmModule (package:wasm/src/module.dart:21:14)
#5      _ExampleState._loadLogi

from what I see wasm was initialized correctly - I'm on a M1 mac.

wasm ➤ dart run wasm:setup                                                                                                                                                                                                                                              git:main*
Dart SDK directory: /opt/homebrew/Caskroom/flutter/2.5.3/flutter/bin/cache/dart-sdk/
Dart SDK include directory: /opt/homebrew/Caskroom/flutter/2.5.3/flutter/bin/cache/dart-sdk/include/
Source directory: /Users/userhome/.pub-cache/hosted/pub.dartlang.org/wasm-0.1.0+1/bin/
Output directory: /Users/userhome/src/github.com/flutter/packages/packages/rfw/example/wasm/.dart_tool/wasm/
Target: aarch64-apple-darwin
OS: darwin
Output library: /Users/userhome/src/github.com/flutter/packages/packages/rfw/example/wasm/.dart_tool/wasm/libwasmer.dylib

cargo build --target aarch64-apple-darwin --target-dir /Users/userhome/src/github.com/flutter/packages/packages/rfw/example/wasm/.dart_tool/wasm/ --manifest-path /Users/userhome/.pub-cache/hosted/pub.dartlang.org/wasm-0.1.0+1/bin/Cargo.toml --release

    Finished release [optimized] target(s) in 0.17s

clang -DDART_SHARED_LIB -DNDEBUG -fno-exceptions -fPIC -O3 -target aarch64-apple-darwin -I /opt/homebrew/Caskroom/flutter/2.5.3/flutter/bin/cache/dart-sdk/include/ -I /Users/userhome/src/github.com/flutter/packages/packages/rfw/example/wasm/.dart_tool/wasm/include/ -c /opt/homebrew/Caskroom/flutter/2.5.3/flutter/bin/cache/dart-sdk/include/dart_api_dl.c -o /Users/userhome/src/github.com/flutter/packages/packages/rfw/example/wasm/.dart_tool/wasm/dart_api_dl.o

clang++ -DDART_SHARED_LIB -DNDEBUG -fno-exceptions -fno-rtti -fPIC -O3 -std=c++11 -target aarch64-apple-darwin -I /opt/homebrew/Caskroom/flutter/2.5.3/flutter/bin/cache/dart-sdk/include/ -I /Users/userhome/src/github.com/flutter/packages/packages/rfw/example/wasm/.dart_tool/wasm/include/ -c /Users/userhome/.pub-cache/hosted/pub.dartlang.org/wasm-0.1.0+1/bin/finalizers.cc -o /Users/userhome/src/github.com/flutter/packages/packages/rfw/example/wasm/.dart_tool/wasm/finalizers.o

clang++ -shared -fPIC -target aarch64-apple-darwin /Users/userhome/src/github.com/flutter/packages/packages/rfw/example/wasm/.dart_tool/wasm/dart_api_dl.o /Users/userhome/src/github.com/flutter/packages/packages/rfw/example/wasm/.dart_tool/wasm/finalizers.o /Users/userhome/src/github.com/flutter/packages/packages/rfw/example/wasm/.dart_tool/wasm/aarch64-apple-darwin/release/libwasmer.a -o /Users/userhome/src/github.com/flutter/packages/packages/rfw/example/wasm/.dart_tool/wasm/libwasmer.dylib
Hixie commented 1 year ago

Can you get the wasm package to work in isolation?

dkbast commented 1 year ago

-> looks like the hello_world example is running just fine

wasmtest ➤ dart run                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       git:main*
Building package executable... 
Built wasmtest:wasmtest.
1522756 == 1522756

had to reinstall rust, but still no luck with rfw, did initialise wasm with flutter pub run as well, so this is not the issue.

Hixie commented 1 year ago

I have no idea... does it work on a Linux box? On this Linux machine, modulo some other issue I'm having with LIBGL, if I just check out the rfw repo and go into the example/wasm directory and run flutter pub run wasm:setup; flutter run -d linux it seems to work.

dkbast commented 1 year ago

Just hopped on my old Macbook with Intel processor and got the same error - the fact that the debug entitlements for network access are missing from macos/Runner/DebugProfile.entitlements leads me to believe that this has not been tested for macos? Because the example as is, is not runnable on a mac without adding this:

<key>com.apple.security.network.client</key>
<true/>

Currently I don't have access to another machine, but if it works on linux for you, thats good - that probably narrows this issue down to being mac specific (but not M1 mac specific)

Hixie commented 1 year ago

Aah, yes, it is probably that exact problem, indeed.

dkbast commented 1 year ago

I just created a small script to checkout and run the wasm example on a mac machine, to make this easier to test and reproduce: https://gist.github.com/dkbast/6af0c48eda59817b525798743f737918

# A shell script which sets everything up to run the rfw wasm example
# Setup Rust:
brew install rustup
rustup-init
source "$HOME/.cargo/env"
# clone the project
git clone https://github.com/flutter/packages.git

cd packages/packages/rfw

cd example/wasm

# Add internet access to the debug entitlements:
echo '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.app-sandbox</key>
    <true/>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
    <key>com.apple.security.network.server</key>
    <true/>
    <key>com.apple.security.network.client</key>
    <true/>
</dict>
</plist>' > macos/Runner/DebugProfile.entitlements

# get dependencies
flutter pub get

# initialize wasm and build wasm runtime, this is why we need rust
flutter pub run wasm:setup

# build and run the wasm example
flutter run -d macos
Hixie commented 1 year ago

If anyone would like to submit a PR to fix the example to add the relevant entitlements, I would be more than happy to review it and land it for you.

Kounex commented 1 year ago

I'm unfortunately running into the same issue by trying to run the example/wasm app on macOS (Intel):

[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: WasmError: Wasm library not found. Did you `dart run wasm:setup`?
#0      _getLibPath (package:wasm/src/runtime.dart:119:3)
#1      new WasmRuntime._init (package:wasm/src/runtime.g.dart:118:52)
#2      runtime (package:wasm/src/runtime.dart:20:29)
#3      runtime (package:wasm/src/runtime.dart)
#4      new WasmModule (package:wasm/src/module.dart:21:14)
#5      _ExampleState._loadLogic (package:rfw_wasm/main.dart:62:14)

The app itself is starting but the window is empty and therefore the additional content by rfw is not loaded. Using dart run wasm:setup did not change this behaviour.

Hixie commented 1 year ago

applying the fix in https://github.com/flutter/packages/pull/2958 should resolve it

dkbast commented 1 year ago

Unfortunately, actually it's not - only with this fix applied you get so far that you see this issue. Without it it won't even download the wasm code.

The problem is a) the compiled wasm could not be download without the entitlement and b) it does not recognise that wasm was properly setup- the pr only fixes a)

Hixie commented 1 year ago

Ah, interesting. That wasn't clear. Would you mind filing a new issue to track that? I've no idea what could be special about macOS that would make it not work...

rodydavis commented 1 year ago

I have been working on this package and wanted to show an alternative approach:

https://github.com/rodydavis/flutter_json_widgets

I am trying to follow the flutter SDK api for creating widgets and being able to create server driven UIs but using dart classes and a runtime parser. (Which can convert to json and have copyWith, map, when methods too)

I think that I could output rfw files, but just found out about this package. Looks really interesting!

njovy commented 11 months ago

This is exactly what I needed to display banners. Smaller than images but flexible. Could you provide an rfw script converter that converts a Flutter widget to an rfw script language in text or blob?

Hixie commented 11 months ago

This is exactly what I needed to display banners. Smaller than images but flexible. Could you provide an rfw script converter that converts a Flutter widget to an rfw script language in text or blob?

Like, something that parses Dart code of the form seen in build methods, and outputs rfw files?

njovy commented 11 months ago

@Hixie Yes! Dart code to rfw. It can also check some sort of validations with registered widgets. It's hard to figure out how to write a script file for rfw until you read the library code.

Hixie commented 11 months ago

Compiling completely arbitrary Dart code into a declarative format is mathematically impossible (via transposition to the halting problem) but one could certainly imagine writing a parser that handles a subset of Dart and a fixed set of Flutter widgets, and turned that into the RFW binary format. Because it would have to be a subset, though, I'm not sure how useful it would be in practice, you'd probably end up spending as much time trying to learn what the subset is as you would just learning to write RFW's dedicated text format directly.

linyongsheng commented 10 months ago

@Hixie Whether there is a dedicated IDE plug-in for RFW's text format. Write RFW's text format directly is low efficiency.

Hixie commented 10 months ago

I'm not aware of that currently, but I would be very happy to provide advice to anyone who is interested in writing such a thing!

rodydavis commented 8 months ago

I have been working on this package and wanted to show an alternative approach:

https://github.com/rodydavis/flutter_json_widgets

I am trying to follow the flutter SDK api for creating widgets and being able to create server driven UIs but using dart classes and a runtime parser. (Which can convert to json and have copyWith, map, when methods too)

I think that I could output rfw files, but just found out about this package. Looks really interesting!

Ended up writing a blog post on my explorations into rfw!

https://rodydavis.com/posts/flutter-ssr/

TLDR: Really love it and i think there is a lot of great use cases.

I also think instead of WASM, QuickJS could be a good option!

https://pub.dev/packages/flutter_qjs

rodydavis commented 8 months ago

Followed up with logic via flutter_js on my blog post and example!

https://rodydavis.com/posts/flutter-ssr/

This allows the logic to be shipped on iOS/MacOS via JavaScriptCore!

maks commented 8 months ago

Nice one @rodydavis ! 👍🏻 There are lots of great packages that could be used here. For example I'm working with LuaDardo for scripting rfw (which has a few benefits such as being in pure Dart and easy to hack on) and am actually using Lua as a bi-directional codegen target in my project since its a nice simple syntax.

rodydavis commented 8 months ago

That's great! I went with JS for AppStore review reasons.

But the example I made would work with any of them!

maks commented 8 months ago

@rodydavis yes thanks again for publishing your example! I think your article is super helpful for anyone wanting to get into using RFW 👍🏻

And yes AppStore review does limit things somewhat, though while its just my personal interpretation of it, I believe interpreters such as LuaDardo should be allowed and I believe C impl of Lua is already used in a number of published games.

rodydavis commented 8 months ago

Of course! Happy to have made the article because I was trying to understand it too.

I almost made it with this package:

https://pub.dev/packages/hetu_script

ralph-bergmann commented 7 months ago

I just discovered this package today, and it sounds like exactly what I was looking for.

But I saw that it doesn't support theming at all. Is there a technical reason for this? Or do you not have the time to implement it? I need it for the application I have in mind and would spend time implementing it. But I don't want to waste my time. So, if you know it won't work, let me know :-)

rodydavis commented 7 months ago

In my example I show how to use theming. You would also have a theme higher than the remote widget with custom themes or provide custom widget resolvers to add more.

Hixie commented 7 months ago

It can more or less support any Flutter widget, if you provide a "local widget" for it. Assuming you mean theming in the sens of controlling the theme from the server: I didn't implement the Theme widget in https://github.com/flutter/packages/blob/main/packages/rfw/lib/src/flutter/material_widgets.dart mostly because Theme has a very large API surface that has been in active flux in recent years, but if anyone wants to submit a PR to add Theme to the widgets implemented by createMaterialWidgets() I'm sure we would be happy to review it. You can also implement a subset of it in your own application, it's relatively straightforward and would only have to support the theming aspects you need.

johnmccutchan commented 4 months ago

I'd like to be able to reflect over the contents of DynamicContent. My use case is to aid in debugging.

Something like:

class DynamicContent {
  void forEach((key, value) => ...);
}

would be sufficient. Alternatively, if the root map of DynamicContent were exposed I could just write the code myself.

Hixie commented 4 months ago

Can you elaborate on your debugging needs?

johnmccutchan commented 4 months ago

I'm trying to make a realtime editor and visualizer, left side is a text box where I can type in a rfw text, right side is the rendered widget.

I'd like to also include a display of DynamicContent.

I'm also interested in source maps so I can associate rfw text sections with widgets and DynamicContent values.

Hixie commented 4 months ago

If you're building a visualizer, you could just subscribe to the various keys that you're rendering. I guess the problem is that you don't want to hard-code the root keys, so that the visualizer can be used in contexts with different root key conventions? We could let you subscribe with an empty key and have that return the root...

Hixie commented 4 months ago

Can you elaborate on what you mean by "source maps" in this context?

johnmccutchan commented 4 months ago

If you're building a visualizer, you could just subscribe to the various keys that you're rendering. I guess the problem is that you don't want to hard-code the root keys, so that the visualizer can be used in contexts with different root key conventions? We could let you subscribe with an empty key and have that return the root...

correct, I want to not have anything hard coded.

johnmccutchan commented 4 months ago

Can you elaborate on what you mean by "source maps" in this context?

imagine I have some rfw text that creates button widget with text fetchec from DynamicContent using the key "foo.bar".

I'd like to be able to track what sections of the rfw text map to the Button (or any widget). Also which keys in the DynamicContent are used by the widget.

Think Chrome DevTools where you can hover over a DOM element and navigate to the HTML tag in the source view and vice versa.