| // 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<MyApp> with SingleTickerProviderStateMixin { | |
| int _widgetBuilds = 0; | |
| FrameState _state; | |
| String _summary = ''; | |
| IconData _icon; | |
| double _flutterFrameRate; | |
| Future<void> _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<void> _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<void> _calibrate() async { | |
| debugPrint('Awaiting calm (3 second pause)...'); | |
| await Future<void>.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: <Widget>[ | |
| 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<String>('summary'), | |
| ), | |
| ), | |
| ), | |
| ], | |
| ), | |
| ), | |
| floatingActionButton: _icon == null ? null : FloatingActionButton( | |
| key: const ValueKey<String>('fab'), | |
| child: Icon(_icon), | |
| onPressed: _nextState, | |
| ), | |
| ), | |
| ); | |
| } | |
| } |