// Copyright 2017 The Chromium Authors. All rights reserved. // 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(); debugPrint('Application starting...'); runApp(MyApp()); } class MyApp extends StatefulWidget { @override State createState() => MyAppState(); } const MethodChannel channel = MethodChannel('texture'); enum FrameState { initial, slow, afterSlow, fast, afterFast } class MyAppState extends State with SingleTickerProviderStateMixin { int _widgetBuilds = 0; FrameState _state; String _summary = ''; IconData _icon; double _flutterFrameRate; Future _summarizeStats() async { 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'''; } Future _nextState() async { switch (_state) { case FrameState.initial: debugPrint('Starting .5x speed test...'); _widgetBuilds = 0; _summary = 'Producing texture frames at .5x speed...'; _state = FrameState.slow; _icon = Icons.stop; channel.invokeMethod('start', _flutterFrameRate ~/ 2); break; case FrameState.slow: debugPrint('Stopping .5x speed test...'); await channel.invokeMethod('stop'); 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; channel.invokeMethod('start', (_flutterFrameRate * 2).toInt()); break; case FrameState.fast: debugPrint('Stopping 2x speed test...'); await channel.invokeMethod('stop'); 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; } setState(() {}); } @override void initState() { super.initState(); _calibrate(); } static const int calibrationTickCount = 600; /// Measures Flutter's frame rate. Future _calibrate() async { debugPrint('Awaiting calm (3 second pause)...'); await Future.delayed(const Duration(milliseconds: 3000)); debugPrint('Calibrating...'); DateTime startTime; int tickCount = 0; Ticker ticker; ticker = createTicker((Duration time) { tickCount += 1; if (tickCount == calibrationTickCount) { // about 10 seconds final Duration elapsed = DateTime.now().difference(startTime); ticker.stop(); ticker.dispose(); setState(() { _flutterFrameRate = tickCount * 1000 / elapsed.inMilliseconds; debugPrint('Calibrated: frame rate ${_flutterFrameRate.toStringAsFixed(1)}fps.'); _summary = ''' Flutter frame rate is ${_flutterFrameRate.toStringAsFixed(1)}fps. Press play to produce texture frames.'''; _icon = Icons.play_arrow; _state = FrameState.initial; }); } else { if ((tickCount % (calibrationTickCount ~/ 20)) == 0) debugPrint('Calibrating... ${(100.0 * tickCount / calibrationTickCount).floor()}%'); } }); ticker.start(); startTime = DateTime.now(); setState(() { _summary = 'Calibrating...'; _icon = null; }); } @override Widget build(BuildContext context) { _widgetBuilds += 1; return MaterialApp( home: Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 300.0, height: 200.0, child: const Texture(textureId: 0), ), Container( width: 300.0, height: 60.0, color: Colors.grey, child: Center( child: Text( _summary, key: const ValueKey('summary'), ), ), ), ], ), ), floatingActionButton: _icon == null ? null : FloatingActionButton( key: const ValueKey('fab'), child: Icon(_icon), onPressed: _nextState, ), ), ); } }