Skip to content

Commit cdab9d5

Browse files
When the app enters the detached state, clear the record of the last SystemChrome style sent to the host (#153586)
This ensures that the style will be sent again after the host is reattached Fixes flutter/flutter#150418
1 parent 4a03b76 commit cdab9d5

File tree

3 files changed

+44
-1
lines changed

3 files changed

+44
-1
lines changed

packages/flutter/lib/src/services/binding.dart

+5-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import 'raw_keyboard.dart' show RawKeyboard;
2727
import 'restoration.dart';
2828
import 'service_extensions.dart';
2929
import 'system_channels.dart';
30+
import 'system_chrome.dart';
3031
import 'text_input.dart';
3132

3233
export 'dart:ui' show ChannelBuffers, RootIsolateToken;
@@ -284,7 +285,10 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
284285
Future<String?> _handleLifecycleMessage(String? message) async {
285286
final AppLifecycleState? state = _parseAppLifecycleMessage(message!);
286287
final List<AppLifecycleState> generated = _generateStateTransitions(lifecycleState, state!);
287-
generated.forEach(handleAppLifecycleStateChanged);
288+
for (final AppLifecycleState stateChange in generated) {
289+
handleAppLifecycleStateChanged(stateChange);
290+
SystemChrome.handleAppLifecycleStateChanged(stateChange);
291+
}
288292
return null;
289293
}
290294

packages/flutter/lib/src/services/system_chrome.dart

+11
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,17 @@ abstract final class SystemChrome {
641641
});
642642
}
643643

644+
/// Called by the binding during a transition to a new app lifecycle state.
645+
static void handleAppLifecycleStateChanged(AppLifecycleState state) {
646+
// When the app is detached, clear the record of the style sent to the host
647+
// so that it will be sent again when the app is reattached.
648+
if (state == AppLifecycleState.detached) {
649+
scheduleMicrotask(() {
650+
_latestStyle = null;
651+
});
652+
}
653+
}
654+
644655
static SystemUiOverlayStyle? _pendingStyle;
645656

646657
/// The last style that was set using [SystemChrome.setSystemUIOverlayStyle].

packages/flutter/test/services/system_chrome_test.dart

+28
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:ui' as ui show AppLifecycleState;
56

67
import 'package:flutter/services.dart';
78
import 'package:flutter_test/flutter_test.dart';
@@ -200,4 +201,31 @@ void main() {
200201
'systemNavigationBarContrastEnforced: null})',
201202
);
202203
});
204+
205+
testWidgets('SystemChrome handles detached lifecycle state', (WidgetTester tester) async {
206+
final List<MethodCall> log = <MethodCall>[];
207+
208+
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, (MethodCall methodCall) async {
209+
log.add(methodCall);
210+
return null;
211+
});
212+
213+
const SystemUiOverlayStyle systemUiOverlayStyle = SystemUiOverlayStyle();
214+
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
215+
await tester.idle();
216+
expect(log.length, equals(1));
217+
218+
// Setting the same state should not send another message to the host.
219+
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
220+
await tester.idle();
221+
expect(log.length, equals(1));
222+
223+
// The state should be sent again if the app was detached.
224+
SystemChrome.handleAppLifecycleStateChanged(ui.AppLifecycleState.detached);
225+
await tester.idle();
226+
SystemChrome.handleAppLifecycleStateChanged(ui.AppLifecycleState.resumed);
227+
SystemChrome.setSystemUIOverlayStyle(systemUiOverlayStyle);
228+
await tester.idle();
229+
expect(log.length, equals(2));
230+
});
203231
}

0 commit comments

Comments
 (0)