| // Copyright 2014 The Flutter 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(const MyApp()); |
| } |
| |
| class MyApp extends StatefulWidget { |
| const MyApp({Key? key}) : super(key: key); |
| |
| @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 = FrameState.initial; |
| String _summary = ''; |
| IconData? _icon; |
| double _flutterFrameRate = 0; |
| |
| 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<void>('start', _flutterFrameRate ~/ 2); |
| break; |
| case FrameState.slow: |
| debugPrint('Stopping .5x speed test...'); |
| await channel.invokeMethod<void>('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<void>('start', (_flutterFrameRate * 2).toInt()); |
| break; |
| case FrameState.fast: |
| debugPrint('Stopping 2x speed test...'); |
| await channel.invokeMethod<void>('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...'); |
| late 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>[ |
| const SizedBox( |
| width: 300.0, |
| height: 200.0, |
| child: 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'), |
| onPressed: _nextState, |
| child: Icon(_icon), |
| ), |
| ), |
| ); |
| } |
| } |