Commit 576b3aa2 authored by Adam Barth's avatar Adam Barth

Merge pull request #1864 from abarth/examples_comments

Adds documentation to the layering examples
parents 981a7f37 025c43de
...@@ -2,19 +2,32 @@ ...@@ -2,19 +2,32 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// This example shows how to use the ui.Canvas interface to draw various shapes
// with gradients and transforms.
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:typed_data'; import 'dart:typed_data';
ui.Picture paint(ui.Rect paintBounds) { ui.Picture paint(ui.Rect paintBounds) {
// First we create a PictureRecorder to record the commands we're going to
// feed in the canvas. The PictureRecorder will eventually produce a Picture,
// which is an immutable record of those commands.
ui.PictureRecorder recorder = new ui.PictureRecorder(); ui.PictureRecorder recorder = new ui.PictureRecorder();
// Next, we create a canvas from the recorder. The canvas is an interface
// which can receive drawing commands. The canvas interface is modeled after
// the SkCanvas interface from Skia. The paintBounds establishes a "cull rect"
// for the canvas, which lets the implementation discard any commands that
// are entirely outside this rectangle.
ui.Canvas canvas = new ui.Canvas(recorder, paintBounds); ui.Canvas canvas = new ui.Canvas(recorder, paintBounds);
ui.Size size = paintBounds.size;
ui.Paint paint = new ui.Paint(); ui.Paint paint = new ui.Paint();
canvas.drawPaint(new ui.Paint()..color = const ui.Color(0xFFFFFFFF));
ui.Size size = paintBounds.size;
ui.Point mid = size.center(ui.Point.origin); ui.Point mid = size.center(ui.Point.origin);
double radius = size.shortestSide / 2.0; double radius = size.shortestSide / 2.0;
canvas.drawPaint(new ui.Paint()..color = const ui.Color(0xFFFFFFFF));
canvas.save(); canvas.save();
canvas.translate(-mid.x/2.0, ui.window.size.height*2.0); canvas.translate(-mid.x/2.0, ui.window.size.height*2.0);
...@@ -46,6 +59,11 @@ ui.Picture paint(ui.Rect paintBounds) { ...@@ -46,6 +59,11 @@ ui.Picture paint(ui.Rect paintBounds) {
canvas.restore(); canvas.restore();
canvas.translate(0.0, 50.0); canvas.translate(0.0, 50.0);
// A DrawLooper is a powerful painting primitive that lets you apply several
// blending and filtering passes over a sequence of commands "in a loop". For
// example, the looper below draws the circle tree times, once with a blur,
// then with a gradient blend, and finally with just an offset.
ui.LayerDrawLooperBuilder builder = new ui.LayerDrawLooperBuilder() ui.LayerDrawLooperBuilder builder = new ui.LayerDrawLooperBuilder()
..addLayerOnTop( ..addLayerOnTop(
new ui.DrawLooperLayerInfo() new ui.DrawLooperLayerInfo()
...@@ -90,6 +108,10 @@ ui.Picture paint(ui.Rect paintBounds) { ...@@ -90,6 +108,10 @@ ui.Picture paint(ui.Rect paintBounds) {
paint.drawLooper = builder.build(); paint.drawLooper = builder.build();
canvas.drawCircle(ui.Point.origin, radius, paint); canvas.drawCircle(ui.Point.origin, radius, paint);
// When we're done issuing painting commands, we end the recording an receive
// a Picture, which is an immutable record of the commands we've issued. You
// can draw a Picture into another canvas or include it as part of a
// composited scene.
return recorder.endRecording(); return recorder.endRecording();
} }
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// This example shows how to put some pixels on the screen using the raw
// interface to the engine.
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'dart:typed_data'; import 'dart:typed_data';
...@@ -12,18 +15,38 @@ import 'package:sky_services/pointer/pointer.mojom.dart'; ...@@ -12,18 +15,38 @@ import 'package:sky_services/pointer/pointer.mojom.dart';
ui.Color color; ui.Color color;
ui.Picture paint(ui.Rect paintBounds) { ui.Picture paint(ui.Rect paintBounds) {
// First we create a PictureRecorder to record the commands we're going to
// feed in the canvas. The PictureRecorder will eventually produce a Picture,
// which is an immutable record of those commands.
ui.PictureRecorder recorder = new ui.PictureRecorder(); ui.PictureRecorder recorder = new ui.PictureRecorder();
// Next, we create a canvas from the recorder. The canvas is an interface
// which can receive drawing commands. The canvas interface is modeled after
// the SkCanvas interface from Skia. The paintBounds establishes a "cull rect"
// for the canvas, which lets the implementation discard any commands that
// are entirely outside this rectangle.
ui.Canvas canvas = new ui.Canvas(recorder, paintBounds); ui.Canvas canvas = new ui.Canvas(recorder, paintBounds);
ui.Size size = paintBounds.size;
double radius = size.shortestSide * 0.45; // The commands draw a circle in the center of the screen.
ui.Paint paint = new ui.Paint()..color = color; ui.Size size = paintBounds.size;
canvas.drawCircle(size.center(ui.Point.origin), radius, paint); canvas.drawCircle(
size.center(ui.Point.origin),
size.shortestSide * 0.45,
new ui.Paint()..color = color
);
// When we're done issuing painting commands, we end the recording an receive
// a Picture, which is an immutable record of the commands we've issued. You
// can draw a Picture into another canvas or include it as part of a
// composited scene.
return recorder.endRecording(); return recorder.endRecording();
} }
ui.Scene composite(ui.Picture picture, ui.Rect paintBounds) { ui.Scene composite(ui.Picture picture, ui.Rect paintBounds) {
// The device pixel ratio gives an approximate ratio of the size of pixels on
// the device's screen to "normal" sized pixels. We commonly work in logical
// pixels, which are then scalled by the device pixel ratio before being drawn
// on the screen.
final double devicePixelRatio = ui.window.devicePixelRatio; final double devicePixelRatio = ui.window.devicePixelRatio;
ui.Rect sceneBounds = new ui.Rect.fromLTWH( ui.Rect sceneBounds = new ui.Rect.fromLTWH(
0.0, 0.0,
...@@ -31,49 +54,83 @@ ui.Scene composite(ui.Picture picture, ui.Rect paintBounds) { ...@@ -31,49 +54,83 @@ ui.Scene composite(ui.Picture picture, ui.Rect paintBounds) {
ui.window.size.width * devicePixelRatio, ui.window.size.width * devicePixelRatio,
ui.window.size.height * devicePixelRatio ui.window.size.height * devicePixelRatio
); );
// This transform scales the x and y coordinates by the devicePixelRatio.
Float64List deviceTransform = new Float64List(16) Float64List deviceTransform = new Float64List(16)
..[0] = devicePixelRatio ..[0] = devicePixelRatio
..[5] = devicePixelRatio ..[5] = devicePixelRatio
..[10] = 1.0 ..[10] = 1.0
..[15] = 1.0; ..[15] = 1.0;
// We build a very simple scene graph with two nodes. The root node is a
// transform that scale its children by the device pixel ratio. This transform
// lets us paint in "logical" pixels which are converted to device pixels by
// this scaling operation.
ui.SceneBuilder sceneBuilder = new ui.SceneBuilder(sceneBounds) ui.SceneBuilder sceneBuilder = new ui.SceneBuilder(sceneBounds)
..pushTransform(deviceTransform) ..pushTransform(deviceTransform)
..addPicture(ui.Offset.zero, picture) ..addPicture(ui.Offset.zero, picture)
..pop(); ..pop();
// When we're done recording the scene, we call build() to obtain an immutable
// record of the scene we've recorded.
return sceneBuilder.build(); return sceneBuilder.build();
} }
void beginFrame(Duration timeStamp) { void beginFrame(Duration timeStamp) {
ui.Rect paintBounds = ui.Point.origin & ui.window.size; ui.Rect paintBounds = ui.Point.origin & ui.window.size;
// First, record a picture with our painting commands.
ui.Picture picture = paint(paintBounds); ui.Picture picture = paint(paintBounds);
// Second, include that picture in a scene graph.
ui.Scene scene = composite(picture, paintBounds); ui.Scene scene = composite(picture, paintBounds);
// Third, instruct the engine to render that scene graph.
ui.window.render(scene); ui.window.render(scene);
} }
void handlePopRoute() { // Pointer input arrives as an array of bytes. The format for the data is
print('Pressed back button.'); // defined by pointer.mojom, which generates serializes and parsers for a
} // number of languages, including Dart, C++, Java, and Go.
void handlePointerPacket(ByteData serializedPacket) { void handlePointerPacket(ByteData serializedPacket) {
// We wrap the byte data up into a Mojo Message object, which we then
// deserialize according to the mojom definition.
bindings.Message message = new bindings.Message(serializedPacket, <core.MojoHandle>[], serializedPacket.lengthInBytes, 0); bindings.Message message = new bindings.Message(serializedPacket, <core.MojoHandle>[], serializedPacket.lengthInBytes, 0);
PointerPacket packet = PointerPacket.deserialize(message); PointerPacket packet = PointerPacket.deserialize(message);
// The deserialized pointer packet contains a number of pointer movements,
// which we iterate through and process.
for (Pointer pointer in packet.pointers) { for (Pointer pointer in packet.pointers) {
if (pointer.type == PointerType.down) { if (pointer.type == PointerType.down) {
color = new ui.Color.fromARGB(255, 0, 0, 255); // If the pointer went down, we change the color of the circle to blue.
color = const ui.Color(0xFF0000FF);
// Rather than calling paint() synchronously, we ask the engine to
// schedule a frame. The engine will call onBeginFrame when it is actually
// time to produce the frame.
ui.window.scheduleFrame(); ui.window.scheduleFrame();
} else if (pointer.type == PointerType.up) { } else if (pointer.type == PointerType.up) {
color = new ui.Color.fromARGB(255, 0, 255, 0); // Similarly, if the pointer went up, we change the color of the circle to
// green and schedule a frame. It's harmless to call scheduleFrame many
// times because the engine will ignore redundant requests up until the
// point where the engine calls onBeginFrame, which signals the boundary
// between one frame and another.
color = const ui.Color(0xFF00FF00);
ui.window.scheduleFrame(); ui.window.scheduleFrame();
} }
} }
} }
// This function is the primary entry point to your application. The engine
// calls main() as soon as it has loaded your code.
void main() { void main() {
// Print statements go either go to stdout or to the system log, as
// appropriate for the operating system.
print('Hello, world'); print('Hello, world');
color = new ui.Color.fromARGB(255, 0, 255, 0); color = const ui.Color(0xFF00FF00);
// The engine calls onBeginFrame whenever it wants us to produce a frame.
ui.window.onBeginFrame = beginFrame; ui.window.onBeginFrame = beginFrame;
ui.window.onPopRoute = handlePopRoute; // The engine calls onPointerPacket whenever it had updated information about
// the pointers directed at our app.
ui.window.onPointerPacket = handlePointerPacket; ui.window.onPointerPacket = handlePointerPacket;
// Here we kick off the whole process by asking the engine to schedule a new
// frame. The engine will eventually call onBeginFrame when it is time for us
// to actually produce the frame.
ui.window.scheduleFrame(); ui.window.scheduleFrame();
} }
...@@ -2,30 +2,39 @@ ...@@ -2,30 +2,39 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:developer'; // This example shows how to perform a simple animation using the raw interface
// to the engine.
import 'dart:math' as math; import 'dart:math' as math;
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:ui' as ui; import 'dart:ui' as ui;
Duration timeBase = null;
void beginFrame(Duration timeStamp) { void beginFrame(Duration timeStamp) {
Timeline.timeSync('beginFrame', () { // The timeStamp argument to beginFrame indicates the timing information we
if (timeBase == null) // should use to clock our animations. It's important to use timeStamp rather
timeBase = timeStamp; // than reading the system time because we want all the parts of the system to
double delta = (timeStamp - timeBase).inMicroseconds / Duration.MICROSECONDS_PER_MILLISECOND; // coordinate the timings of their animations. If each component read the
// system clock independently, the animations that we processed later would be
// slightly ahead of the animations we processed earlier.
// PAINT
// paint
ui.Rect paintBounds = ui.Point.origin & ui.window.size; ui.Rect paintBounds = ui.Point.origin & ui.window.size;
ui.PictureRecorder recorder = new ui.PictureRecorder(); ui.PictureRecorder recorder = new ui.PictureRecorder();
ui.Canvas canvas = new ui.Canvas(recorder, paintBounds); ui.Canvas canvas = new ui.Canvas(recorder, paintBounds);
canvas.translate(paintBounds.width / 2.0, paintBounds.height / 2.0); canvas.translate(paintBounds.width / 2.0, paintBounds.height / 2.0);
canvas.rotate(math.PI * delta / 1800);
// Here we determine the rotation according to the timeStamp given to us by
// the engine.
double t = timeStamp.inMicroseconds / Duration.MICROSECONDS_PER_MILLISECOND / 1800.0;
canvas.rotate(math.PI * (t % 1.0));
canvas.drawRect(new ui.Rect.fromLTRB(-100.0, -100.0, 100.0, 100.0), canvas.drawRect(new ui.Rect.fromLTRB(-100.0, -100.0, 100.0, 100.0),
new ui.Paint()..color = const ui.Color.fromARGB(255, 0, 255, 0)); new ui.Paint()..color = const ui.Color.fromARGB(255, 0, 255, 0));
ui.Picture picture = recorder.endRecording(); ui.Picture picture = recorder.endRecording();
// composite // COMPOSITE
final double devicePixelRatio = ui.window.devicePixelRatio; final double devicePixelRatio = ui.window.devicePixelRatio;
ui.Rect sceneBounds = new ui.Rect.fromLTWH( ui.Rect sceneBounds = new ui.Rect.fromLTWH(
0.0, 0.0,
...@@ -43,8 +52,10 @@ void beginFrame(Duration timeStamp) { ...@@ -43,8 +52,10 @@ void beginFrame(Duration timeStamp) {
..addPicture(ui.Offset.zero, picture) ..addPicture(ui.Offset.zero, picture)
..pop(); ..pop();
ui.window.render(sceneBuilder.build()); ui.window.render(sceneBuilder.build());
});
// After rendering the current frame of the animation, we ask the engine to
// schedule another frame. The engine will call beginFrame again when its time
// to produce the next frame.
ui.window.scheduleFrame(); ui.window.scheduleFrame();
} }
......
...@@ -2,9 +2,13 @@ ...@@ -2,9 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// This example shows how to draw some bi-directional text using the raw
// interface to the engine.
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'dart:typed_data'; import 'dart:typed_data';
// A paragraph represents a rectangular region that contains some text.
ui.Paragraph paragraph; ui.Paragraph paragraph;
ui.Picture paint(ui.Rect paintBounds) { ui.Picture paint(ui.Rect paintBounds) {
...@@ -15,8 +19,9 @@ ui.Picture paint(ui.Rect paintBounds) { ...@@ -15,8 +19,9 @@ ui.Picture paint(ui.Rect paintBounds) {
canvas.drawRect(new ui.Rect.fromLTRB(-100.0, -100.0, 100.0, 100.0), canvas.drawRect(new ui.Rect.fromLTRB(-100.0, -100.0, 100.0, 100.0),
new ui.Paint()..color = const ui.Color.fromARGB(255, 0, 255, 0)); new ui.Paint()..color = const ui.Color.fromARGB(255, 0, 255, 0));
canvas.translate(paragraph.maxWidth / -2.0, (paragraph.maxWidth / 2.0) - 125); // The paint method of Pargraph draws the contents of the paragraph unto the
paragraph.paint(canvas, ui.Offset.zero); // given canvas.
paragraph.paint(canvas, new ui.Offset(paragraph.maxWidth / -2.0, (paragraph.maxWidth / 2.0) - 125));
return recorder.endRecording(); return recorder.endRecording();
} }
...@@ -49,19 +54,37 @@ void beginFrame(Duration timeStamp) { ...@@ -49,19 +54,37 @@ void beginFrame(Duration timeStamp) {
} }
void main() { void main() {
// To create a paragraph of text, we use ParagraphBuilder.
ui.ParagraphBuilder builder = new ui.ParagraphBuilder() ui.ParagraphBuilder builder = new ui.ParagraphBuilder()
// We first push a style that turns the text blue.
..pushStyle(new ui.TextStyle(color: const ui.Color(0xFF0000FF))) ..pushStyle(new ui.TextStyle(color: const ui.Color(0xFF0000FF)))
..addText("Hello, ") ..addText('Hello, ')
// The next run of text will be bold.
..pushStyle(new ui.TextStyle(fontWeight: ui.FontWeight.bold)) ..pushStyle(new ui.TextStyle(fontWeight: ui.FontWeight.bold))
..addText("world. ") ..addText('world. ')
// The pop() command signals the end of the bold styling.
..pop() ..pop()
..addText("هذا هو قليلا طويلة من النص الذي يجب التفاف .") // We add text to the paragraph in logical order. The paragraph object
// understands bi-directional text and will compute the visual ordering
// during layout.
..addText('هذا هو قليلا طويلة من النص الذي يجب التفاف .')
// The second pop() removes the blue color.
..pop() ..pop()
..addText(" و أكثر قليلا لجعله أطول. "); // We can add more text with the default styling.
..addText(' و أكثر قليلا لجعله أطول. ')
..addText('สวัสดี');
// When we're done adding styles and text, we build the Paragraph object, at
// which time we can apply styling that affects the entire paragraph, such as
// left, right, or center alignment. Once built, the contents of the paragraph
// cannot be altered, but sizing and positioning information can be updated.
paragraph = builder.build(new ui.ParagraphStyle()) paragraph = builder.build(new ui.ParagraphStyle())
// Next, we supply a maximum width that the text is permitted to occupy.
..maxWidth = 180.0 ..maxWidth = 180.0
// ... and we ask the paragraph to the visual position of each its glyphs as
// well as its overall size, subject to its sizing constraints.
..layout(); ..layout();
// Finally, we register our beginFrame callback and kick off the first frame.
ui.window.onBeginFrame = beginFrame; ui.window.onBeginFrame = beginFrame;
ui.window.scheduleFrame(); ui.window.scheduleFrame();
} }
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui'; // This example shows how to use the Cassowary autolayout system directly in the
// underlying render tree.
import 'package:cassowary/cassowary.dart' as al; import 'package:cassowary/cassowary.dart' as al;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// This example shows how to build a render tree with a non-cartesian coordinate
// system. Most of the guts of this examples are in src/sector_layout.dart.
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'src/sector_layout.dart'; import 'src/sector_layout.dart';
......
...@@ -2,6 +2,9 @@ ...@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// This example shows how to use flex layout directly in the underlying render
// tree.
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'src/solid_color_box.dart'; import 'src/solid_color_box.dart';
......
...@@ -2,12 +2,20 @@ ...@@ -2,12 +2,20 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// This example shows how to show the text 'Hello, world.' using the underlying
// render tree.
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
void main() { void main() {
// We use RenderingFlutterBinding to attach the render tree to the window.
new RenderingFlutterBinding( new RenderingFlutterBinding(
// The root of our render tree is a RenderPositionedBox, which centers its
// child both vertically and horizontally.
root: new RenderPositionedBox( root: new RenderPositionedBox(
alignment: const FractionalOffset(0.5, 0.5), alignment: const FractionalOffset(0.5, 0.5),
// We use a RenderParagraph to display the text "Hello, world." without
// any explicit styling.
child: new RenderParagraph(new PlainTextSpan('Hello, world.')) child: new RenderParagraph(new PlainTextSpan('Hello, world.'))
) )
); );
......
...@@ -2,43 +2,59 @@ ...@@ -2,43 +2,59 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// This example shows how to perform a simple animation using the underlying
// render tree.
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter/animation.dart'; import 'package:flutter/animation.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
void main() { void main() {
// A green box... // We first create a render object that represents a green box.
RenderBox green = new RenderDecoratedBox( RenderBox green = new RenderDecoratedBox(
decoration: const BoxDecoration(backgroundColor: const Color(0xFF00FF00)) decoration: const BoxDecoration(backgroundColor: const Color(0xFF00FF00))
); );
// of a certain size... // Second, we wrap that green box in a render object that forces the green box
// to have a specific size.
RenderBox square = new RenderConstrainedBox( RenderBox square = new RenderConstrainedBox(
additionalConstraints: const BoxConstraints.tightFor(width: 200.0, height: 200.0), additionalConstraints: const BoxConstraints.tightFor(width: 200.0, height: 200.0),
child: green child: green
); );
// With a given rotation (starts off as the identity transform)... // Third, we wrap the sized green square in a render object that applies rotation
// transform before painting its child. Each frame of the animation, we'll
// update the transform of this render object to cause the green square to
// spin.
RenderTransform spin = new RenderTransform( RenderTransform spin = new RenderTransform(
transform: new Matrix4.identity(), transform: new Matrix4.identity(),
alignment: const FractionalOffset(0.5, 0.5), alignment: const FractionalOffset(0.5, 0.5),
child: square child: square
); );
// centered... // Finally, we center the spinning green square...
RenderBox root = new RenderPositionedBox( RenderBox root = new RenderPositionedBox(
alignment: const FractionalOffset(0.5, 0.5), alignment: const FractionalOffset(0.5, 0.5),
child: spin child: spin
); );
// on the screen. // and attach it to the window.
new RenderingFlutterBinding(root: root); new RenderingFlutterBinding(root: root);
// A repeating animation every 1800 milliseconds... // To make the square spin, we use an animation that repeats every 1800
// milliseconds.
AnimationController animation = new AnimationController( AnimationController animation = new AnimationController(
duration: const Duration(milliseconds: 1800) duration: const Duration(milliseconds: 1800)
)..repeat(); )..repeat();
// From 0.0 to math.PI. // The animation will produce a value between 0.0 and 1.0 each frame, but we
// want to rotate the square using a value between 0.0 and math.PI. To change
// the range of the animation, we use a Tween.
Tween<double> tween = new Tween<double>(begin: 0.0, end: math.PI); Tween<double> tween = new Tween<double>(begin: 0.0, end: math.PI);
// We add a listener to the animation, which will be called every time the
// animation ticks.
animation.addListener(() { animation.addListener(() {
// Each frame of the animation, set the rotation of the square. // This code runs every tick of the animation and sets a new transform on
// the "spin" render object by evaluating the tween on the current value
// of the animation. Setting this value will mark a number of dirty bits
// inside the render tree, which cause the render tree to repaint with the
// new transform value this frame.
spin.transform = new Matrix4.rotationZ(tween.evaluate(animation)); spin.transform = new Matrix4.rotationZ(tween.evaluate(animation));
}); });
} }
...@@ -2,7 +2,10 @@ ...@@ -2,7 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/material.dart'; // This example shows how to use process input events in the underlying render
// tree.
import 'package:flutter/material.dart'; // Imported just for its color palette.
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
// Material design colors. :p // Material design colors. :p
...@@ -34,12 +37,23 @@ class Dot { ...@@ -34,12 +37,23 @@ class Dot {
} }
/// A render object that draws dots under each pointer. /// A render object that draws dots under each pointer.
class RenderDots extends RenderConstrainedBox { class RenderDots extends RenderBox {
RenderDots() : super(additionalConstraints: const BoxConstraints.expand()); RenderDots();
/// State to remember which dots to paint. /// State to remember which dots to paint.
final Map<int, Dot> _dots = <int, Dot>{}; final Map<int, Dot> _dots = <int, Dot>{};
/// Indicates that the size of this render object depends only on the
/// layout constraints provided by the parent.
bool get sizedByParent => true;
/// By selecting the biggest value permitted by the incomming constraints
/// during layout, this function makes this render object as large as
/// possible (i.e., fills the entire screen).
void performResize() {
size = constraints.biggest;
}
/// Makes this render object hittable so that it receives pointer events. /// Makes this render object hittable so that it receives pointer events.
bool hitTestSelf(Point position) => true; bool hitTestSelf(Point position) => true;
...@@ -49,6 +63,10 @@ class RenderDots extends RenderConstrainedBox { ...@@ -49,6 +63,10 @@ class RenderDots extends RenderConstrainedBox {
if (event is PointerDownEvent) { if (event is PointerDownEvent) {
Color color = _kColors[event.pointer.remainder(_kColors.length)]; Color color = _kColors[event.pointer.remainder(_kColors.length)];
_dots[event.pointer] = new Dot(color: color)..update(event); _dots[event.pointer] = new Dot(color: color)..update(event);
// We call markNeedsPaint to indicate that our painting commands have
// changed and that paint needs to be called before displaying a new frame
// to the user. It's harmless to call markNeedsPaint multiple times
// because the render tree will ignore redundant calls.
markNeedsPaint(); markNeedsPaint();
} else if (event is PointerUpEvent || event is PointerCancelEvent) { } else if (event is PointerUpEvent || event is PointerCancelEvent) {
_dots.remove(event.pointer); _dots.remove(event.pointer);
...@@ -62,31 +80,50 @@ class RenderDots extends RenderConstrainedBox { ...@@ -62,31 +80,50 @@ class RenderDots extends RenderConstrainedBox {
/// Issues new painting commands. /// Issues new painting commands.
void paint(PaintingContext context, Offset offset) { void paint(PaintingContext context, Offset offset) {
final Canvas canvas = context.canvas; final Canvas canvas = context.canvas;
// The "size" property indicates the size of that this render box was
// alotted during layout. Here we paint our bounds white. Notice that we're
// located at "offset" from the origin of the canvas' coordinate system.
// Passing offset during the render tree's paint walk is an optimization to
// avoid having to change the origin of the canvas's coordinate system too
// often.
canvas.drawRect(offset & size, new Paint()..color = const Color(0xFFFFFFFF)); canvas.drawRect(offset & size, new Paint()..color = const Color(0xFFFFFFFF));
// We iterate through our model and paint each dot.
for (Dot dot in _dots.values) for (Dot dot in _dots.values)
dot.paint(canvas, offset); dot.paint(canvas, offset);
super.paint(context, offset);
} }
} }
void main() { void main() {
// Create some styled text to tell the user to interact with the app.
RenderParagraph paragraph = new RenderParagraph( RenderParagraph paragraph = new RenderParagraph(
new StyledTextSpan( new StyledTextSpan(
new TextStyle(color: Colors.black87), new TextStyle(color: Colors.black87),
[ new PlainTextSpan("Touch me!") ] <TextSpan>[ new PlainTextSpan("Touch me!") ]
) )
); );
// A stack is a render object that layers its children on top of each other.
// The bottom later is our RenderDots object, and on top of that we show the
// text.
RenderStack stack = new RenderStack( RenderStack stack = new RenderStack(
children: <RenderBox>[ children: <RenderBox>[
new RenderDots(), new RenderDots(),
paragraph, paragraph,
] ]
); );
// Prevent the RenderParagraph from filling the whole screen so // The "parentData" field of a render object is controlled by the render
// that it doesn't eat events. // object's parent render object. Now that we've added the paragraph as a
// child of the RenderStack, the paragraph's parentData field has been
// populated with a StackParentData, which we can use to provide input to the
// stack's layout algorithm.
//
// We use the StackParentData of the paragraph to position the text in the top
// left corner of the screen.
final StackParentData paragraphParentData = paragraph.parentData; final StackParentData paragraphParentData = paragraph.parentData;
paragraphParentData paragraphParentData
..top = 40.0 ..top = 40.0
..left = 20.0; ..left = 20.0;
// Finally, we attach the render tree we've built to the screen.
new RenderingFlutterBinding(root: stack); new RenderingFlutterBinding(root: stack);
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment