mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
289 lines
9.5 KiB
Dart
289 lines
9.5 KiB
Dart
// 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 'package:flutter/material.dart';
|
|
import 'package:flutter/scheduler.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
void main() {
|
|
testWidgets('Nested TickerMode cannot turn tickers back on', (WidgetTester tester) async {
|
|
int outerTickCount = 0;
|
|
int innerTickCount = 0;
|
|
|
|
Widget nestedTickerModes({required bool innerEnabled, required bool outerEnabled}) {
|
|
return Directionality(
|
|
textDirection: TextDirection.rtl,
|
|
child: TickerMode(
|
|
enabled: outerEnabled,
|
|
child: Row(
|
|
children: <Widget>[
|
|
_TickingWidget(
|
|
onTick: () {
|
|
outerTickCount++;
|
|
},
|
|
),
|
|
TickerMode(
|
|
enabled: innerEnabled,
|
|
child: _TickingWidget(
|
|
onTick: () {
|
|
innerTickCount++;
|
|
},
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
await tester.pumpWidget(
|
|
nestedTickerModes(
|
|
outerEnabled: false,
|
|
innerEnabled: true,
|
|
),
|
|
);
|
|
|
|
expect(outerTickCount, 0);
|
|
expect(innerTickCount, 0);
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(outerTickCount, 0);
|
|
expect(innerTickCount, 0);
|
|
|
|
await tester.pumpWidget(
|
|
nestedTickerModes(
|
|
outerEnabled: true,
|
|
innerEnabled: false,
|
|
),
|
|
);
|
|
outerTickCount = 0;
|
|
innerTickCount = 0;
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(outerTickCount, 4);
|
|
expect(innerTickCount, 0);
|
|
|
|
await tester.pumpWidget(
|
|
nestedTickerModes(
|
|
outerEnabled: true,
|
|
innerEnabled: true,
|
|
),
|
|
);
|
|
outerTickCount = 0;
|
|
innerTickCount = 0;
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(outerTickCount, 4);
|
|
expect(innerTickCount, 4);
|
|
|
|
await tester.pumpWidget(
|
|
nestedTickerModes(
|
|
outerEnabled: false,
|
|
innerEnabled: false,
|
|
),
|
|
);
|
|
outerTickCount = 0;
|
|
innerTickCount = 0;
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
await tester.pump(const Duration(seconds: 1));
|
|
expect(outerTickCount, 0);
|
|
expect(innerTickCount, 0);
|
|
});
|
|
|
|
testWidgets('Changing TickerMode does not rebuild widgets with SingleTickerProviderStateMixin', (WidgetTester tester) async {
|
|
Widget widgetUnderTest({required bool tickerEnabled}) {
|
|
return TickerMode(
|
|
enabled: tickerEnabled,
|
|
child: const _TickingWidget(),
|
|
);
|
|
}
|
|
_TickingWidgetState state() => tester.state<_TickingWidgetState>(find.byType(_TickingWidget));
|
|
|
|
await tester.pumpWidget(widgetUnderTest(tickerEnabled: true));
|
|
expect(state().ticker.isTicking, isTrue);
|
|
expect(state().buildCount, 1);
|
|
|
|
await tester.pumpWidget(widgetUnderTest(tickerEnabled: false));
|
|
expect(state().ticker.isTicking, isFalse);
|
|
expect(state().buildCount, 1);
|
|
|
|
await tester.pumpWidget(widgetUnderTest(tickerEnabled: true));
|
|
expect(state().ticker.isTicking, isTrue);
|
|
expect(state().buildCount, 1);
|
|
});
|
|
|
|
testWidgets('Changing TickerMode does not rebuild widgets with TickerProviderStateMixin', (WidgetTester tester) async {
|
|
Widget widgetUnderTest({required bool tickerEnabled}) {
|
|
return TickerMode(
|
|
enabled: tickerEnabled,
|
|
child: const _MultiTickingWidget(),
|
|
);
|
|
}
|
|
_MultiTickingWidgetState state() => tester.state<_MultiTickingWidgetState>(find.byType(_MultiTickingWidget));
|
|
|
|
await tester.pumpWidget(widgetUnderTest(tickerEnabled: true));
|
|
expect(state().ticker.isTicking, isTrue);
|
|
expect(state().buildCount, 1);
|
|
|
|
await tester.pumpWidget(widgetUnderTest(tickerEnabled: false));
|
|
expect(state().ticker.isTicking, isFalse);
|
|
expect(state().buildCount, 1);
|
|
|
|
await tester.pumpWidget(widgetUnderTest(tickerEnabled: true));
|
|
expect(state().ticker.isTicking, isTrue);
|
|
expect(state().buildCount, 1);
|
|
});
|
|
|
|
testWidgets('Moving widgets with SingleTickerProviderStateMixin to a new TickerMode ancestor works', (WidgetTester tester) async {
|
|
final GlobalKey tickingWidgetKey = GlobalKey();
|
|
Widget widgetUnderTest({required LocalKey tickerModeKey, required bool tickerEnabled}) {
|
|
return TickerMode(
|
|
key: tickerModeKey,
|
|
enabled: tickerEnabled,
|
|
child: _TickingWidget(key: tickingWidgetKey),
|
|
);
|
|
}
|
|
// Using different local keys to simulate changing TickerMode ancestors.
|
|
await tester.pumpWidget(widgetUnderTest(tickerEnabled: true, tickerModeKey: UniqueKey()));
|
|
final State tickerModeState = tester.state(find.byType(TickerMode));
|
|
final _TickingWidgetState tickingState = tester.state<_TickingWidgetState>(find.byType(_TickingWidget));
|
|
expect(tickingState.ticker.isTicking, isTrue);
|
|
|
|
await tester.pumpWidget(widgetUnderTest(tickerEnabled: false, tickerModeKey: UniqueKey()));
|
|
expect(tester.state(find.byType(TickerMode)), isNot(same(tickerModeState)));
|
|
expect(tickingState, same(tester.state<_TickingWidgetState>(find.byType(_TickingWidget))));
|
|
expect(tickingState.ticker.isTicking, isFalse);
|
|
});
|
|
|
|
testWidgets('Moving widgets with TickerProviderStateMixin to a new TickerMode ancestor works', (WidgetTester tester) async {
|
|
final GlobalKey tickingWidgetKey = GlobalKey();
|
|
Widget widgetUnderTest({required LocalKey tickerModeKey, required bool tickerEnabled}) {
|
|
return TickerMode(
|
|
key: tickerModeKey,
|
|
enabled: tickerEnabled,
|
|
child: _MultiTickingWidget(key: tickingWidgetKey),
|
|
);
|
|
}
|
|
// Using different local keys to simulate changing TickerMode ancestors.
|
|
await tester.pumpWidget(widgetUnderTest(tickerEnabled: true, tickerModeKey: UniqueKey()));
|
|
final State tickerModeState = tester.state(find.byType(TickerMode));
|
|
final _MultiTickingWidgetState tickingState = tester.state<_MultiTickingWidgetState>(find.byType(_MultiTickingWidget));
|
|
expect(tickingState.ticker.isTicking, isTrue);
|
|
|
|
await tester.pumpWidget(widgetUnderTest(tickerEnabled: false, tickerModeKey: UniqueKey()));
|
|
expect(tester.state(find.byType(TickerMode)), isNot(same(tickerModeState)));
|
|
expect(tickingState, same(tester.state<_MultiTickingWidgetState>(find.byType(_MultiTickingWidget))));
|
|
expect(tickingState.ticker.isTicking, isFalse);
|
|
});
|
|
|
|
testWidgets('Ticking widgets in old route do not rebuild when new route is pushed', (WidgetTester tester) async {
|
|
await tester.pumpWidget(MaterialApp(
|
|
routes: <String, WidgetBuilder>{
|
|
'/foo' : (BuildContext context) => const Text('New route'),
|
|
},
|
|
home: Row(
|
|
children: const <Widget>[
|
|
_TickingWidget(),
|
|
_MultiTickingWidget(),
|
|
Text('Old route'),
|
|
],
|
|
),
|
|
));
|
|
|
|
_MultiTickingWidgetState multiTickingState() => tester.state<_MultiTickingWidgetState>(find.byType(_MultiTickingWidget, skipOffstage: false));
|
|
_TickingWidgetState tickingState() => tester.state<_TickingWidgetState>(find.byType(_TickingWidget, skipOffstage: false));
|
|
|
|
expect(find.text('Old route'), findsOneWidget);
|
|
expect(find.text('New route'), findsNothing);
|
|
|
|
expect(multiTickingState().ticker.isTicking, isTrue);
|
|
expect(multiTickingState().buildCount, 1);
|
|
expect(tickingState().ticker.isTicking, isTrue);
|
|
expect(tickingState().buildCount, 1);
|
|
|
|
tester.state<NavigatorState>(find.byType(Navigator)).pushNamed('/foo');
|
|
await tester.pumpAndSettle();
|
|
expect(find.text('Old route'), findsNothing);
|
|
expect(find.text('New route'), findsOneWidget);
|
|
|
|
expect(multiTickingState().ticker.isTicking, isFalse);
|
|
expect(multiTickingState().buildCount, 1);
|
|
expect(tickingState().ticker.isTicking, isFalse);
|
|
expect(tickingState().buildCount, 1);
|
|
});
|
|
}
|
|
|
|
class _TickingWidget extends StatefulWidget {
|
|
const _TickingWidget({super.key, this.onTick});
|
|
|
|
final VoidCallback? onTick;
|
|
|
|
@override
|
|
State<_TickingWidget> createState() => _TickingWidgetState();
|
|
}
|
|
|
|
class _TickingWidgetState extends State<_TickingWidget> with SingleTickerProviderStateMixin {
|
|
late Ticker ticker;
|
|
int buildCount = 0;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
ticker = createTicker((Duration _) {
|
|
widget.onTick?.call();
|
|
})..start();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
buildCount += 1;
|
|
return Container();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
ticker.dispose();
|
|
super.dispose();
|
|
}
|
|
}
|
|
|
|
class _MultiTickingWidget extends StatefulWidget {
|
|
const _MultiTickingWidget({super.key});
|
|
|
|
@override
|
|
State<_MultiTickingWidget> createState() => _MultiTickingWidgetState();
|
|
}
|
|
|
|
class _MultiTickingWidgetState extends State<_MultiTickingWidget> with TickerProviderStateMixin {
|
|
late Ticker ticker;
|
|
int buildCount = 0;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
ticker = createTicker((Duration _) {
|
|
})..start();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
buildCount += 1;
|
|
return Container();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
ticker.dispose();
|
|
super.dispose();
|
|
}
|
|
}
|