| Index: sky/sdk/lib/framework/README.md
|
| diff --git a/sky/sdk/lib/framework/README.md b/sky/sdk/lib/framework/README.md
|
| index a15a276002266ab10f658e339af2029febf87648..b60665f4547c3a1a0fbbd988c0e7ebeb34c28121 100644
|
| --- a/sky/sdk/lib/framework/README.md
|
| +++ b/sky/sdk/lib/framework/README.md
|
| @@ -1,219 +1,22 @@
|
| -Sky Framework
|
| -=============
|
| +SKY SDK
|
| +========
|
|
|
| -Effen is a functional-reactive framework for Sky which takes inspiration from
|
| -[React](http://facebook.github.io/react/). Effen is comprised of three main
|
| -parts: a virtual-dom and diffing engine, a component mechanism and a very early
|
| -set of widgets for use in creating applications.
|
| +Sky and Sky's SDK are designed as layered frameworks, where each layer
|
| +depends on the ones below it but could be replaced wholesale.
|
|
|
| -The central idea is that you build your UI out of components. Components
|
| -describe what their view should look like given their current configuration &
|
| -state. The diffing engine ensures that the DOM looks how the component describes
|
| -by applying minimal diffs to transition it from one state to the next.
|
| +The bottom-most layer is the Sky Platform, which is exposed to Dart
|
| +code as the ```dart:sky``` package.
|
|
|
| -If you just want to dive into code, see the [stocks example](../../../../examples/stocks).
|
| +Above this are the files in the [painting/](painting/) directory,
|
| +which provide APIs related to drawing graphics.
|
|
|
| -Hello World
|
| ------------
|
| +Layout primitives are provided in the next layer, found in the
|
| +[rendering/](rendering/) directory. They use ```dart:sky``` and the
|
| +APIs exposed in painting/ to provide a retained-mode layout and
|
| +rendering model for applications or documents.
|
|
|
| -To build an application, create a subclass of App and instantiate it.
|
| +Widgets are provided by the files in the [widgets/](widgets/)
|
| +directory, using a reactive framework.
|
|
|
| -```HTML
|
| -<script>
|
| -import 'hello_world.dart';
|
| -
|
| -main() {
|
| - new HelloWorldApp();
|
| -}
|
| -</script>
|
| -```
|
| -
|
| -```dart
|
| -// In hello_world.dart
|
| -import 'package:sky/framework/fn.dart';
|
| -
|
| -class HelloWorldApp extends App {
|
| - UINode build() {
|
| - return new Text('Hello, world!');
|
| - }
|
| -}
|
| -```
|
| -
|
| -An app is comprised of (and is, itself, a) components. A component's main job is
|
| -to implement `UINode build()`. The idea here is that the `build` method describes
|
| -the DOM of a component at any given point during its lifetime. In this case, our
|
| -`HelloWorldApp`'s `build` method just returns a `Text` node which displays the
|
| -obligatory line of text.
|
| -
|
| -Nodes
|
| ------
|
| -
|
| -A component's `build` method must return a single `UINode` which *may* have
|
| -children (and so on, forming a *subtree*). Effen comes with a few built-in nodes
|
| -which mirror the built-in nodes/elements of sky: `Text`, `Anchor` (`<a />`,
|
| -`Image` (`<img />`) and `Container` (`<div />`). `build` can return a tree of
|
| -Nodes comprised of any of these nodes and plus any other imported object which
|
| -extends `Component`.
|
| -
|
| -How to structure you app
|
| -------------------------
|
| -
|
| -If you're familiar with React, the basic idea is the same: Application data
|
| -flows *down* from components which have data to components & nodes which they
|
| -construct via construction parameters. Generally speaking, View-Model data (data
|
| -which is derived from *model* data, but exists only because the view needs it),
|
| -is computed during the course of `build` and is short-lived, being handed into
|
| -nodes & components as configuration data.
|
| -
|
| -What does "data flowing down the tree" mean?
|
| ---------------------------------------------
|
| -
|
| -Consider the case of a checkbox. (i.e. `widgets/checkbox.dart`). The `Checkbox`
|
| -constructor looks like this:
|
| -
|
| -```dart
|
| - ValueChanged onChanged;
|
| - bool checked;
|
| -
|
| - Checkbox({ Object key, this.onChanged, this.checked }) : super(key: key);
|
| -```
|
| -
|
| -What this means is that the `Checkbox` component *never* "owns" the state of
|
| -the checkbox. It's current state is handed into the `checked` parameter, and
|
| -when a click occurs, the checkbox invokes its `onChanged` callback with the
|
| -value it thinks it should be changed to -- but it never directly changes the
|
| -value itself. This is a bit odd at first look, but if you think about it: a
|
| -control isn't very useful unless it gets its value out to someone and if you
|
| -think about databinding, the same thing happens: databinding basically tells a
|
| -control to *treat some remote variable as its storage*. That's all that is
|
| -happening here. In this case, some owning component probably has a set of values
|
| -which describe a form.
|
| -
|
| -Stateful vs. Stateless components
|
| ----------------------------------
|
| -
|
| -All components have access to two kinds of state: (1) configuration data
|
| -(constructor arguments) and (2) private data (data they mutate themselves).
|
| -While react components have explicit property bags for these two kinds of state
|
| -(`this.prop` and `this.state`), Effen maps these ideas to the public and private
|
| -fields of the component. Constructor arguments should (by convention) be
|
| -reflected as public fields of the component and state should only be set on
|
| -private (with a leading underbar `_`) fields.
|
| -
|
| -All (non-component) Effen nodes are stateless. Some components will be stateful.
|
| -This state will likely encapsulate transient states of the UI, such as scroll
|
| -position, animation state, uncommitted form values, etc...
|
| -
|
| -A component can become stateful in two ways: (1) by passing `super(stateful:
|
| -true)` to its call to the superclass's constructor, or by calling
|
| -`setState(Function fn)`. The former is a way to have a component start its life
|
| -stateful, and the latter results in the component becoming statefull *as well
|
| -as* scheduling the component to re-build at the end of the current animation
|
| -frame.
|
| -
|
| -What does it mean to be stateful? It means that the diffing mechanism retains
|
| -the specific *instance* of the component as long as the component which builds
|
| -it continues to require its presence. The component which constructed it may
|
| -have provided new configuration in form of different values for the constructor
|
| -parameters, but these values (public fields) will be copied (using reflection)
|
| -onto the retained instance whose privates fields are left unmodified.
|
| -
|
| -Rendering
|
| ----------
|
| -
|
| -At the end of each animation frame, all components (including the root `App`)
|
| -which have `setState` on themselves will be rebuilt and the resulting changes
|
| -will be minimally applied to the DOM. Note that components of lower "order"
|
| -(those near the root of the tree) will build first because their building may
|
| -require rebuilding of higher order (those near the leaves), thus avoiding the
|
| -possibility that a component which is dirty build more than once during a single
|
| -cycle.
|
| -
|
| -Keys
|
| -----
|
| -
|
| -In order to efficiently apply changes to the DOM and to ensure that stateful
|
| -components are correctly identified, Effen requires that `no two nodes (except
|
| -Text) or components of the same type may exist as children of another element
|
| -without being distinguished by unique keys`. [`Text` is excused from this rule].
|
| -In many cases, nodes don't require a key because there is only one type amongst
|
| -its siblings -- but if there is more one, you must assign each a key. This is
|
| -why most nodes will take `({ Object key })` as an optional constructor
|
| -parameter. In development mode (i.e. when sky is built `Debug`) Effen will throw
|
| -an error if you forget to do this.
|
| -
|
| -Event Handling
|
| ---------------
|
| -
|
| -Events logically fire through the Effen node tree. If want to handle an event as
|
| -it bubbles from the target to the root, create an `EventListenerNode`. `EventListenerNode`
|
| -has named (typed) parameters for a small set of events that we've hit so far, as
|
| -well as a 'custom' argument which is a `Map<String, sky.EventListener>`. If
|
| -you'd like to add a type argument for an event, just post a patch.
|
| -
|
| -```dart
|
| -class MyComp extends Component {
|
| - MyComp({
|
| - Object key
|
| - }) : super(key: key);
|
| -
|
| - void _handleTap(sky.GestureEvent e) {
|
| - // do stuff
|
| - }
|
| -
|
| - void _customEventCallback(sky.Event e) {
|
| - // do other stuff
|
| - }
|
| -
|
| - UINode build() {
|
| - new EventListenerNode(
|
| - new Container(
|
| - children: // ...
|
| - ),
|
| - onGestureTap: _handleTap,
|
| - custom: {
|
| - 'myCustomEvent': _customEventCallback
|
| - }
|
| - );
|
| - }
|
| -
|
| - _handleScroll(sky.Event e) {
|
| - setState(() {
|
| - // update the scroll position
|
| - });
|
| - }
|
| -}
|
| -```
|
| -
|
| -Styling
|
| --------
|
| -
|
| -Styling is the part of Effen which is least designed and is likely to change.
|
| -There are three ways to specify styles:
|
| -
|
| - * `Style` objects which are interned and can be applied to WrapperNodes via the
|
| - ``style` constructor parameter. Use `Style` objects for styles which are
|
| - `*not* animated.
|
| -
|
| - * An `inlineStyle` string which can be applied to Elements via the
|
| - `inlineStyle` constructor parameter. Use `inlineStyle` for styles which
|
| - *are* animated.
|
| -
|
| -If you need to apply a Style to a Component or UINode which you didn't construct
|
| -(i.e. one that was handed into your constructor), you can wrap it in a
|
| -`StyleNode` which also takes a `Style` constructor in it's `style` constructor
|
| -parameter.
|
| -
|
| -Animation
|
| ----------
|
| -
|
| -Animation is still an area of exploration. Have a look at
|
| -[AnimatedComponent](components/animated_component.dart) and
|
| -[Drawer](components/drawer.dart) for an example of this this currently works.
|
| -
|
| -Performance
|
| ------------
|
| -
|
| -It is a design goal that it should be *possible* to arrange that all "build"
|
| -cycles which happen during animations can complete in less than one milliesecond
|
| -on a Nexus 5.
|
| +Text input widgets are layered on this mechanism and can be found in
|
| +the [editing2/](editing2/) directory.
|
|
|