当前位置:网站首页>Flutter achieves the effect of selecting seats in the cinema!

Flutter achieves the effect of selecting seats in the cinema!

2022-06-23 08:35:00 User 9239674

Introduction

Received a demand for imitation cinema , I almost searched Baidu last week , Google ,stackoverflow. None of them were found to be useful flutter To achieve the effect of , I can only write one by myself . This article only talks about ideas , The specific implementation needs your own hands . As long as you understand the following ideas , It's very easy to implement .

Go straight to the renderings

Vertical screen :

Initialize the zoom in and zoom out effect of the adaptive screen :

Layout analysis

Middle seat => matrix , adopt Column nesting Row Realization , Cannot pass GridView Realization ( Slide conflict , It will be explained below )

Left navigation bar => A simple Column( Out-of-service ListView, It will also cause sliding conflict )

Interactive analysis & Realization

Zoom in and out the drag effect :

For the effect of zooming in and out ,Flutter Now there are self-contained components InteractiveViewer

Through this component, the effect of zooming in and out can be perfectly realized . Component properties are not explained here , Relatively simple , You can click the link above to learn about it by yourself .

Here are two key attributes :

1、 Callback events

  • Interaction begins  onInteractionStart
  • Interactive update  onInteractionUpdate
  • End of interaction  onInteractionEnd

2、 Transform controller transformationController

You can use this class to control the zoom in and zoom out effect through code

The navigation bar zooms in and out with the seat table :

The left navigation bar follows the zoom in and zoom out of the middle seat , And the line number positioning does not deviate :

The things mentioned above are generally thought of by everyone , It's also very easy to achieve . The real difficulty with this interaction is this Follow the sliding effect .

Because the navigation bar on the left is fixed on the leftmost side , The seat table can be dragged in full screen , Therefore, the seat table and navigation bar cannot be placed in a zoom component , Otherwise, when the seat table is enlarged , Zoom the navigation bar directly out of the screen . So our idea is to use the navigation bar and seat table as Stack The child components , Then the seat table is enlarged and reduced , And let the navigation bar follow the seat table to zoom in and out . I have tried many methods here :

Method 1 :

Both the left navigation bar and the middle seat table use InteractiveViewer

And then through InteractiveViewer The callback event and the transform controller are used to realize the effect synchronization

result :

Failure ,transformationController The principle is Matrix4 generic ValueNotifier( Four dimensional matrix ), Simple mobile amplification can also be achieved , Fully clone a zoom in and zoom out drag effect , The author cannot .. If linear algebra is very awesome, you can try it .

Method 2 :

flutter There is a synchronized scrolling component called linked_scroll_controller

He can put two scrollController Bind together , Realize synchronous scrolling .

So let the left navigation bar use ListView, Middle seat table use InteractiveViewer nesting GridView, And then ListView and GridView Of ScrollController Bound together to achieve synchronous scrolling .

result :

Failure ,InteractiveViewer The sliding of is through Matrix4 Realized , and ListView Sliding conflict of .

Synchronous scrolling achieves , But zooming in and out cannot be performed .

Method 3 :

Use InteractiveViewer There is no escape , Otherwise, it will be a headache to realize the effect of zooming in and out by yourself , If it can be like the above linked_scroll_controller equally , take InteractiveViewer Copy the zoom effect of to another InteractiveViewer In the middle , That would be perfect .

Is the idea of method one , But with InteractiveViewer Open interfaces and controllers , Unable to complete , This is the time to read and understand InteractiveViewer Source code , See if there is any inspiration .

@override
  Widget build(BuildContext context) {
    Widget child = Transform(
      transform: _transformationController.value,
      child: KeyedSubtree(
        key: _childKey,
        child: widget.child,
      ),
    );

    if (!widget.constrained) {
      child = OverflowBox(
        alignment: Alignment.topLeft,
        minWidth: 0.0,
        minHeight: 0.0,
        maxWidth: double.infinity,
        maxHeight: double.infinity,
        // maxHeight: 220.w,
        child: child,
      );
    }

    if (widget.clipBehavior != Clip.none) {
      child = ClipRRect(
        clipBehavior: widget.clipBehavior,
        child: child,
      );
    }

    // A GestureDetector allows the detection of panning and zooming gestures on
    // the child.
    return Listener(
      key: _parentKey,
      onPointerSignal: _receivedPointerSignal,
      child: GestureDetector(
        behavior: HitTestBehavior.opaque,
        // Necessary when panning off screen.
        dragStartBehavior: DragStartBehavior.start,
        onScaleEnd: onScaleEnd,
        onScaleStart: onScaleStart,
        onScaleUpdate: onScaleUpdate,
        child: child,
      ),
    );
  }

Don't see, don't know , It's a shock at first sight , Actually InteractiveViewer All the methods have been encapsulated for us .

Notice the top GestureDetector, Whole InteractiveViewer Gesture interaction method , In fact, that is onScaleEnd,onScaleStart,onScaleUpdate These three methods .

Then we only need to call back the parameters in the three methods of the seat table component , Just transfer it to the navigation bar component , Then delete the navigation bar component GestureDetector, Let the navigation bar component only accept gesture interaction parameters from the seat table component .

We just need to rewrite two InteractiveViewer, A main component ( Seating table ), One is the slave component ( Navigation bar ), And open InteractiveViewerState, When the seat table component recalls the three methods of gesture , adopt key Pass the parameters of the three methods into the navigation bar component OK.

_onInteractionUpdate(ScaleUpdateDetails details) {
    if (controller.fromInteractiveViewKey.currentState != null) {
      controller.fromInteractiveViewKey.currentState.onScaleUpdate(details);
    }
  }

  _onInteractionStart(ScaleStartDetails details) {
    if (controller.fromInteractiveViewKey.currentState != null) {
      controller.fromInteractiveViewKey.currentState.onScaleStart(details);
    }
  }

  _onInteractionEnd(ScaleEndDetails details) {
    if (controller.fromInteractiveViewKey.currentState != null) {
      controller.fromInteractiveViewKey.currentState.onScaleEnd(details);
    }
  }

Completely without any processing , Copy the parameters into the navigation bar component . We can achieve the effect of synchronous scaling and dragging !

Special attention must be paid here : Individual of the seat table and navigation bar assembly item Must be exactly the same height , Include margin,padding, Otherwise, there will still be dislocation

thus , The biggest difficulty is to synchronize scaling and sliding .

The bottom spring frame is suspended above the seat table :

Click the seat and a pop-up box will pop up at the bottom , Cover part of the seating table , But the seat table can continue to drag upward to display the data of the last row

At first glance, it doesn't seem difficult , But it's a little complicated to think about it . First , Make it clear that the display area of the seat table contains the bottom pop-up frame , Because the bottom spring frame is suspended on the seat table , Then we can only use margin instead of padding, So according to the bottom of the design drawing height, We will marginBottom Set this height Just go , But there's a problem :

When the whole seat table is enlarged margin Some of them will also be amplified synchronously , This will lead to the bigger , The greater the space between the seat table and the bottom .

Solutions :

We need to get the current magnification , Dynamic adjustment margin, Current zoom in X times , original margin by Y, The current magnified margin=Y/X,Y It is known that , We just need to know X Just go . But in _onInteractionUpdate Interface ,X Not a few times the current magnification , It is the zoom multiple after the last zoom . namely :

  • initial 1.0 times .
  • Zoom in for the first time to 2 times , The magnification of the interface callback is 2
  • Zoom in for the second time to 3 times , The magnification of the interface callback is 1.5( It is larger than the first time 1.5 times ).

And even worse, when you zoom in to maxScale after , The interface will continue to call back the magnification . This bothers us , After reading the source code, I found that , The current magnification parameter of the original magnification we want is InteractiveViewer Class .

// Return a new matrix representing the given matrix after applying the given
  // scale.
  Matrix4 _matrixScale(Matrix4 matrix, double scale) {
    if (scale == 1.0) {
      return matrix.clone();
    }
    assert(scale != 0.0);

    // Don't allow a scale that results in an overall scale beyond min/max
    // scale.
    final double currentScale =
        _transformationController.value.getMaxScaleOnAxis();
    final double totalScale =currentScale * scale;
    // Changed the algorithm 
    // final double totalScale = math.max(
    //   currentScale * scale,
    //   // Ensure that the scale cannot make the child so big that it can't fit
    //   // inside the boundaries (in either direction).
    //   math.max(
    //     _viewport.width / _boundaryRect.width,
    //     _viewport.height / _boundaryRect.height,
    //   ),
    // );
    final double clampedTotalScale = totalScale.clamp(
      widget.minScale,
      widget.maxScale,
    );

    widget.scaleCallback?.call(clampedTotalScale);

    final double clampedScale = clampedTotalScale / currentScale;
    return matrix.clone()..scale(clampedScale);
  }

Notice the top scaleCallback, This is the callback method implemented by the author , Among them clampedTotalScale Is the current magnification that we want compared to the initial magnification , namely : initial 1.0 times , Zoom in for the first time to 2 times , The magnification of the interface callback is 2, Zoom in for the second time to 3 times , The magnification of the interface callback is 3( Larger than the original 3 times ).

And clampedTotalScale Forever minScale and maxScale Within the range of . It is very convenient to use it immediately .

There is an algorithm in the above code that I commented out , This code is going to look like this :

When InteractiveViewer Medium child When it has been fully displayed , Can no longer be shrunk , namely minScale It doesn't just depend on the value we set , It also depends on InteractiveViewer Of child According to the effect , I don't need this restriction here , He is annotated out .

In fact, if we want to achieve perfection UI Given the effect , There are many places to use margin, For example, the upper, lower, left and right of the seat table margin, As long as you get the top clampedTotalScale, Can be calculated dynamically , Very convenient .

Horizontal and vertical screen adaptation effect

above gif The picture has a horizontal screen effect , The horizontal and vertical screen switching is also official API,OrientationBuilder, This is also very simple to use . Here's a UI Precautions for adaptation :

Because the author used ScreenUtil(UI The adaptive ), So in the vertical screen , Incoming vertical screen UI Dimensional drawings , And the dimension ends with .w ADAPTS , When the screen is horizontal , Incoming horizontal screen UI Dimensional drawings ( In fact, it is the vertical screen width and height The horse ), Then use... At the end of the dimension .h ADAPTS . In this way, the horizontal and vertical screens can be perfectly adapted , The remaining details can be fine tuned .

Initial magnification

As shown in the picture above , When entering for the first time or switching between horizontal and vertical screens , When there are too many seat tables ( When the default display is not lower ), Zoom out as much as possible to show more ( The lower limit is reduced to minScale), When the seating table layout is too small ( The screen is empty by default ), Zoom in as much as possible until the screen is full ( The upper limit is enlarged to maxScale).

The above effects can be summarized as : As large as possible with as complete a display as possible .

InteractiveViewer There is no initial magnification parameter , The default entry is to zoom in 1.0 times . Here we need to calculate the initial magnification by ourselves .

Calculation

If useful screenUtil, The following calculation shall distinguish between horizontal and vertical screens , When the screen is horizontal, it is used for the end .w, For vertical screen .h, Among them, the special-shaped screen padding There is no need to distinguish between horizontal and vertical screens , The system will automatically change

  • 1、 The display area of the whole seat table :

The screen is high - Irregular screen up and down padding- When the screen is vertical, the bottom of the suspended frame height( If the horizontal screen suspension frame is not at the bottom , Then for 0)- The height of the title block and the height of some other layouts you add . Wide screen - Irregular screen left and right padding- When the screen is horizontal, the right suspension frame width( If the suspension frame is not on the right side when the screen is vertical , Then for 0)- Navigation bar width ( The width of the navigation bar also needs to be dynamically calculated according to the magnification )- Other layout width added by yourself .

  • 2、 Calculate the initial magnification (1.0) The seat table below item Of width and height as well as padding, Just follow the design drawing directly .
  • 3、 Get the current seat table x Axis and y Axis . That is, how many seats in each row , There are several rows of seats .
  • 4、 The calculation assumes that all seating tables are shown below , Every item Of width and height.

Just use the top 1. The width and height of the display area of the resulting seat table are divided by x and y,

  • 5、 take 2. Of width Divide 4.width, Just as X The axis fully displays the value to be scaled SX,

take 2. Of height Divide 4.height, Just as Y The axis fully displays the value to be scaled SY,

  • 6、 Compare SX and SY Two values , Take a small value defaultS( As large as possible with as complete a display as possible )
  • 7、 If defaultS stay minScale and maxScale Within the interval , Then take defaultS, On the contrary, take the interval boundary value .

The zoom

use transformationController take InteractiveViewer Zoom to defaultS

//  Seating table 
mainTransformationController.value = Matrix4.identity()..scale(defaultS);
//  Navigation bar 
fromTransformationController.value = Matrix4.identity()..scale(defaultS);

Note that the seat table and navigation bar should be zoomed .

Zoom dynamic margin

Finally, don't forget to put all kinds of dynamic computing margin Also shrink to defaultS value .

If there is a horizontal and vertical screen switching effect , The initial amplification value is dynamically calculated during each horizontal and vertical screen switching , We need to pay attention to , Every time you calculate, you should dynamically calculate the margin Set to initial value ( That is, when the zoom size is 1.0 At the time of the margin value ).

Sometimes if you can't think of it, just look at the source code , You'll be impressed immediately .

原网站

版权声明
本文为[User 9239674]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/01/202201112035010826.html