×
Community Blog Flutter Analysis and Practice: Design of the Statistical Framework

Flutter Analysis and Practice: Design of the Statistical Framework

This article discusses the concept of behavior tracking and introduces Xianyu's design of the statistical framework.

With the booming development of mobile smart devices, a mobile multi-terminal development framework has become a general trend. Download the Flutter Analysis and Practice: Evolution and Innovation of Xianyu Technologies eBook for step-by-step analyses based on real-life problems, as well as clear explanations of the important concepts of Flutter.

4.1.1 What Is User Behavior Tracking?

First, we will discuss how user behavior tracking is defined. On the user timeline shown in Figure 4-1, after entering page A, the user sees button X and taps it. Then, the new page B appears.

1
Figure 4-1

The following five tracking events occurred on this timeline:

  • Enter page A: The first frame of page A is rendered and page A obtains the focus.
  • Expose button X: Button X is displayed on the mobile screen for a while so the user can see and tap the button.
  • Tap button X: The user is interested in the content of button X and taps button X. Button X responds to the tap and a new page is opened.
  • Leave page A: Page A loses the focus.
  • Enter page B: The first frame of page B is rendered and page B obtains the focus.

Here, the most important thing is the tracking time, or to be more precise, the time for triggering the tracking of a specific event. The following describes Xianyu's implementation solution in Flutter.

4.1.2 User Behavior Tracking in Flutter

4.1.2.1 A Proposed Solution

In native development, an Android client listens to the onResume and onPause events of an activity as the events of entering and leaving a page. Similarly, an iOS client listens to the viewWillAppear and viewDidDisappear events of the UIViewController as the events of entering and leaving a page. The entire page stack is maintained by the Android and iOS operating systems.

In Flutter, Android and iOS clients use FlutterActivity and FlutterViewController as the containers for carrying Flutter pages. Through these containers, native Flutter page switching can be implemented on a native page. In other words, Flutter maintains its own page stack. This means that the native solution we are most familiar with cannot directly work in Flutter.

To solve this problem, many people may register a NavigatorObserver that can listen to Flutter to detect the push and pop events on Flutter pages. However, two problems may occur in this case:

  • Assume that pages A and B enter the page stack in sequence (A enters > A leaves > B enters.) Then, page B exits (B leaves.) Page A is visible again, but no push event for page A (A enters) is received.
  • Assume that Dialog or BottomSheet pops up on page A. Although the push operation is performed, page A does not exit.

Fortunately, the page stack of Flutter is not as complex as the Android native. The first problem can be solved by maintaining an index list that matches the page stack. When a push event for page A is received, an index of page A is inserted into the queue. When a push event for page B is received, the system checks whether the list contains pages. If yes, the system records a pop event for the last page in the list, records a push event for page B, and inserts an index of page B into the queue. When a pop event on page B is received, the system records a pop event for page B and determines whether the page (assuming it is page A) corresponding to the last index in the queue is on the top of the stack (ModalRoute.of(context).isCurrent). If it is, the system records a push event for page A.

To solve the second problem, the Route class provides the member variable overlayEntries, which can be used to obtain the OverlayEntry of all layers corresponding to the current route. The OverlayEntry object provides the member variable opaque, which can be used to determine whether the current layer covers the full screen so the Dialog and BottomSheet can be excluded. In combination with the first problem, an operation must be added to the preceding solution to determine whether a newly pushed page is valid. If the newly pushed page is valid, the pop event is recorded for the previous page in the index list and the valid page is added to the index list. If the newly pushed page is invalid, no operation is performed on the index list.

4.1.2.2 Xianyu In-House Solution

The preceding solution was not provided by Xianyu. It came as a recommendation from the author. When implementing the Flutter framework, Xianyu used the in-house hybrid solution FlutterBoost instead of the native page stack management solution of Flutter. Therefore, we will introduce Xianyu's solution based on this hybrid solution. However, as the number of Flutter pages on the Xianyu application increases, Flutter native solutions will be implemented in the future.

Figure 4-2 and Figure 4-3 show the Xianyu solution. For this description, we use Android as an example, but this also applies to iOS.

2
Figure 4-2

3
Figure 4-3

A page opened for the first time is a new page that is opened based on the hybrid stack. If a page is not opened for the first time, the page is moved from the backend to the frontend through page rollback.

It seems that Flutter can be used to determine when to trigger the push and pop events. The page stack management in native remains. The tracking time in native is sent to Flutter and Flutter immediately calls the tracking method in native through a channel. You may ask why we do not have native directly complete all management operations. Direct management by native is suitable when a page is not opened for the first time but is not applicable when a page is opened for the first time. When a page is opened for the first time, Flutter does not initialize the page or know the page information when onResume is called. In this case, the push tracking API in native must be called upon initialization (init) of the Flutter page. We directly trigger the push and pop events in Flutter so developers do not have to check whether a Flutter page is opened for the first time.

4.1.3 Placement Exposure

We think that images and text need to be exposed, but what users cannot see does not need to be exposed. Therefore, valid exposure occurs when a placement meets the following two conditions:

  • The visible area of the placement on the screen is equal to or greater than half of the overall area.
  • The placement stays within the visible area of the screen for more than 500 milliseconds.

Based on this, we can quickly design a scenario like that shown in Figure 4-4. A scrolling page contains placements A, B, C, and D. Among the four placements:

  • Placement A is outside the visible area of the screen and therefore is invisible.
  • Placement B is about to slide up from the visible area of the screen and therefore will switch from visible to invisible.
  • Placement C is in the middle of the visible area of the screen and therefore is visible.
  • Placement D is about to slide into the visible area of the screen and therefore will switch from invisible to visible.

4
Figure 4-4

Then, we need to calculate the proportion of the exposed area of a placement on the screen. To calculate this proportion, we need to know the following values:

  • Offset of the container from the screen
  • Offset of the placement from the container
  • Locations, widths, and heights of the placement and container

It is easy to obtain and calculate the widths and heights of the placement and container. Details are omitted here.

1) Obtain the offset of the container from the screen

//监听容器滚动,得到容器的偏移量
double _scrollContainerOffset = scrollNotification.metrics.pixels;

2) Obtain the offset of the placement from the container

//曝光坑位Widget的context
final RenderObject childRenderObject = context.findRenderObject();
final RenderAbstractViewport viewport = RenderAbstractViewport.of (childRenderObject);
if (viewport == null) {
  return;
}
if (!childRenderObject.attached) {
  return;
}
//曝光坑位在容器内的偏移量
final RevealedOffset offsetToRevealTop = viewport.getOffsetToReveal (childRenderObject, 0.0);

3) Perform logical judgment

if (当前坑位是invisible && 曝光比例 >= 0.5) {
  // 记录当前坑位是visible状态
  // 记录出现时间
} else if (当前坑位是visible && 曝光比例 < 0.5) {
  // 记录当前坑位是invisible状态
  if (当前时间-出现时间 > 500ms) {
    // 调用曝光埋点接口
  }
}

4) Tap placements

It is easy to tap tracking placements by using the solution shown in Figure 4-5.

5
Figure 4-5

0 0 0
Share on

XianYu Tech

56 posts | 4 followers

You may also like

Comments