blob: d2dec8ee443c2287a986e743885226a3d992c17f [file] [log] [blame]
// 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:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
// Disconnects semantics listener for testing purposes.
// If the test passes, LifeCycleSpy will rewire the semantics listener back.
SwitchableSemanticsBinding.ensureInitialized();
assert(!SwitchableSemanticsBinding.instance.semanticsEnabled);
runApp(const LifeCycleSpy());
}
/// A Test widget that spies on app life cycle changes.
///
/// It will collect the AppLifecycleState sequence during its lifetime, and it
/// will rewire semantics harness if the sequence it receives matches the
/// expected list.
///
/// Rewiring semantics is a signal to native IOS test that the test has passed.
class LifeCycleSpy extends StatefulWidget {
const LifeCycleSpy({super.key});
@override
State<LifeCycleSpy> createState() => _LifeCycleSpyState();
}
class _LifeCycleSpyState extends State<LifeCycleSpy> with WidgetsBindingObserver {
final List<AppLifecycleState> _expectedLifeCycleSequence = <AppLifecycleState>[
AppLifecycleState.detached,
AppLifecycleState.inactive,
AppLifecycleState.resumed,
];
List<AppLifecycleState?>? _actualLifeCycleSequence;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_actualLifeCycleSequence = <AppLifecycleState?>[
ServicesBinding.instance.lifecycleState,
];
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
setState(() {
_actualLifeCycleSequence = List<AppLifecycleState>.from(_actualLifeCycleSequence!);
_actualLifeCycleSequence?.add(state);
});
}
@override
Widget build(BuildContext context) {
if (const ListEquality<AppLifecycleState?>().equals(_actualLifeCycleSequence, _expectedLifeCycleSequence)) {
// Rewires the semantics harness if test passes.
SwitchableSemanticsBinding.instance.semanticsEnabled = true;
}
return const MaterialApp(
title: 'Flutter View',
home: Text('test'),
);
}
}
class SwitchableSemanticsBinding extends WidgetsFlutterBinding {
static SwitchableSemanticsBinding get instance => BindingBase.checkInstance(_instance);
static SwitchableSemanticsBinding? _instance;
static SwitchableSemanticsBinding ensureInitialized() {
if (_instance == null) {
SwitchableSemanticsBinding();
}
return SwitchableSemanticsBinding.instance;
}
VoidCallback? _originalSemanticsListener;
@override
void initInstances() {
super.initInstances();
_instance = this;
_updateHandler();
}
@override
bool get semanticsEnabled => _semanticsEnabled.value;
final ValueNotifier<bool> _semanticsEnabled = ValueNotifier<bool>(false);
set semanticsEnabled(bool value) {
_semanticsEnabled.value = value;
_updateHandler();
}
void _updateHandler() {
if (_semanticsEnabled.value) {
platformDispatcher.onSemanticsEnabledChanged = _originalSemanticsListener;
_originalSemanticsListener = null;
} else {
_originalSemanticsListener = platformDispatcher.onSemanticsEnabledChanged;
platformDispatcher.onSemanticsEnabledChanged = null;
}
}
@override
void addSemanticsEnabledListener(VoidCallback listener) {
_semanticsEnabled.addListener(listener);
}
@override
void removeSemanticsEnabledListener(VoidCallback listener) {
_semanticsEnabled.removeListener(listener);
}
}