main.dart 4.97 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter_driver/driver_extension.dart';

void main() {
  enableFlutterDriverExtension();
13
  debugPrint('Application starting...');
14
  runApp(MyApp());
15 16 17 18
}

class MyApp extends StatefulWidget {
  @override
19
  State createState() => MyAppState();
20 21
}

22
const MethodChannel channel = MethodChannel('texture');
23 24 25 26 27 28 29 30 31 32

enum FrameState { initial, slow, afterSlow, fast, afterFast }

class MyAppState extends State<MyApp> with SingleTickerProviderStateMixin {
  int _widgetBuilds = 0;
  FrameState _state;
  String _summary = '';
  IconData _icon;
  double _flutterFrameRate;

33
  Future<void> _summarizeStats() async {
34 35 36 37 38 39 40 41
    final double framesProduced = await channel.invokeMethod('getProducedFrameRate');
    final double framesConsumed = await channel.invokeMethod('getConsumedFrameRate');
    _summary = '''
Produced: ${framesProduced.toStringAsFixed(1)}fps
Consumed: ${framesConsumed.toStringAsFixed(1)}fps
Widget builds: $_widgetBuilds''';
  }

42
  Future<void> _nextState() async {
43
    switch (_state) {
44 45 46 47 48 49
      case FrameState.initial:
        debugPrint('Starting .5x speed test...');
        _widgetBuilds = 0;
        _summary = 'Producing texture frames at .5x speed...';
        _state = FrameState.slow;
        _icon = Icons.stop;
50
        channel.invokeMethod<void>('start', _flutterFrameRate ~/ 2);
51 52 53
        break;
      case FrameState.slow:
        debugPrint('Stopping .5x speed test...');
54
        await channel.invokeMethod<void>('stop');
55 56 57 58 59 60 61 62 63 64
        await _summarizeStats();
        _icon = Icons.fast_forward;
        _state = FrameState.afterSlow;
        break;
      case FrameState.afterSlow:
        debugPrint('Starting 2x speed test...');
        _widgetBuilds = 0;
        _summary = 'Producing texture frames at 2x speed...';
        _state = FrameState.fast;
        _icon = Icons.stop;
65
        channel.invokeMethod<void>('start', (_flutterFrameRate * 2).toInt());
66 67 68
        break;
      case FrameState.fast:
        debugPrint('Stopping 2x speed test...');
69
        await channel.invokeMethod<void>('stop');
70 71 72 73 74 75 76 77 78 79
        await _summarizeStats();
        _state = FrameState.afterFast;
        _icon = Icons.replay;
        break;
      case FrameState.afterFast:
        debugPrint('Test complete.');
        _summary = 'Press play to start again';
        _state = FrameState.initial;
        _icon = Icons.play_arrow;
        break;
80 81 82 83 84 85 86 87 88 89
    }
    setState(() {});
  }

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

90 91
  static const int calibrationTickCount = 600;

92
  /// Measures Flutter's frame rate.
93
  Future<void> _calibrate() async {
94
    debugPrint('Awaiting calm (3 second pause)...');
95
    await Future<void>.delayed(const Duration(milliseconds: 3000));
96
    debugPrint('Calibrating...');
97 98 99
    DateTime startTime;
    int tickCount = 0;
    Ticker ticker;
100 101 102
    ticker = createTicker((Duration time) {
      tickCount += 1;
      if (tickCount == calibrationTickCount) { // about 10 seconds
103
        final Duration elapsed = DateTime.now().difference(startTime);
104 105 106 107
        ticker.stop();
        ticker.dispose();
        setState(() {
          _flutterFrameRate = tickCount * 1000 / elapsed.inMilliseconds;
108
          debugPrint('Calibrated: frame rate ${_flutterFrameRate.toStringAsFixed(1)}fps.');
109 110 111
          _summary = '''
Flutter frame rate is ${_flutterFrameRate.toStringAsFixed(1)}fps.
Press play to produce texture frames.''';
112 113 114
          _icon = Icons.play_arrow;
          _state = FrameState.initial;
        });
115 116 117
      } else {
        if ((tickCount % (calibrationTickCount ~/ 20)) == 0)
          debugPrint('Calibrating... ${(100.0 * tickCount / calibrationTickCount).floor()}%');
118 119 120
      }
    });
    ticker.start();
121
    startTime = DateTime.now();
122 123 124 125 126 127 128 129
    setState(() {
      _summary = 'Calibrating...';
      _icon = null;
    });
  }

  @override
  Widget build(BuildContext context) {
130
    _widgetBuilds += 1;
131 132 133 134
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Column(
135 136
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
137
              Container(
138 139 140 141
                width: 300.0,
                height: 200.0,
                child: const Texture(textureId: 0),
              ),
142
              Container(
143 144 145
                width: 300.0,
                height: 60.0,
                color: Colors.grey,
146 147
                child: Center(
                  child: Text(
148 149 150 151 152 153 154 155
                    _summary,
                    key: const ValueKey<String>('summary'),
                  ),
                ),
              ),
            ],
          ),
        ),
156
        floatingActionButton: _icon == null ? null : FloatingActionButton(
157
          key: const ValueKey<String>('fab'),
158
          child: Icon(_icon),
159
          onPressed: _nextState,
160
        ),
161 162 163 164
      ),
    );
  }
}