Open Hixie opened 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
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 WidgetBuilder
s can support ? If it makes sense to you, happy to contribute as well.
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.
@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.
@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.
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
@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
(responded in the other issue — thanks!)
@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.
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.
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.
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,
@hmbenhaim do you mean from an .rfwtxt file?
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.
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>;
});
},
),
@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.
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
Can you get the wasm package to work in isolation?
-> 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.
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.
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)
Aah, yes, it is probably that exact problem, indeed.
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
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.
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.
applying the fix in https://github.com/flutter/packages/pull/2958 should resolve it
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)
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...
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!
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?
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?
@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.
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.
@Hixie Whether there is a dedicated IDE plug-in for RFW's text format. Write RFW's text format directly is low efficiency.
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!
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!
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!
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.
That's great! I went with JS for AppStore review reasons.
But the example I made would work with any of them!
@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.
Of course! Happy to have made the article because I was trying to understand it too.
I almost made it with this package:
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 :-)
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.
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.
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.
Can you elaborate on your debugging needs?
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.
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...
Can you elaborate on what you mean by "source maps" in this context?
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.
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.
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.