Experiments: Stronger typing, Mixing Dynamic/Static views, Componentization, SwiftUI-like DSL
See original GitHub issueDISCLAIMER: All the following points are experiments only and there’s no guarantee they will be available anytime in the future.
Index:
- Introduction
- Stronger typing & attribute-based diffing
- Merging Fabulous and Fabulous.StaticViews
- Running Fabulous as a component
- Why should we change the Fabulous DSL?
- Why not use Elmish.React / Feliz / Avalonia.FuncUI DSL instead?
- SwiftUI-like DSL
In the last few days, I’ve been experimenting with a couple of ideas.
Introduction
- Stronger view typing (#341)
Instead of having all View.XXX()
return untyped ViewElement
, it should return a typed ViewElement to make the compiler check for invalid uses (like putting a Button as an item of ListView – ListView only accepts Cell-derivated controls).
- Diffing per “attribute” instead of per “control”
Currently in Fabulous.XamarinForms, we diff controls by calling the Update method attached to it (e.g. View.Button()
will have a link to ViewBuilders.UpdateButton()
).
This update method will check each one of the properties of Button (and will call UpdateVisualElement, etc. to update the inherited properties/events). This is perfectly fine in 99% of the cases, except when dealing with overridden properties or attached properties.
This becomes particularly tricking to always apply the correct behavior and it results in a lot of custom code.
Instead if we went with “attribute”-based diffing like FuncUI, overridden properties could simply be dealt with by storing the overridden attribute updater instead of the base one in an attributes array (e.g. ViewElement.Attributes).
Same with attached properties, there would be no need to be aware that Grid.Row
is an attached property applicable only on the Grid inherited Layout<T>.Children
items.
- Merging Fabulous and Fabulous.StaticViews
Currently you need to choose from either Fabulous
or Fabulous.StaticViews
to write apps. Mixing the 2 is not possible.
Though there’s instances where being able to use the 2 makes sense. Fabulous.StaticViews makes it easy to use 3rd party controls (or custom XAML views) without spending too much time writing wrappers for it.
It would also be way easier to “market” Fabulous, less confusing on which package to use.
- Allow Fabulous.XamarinForms to run as a component of an existing app, or as an app as today
Fabulous.XamarinForms currently plugs itself in the Xamarin.Forms.Application.MainPage property and acts from there. It is quite easy to allow it to plug into a ContentView.Content property instead, allowing it to run anywhere, in multiple instances, even in existing C# apps.
- A SwiftUI-like DSL
I really like the Fabulous DSL, though IDEs make it difficulty to know what’s exactly is available when having like 20 optional method parameters. Elmish.React - Feliz - Avalonia.FuncUI all have great advantages, but also have different issues in my opinion, I will explain why in a comment below.
So I tried various approaches to keep it close to what’s Fabulous DSL offers today, while trying to make it IDE-friendly. After pushing the F# syntax to its limit several times, I went with a SwiftUI-like syntax. I think it offers the best balance between readability and discoverability, as well as being nice with the IDEs.
I added a little Flutter-like widgets to the mix to make it great with static views code.
Current experiments can be found in https://github.com/TimLariviere/Fabulous/tree/play
Read the comments below for more information.
Issue Analytics
- State:
- Created 3 years ago
- Reactions:14
- Comments:48 (22 by maintainers)
Top GitHub Comments
SwiftUI-like DSL
I must say I was quite in awe when first playing with SwiftUI from Apple. Their IDE-integration was so sweet. The way they declare UI also made a lot of sense to me.
Basically, you have the following:
So to use a control, you have 4 distinct parts.
HStack
The meaningful properties are not available as additional property methods.
Even though F# doesn’t have the
{ }
syntax for the children, it could be mimicked by[ ]
. Here’s a Todo List app using the SwiftUI-like DSL (use F# 4.7 open static classes):The signal-to-noise ratio is pretty good.
You’re also immediately aware of what’s important and what’s not with the most meaningful properties inside the parentheses.
If you use a Label, 100% of the time you’ll want a text set to it. So make it mandatory. Also why specify the field name? e.g.
View.Label(text = "Hello World")
If you want a Button, you’ll want to set a content and listen for the click so make it mandatory. If you use a Grid, you’ll most likely set columns and rows. Though you might not necessarily do that because the default values are fine. So optional is good but keep it close to the type.
Optional properties that are less-likely to be used are pushed as external methods. This helps understand the intent, as well as allow overloaded signatures. For instance, a font size could be an absolute value or a named size. This is possible to have both without having a DU.
This also allows us to provide helpers for things done often
Controls can have different constructors depending how you want to use them.
Note: This is all super experimental, it is completely based on a “compilable” syntax, not actually ran. A lot of hurdles are to be expected before having a working sample.
It’s infinitely better in the IDEs with this new syntax.