Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(146)

Side by Side Diff: sky/framework/README.md

Issue 1132063007: Rationalize Dart mojo and sky package structure (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sky/engine/bindings/BUILD.gn ('k') | sky/framework/animation/animated_value.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 Sky Framework
2 =============
3
4 Effen is a functional-reactive framework for Sky which takes inspiration from
5 [React](http://facebook.github.io/react/). Effen is comprised of three main
6 parts: a virtual-dom and diffing engine, a component mechanism and a very early
7 set of widgets for use in creating applications.
8
9 The central idea is that you build your UI out of components. Components
10 describe what their view should look like given their current configuration &
11 state. The diffing engine ensures that the DOM looks how the component describes
12 by applying minimal diffs to transition it from one state to the next.
13
14 If you just want to dive into code, see the [stocks example](../../../../example s/stocks).
15
16 Hello World
17 -----------
18
19 To build an application, create a subclass of App and instantiate it.
20
21 ```HTML
22 <script>
23 import 'hello_world.dart';
24
25 main() {
26 new HelloWorldApp();
27 }
28 </script>
29 ```
30
31 ```dart
32 // In hello_world.dart
33 import 'package:sky/framework/fn.dart';
34
35 class HelloWorldApp extends App {
36 UINode build() {
37 return new Text('Hello, world!');
38 }
39 }
40 ```
41
42 An app is comprised of (and is, itself, a) components. A component's main job is
43 to implement `UINode build()`. The idea here is that the `build` method describe s
44 the DOM of a component at any given point during its lifetime. In this case, our
45 `HelloWorldApp`'s `build` method just returns a `Text` node which displays the
46 obligatory line of text.
47
48 Nodes
49 -----
50
51 A component's `build` method must return a single `UINode` which *may* have
52 children (and so on, forming a *subtree*). Effen comes with a few built-in nodes
53 which mirror the built-in nodes/elements of sky: `Text`, `Anchor` (`<a />`,
54 `Image` (`<img />`) and `Container` (`<div />`). `build` can return a tree of
55 Nodes comprised of any of these nodes and plus any other imported object which
56 extends `Component`.
57
58 How to structure you app
59 ------------------------
60
61 If you're familiar with React, the basic idea is the same: Application data
62 flows *down* from components which have data to components & nodes which they
63 construct via construction parameters. Generally speaking, View-Model data (data
64 which is derived from *model* data, but exists only because the view needs it),
65 is computed during the course of `build` and is short-lived, being handed into
66 nodes & components as configuration data.
67
68 What does "data flowing down the tree" mean?
69 --------------------------------------------
70
71 Consider the case of a checkbox. (i.e. `widgets/checkbox.dart`). The `Checkbox`
72 constructor looks like this:
73
74 ```dart
75 ValueChanged onChanged;
76 bool checked;
77
78 Checkbox({ Object key, this.onChanged, this.checked }) : super(key: key);
79 ```
80
81 What this means is that the `Checkbox` component *never* "owns" the state of
82 the checkbox. It's current state is handed into the `checked` parameter, and
83 when a click occurs, the checkbox invokes its `onChanged` callback with the
84 value it thinks it should be changed to -- but it never directly changes the
85 value itself. This is a bit odd at first look, but if you think about it: a
86 control isn't very useful unless it gets its value out to someone and if you
87 think about databinding, the same thing happens: databinding basically tells a
88 control to *treat some remote variable as its storage*. That's all that is
89 happening here. In this case, some owning component probably has a set of values
90 which describe a form.
91
92 Stateful vs. Stateless components
93 ---------------------------------
94
95 All components have access to two kinds of state: (1) configuration data
96 (constructor arguments) and (2) private data (data they mutate themselves).
97 While react components have explicit property bags for these two kinds of state
98 (`this.prop` and `this.state`), Effen maps these ideas to the public and private
99 fields of the component. Constructor arguments should (by convention) be
100 reflected as public fields of the component and state should only be set on
101 private (with a leading underbar `_`) fields.
102
103 All (non-component) Effen nodes are stateless. Some components will be stateful.
104 This state will likely encapsulate transient states of the UI, such as scroll
105 position, animation state, uncommitted form values, etc...
106
107 A component can become stateful in two ways: (1) by passing `super(stateful:
108 true)` to its call to the superclass's constructor, or by calling
109 `setState(Function fn)`. The former is a way to have a component start its life
110 stateful, and the latter results in the component becoming statefull *as well
111 as* scheduling the component to re-build at the end of the current animation
112 frame.
113
114 What does it mean to be stateful? It means that the diffing mechanism retains
115 the specific *instance* of the component as long as the component which builds
116 it continues to require its presence. The component which constructed it may
117 have provided new configuration in form of different values for the constructor
118 parameters, but these values (public fields) will be copied (using reflection)
119 onto the retained instance whose privates fields are left unmodified.
120
121 Rendering
122 ---------
123
124 At the end of each animation frame, all components (including the root `App`)
125 which have `setState` on themselves will be rebuilt and the resulting changes
126 will be minimally applied to the DOM. Note that components of lower "order"
127 (those near the root of the tree) will build first because their building may
128 require rebuilding of higher order (those near the leaves), thus avoiding the
129 possibility that a component which is dirty build more than once during a single
130 cycle.
131
132 Keys
133 ----
134
135 In order to efficiently apply changes to the DOM and to ensure that stateful
136 components are correctly identified, Effen requires that `no two nodes (except
137 Text) or components of the same type may exist as children of another element
138 without being distinguished by unique keys`. [`Text` is excused from this rule].
139 In many cases, nodes don't require a key because there is only one type amongst
140 its siblings -- but if there is more one, you must assign each a key. This is
141 why most nodes will take `({ Object key })` as an optional constructor
142 parameter. In development mode (i.e. when sky is built `Debug`) Effen will throw
143 an error if you forget to do this.
144
145 Event Handling
146 --------------
147
148 Events logically fire through the Effen node tree. If want to handle an event as
149 it bubbles from the target to the root, create an `EventListenerNode`. `EventLis tenerNode`
150 has named (typed) parameters for a small set of events that we've hit so far, as
151 well as a 'custom' argument which is a `Map<String, sky.EventListener>`. If
152 you'd like to add a type argument for an event, just post a patch.
153
154 ```dart
155 class MyComp extends Component {
156 MyComp({
157 Object key
158 }) : super(key: key);
159
160 void _handleTap(sky.GestureEvent e) {
161 // do stuff
162 }
163
164 void _customEventCallback(sky.Event e) {
165 // do other stuff
166 }
167
168 UINode build() {
169 new EventListenerNode(
170 new Container(
171 children: // ...
172 ),
173 onGestureTap: _handleTap,
174 custom: {
175 'myCustomEvent': _customEventCallback
176 }
177 );
178 }
179
180 _handleScroll(sky.Event e) {
181 setState(() {
182 // update the scroll position
183 });
184 }
185 }
186 ```
187
188 Styling
189 -------
190
191 Styling is the part of Effen which is least designed and is likely to change.
192 There are three ways to specify styles:
193
194 * `Style` objects which are interned and can be applied to WrapperNodes via th e
195 ``style` constructor parameter. Use `Style` objects for styles which are
196 `*not* animated.
197
198 * An `inlineStyle` string which can be applied to Elements via the
199 `inlineStyle` constructor parameter. Use `inlineStyle` for styles which
200 *are* animated.
201
202 If you need to apply a Style to a Component or UINode which you didn't construct
203 (i.e. one that was handed into your constructor), you can wrap it in a
204 `StyleNode` which also takes a `Style` constructor in it's `style` constructor
205 parameter.
206
207 Animation
208 ---------
209
210 Animation is still an area of exploration. Have a look at
211 [AnimatedComponent](components/animated_component.dart) and
212 [Drawer](components/drawer.dart) for an example of this this currently works.
213
214 Performance
215 -----------
216
217 It is a design goal that it should be *possible* to arrange that all "build"
218 cycles which happen during animations can complete in less than one milliesecond
219 on a Nexus 5.
OLDNEW
« no previous file with comments | « sky/engine/bindings/BUILD.gn ('k') | sky/framework/animation/animated_value.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698