Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 Sky Widgets | |
| 2 =========== | |
| 3 | |
| 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 | |
| 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 | |
| 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 | |
| 10 in the underlying render tree to transition from one state to the next. | |
| 11 | |
| 12 Hello World | |
| 13 ----------- | |
| 14 | |
| 15 To build an application, create a subclass of App and instantiate it: | |
|
Hixie
2015/06/17 16:56:43
This example has the unfortunate problem that it'l
| |
| 16 | |
| 17 ```dart | |
| 18 import 'package:sky/widgets/basic.dart'; | |
| 19 | |
| 20 class HelloWorldApp extends App { | |
| 21 Widget build() { | |
| 22 return new Text('Hello, world!'); | |
| 23 } | |
| 24 } | |
| 25 | |
| 26 void main() { | |
| 27 new HelloWorldApp(); | |
| 28 } | |
| 29 ``` | |
| 30 | |
| 31 An app is comprised of (and is, itself, a) widgets. The most commonly authored | |
| 32 widgets are, like `App`, subclasses of `Component`. A component's main job is | |
| 33 to implement `Widget build()` by returning newly-created instances of other | |
| 34 widgets. If a component builds other components, the framework will build those | |
| 35 components in turn until the process bottoms out in a collection of basic | |
| 36 widgets, such as those in `sky/widgets/basic.dart`. In the case of | |
| 37 `HelloWorldApp`, the `build` function simply returns a new `Text` node, which is | |
| 38 a basic widget representing a string of text. | |
| 39 | |
| 40 Basic Widgets | |
| 41 ------------- | |
| 42 | |
| 43 Sky comes with a suite of powerful basic widgets, of which the following are | |
| 44 very commonly used: | |
| 45 | |
| 46 * `Text`. The `Text` widget lets you create a run of styled text within your | |
| 47 application. | |
| 48 | |
| 49 * `Flex`. The `Flex` widget lets you create flexible layouts in both the | |
| 50 horizontal and vertical direction. Its design is based on the web's flexbox | |
| 51 layout model. You can also use the simpler `Block` widget to create vertical | |
| 52 layouts of inflexible items. | |
| 53 | |
| 54 * `Container`. The `Container` widget lets you create rectangular visual | |
| 55 element. A container can be decorated with a `BoxDecoration`, such as a | |
| 56 background, a border, or a shadow. A `Container` can also have margins, | |
| 57 padding, and constraints applied to its size. In addition, a `Container` can | |
| 58 be transformed in three dimensional space using a matrix. | |
| 59 | |
| 60 * `Image`. The `Image` widget lets you display an image, referenced using a | |
| 61 URL. The underlying image is cached, which means if several `Image` widgets | |
| 62 refer to the same URL, they'll share the underlying image resource. | |
| 63 | |
| 64 Below is a simple toolbar example that shows how to combine these widgets: | |
| 65 | |
| 66 ```dart | |
| 67 import 'package:sky/widgets/basic.dart'; | |
| 68 | |
| 69 class MyToolBar extends Component { | |
| 70 Widget build() { | |
| 71 return new Container( | |
| 72 child: new Flex([ | |
|
Hixie
2015/06/17 16:56:43
put the child last in the list of arguments. In fa
| |
| 73 new Image(src: 'menu.png', size: const Size(25.0, 25.0)), | |
| 74 new Flexible(child: new Text('My awesome toolbar')), | |
| 75 new Image(src: 'search.png', size: const Size(25.0, 25.0)), | |
| 76 ]), | |
| 77 height: 56.0, | |
| 78 padding: new EdgeDims.symmetric(horizontal: 8.0), | |
| 79 decoration: const BoxDecoration( | |
| 80 backgroundColor: const Color(0xFF00FFFF), | |
| 81 ) | |
| 82 ); | |
| 83 } | |
| 84 } | |
| 85 ``` | |
| 86 | |
| 87 The `MyToolBar` component creates a cyan `Container` with a height of 56 | |
| 88 device-independent pixels with an internal padding of 8 pixels, both on the | |
| 89 left and the right. Inside the container, `MyToolBar` uses a `Flex` layout | |
| 90 in the (default) horizontal direction. The middle child, the `Text` widget, is | |
| 91 marked as `Flexible`, which means it expands to fill any remaining available | |
| 92 space that hasn't been consumed by the inflexible children. You can have | |
| 93 multiple `Flexible` children and determine the ratio in which they consume the | |
| 94 available space using the `flex` argument to `Flexible`. | |
| 95 | |
| 96 To use this component, we simply create an instance of `MyToolBar` in a `build` | |
| 97 function: | |
| 98 | |
| 99 ```dart | |
| 100 import 'package:sky/widgets/basic.dart'; | |
| 101 | |
| 102 import `my_tool_bar.dart'; | |
| 103 | |
| 104 class DemoApp extends App { | |
| 105 Widget build() { | |
| 106 return new Center(child: new MyToolBar()); | |
| 107 } | |
| 108 } | |
| 109 | |
| 110 void main() { | |
| 111 new DemoApp(); | |
| 112 } | |
| 113 ``` | |
| 114 | |
| 115 Here, we've used the `Center` widget to center the toolbar within the view, both | |
| 116 vertically and horizontally. Because the toolbar uses a flexible layout | |
| 117 internally, it expands to fill all the available horizontal space. | |
|
Hixie
2015/06/17 16:56:43
"...so if we didn't do this, it would fill the ent
| |
| 118 | |
| 119 Listening to Events | |
| 120 ------------------- | |
| 121 | |
| 122 In addition to being stunningly beautiful, most applications react to user | |
| 123 input. The first step in building an interactive application is to listen for | |
| 124 input events. Let's see how that works by creating a simple button: | |
| 125 | |
| 126 ```dart | |
| 127 import 'package:sky/widgets/basic.dart'; | |
| 128 | |
| 129 final BoxDecoration _decoration = new BoxDecoration( | |
| 130 borderRadius: 5.0, | |
| 131 gradient: new LinearGradient( | |
| 132 endPoints: [ Point.origin, new Point(0.0, 36.0) ], | |
| 133 colors: [ const Color(0xFFEEEEEE), const Color(0xFFCCCCCC) ] | |
| 134 ) | |
| 135 ); | |
| 136 | |
| 137 class MyButton extends Component { | |
| 138 Widget build() { | |
| 139 return new EventListener( | |
| 140 child: new Container( | |
| 141 child: new Center( | |
| 142 child: new Text('Engage') | |
| 143 ), | |
| 144 height: 36.0, | |
| 145 padding: new EdgeDims.all(8.0), | |
| 146 margin: new EdgeDims.symmetric(horizontal: 8.0), | |
| 147 decoration: _decoration | |
| 148 ), | |
| 149 onGestureTap: (event) { | |
| 150 print('MyButton was tapped!'); | |
| 151 } | |
| 152 ); | |
| 153 } | |
| 154 } | |
| 155 ``` | |
| 156 | |
| 157 The `EventListener` widget doesn't have an visual representation but instead | |
| 158 listens for events bubbling through the application. When a tap gesture bubbles | |
| 159 out from the `Container`, the `EventListener` will call its `onGestureTap` | |
| 160 callback, in this case printing a message to the console. | |
| 161 | |
| 162 You can use `EventListener` to listen for a variety of input events, including | |
| 163 low-level pointer events and higher-level gesture events, such as taps, scrolls, | |
| 164 and flings. | |
| 165 | |
| 166 Generic Components | |
| 167 ------------------ | |
| 168 | |
| 169 One of the most powerful features of components is the ability to pass around | |
| 170 references to already-built widgets and reuse them in your `build` function. For | |
| 171 example, we wouldn't want to define a new button component every time we wanted | |
| 172 a button with a novel label: | |
| 173 | |
| 174 ```dart | |
| 175 class MyButton extends Component { | |
| 176 MyButton({ this.child, this.onPressed }); | |
| 177 | |
| 178 final Widget child; | |
| 179 final Function onPressed; | |
| 180 | |
| 181 Widget build() { | |
| 182 return new EventListener( | |
| 183 child: new Container( | |
| 184 child: new Center(child: child), | |
| 185 height: 36.0, | |
| 186 padding: new EdgeDims.all(8.0), | |
| 187 margin: new EdgeDims.symmetric(horizontal: 8.0), | |
| 188 decoration: _decoration | |
| 189 ), | |
| 190 onGestureTap: (_) { | |
| 191 if (onPressed != null) | |
| 192 onPressed(); | |
| 193 } | |
| 194 ); | |
| 195 } | |
| 196 } | |
| 197 ``` | |
| 198 | |
| 199 Rather than providing the button's label as a `String`, we've let the code that | |
| 200 uses `MyButton` provide an arbitrary `Widget` to put inside the button. For | |
| 201 example, we can put an elaborate layout involving text and an image inside the | |
| 202 button: | |
| 203 | |
| 204 ```dart | |
| 205 Widget build() { | |
| 206 return new MyButton( | |
| 207 child: new ShrinkWrapWidth( | |
| 208 child: new Flex([ | |
| 209 new Image(src: 'thumbs-up.png', size: const Size(25.0, 25.0)), | |
| 210 new Container( | |
| 211 child: new Text('Thumbs up'), | |
| 212 padding: new EdgeDims.only(left: 10.0) | |
| 213 ) | |
| 214 ]) | |
| 215 ) | |
| 216 ); | |
| 217 } | |
| 218 ``` | |
| 219 | |
| 220 State | |
| 221 ----- | |
| 222 | |
| 223 TODO(abarth) | |
| OLD | NEW |