| // 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/rendering.dart'; |
| import 'package:flutter/widgets.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| const String _actualContent = 'Actual Content'; |
| const String _loading = 'Loading...'; |
| |
| void main() { |
| testWidgets('deferFirstFrame/allowFirstFrame stops sending frames to engine', (WidgetTester tester) async { |
| expect(RendererBinding.instance!.sendFramesToEngine, isTrue); |
| |
| final Completer<void> completer = Completer<void>(); |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: _DeferringWidget( |
| key: UniqueKey(), |
| loader: completer.future, |
| ), |
| ), |
| ); |
| final _DeferringWidgetState state = tester.state<_DeferringWidgetState>(find.byType(_DeferringWidget)); |
| |
| expect(find.text(_loading), findsOneWidget); |
| expect(find.text(_actualContent), findsNothing); |
| expect(RendererBinding.instance!.sendFramesToEngine, isFalse); |
| |
| await tester.pump(); |
| expect(find.text(_loading), findsOneWidget); |
| expect(find.text(_actualContent), findsNothing); |
| expect(RendererBinding.instance!.sendFramesToEngine, isFalse); |
| expect(state.doneLoading, isFalse); |
| |
| // Complete the future to start sending frames. |
| completer.complete(); |
| await tester.idle(); |
| expect(state.doneLoading, isTrue); |
| expect(RendererBinding.instance!.sendFramesToEngine, isTrue); |
| |
| await tester.pump(); |
| expect(find.text(_loading), findsNothing); |
| expect(find.text(_actualContent), findsOneWidget); |
| expect(RendererBinding.instance!.sendFramesToEngine, isTrue); |
| }); |
| |
| testWidgets('Two widgets can defer frames', (WidgetTester tester) async { |
| expect(RendererBinding.instance!.sendFramesToEngine, isTrue); |
| |
| final Completer<void> completer1 = Completer<void>(); |
| final Completer<void> completer2 = Completer<void>(); |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: Row( |
| children: <Widget>[ |
| _DeferringWidget( |
| key: UniqueKey(), |
| loader: completer1.future, |
| ), |
| _DeferringWidget( |
| key: UniqueKey(), |
| loader: completer2.future, |
| ), |
| ], |
| ), |
| ), |
| ); |
| expect(find.text(_loading), findsNWidgets(2)); |
| expect(find.text(_actualContent), findsNothing); |
| expect(RendererBinding.instance!.sendFramesToEngine, isFalse); |
| |
| completer1.complete(); |
| completer2.complete(); |
| await tester.idle(); |
| |
| await tester.pump(); |
| expect(find.text(_loading), findsNothing); |
| expect(find.text(_actualContent), findsNWidgets(2)); |
| expect(RendererBinding.instance!.sendFramesToEngine, isTrue); |
| }); |
| } |
| |
| class _DeferringWidget extends StatefulWidget { |
| const _DeferringWidget({required Key key, required this.loader}) : super(key: key); |
| |
| final Future<void> loader; |
| |
| @override |
| State<_DeferringWidget> createState() => _DeferringWidgetState(); |
| } |
| |
| class _DeferringWidgetState extends State<_DeferringWidget> { |
| bool doneLoading = false; |
| |
| @override |
| void initState() { |
| super.initState(); |
| RendererBinding.instance!.deferFirstFrame(); |
| widget.loader.then((_) { |
| setState(() { |
| doneLoading = true; |
| RendererBinding.instance!.allowFirstFrame(); |
| }); |
| }); |
| } |
| |
| @override |
| Widget build(BuildContext context) { |
| return doneLoading |
| ? const Text(_actualContent) |
| : const Text(_loading); |
| } |
| } |