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

Side by Side Diff: sky/sdk/lib/widgets/README.md

Issue 1185933003: Add sections on State and Keys to the widget's README.md (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 6 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 Sky Widgets 1 Sky Widgets
2 =========== 2 ===========
3 3
4 Sky widgets are built using a functional-reactive framework, which takes 4 Sky widgets are built using a functional-reactive framework, which takes
5 inspiration from [React](http://facebook.github.io/react/). The central idea is 5 inspiration from [React](http://facebook.github.io/react/). The central idea is
6 that you build your UI out of components. Components describe what their view 6 that you build your UI out of components. Components describe what their view
7 should look like given their current configuration and state. When a component's 7 should look like given their current configuration and state. When a component's
8 state changes, the component rebuilds its description, which the framework diffs 8 state changes, the component rebuilds its description, which the framework diffs
9 against the previous description in order to determine the minial changes needed 9 against the previous description in order to determine the minial changes needed
10 in the underlying render tree to transition from one state to the next. 10 in the underlying render tree to transition from one state to the next.
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 ) 214 )
215 ]) 215 ])
216 ) 216 )
217 ); 217 );
218 } 218 }
219 ``` 219 ```
220 220
221 State 221 State
222 ----- 222 -----
223 223
224 TODO(abarth) 224 By default, components are stateless. Components usually receive
225 arguments from their parent component in their constructor, which they typically
226 store in `final` member variables. When a component is asked to `build`, it uses
227 these stored values to derive new arguments for the subcomponents it creates.
228 For example, the generic version of `MyButton` above follows this pattern. In
229 this way, state naturally flows "down" the component hierachy.
230
231 Some components, however, have mutable state that represents the transient state
232 of that part of the user interface. For example, consider a dialog widget with
233 a checkbox. While the dialog is open, the user might check and uncheck the
234 checkbox several times before closing the dialog and committing the final value
235 of the checkbox to the underlying application data model.
236
237 ```dart
238 class MyCheckbox extends Component {
239 MyCheckbox({ this.value, this.onChanged });
240
241 final bool value;
242 final Function onChanged;
243
244 Widget build() {
245 Color color = value ? const Color(0xFF00FF00) : const Color(0xFF0000FF);
246 return new Listener(
247 onGestureTap: (_) => onChanged(!value),
248 child: new Container(
249 height: 25.0,
250 width: 25.0,
251 decoration: new BoxDecoration(backgroundColor: color)
252 )
253 );
254 }
255 }
256
257 class MyDialog extends Component {
258 MyDialog({ this.onDismissed }) : super(stateful: true);
259
260 Function onDismissed;
261 bool _checkboxValue = false;
262
263 void _handleCheckboxValueChanged(bool value) {
264 setState(() {
265 _checkboxValue = value;
266 });
267 }
268
269 void syncFields(MyDialog source) {
270 onDismissed = source.onDismissed;
271 }
272
273 Widget build() {
274 return new Flex([
275 new MyCheckbox(
276 value: _checkboxValue,
277 onChanged: _handleCheckboxValueChanged
278 ),
279 new MyButton(
280 onPressed: () => onDismissed(_checkboxValue),
281 child: new Text("Save")
282 ),
283 ],
284 justifyContent: FlexJustifyContent.center);
285 }
286 }
287 ```
288
289 The `MyCheckbox` component follows the pattern for stateless components. It
290 stores the values it receives in its constructor in `final` member variables,
291 which it then uses during its `build` function. Notice that when the user taps
292 on the checkbox, the checkbox itself doesn't use `value`. Instead, the checkbox
293 calls a function it received from its parent component. This pattern lets you
294 store state higher in the component hierarchy, which causes the state to persist
295 for longer periods of time. In the extreme, the state stored on the `App`
296 component persists for the lifetime of the application.
297
298 The `MyDialog` component is more complicated because it is a stateful component.
299 Let's walk through the differences in `MyDialog` caused by its being stateful:
300
301 * `MyDialog` passes `stateful: true` to the `Component` constructor, marking
302 the component stateful.
303
304 * `MyDialog` has non-`final` member variables. Over the lifetime of the dialog,
305 we'll need to modify the values of these member variables, which means we
306 cannot mark them `final`.
307
308 * `MyDialog` has private member variables. By convention, components store
309 values they receive from their parent in public member variables and store
310 their own internal, transient state in private member variables. There's no
311 requirement to follow this convention, but we've found that it helps keep us
312 organized.
313
314 * Whenever `MyDialog` modifies its transient state, the dialog does so inside
315 a `setState` callback. Using `setState` is important because it marks the
316 component as dirty and schedules it to be rebuilt. If a component modifies
317 its transient state outside of a `setState` callback, the framework won't
318 know that the component has changed state and might not call the component's
319 `build` function, which means the user interface might not update to reflect
320 the changed state.
321
322 * `MyDialog` implements the `syncFields` member function. To understand
323 `syncFields`, we'll need to dive a bit deeper into how the `build` function
324 is used by the framework.
325
326 A component's `build` function returns a tree of widgets that represent a
327 "virtual" description of its appearance. The first time the framework calls
328 `build`, the framework walks this description and creates a "physical" tree
329 of `RenderObjects` that matches the description. When the framework calls
330 `build` again, the component still returns a fresh description of its
331 appearence, but this time the framework compares the new description with the
332 previous description and makes the minimal modifications to the underlying
333 `RenderObjects` to make them match the new description.
334
335 In this process, old stateless components are discarded and the new stateless
336 components created by the parent component are retained in the widget
337 hierchy. Old _stateful_ components, however, cannot simply be discarded
338 because they contain state that needs to be preserved. Instead, the old
339 stateful components are retained in the widget hiearchy and asked to
340 `syncFields` with the new instance of the component created by the parent in
341 its `build` function.
342
343 Without `syncFields`, the new values the parent component passed to the
344 `MyDialog` constructor in the parent's `build` function would be lost because
345 they would be stored only as member variables on the new instance of the
346 component, which is not retained in the component hiearchy. Typically, the
347 `syncFields` function in a component will copy the public member
348 variables from the `source` instance of the component to the retained
349 instance of the component because, by convention, these public member
350 variables hold the values passed in by the parent component.
351
352 Finally, when the user taps on the "Save" button, `MyDialog` follows the same
353 pattern as `MyCheckbox` and calls a function passed in by its parent component
354 to return the final value of the checkbox up the hierarchy.
355
356 Keys
357 ----
358
359 If a component requires fine-grained control over which widgets sync with each
360 other, the component can assign keys to the widgets it builds. Without keys, the
361 framework matches widgets in the current and previous build according to their
362 `runtimeType` and the order in which they appear. With keys, the framework
363 requires that the two widgets have the same `key` as well as the same
364 `runtimeType`.
365
366 Keys are most useful in components that build many instances of the same type of
367 widget. For example, consider an infinite list component that builds just enough
368 copies of a particular widget to fill its visible region:
369
370 * Without keys, the first entry in the current build would always sync with the
371 first entry in the previous build, even if, semantically, the first entry in
372 the list just scrolled off screen and is no longer visible in the viewport.
373
374 * By assigning each entry in the list a "semantic" key, the infinite list can
375 be more efficient because the framework will sync entries with matching
376 semantic keys and therefore similiar (or identical) visual appearances.
377 Moreover, syncing the entries semantically means that state retained in
378 stateful subcomponents will remain attached to the same semantic entry rather
379 than the entry in the same numerical position in the viewport.
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698