Skip to content

Proposal: API - Move from 'style' to Compositional Components #489

@bryphe

Description

@bryphe

This came from some discussions with @manuhornung and others around Flutter's model vs our style model. IMO, one thing Flutter did really well in their design is their compositional component model (moving away from the uber-style grab bag of properties).

To clarify, what I mean by a compositional model, is instead of having a Style.[...] with arbitrary properties (like positioning, transform, etc), Flutter has more scoped widgets - like a Transform widget, or Positioned widget. This is nice in terms of discoverability - each of those widgets takes a relatively small and concise set of properties.

In addition, the Style.[..] model is coupled to a single layout system, whereas Flutter uses component composition to express a variety of layout models - this is very convenient for building UI apps.

The Style.[..] model has gotten us really far, but there are a few downsides I've seen so far:

  • Discoverability: The use of Polymorphic Variants isn't as well supported with auto-complete (as far as I can tell), whereas specific properties would be more discoverable
  • Discoverability: It's challenging to know all the potential properties, and how they interact, when using Style.[..].
  • Performance: We always have to fold our style-list into a style-object:
    let applyStyle = (style, styleRule) =>
    - I wonder if there could be a cheaper way to handle this? Merging is always challenging:
    let merge = (~source, ~target) =>
  • Performance: Our Node class ends up having to do a lot of different things - recompute transforms, even if there is are no extra transforms, check the overflow property, etc.

I think the compositional model could help with some of the discoverability, by having a more constrained set of properties per-component, and help with the performance by giving each component a more constrained set of responsibilities (potentially minimizing extra transform calculations, overflow calculations, etc).

If people are on-board with moving to the compositional model vs the uber-style model, I'd propose tackling it incrementally, by removing style properties and replacing them with compositional components:

  • The transform property could become the responsibility of Transform.Rotate/Transform.Scale, etc components ([WIP] API: Transform composite component #488)
  • The position and top/left/right/bottom could become the responsibility of Stack and Positioned components (Feature - API: Some initial compositional components #483)
  • The backgroundColor property could become the color property of a Container
  • The color property could become the color property of a Text
  • padding property could be moved to a Padding component
  • Our current flex row / column settings could be moved to Row and Column components
  • border properties could be moved to a Border component
  • opacity could be moved to an Opacity component
  • overflow could be moved to a ClipRect component
  • boxShadow could be moved to a BoxShadow component
  • cursor and mouse event handling could be moved to a MouseInput component
  • focus behavior and keyboard handling could be moved to a KeyboardInput component

In addition, this model would be amenable for future work:

  • We could model accessibility (Proposal: Accessibility #293) with compositional components
  • Gestures could use some sort of GestureDetector component (potentially plugging into @jordwalke 's prototype)
  • We could model alternative layout strategies, if it makes sense. For this, it'd be nice if we had a way to statically type the component tree (ie, what children are allowed)

The ultimate goal would be to remove the style property entirely, and represent everything it modelled via new components, instead.

Some things to think about:

  • Would each of these components need a Node in our scene-tree (aka, would they be primitive components?)? If so, they probably don't all need to generate LayoutNode's - we might need to take a look at that. Also, if we did model these as Node's - likely a lot of the 'core' behavior in the Node class could be factored across these new, smaller primitives. For example - this Overflow.render behavior would only need to be used by the new ClipRect's node.

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-componentsArea: Consumer-exposed components providing some convenience over the primitivesA-layoutArea: LayoutA-primitivesArea: Primitives, the basic building blocksA-renderingArea: Rendering artifacts, features etc.A-technicalArea: Technical issues not directly related to featuresenhancementNew feature or requesthelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions