user8320143
user8320143

Reputation:

Page transition becomes painfully slow after several pages deep

I am building a simple Wikipedia browser using Flutter. Since there is no native Flutter Webview, I have to manually parse and convert the HTML fragment into equivalent Flutter widgets. I managed to do so but after navigating several pages deep (by clicking on the blue links), the page transition animation became excruciatingly slow.

Steps to reproduce:

Add the following dependencies

dependencies:
  flutter:
    sdk: flutter
  fluro: "^1.1.0"
  html: "^0.13.2"

Paste and run the following code (in release mode for clearer observation).

import 'dart:async';
import 'dart:convert';

import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:fluro/fluro.dart';
import 'package:html/dom.dart' as DOM;
import 'package:html/parser.dart' show parse;

void main() {
  runApp(new MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final router = new Router(); // Fluro router

  @override
  void initState() {
    super.initState();
    router.define(
      '/wiki',
      handler: new Handler(
        handlerFunc: (_, params) => new WikiPage(title: params['q'])
      )
    );
  }

  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      onGenerateRoute: router.generator, // delegate to Fluro
      routes: {
        '/': (BuildContext context) => new WikiPage(title: 'Firefox')
      },
    );
  }
}

class WikiPage extends StatefulWidget {
  WikiPage({Key key, this.title}) : super(key: key);
  final String title;

  @override
  _WikiPageState createState() => new _WikiPageState();
}

class _WikiPageState extends State<WikiPage> {

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(title: new Text(widget.title)),
      body: new FutureBuilder<String>(
        future: getPage(widget.title),
        builder: (context, snapshot) {
          if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
          if (snapshot.hasData) {
            if (snapshot.data.isEmpty) return new Container(); // empty result

            // parse HTML
            DOM.Document document = parse(snapshot.data);
            document.querySelector('.infobox')?.remove(); // remove the infobox table
            List<DOM.Element> paragraphs = document.getElementsByTagName('p');

            // convert HTML tree to Flutter widgets
            return new ListView(
              padding: const EdgeInsets.all(16.0),
              children: paragraphs.map((paragraph) =>
                new RichText(
                  text: new TextSpan(
                    text: '',
                    style: DefaultTextStyle.of(context).style,
                    children: paragraph.nodes.map((node) {
                      if (node.toString() == '<html a>') { // HTML <a> tag
                        String href = node.attributes['href'];
                        return new TextSpan( // as blue link
                          text: node.text,
                          style: Theme.of(context).textTheme.body1.copyWith(
                            color: Colors.blue
                          ),
                          recognizer: new TapGestureRecognizer()
                            ..onTap = () => Navigator.of(context).pushNamed(
                              '/wiki?q=${href.split('/')[2]}'
                            )
                        );
                      } else {
                        return new TextSpan(text: node.text);
                      }
                    }).toList()
                  )
                )
              ).toList()
            );

          } else { // waiting for data
            return new Center(child: new CircularProgressIndicator());
          }
        },
      )
    );
  }

  Future<String> getPage(String title) async {
    final String url = 'https://en.wikipedia.org/w/api.php?' +
    'action=mobileview&format=json&sections=0&noimages=1&noheadings=1' +
    '&formatversion=2&page=${Uri.encodeComponent(title)}';

    final response = await http.get(url);
    final json = JSON.decode(response.body);
    return json['mobileview']['sections'][0]['text']; // retrieve HTML string
  }
}
  1. Click on any of the blue Wikipedia link to navigate. Initially the page transition animations (first 2-3 pages) are smooth. New pages slide in from bottom (Android platform).
  2. But the page transition becomes jerky with each navigation, up to the point it just freezes for a while (the bottom-up page transition is completely gone).

What could be the problem? Garbage collector issue? Any help is appreciated.

[√] Flutter (on Microsoft Windows [Version 10.0.15063], locale en-US, channel master)
    • Flutter at C:\Users\tzm\Downloads\flutter_sdk
    • Framework revision 6655074b37 (2 days ago), 2017-07-28 15:44:38 -0700
    • Engine revision 232f4636e5
    • Tools Dart version 1.25.0-dev.7.0

Upvotes: 2

Views: 3574

Answers (1)

user8320143
user8320143

Reputation:

I found the solution. The trick is to

  • offload expensive HTML parsing to a separate thread using Dart Isolate.
  • set maintainState in MaterialPageRoute to false so that previous computations are always discarded from memory. The downside is that we need to regenerate previously visited pages as we pop from our current page.

Upvotes: 1

Related Questions