Adds documentation to the layering examples
This commit is contained in:
parent
aba27211ef
commit
025c43de1c
@ -2,19 +2,32 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// 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:math' as math;
|
||||
import 'dart:typed_data';
|
||||
|
||||
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();
|
||||
|
||||
// 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.Size size = paintBounds.size;
|
||||
|
||||
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);
|
||||
double radius = size.shortestSide / 2.0;
|
||||
canvas.drawPaint(new ui.Paint()..color = const ui.Color(0xFFFFFFFF));
|
||||
|
||||
canvas.save();
|
||||
canvas.translate(-mid.x/2.0, ui.window.size.height*2.0);
|
||||
@ -46,6 +59,11 @@ ui.Picture paint(ui.Rect paintBounds) {
|
||||
canvas.restore();
|
||||
|
||||
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()
|
||||
..addLayerOnTop(
|
||||
new ui.DrawLooperLayerInfo()
|
||||
@ -90,6 +108,10 @@ ui.Picture paint(ui.Rect paintBounds) {
|
||||
paint.drawLooper = builder.build();
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// 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:typed_data';
|
||||
|
||||
@ -12,18 +15,38 @@ import 'package:sky_services/pointer/pointer.mojom.dart';
|
||||
ui.Color color;
|
||||
|
||||
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();
|
||||
|
||||
// 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);
|
||||
|
||||
// The commands draw a circle in the center of the screen.
|
||||
ui.Size size = paintBounds.size;
|
||||
canvas.drawCircle(
|
||||
size.center(ui.Point.origin),
|
||||
size.shortestSide * 0.45,
|
||||
new ui.Paint()..color = color
|
||||
);
|
||||
|
||||
double radius = size.shortestSide * 0.45;
|
||||
ui.Paint paint = new ui.Paint()..color = color;
|
||||
canvas.drawCircle(size.center(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();
|
||||
}
|
||||
|
||||
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;
|
||||
ui.Rect sceneBounds = new ui.Rect.fromLTWH(
|
||||
0.0,
|
||||
@ -31,49 +54,83 @@ ui.Scene composite(ui.Picture picture, ui.Rect paintBounds) {
|
||||
ui.window.size.width * devicePixelRatio,
|
||||
ui.window.size.height * devicePixelRatio
|
||||
);
|
||||
|
||||
// This transform scales the x and y coordinates by the devicePixelRatio.
|
||||
Float64List deviceTransform = new Float64List(16)
|
||||
..[0] = devicePixelRatio
|
||||
..[5] = devicePixelRatio
|
||||
..[10] = 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)
|
||||
..pushTransform(deviceTransform)
|
||||
..addPicture(ui.Offset.zero, picture)
|
||||
..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();
|
||||
}
|
||||
|
||||
void beginFrame(Duration timeStamp) {
|
||||
ui.Rect paintBounds = ui.Point.origin & ui.window.size;
|
||||
// First, record a picture with our painting commands.
|
||||
ui.Picture picture = paint(paintBounds);
|
||||
// Second, include that picture in a scene graph.
|
||||
ui.Scene scene = composite(picture, paintBounds);
|
||||
// Third, instruct the engine to render that scene graph.
|
||||
ui.window.render(scene);
|
||||
}
|
||||
|
||||
void handlePopRoute() {
|
||||
print('Pressed back button.');
|
||||
}
|
||||
|
||||
// Pointer input arrives as an array of bytes. The format for the data is
|
||||
// defined by pointer.mojom, which generates serializes and parsers for a
|
||||
// number of languages, including Dart, C++, Java, and Go.
|
||||
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);
|
||||
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) {
|
||||
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();
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function is the primary entry point to your application. The engine
|
||||
// calls main() as soon as it has loaded your code.
|
||||
void main() {
|
||||
// Print statements go either go to stdout or to the system log, as
|
||||
// appropriate for the operating system.
|
||||
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.onPopRoute = handlePopRoute;
|
||||
// The engine calls onPointerPacket whenever it had updated information about
|
||||
// the pointers directed at our app.
|
||||
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();
|
||||
}
|
||||
|
@ -2,49 +2,60 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// 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:typed_data';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
Duration timeBase = null;
|
||||
|
||||
void beginFrame(Duration timeStamp) {
|
||||
Timeline.timeSync('beginFrame', () {
|
||||
if (timeBase == null)
|
||||
timeBase = timeStamp;
|
||||
double delta = (timeStamp - timeBase).inMicroseconds / Duration.MICROSECONDS_PER_MILLISECOND;
|
||||
// The timeStamp argument to beginFrame indicates the timing information we
|
||||
// should use to clock our animations. It's important to use timeStamp rather
|
||||
// than reading the system time because we want all the parts of the system to
|
||||
// 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
|
||||
ui.Rect paintBounds = ui.Point.origin & ui.window.size;
|
||||
ui.PictureRecorder recorder = new ui.PictureRecorder();
|
||||
ui.Canvas canvas = new ui.Canvas(recorder, paintBounds);
|
||||
canvas.translate(paintBounds.width / 2.0, paintBounds.height / 2.0);
|
||||
canvas.rotate(math.PI * delta / 1800);
|
||||
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));
|
||||
ui.Picture picture = recorder.endRecording();
|
||||
// PAINT
|
||||
|
||||
// composite
|
||||
final double devicePixelRatio = ui.window.devicePixelRatio;
|
||||
ui.Rect sceneBounds = new ui.Rect.fromLTWH(
|
||||
0.0,
|
||||
0.0,
|
||||
ui.window.size.width * devicePixelRatio,
|
||||
ui.window.size.height * devicePixelRatio
|
||||
);
|
||||
Float64List deviceTransform = new Float64List(16)
|
||||
..[0] = devicePixelRatio
|
||||
..[5] = devicePixelRatio
|
||||
..[10] = 1.0
|
||||
..[15] = 1.0;
|
||||
ui.SceneBuilder sceneBuilder = new ui.SceneBuilder(sceneBounds)
|
||||
..pushTransform(deviceTransform)
|
||||
..addPicture(ui.Offset.zero, picture)
|
||||
..pop();
|
||||
ui.window.render(sceneBuilder.build());
|
||||
});
|
||||
ui.Rect paintBounds = ui.Point.origin & ui.window.size;
|
||||
ui.PictureRecorder recorder = new ui.PictureRecorder();
|
||||
ui.Canvas canvas = new ui.Canvas(recorder, paintBounds);
|
||||
canvas.translate(paintBounds.width / 2.0, paintBounds.height / 2.0);
|
||||
|
||||
// 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),
|
||||
new ui.Paint()..color = const ui.Color.fromARGB(255, 0, 255, 0));
|
||||
ui.Picture picture = recorder.endRecording();
|
||||
|
||||
// COMPOSITE
|
||||
|
||||
final double devicePixelRatio = ui.window.devicePixelRatio;
|
||||
ui.Rect sceneBounds = new ui.Rect.fromLTWH(
|
||||
0.0,
|
||||
0.0,
|
||||
ui.window.size.width * devicePixelRatio,
|
||||
ui.window.size.height * devicePixelRatio
|
||||
);
|
||||
Float64List deviceTransform = new Float64List(16)
|
||||
..[0] = devicePixelRatio
|
||||
..[5] = devicePixelRatio
|
||||
..[10] = 1.0
|
||||
..[15] = 1.0;
|
||||
ui.SceneBuilder sceneBuilder = new ui.SceneBuilder(sceneBounds)
|
||||
..pushTransform(deviceTransform)
|
||||
..addPicture(ui.Offset.zero, picture)
|
||||
..pop();
|
||||
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();
|
||||
}
|
||||
|
||||
|
@ -2,9 +2,13 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// 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:typed_data';
|
||||
|
||||
// A paragraph represents a rectangular region that contains some text.
|
||||
ui.Paragraph paragraph;
|
||||
|
||||
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),
|
||||
new ui.Paint()..color = const ui.Color.fromARGB(255, 0, 255, 0));
|
||||
|
||||
canvas.translate(paragraph.maxWidth / -2.0, (paragraph.maxWidth / 2.0) - 125);
|
||||
paragraph.paint(canvas, ui.Offset.zero);
|
||||
// The paint method of Pargraph draws the contents of the paragraph unto the
|
||||
// given canvas.
|
||||
paragraph.paint(canvas, new ui.Offset(paragraph.maxWidth / -2.0, (paragraph.maxWidth / 2.0) - 125));
|
||||
|
||||
return recorder.endRecording();
|
||||
}
|
||||
@ -49,19 +54,37 @@ void beginFrame(Duration timeStamp) {
|
||||
}
|
||||
|
||||
void main() {
|
||||
// To create a paragraph of text, we use 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)))
|
||||
..addText("Hello, ")
|
||||
..addText('Hello, ')
|
||||
// The next run of text will be bold.
|
||||
..pushStyle(new ui.TextStyle(fontWeight: ui.FontWeight.bold))
|
||||
..addText("world. ")
|
||||
..addText('world. ')
|
||||
// The pop() command signals the end of the bold styling.
|
||||
..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()
|
||||
..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())
|
||||
// Next, we supply a maximum width that the text is permitted to occupy.
|
||||
..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();
|
||||
|
||||
// Finally, we register our beginFrame callback and kick off the first frame.
|
||||
ui.window.onBeginFrame = beginFrame;
|
||||
ui.window.scheduleFrame();
|
||||
}
|
||||
|
@ -2,7 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// 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:flutter/rendering.dart';
|
||||
|
@ -2,6 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// 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 'src/sector_layout.dart';
|
||||
|
||||
|
@ -2,6 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// 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 'src/solid_color_box.dart';
|
||||
|
@ -2,12 +2,20 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// 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';
|
||||
|
||||
void main() {
|
||||
// We use RenderingFlutterBinding to attach the render tree to the window.
|
||||
new RenderingFlutterBinding(
|
||||
// The root of our render tree is a RenderPositionedBox, which centers its
|
||||
// child both vertically and horizontally.
|
||||
root: new RenderPositionedBox(
|
||||
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.'))
|
||||
)
|
||||
);
|
||||
|
@ -2,43 +2,59 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// 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 'package:flutter/animation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
|
||||
void main() {
|
||||
// A green box...
|
||||
// We first create a render object that represents a green box.
|
||||
RenderBox green = new RenderDecoratedBox(
|
||||
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(
|
||||
additionalConstraints: const BoxConstraints.tightFor(width: 200.0, height: 200.0),
|
||||
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(
|
||||
transform: new Matrix4.identity(),
|
||||
alignment: const FractionalOffset(0.5, 0.5),
|
||||
child: square
|
||||
);
|
||||
// centered...
|
||||
// Finally, we center the spinning green square...
|
||||
RenderBox root = new RenderPositionedBox(
|
||||
alignment: const FractionalOffset(0.5, 0.5),
|
||||
child: spin
|
||||
);
|
||||
// on the screen.
|
||||
// and attach it to the window.
|
||||
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(
|
||||
duration: const Duration(milliseconds: 1800)
|
||||
)..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);
|
||||
// We add a listener to the animation, which will be called every time the
|
||||
// animation ticks.
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
@ -2,7 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// 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';
|
||||
|
||||
// Material design colors. :p
|
||||
@ -34,12 +37,23 @@ class Dot {
|
||||
}
|
||||
|
||||
/// A render object that draws dots under each pointer.
|
||||
class RenderDots extends RenderConstrainedBox {
|
||||
RenderDots() : super(additionalConstraints: const BoxConstraints.expand());
|
||||
class RenderDots extends RenderBox {
|
||||
RenderDots();
|
||||
|
||||
/// State to remember which dots to paint.
|
||||
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.
|
||||
bool hitTestSelf(Point position) => true;
|
||||
|
||||
@ -49,6 +63,10 @@ class RenderDots extends RenderConstrainedBox {
|
||||
if (event is PointerDownEvent) {
|
||||
Color color = _kColors[event.pointer.remainder(_kColors.length)];
|
||||
_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();
|
||||
} else if (event is PointerUpEvent || event is PointerCancelEvent) {
|
||||
_dots.remove(event.pointer);
|
||||
@ -62,31 +80,50 @@ class RenderDots extends RenderConstrainedBox {
|
||||
/// Issues new painting commands.
|
||||
void paint(PaintingContext context, Offset offset) {
|
||||
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));
|
||||
|
||||
// We iterate through our model and paint each dot.
|
||||
for (Dot dot in _dots.values)
|
||||
dot.paint(canvas, offset);
|
||||
super.paint(context, offset);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Create some styled text to tell the user to interact with the app.
|
||||
RenderParagraph paragraph = new RenderParagraph(
|
||||
new StyledTextSpan(
|
||||
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(
|
||||
children: <RenderBox>[
|
||||
new RenderDots(),
|
||||
paragraph,
|
||||
]
|
||||
);
|
||||
// Prevent the RenderParagraph from filling the whole screen so
|
||||
// that it doesn't eat events.
|
||||
// The "parentData" field of a render object is controlled by the render
|
||||
// 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;
|
||||
paragraphParentData
|
||||
..top = 40.0
|
||||
..left = 20.0;
|
||||
|
||||
// Finally, we attach the render tree we've built to the screen.
|
||||
new RenderingFlutterBinding(root: stack);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user