Finally pulled out the basic components, really a lot of hair. Now let’s look at how controls interact

Finally, a simple, somewhat brushstroke canvas is implemented to illustrate how to use gesture interaction.


1. Event interaction from RaisedButton

Many of Flutter components have click events, such as buttons. Here’s a quick look at the source code.

1.1: Use of RaisedButton

Here is a simple use of the RaisedButton. Clicking the button prints a log

var show = RaisedButton( child: Text("RaisedButton", style: TextStyle(fontSize: 12),), onPressed: () { print("onPressed"); });Copy the code

1.2: Journey to the Source

The key is to track down the source of onPressed and draw a simple diagram to illustrate it.

---->[flutter/lib/src/material/raised_button.dart:101]------- class RaisedButton extends MaterialButton{ const RaisedButton({Key Key, @required VoidCallback onPressed, // onPressed is a VoidCallback object, which is an empty callback. }): super( key: key, onPressed: OnPressed, / / call the superclass onPressed} -- -- -- - > [flutter/lib/SRC/material/material_button dart: 40] -- -- -- -- -- -- -- the class MaterialButton extends StatelessWidget {// onPressed is passed to rawMaterialButton@override Widget Build (BuildContext Context) {return in the build method RawMaterialButton(onPressed: onPressed, // ; } } ---->[flutter/lib/src/material/material_button.dart:40]------- class RawMaterialButton extends StatefulWidget { @override _RawMaterialButtonState createState() => _RawMaterialButtonState(); } class _RawMaterialButtonState extends State<RawMaterialButton> {// When the RawMaterialButton is created,onPressed is used on InkWell @override Widget build(BuildContext context) {final Widget result = Focus(//... child: InkWell( onTap: widget.onPressed, } ---->[flutter/lib/src/material/ink_well.dart:813]------- class InkWell extends InkResponse { const InkWell({ GestureTapCallback onTap, }) : super( onTap: OnTap, / / onTap passed it on to the parent class} -- -- -- - > [flutter/lib/SRC/material/ink_well dart: 184] -- -- -- -- -- -- -- the class InkResponse extends StatefulWidget {  @override _InkResponseState<InkResponse> createState() => _InkResponseState<InkResponse>(); } class _InkResponseState<T extends InkResponse> extends State<T> with AutomaticKeepAliveClientMixin<T> { @override Widget build(BuildContext context) {return Listener(//... Child: GestureDetector(// Through onTap callback _handleTap method onTap: enabled? () => _handleTap(context) : null,} void _handleTap(BuildContext context) { if (widget.onTap ! = null) { if (widget.enableFeedback) Feedback.forTap(context); widget.onTap(); // Location of the final OnTap call}}}Copy the code

So we found a master behind the scenes :GestureDetector


2.GestureDetector Event processing

The first step is to recognize that the GestureDetector is essentially a stateless Widget

2.1: Boxes that respond to events

Since GestureDetector’s onTap can pass in a function to handle as a callback, why not try

var box = Container( color: Colors.cyanAccent, width: 100, height: 100, ); var show = GestureDetector( child: box, onTap: () { print("onTap in my box"); });Copy the code

2.2 events (first wave):The Seven Brothers of Hulu

The first introduction is the commonly used seven, according to the name should be easy to understand

The event name Introduction to the A callback object Introduction to the
onTap Click on the There is no There is no
onTapDown Press the TapDownDetails Contact information when pressed
onTapUp Lift up TapUpDetails Contact information when lifting
onTapCancel Cancel the press There is no There is no
onDoubleTap Double click on the There is no There is no
onLongPress Long press There is no There is no
onLongPressUp Long press to lift There is no There is no
var box = Container( color: Colors.cyanAccent, width: 100, height: 100, ); var show = GestureDetector( child: box, onTap: () { print("onTap in my box"); {}, onTapDown: (pos) print (" placement - (x, y) : (${pos. GlobalPosition. Dx}, ${pos. GlobalPosition. Dy}) "); {}, onTapUp: (pos) print (" lift starting point - (x, y) : (${pos. GlobalPosition. Dx}, ${pos. GlobalPosition. Dy}) "); }, onTapCancel: () { print("onTapCancel in my box"); }, onDoubleTap: () { print("onDoubleTap in my box"); }, onLongPress: () { print("onLongPress in my box"); }, onLongPressUp: () { print("onLongPressUp in my box"); });Copy the code

Here are two things to mention :1. The click event will not be triggered when you double-click

2. About onTapCancel, what is click Cancel?

-- -- -- - > [scene 1: general slide] - I/flutter (13474) : placement - (x, y) : (55.61517333984375, 157.59931437174478) I/flutter (13474) : OnTapCancel in my box ---->[scenario 2: Long press]---- I/flutter (13474): Placement - (x, y) : (52.28492228190104, 140.27338663736978) I/flutter (13474) : onTapCancel in my box I/flutter (13474) : onLongPress in my box I/flutter (13474): onLongPressUp in my boxCopy the code

2.3 events (Second wave):Ten brothers
The event name Introduction to the A callback object Introduction to the
onVerticalDragDown Drag vertically and press down DragDownDetails Contact information
onVerticalDragStart Drag vertically and start DragStartDetails Contact information
onVerticalDragUpdate Drag vertically to update DragUpdateDetails Contact information
onVerticalDragEnd Drag vertically End DragEndDetails Contact information
onVerticalDragCancel Drag vertically to cancel There is no There is no
onHorizontalDragDown Drag horizontally and press down DragDownDetails Contact information
onHorizontalDragStart Start dragging horizontally DragStartDetails Contact information
onHorizontalDragUpdate Drag horizontally to update DragUpdateDetails Contact information
onHorizontalDragEnd End of horizontal drag DragEndDetails Contact information
onHorizontalDragCancel Drag horizontally to cancel There is no There is no

Here we test the vertical five, and the horizontal five, similarly

var show = GestureDetector( child: box, onVerticalDragDown: (pos) {print (" vertical drag press - (x, y) : (${pos. GlobalPosition. Dx}, ${pos. GlobalPosition. Dy}) "); }, onVerticalDragStart: (pos) {print(" ----(x,y):(${pos.globalposition.dx},${pos.globalposition.dy})"); }, onVerticalDragUpdate: (pos) {print(" ----(x,y):(${pos.globalposition.dx},${pos.globalposition.dy})"); }, onVerticalDragEnd: (pos) {print (" end of the vertical drag speed - (x, y) : (${pos. Velocity. PixelsPerSecond. Dx}, ${pos. Velocity. PixelsPerSecond. Dy}) "); }, onVerticalDragCancel: () { print("onVerticalDragCancel in my box"); });Copy the code

Here I think I did a quick swipe in the top left corner, and the log is:

I/flutter (13474) : the vertical drag press - (x, y) : (68.27012125651042, 171.9265340169271) I/flutter (13474) : Began to vertical drag - (x, y) : (68.27012125651042, 171.9265340169271) I/flutter (13474) : Vertical drag-and-drop update - (x, y) : (64.60684712727864, 167.26185099283853) I/flutter (13474) : Vertical drag-and-drop update - (x, y) : (57.94634501139323, 159.26526896158853) I/flutter (13474) : Vertical drag-and-drop update - (x, y) : (49.95374552408854, 148.93635050455728) I/flutter (13474) : Vertical drag-and-drop update - (x, y) : (39.62997182210287, 137.60785929361978) I/flutter (13474) : Vertical drag-and-drop update - (x, y) : (28.640146891276043, 125.6129862467448) I/flutter (13474) : Vertical drag-and-drop update - (x, y) : (16.31822458902995, 113.6181131998698) I/flutter (13474) : Vertical drag end speed ----(x,y):(-1476.3951158711095,-1569.520405720337)Copy the code

Note that through testing, even horizontal sliding triggers a callback if only vertical processing is performed

But when the vertical horizontal appears at the same time, it will automatically determine your sliding direction to make the corresponding callback. In addition to the source said: the two had better not be used together. For easy use, use a pan

// Horizontal and vertical drag callbacks cannot be used simultaneously /// because of a combination of Horizontal and vertical drag callbacks horizontal and vertical drag is a pan. Simply /// use the pan callbacks instead.Copy the code

2.4: Event Overview (Third wave):Five fire hierarch

Fear not, as mentioned above, these are also five drag events, but there is no directional differentiation

The event name Introduction to the A callback object Introduction to the
onPanDown Drag vertically and press down DragDownDetails Contact information
onPanStart Drag vertically and start DragStartDetails Contact information
onPanUpdate Drag vertically to update DragUpdateDetails Contact information
onPanEnd Drag vertically End DragEndDetails Speed information
onPanCancel Drag vertically to cancel There is no There is no
var box = Container( color: Colors.cyanAccent, width: 200, height: 200, ); var show = GestureDetector( child: box, onPanDown: (pos) {print (" drag and drop press - (x, y) : (${pos. GlobalPosition. Dx}, ${pos. GlobalPosition. Dy}) "); {}, onPanStart: (pos) print (" began to drag and drop - (x, y) : (${pos. GlobalPosition. Dx}, ${pos. GlobalPosition. Dy}) "); {}, onPanUpdate: (pos) print (" drag-and-drop update - (x, y) : (${pos. GlobalPosition. Dx}, ${pos. GlobalPosition. Dy}) "); {}, onPanEnd: (pos) print (" end of the drag speed - (x, y) : (${pos. Velocity. PixelsPerSecond. Dx}, ${pos. Velocity. PixelsPerSecond. Dy}) "); }, onPanCancel: () { print("onPanCancel in my box"); });Copy the code

2.5: Event Overview (Fourth wave):Three pillar

Pan and scale callbacks cannot be used together because scale is a superset of Pan. Simply use the scale callback function.

In use and the previous drag time is basically the same, here will not repeat.

var box = Container( color: Colors.cyanAccent, width: 200, height: 200, ); var show = GestureDetector( child: box, onScaleStart: (pos) { print( "onScaleStart----(x,y):(${pos.focalPoint.dx},${pos.focalPoint.dy})"); }, onScaleUpdate: (pos) { print( "onScaleUpdate----(x,y):(${pos.focalPoint.dx},${pos.focalPoint.dy})"); }, onScaleEnd: (pos) { print( "onScaleEnd----(x,y):(${pos.velocity.pixelsPerSecond.dx},${pos.velocity.pixelsPerSecond.dy})"); });Copy the code

2.6: About InkWell

InkWell is also a component with event handling capabilities, though it supports fewer events

Commonly used containers include click, double click, long press, and down. The Container has a water ripple effect (note: the background color of the Container will cover the water ripple).

Var box = Container(width: 120, height: 120*0.681,); Var show = InkWell (child: box, focusColor: colors. red,// hoverColor: color. yellow,// SplashColor: colors. grey,// highlightColor: color. blue,// The length shows the color borderRadius: BorderRadius.all(Radius.elliptical(10, 10)), onTap: () { print("OnTap in InkWell"); });Copy the code

3. Hand-painted board V0.01

3.0: Preparation
Gesture interaction in Flutter is mainly related to movement 1. A line is a set of points, and the palette has to draw n lines, so it's a set of points _lines 2. Component is a stateful component. _lines is a state quantity. The point is added to the currently drawn line 3 as it moves. When a line is lifted, you should copy _lines and empty the current line for the next 4. TolyCircle class TolyDrawable {Color Color; // Offset pos; / / position TolyDrawable (enclosing color, enclosing pos); } class TolyCicle extends TolyDrawable{ double radius; // size TolyCicle(Color Color, Offset pos,{this.radius=1}) : super(Color, pos); }Copy the code

3.1: Prepare drawing board Paper

So we pass lines as a line set, and we iterate over the lines and iterate over the points

class Paper extends CustomPainter{ Paper({ @required this.lines, }) { _paint = Paint().. style=PaintingStyle.stroke .. strokeCap = StrokeCap.round; } Paint _paint; final List<List<TolyCicle>> lines; @override void paint(Canvas canvas, Size size) { for (int i = 0; i < lines.length; i++) { drawLine(canvas,lines[i]); } @override bool shouldRepaint(CustomPainter oldDelegate) { return true; Void drawLine(Canvas Canvas,List<TolyCicle> positions) {for (int I = 0; i < positions.length - 1; i++) { if (positions[i] ! = null && positions[i + 1] ! = null) canvas.drawLine(positions[i].pos, positions[i + 1].pos, _paint.. strokeWidth=positions[i].radius); }}}Copy the code

3.2: Drawing board components

That’s fine. There’s a lot of work to be done here, but it’s a good example of an interactive use of gestures

class TolyCanvas extends StatefulWidget{ @override State<StatefulWidget> createState() => _TolyCanvasState(); } class _TolyCanvasState extends State<TolyCanvas> { var _positions=<TolyCicle>[]; var _lines=<List<TolyCicle>>[]; Offset _oldPos; @override Widget build(BuildContext context) {var body=CustomPaint(Painter: Paper(lines: _lines),); var scaffold = Scaffold( body: body, ); var result =GestureDetector( child: scaffold, onPanDown: _panDown, onPanUpdate: _panUpdate, onPanEnd: _panEnd, onDoubleTap: (){ _lines.clear(); _render(); }); return result; Void _panDown(DragDownDetails details) {print(details.tostring ()); _lines.add(_positions); var x=details.globalPosition.dx; var y=details.globalPosition.dy; _oldPos= Offset(x, y); Void _render(){setState(() {}); } / / / move, add point to point on void _panUpdate (DragUpdateDetails details) {var x = details. GlobalPosition. Dx. var y=details.globalPosition.dy; var curPos = Offset(x, y); If ((curPos-_oldPos).distance>3) {var len = (curPos-_oldPos).distance; Var width = 40 * pow (len, 1.2); Var tolyCicle = tolyCicle (Colors. Blue, curPos,radius:width); _positions.add(tolyCicle); _oldPos=curPos; _render(); Void _panEnd(DragEndDetails details) {var oldBall = <TolyCicle>[]; for (int i = 0; i < _positions.length; i++) { oldBall.add(_positions[i]); } _lines.add(oldBall); _positions.clear(); }}Copy the code

conclusion

This is the end of this article. In addition, I have a wechat discussion group about Flutter. You are welcome to join us to discuss Flutter issues.

My wechat account: ZDL1994328, looking forward to exchange and play with you. If you want to get a quick taste of Flutter, Flutter 7 is a must-have.