flutter_page_tracker

Making connections

Introduction to the

FlutterPageTracker is a plugin that listens for pages to come and go. It has the following features:

  • 1. Listen on common pagesshowandleaveEvent (PageRoute),
    • The current page is triggered when the current page is pushedExposure eventsAnd the previous pageLeave the event.
    • The current page is triggered when the current page is out of the stackLeave the eventAnd the previous pageExposure events.
  • 2. Listen to the dialog boxshowandleave(PopupRoute),
    • The difference with PageRoute is that the opening and closing of the current dialog does not trigger the previous pageshow,leaveThe event
  • 3. Monitor PageView and TabView componentsswitchThe event
    • When a PageView or a TabViewInto the stack, the previous page triggers the pageLeave the event
    • When a PageView or a TabViewOut of the stack, the previous page triggers the pageExposure events
    • When the focus page changes, the old page triggers the page to be exposed and the new page triggers PageView
    • PageView components
    • TabView components
  • 4. Nested use of PageView and TabView
    • We can nest the two components together without limiting the level of nesting
    • The PageView (or TabView) whose focus changes and its children are affectedExposure eventsandLeave the event
  • 5. Slide exposure events
    • If you’re interested in slide-uncovering events for lists, you can refer to the flutter_sliver_tracker plugin
    • https://github.com/SBDavid/flutter_sliver_tracker

Run the Demo program

  • Cloning code to the local: git clone [email protected]: SBDavid/flutter_page_tracker git
  • To switch the working path: CD flutter_page_tracker/example/
  • Start simulator
  • Run: flutter run

use

1. Install

dependencies:
  flutter_page_tracker: ^ 1.2.2
Copy the code

2. Introduce the flutter_page_tracker

import 'package:flutter_page_tracker/flutter_page_tracker.dart';
Copy the code

3. Send the event of burying a common page

3.1 Adding A Route Listener

void main() => runApp(
  TrackerRouteObserverProvider(
    child: MyApp(),
  )
);
Copy the code

3.2 Sending buried events in components

Two mixins, PageTrackerAware and TrackerPageMixin, must be used

class HomePageState extends State<MyHomePage> with PageTrackerAware.TrackerPageMixin {
    @override
    Widget build(BuildContext context) {
        return Container();
    }

    @override
    void didPageView() {
        super.didPageView();
        // Send the page expose event
    }

    @override
    void didPageExit() {
        super.didPageExit();
        // Send the page away event}}Copy the code

3.3 Dialog buried points

class PopupPage extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return SimpleDialog(
      children: <Widget>[
        TrackerDialogWrapper(
        
          didPageView: () {
            // Send page exposure events
          },
          
          didPageExit: () {
            // Send the page away event}, child: Container(), ), ], ); }}Copy the code

3.3 TabView sends buried events

class TabViewPage extends StatefulWidget {
  TabViewPage({Key key,}) : super(key: key);

  @override
  _State createState() => _State();
}

class _State extends State<TabViewPage> with TickerProviderStateMixin {
  TabController tabController = TabController(initialIndex: 0, length: 3, vsync: this);

  @override
  Widget build(BuildContext context) {

    return Scaffold(
        // Add the TabView wrapper
        body: PageViewWrapper(
          // Number of Tab pages
          pageAmount: 3.// Initial Tab subscript
          initialPage: 0.// Listen for the Tab onChange event
          changeDelegate: TabViewChangeDelegate(tabController),
          child: TabBarView(
            controller: tabController,
            children: <Widget>[
              Builder(
                builder: (_) {
                  // Listen for PageView, PageExit events forwarded by PageViewWrapper
                  return PageViewListenerWrapper(
                    0,
                    onPageView: () {
                      // Send page exposure events
                    },
                    onPageExit: () {
                      // Send the page away event}, child: Container(), ); },),// The second Tab
              // The third Tab],),),); }}Copy the code

PageView can also be nested with TabView, and TabView can also be nested with TabView.

class PageViewInTabViewPage extends StatefulWidget {

  @override
  _State createState() => _State();
}

class _State extends State<PageViewInTabViewPage> with TickerProviderStateMixin {

  TabController tabController;
  PageController pageController;

  @override
  void initState() {
    super.initState();
    tabController = TabController(initialIndex: 0, length: 3, vsync: this);
    pageController = PageController();
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
        / / outer TabView
        body: PageViewWrapper(
          pageAmount: 3.// Number of subtabs
          initialPage: 0.// The first Tab to be displayed
          changeDelegate: TabViewChangeDelegate(tabController),
          child: TabBarView(
            controller: tabController,
            children: <Widget>[
              Builder(
                builder: (BuildContext context) {
                  // Forward upper-level events
                  return PageViewListenerWrapper(
                      0./ / the inner PageView
                      child: PageViewWrapper(
                        changeDelegate: PageViewChangeDelegate(pageController),
                        pageAmount: 3,
                        initialPage: pageController.initialPage,
                        child: PageView(
                          controller: pageController,
                          children: <Widget>[
                            PageViewListenerWrapper(
                              0,
                              onPageView: () {
                                // Page exposure event
                              },
                              onPageExit: () {
                                // The page leaves the event
                              },
                              child: Container()
                            ),
                            // the second page in PageView
                            // the third page in PageView],))); },),// tab2
              // tab3],))); }}Copy the code

The principle of article

1. An overview of the

Buried point tracking of pages is usually in the last link of business development, and the development time for buried point is usually not sufficient, but buried point data is of great significance for later product adjustment, so a stable and efficient buried point framework is very important.

2. The functionality we expect from a buried point framework

2.1 PageView, PageExit event

We expect when Navigator. Of (context).pushnamed (“XXX Page”) is called; First send PageExit to the previous page and then send a PageView event to the current page. When calling the Navigator. Of (context). Pop (); , first send the PageExit event of the current page, and then send the PageView event of the previous page.

The first thing that comes to mind is to use RouteObserver, but PageView and PageExit are sent in the opposite order. And A PopupRoute type route would affect the sending of buried events on the previous page, for example we pushed the page A -> popover on page A -> page B, but the PageExit event on page A was not sent in the process.

So we have to manage the routing stack ourselves to determine the types of different routes and control the order of events. The detailed implementation is described below.

2.2 TagView component to PageView component

These two components have nothing to do with the route of the Flutter, but they still belong to the page in the eyes of the product manager. And we need to send the buried event whenever a Tab is first exposed or switched.

For example, when Tab page A is first exposed, we send the PageExit event of the previous page first, and then the PageView event of TabA. When we switch from TabA to TabB, we send TabA’s PageExit event first, and then TabB’s PageView event. When we push a new route, we need to send TabB’s PageExit event.

This process requires Tab pages to interact with regular pages through an event mechanism, and if you move this mechanism directly into business code, the business code will contain a lot of irrelevant and repetitive code. Detailed abstractions are described below.

3. Solve these problems

3.1 Solve the order problem of PageView and PageExit

RouteObserver gives us a good starting point, and we can solve this problem by overriding the didPop and didPush methods and adjusting the order in which events are sent. See TrackerStackObserver for details. In the DidPOP method we fire PageExit for the previous route and then PageView for the current route.

3.2 Avoid pop-ups (e.g., Dialog)

In routeobserver. didPop(Route Route, Route previousRoute), we can find the previousRoute by previousRoute and send the PageView event of the previousRoute to it. But if the last route was a Dialog, it would cause an error, because what we really want is the route that contains the Dialog.

To solve this problem we have to maintain a routing stack ourselves so that when didPop fires we can find the actual last route. Please refer to this code, where routes is the current routing stack.

3.3 How to report buried events in TabView and connect them with other pages

This problem can be broken down into two small questions:

    1. How do I concatenate TabView pages with normal routes?
    1. How to send buried events when Tab is switched?

To solve these two problems, we need a container that manages TAB page state and hosts event forwarding. See figure below:

The TabsWrapper will listen for routing events from the Flutter and forward them to the currently exposed Tab. This solves problem 1.

TabsWrappe also contains a TabController and the index of the last open Tab. The TabsWrappe listens for onChange(index) events from the TabController and forwards the events to the corresponding Tab.