mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Remove fn.md
The fn1 framework is deprecated. We don't need docs for it anymore. TBR=ianh@google.com Review URL: https://codereview.chromium.org/1189323002.
This commit is contained in:
parent
241cf09f4b
commit
2bd4e5bd81
8
sdk/lib/framework/README.md
Normal file
8
sdk/lib/framework/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
Legacy Framework
|
||||
================
|
||||
|
||||
The code in this directory represents a previous iteration of the Sky framework
|
||||
and will be removed at some point.
|
||||
|
||||
The [current framework](../widgets/README.md) is availabe in
|
||||
[`package:sky/widgets`](../widgets).
|
||||
@ -1,222 +0,0 @@
|
||||
Sky Framework
|
||||
=============
|
||||
|
||||
(This file applies to fn.dart, which we are in the process of porting
|
||||
to a new architecture.)
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
If you just want to dive into code, see the [stocks example](../../../../examples/stocks).
|
||||
|
||||
Hello World
|
||||
-----------
|
||||
|
||||
To build an application, create a subclass of App and instantiate it.
|
||||
|
||||
```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.
|
||||
Loading…
x
Reference in New Issue
Block a user