Unverified Commit f1cdfa28 authored by Bruno Leroux's avatar Bruno Leroux Committed by GitHub

Use AppBar.systemOverlayStyle to style system navigation bar (#104827)

* Use upper AnnotatedRegion properties when no bottom one found (and vice versa)

* Update comments
Co-authored-by: 's avatarBruno Leroux <bruno.leroux@gmail.com>
parent 5628ebf6
...@@ -879,7 +879,13 @@ class _AppBarState extends State<AppBar> { ...@@ -879,7 +879,13 @@ class _AppBarState extends State<AppBar> {
final SystemUiOverlayStyle style = brightness == Brightness.dark final SystemUiOverlayStyle style = brightness == Brightness.dark
? SystemUiOverlayStyle.light ? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark; : SystemUiOverlayStyle.dark;
return style.copyWith(statusBarColor: backgroundColor); // For backward compatibility, create an overlay style without system navigation bar settings.
return SystemUiOverlayStyle(
statusBarColor: backgroundColor,
statusBarBrightness: style.statusBarBrightness,
statusBarIconBrightness: style.statusBarIconBrightness,
systemStatusBarContrastEnforced: style.systemStatusBarContrastEnforced,
);
} }
@override @override
......
...@@ -112,6 +112,11 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> ...@@ -112,6 +112,11 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
/// and the hit-test result from the bottom of the screen provides the system /// and the hit-test result from the bottom of the screen provides the system
/// nav bar settings. /// nav bar settings.
/// ///
/// If there is no [AnnotatedRegionLayer] on the bottom, the hit-test result
/// from the top provides the system nav bar settings. If there is no
/// [AnnotatedRegionLayer] on the top, the hit-test result from the bottom
/// provides the system status bar settings.
///
/// Setting this to false does not cause previous automatic adjustments to be /// Setting this to false does not cause previous automatic adjustments to be
/// reset, nor does setting it to true cause the app to update immediately. /// reset, nor does setting it to true cause the app to update immediately.
/// ///
...@@ -312,20 +317,47 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> ...@@ -312,20 +317,47 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
case TargetPlatform.windows: case TargetPlatform.windows:
break; break;
} }
// If there are no overlay styles in the UI don't bother updating. // If there are no overlay style in the UI don't bother updating.
if (upperOverlayStyle != null || lowerOverlayStyle != null) { if (upperOverlayStyle == null && lowerOverlayStyle == null) {
return;
}
// If both are not null, the upper provides the status bar properties and the lower provides
// the system navigation bar properties. This is done for advanced use cases where a widget
// on the top (for instance an app bar) will create an annotated region to set the status bar
// style and another widget on the bottom will create an annotated region to set the system
// navigation bar style.
if (upperOverlayStyle != null && lowerOverlayStyle != null) {
final SystemUiOverlayStyle overlayStyle = SystemUiOverlayStyle( final SystemUiOverlayStyle overlayStyle = SystemUiOverlayStyle(
statusBarBrightness: upperOverlayStyle?.statusBarBrightness, statusBarBrightness: upperOverlayStyle.statusBarBrightness,
statusBarIconBrightness: upperOverlayStyle?.statusBarIconBrightness, statusBarIconBrightness: upperOverlayStyle.statusBarIconBrightness,
statusBarColor: upperOverlayStyle?.statusBarColor, statusBarColor: upperOverlayStyle.statusBarColor,
systemStatusBarContrastEnforced: upperOverlayStyle?.systemStatusBarContrastEnforced, systemStatusBarContrastEnforced: upperOverlayStyle.systemStatusBarContrastEnforced,
systemNavigationBarColor: lowerOverlayStyle?.systemNavigationBarColor, systemNavigationBarColor: lowerOverlayStyle.systemNavigationBarColor,
systemNavigationBarDividerColor: lowerOverlayStyle?.systemNavigationBarDividerColor, systemNavigationBarDividerColor: lowerOverlayStyle.systemNavigationBarDividerColor,
systemNavigationBarIconBrightness: lowerOverlayStyle?.systemNavigationBarIconBrightness, systemNavigationBarIconBrightness: lowerOverlayStyle.systemNavigationBarIconBrightness,
systemNavigationBarContrastEnforced: lowerOverlayStyle?.systemNavigationBarContrastEnforced, systemNavigationBarContrastEnforced: lowerOverlayStyle.systemNavigationBarContrastEnforced,
); );
SystemChrome.setSystemUIOverlayStyle(overlayStyle); SystemChrome.setSystemUIOverlayStyle(overlayStyle);
return;
} }
// If only one of the upper or the lower overlay style is not null, it provides all properties.
// This is done for developer convenience as it allows setting both status bar style and
// navigation bar style using only one annotated region layer (for instance the one
// automatically created by an [AppBar]).
final bool isAndroid = defaultTargetPlatform == TargetPlatform.android;
final SystemUiOverlayStyle definedOverlayStyle = (upperOverlayStyle ?? lowerOverlayStyle)!;
final SystemUiOverlayStyle overlayStyle = SystemUiOverlayStyle(
statusBarBrightness: definedOverlayStyle.statusBarBrightness,
statusBarIconBrightness: definedOverlayStyle.statusBarIconBrightness,
statusBarColor: definedOverlayStyle.statusBarColor,
systemStatusBarContrastEnforced: definedOverlayStyle.systemStatusBarContrastEnforced,
systemNavigationBarColor: isAndroid ? definedOverlayStyle.systemNavigationBarColor : null,
systemNavigationBarDividerColor: isAndroid ? definedOverlayStyle.systemNavigationBarDividerColor : null,
systemNavigationBarIconBrightness: isAndroid ? definedOverlayStyle.systemNavigationBarIconBrightness : null,
systemNavigationBarContrastEnforced: isAndroid ? definedOverlayStyle.systemNavigationBarContrastEnforced : null,
);
SystemChrome.setSystemUIOverlayStyle(overlayStyle);
} }
@override @override
......
...@@ -558,7 +558,13 @@ class SystemChrome { ...@@ -558,7 +558,13 @@ class SystemChrome {
/// it can be hit-tested by the framework. On every frame, the framework will /// it can be hit-tested by the framework. On every frame, the framework will
/// hit-test and select the annotated region it finds under the status and /// hit-test and select the annotated region it finds under the status and
/// navigation bar and synthesize them into a single style. This can be used /// navigation bar and synthesize them into a single style. This can be used
/// to configure the system styles when an app bar is not used. /// to configure the system styles when an app bar is not used. When an app
/// bar is used, apps should not enclose the app bar in an annotated region
/// because one is automatically created. If an app bar is used and the app
/// bar is enclosed in an annotated region, the app bar overlay style supercedes
/// the status bar properties defined in the enclosing annotated region overlay
/// style and the enclosing annotated region overlay style supercedes the app bar
/// overlay style navigation bar properties.
/// ///
/// {@tool sample} /// {@tool sample}
/// The following example creates a widget that changes the status bar color /// The following example creates a widget that changes the status bar color
......
...@@ -2421,6 +2421,56 @@ void main() { ...@@ -2421,6 +2421,56 @@ void main() {
} }
}); });
testWidgets('Default status bar color', (WidgetTester tester) async {
Future<void> pumpBoilerplate({required bool useMaterial3, required bool backwardsCompatibility}) async {
await tester.pumpWidget(
MaterialApp(
key: GlobalKey(),
theme: ThemeData.light().copyWith(
useMaterial3: useMaterial3,
appBarTheme: AppBarTheme(
backwardsCompatibility: backwardsCompatibility,
),
),
home: Scaffold(
appBar: AppBar(
title: const Text('title'),
),
),
),
);
}
await pumpBoilerplate(useMaterial3: false, backwardsCompatibility: false);
expect(SystemChrome.latestStyle!.statusBarColor, null);
await pumpBoilerplate(useMaterial3: false, backwardsCompatibility: true);
expect(SystemChrome.latestStyle!.statusBarColor, null);
await pumpBoilerplate(useMaterial3: true, backwardsCompatibility: false);
expect(SystemChrome.latestStyle!.statusBarColor, Colors.transparent);
await pumpBoilerplate(useMaterial3: true, backwardsCompatibility: true);
expect(SystemChrome.latestStyle!.statusBarColor, null);
});
testWidgets('AppBar systemOverlayStyle is use to style status bar and navigation bar', (WidgetTester tester) async {
final SystemUiOverlayStyle systemOverlayStyle = SystemUiOverlayStyle.light.copyWith(
statusBarColor: Colors.red,
systemNavigationBarColor: Colors.green,
);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('test'),
systemOverlayStyle: systemOverlayStyle,
),
),
),
);
expect(SystemChrome.latestStyle!.statusBarColor, Colors.red);
expect(SystemChrome.latestStyle!.systemNavigationBarColor, Colors.green);
});
testWidgets('Changing SliverAppBar snap from true to false', (WidgetTester tester) async { testWidgets('Changing SliverAppBar snap from true to false', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/17598 // Regression test for https://github.com/flutter/flutter/issues/17598
const double appBarHeight = 256.0; const double appBarHeight = 256.0;
......
...@@ -228,6 +228,72 @@ void main() { ...@@ -228,6 +228,72 @@ void main() {
variant: TargetPlatformVariant.only(TargetPlatform.android), variant: TargetPlatformVariant.only(TargetPlatform.android),
); );
}); });
testWidgets('Top AnnotatedRegion provides status bar overlay style and bottom AnnotatedRegion provides navigation bar overlay style', (WidgetTester tester) async {
setupTestDevice();
await tester.pumpWidget(
Column(children: const <Widget>[
Expanded(child: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
systemNavigationBarColor: Colors.blue,
statusBarColor: Colors.blue
),
child: SizedBox.expand(),
)),
Expanded(child: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
systemNavigationBarColor: Colors.green,
statusBarColor: Colors.green,
),
child: SizedBox.expand(),
)),
]),
);
await tester.pumpAndSettle();
expect(SystemChrome.latestStyle?.statusBarColor, Colors.blue);
expect(SystemChrome.latestStyle?.systemNavigationBarColor, Colors.green);
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
testWidgets('Top only AnnotatedRegion provides status bar and navigation bar style properties', (WidgetTester tester) async {
setupTestDevice();
await tester.pumpWidget(
Column(children: const <Widget>[
Expanded(child: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
systemNavigationBarColor: Colors.blue,
statusBarColor: Colors.blue
),
child: SizedBox.expand(),
)),
Expanded(child: SizedBox.expand()),
]),
);
await tester.pumpAndSettle();
expect(SystemChrome.latestStyle?.statusBarColor, Colors.blue);
expect(SystemChrome.latestStyle?.systemNavigationBarColor, Colors.blue);
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
testWidgets('Bottom only AnnotatedRegion provides status bar and navigation bar style properties', (WidgetTester tester) async {
setupTestDevice();
await tester.pumpWidget(
Column(children: const <Widget>[
Expanded(child: SizedBox.expand()),
Expanded(child: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
systemNavigationBarColor: Colors.green,
statusBarColor: Colors.green
),
child: SizedBox.expand(),
)),
]),
);
await tester.pumpAndSettle();
expect(SystemChrome.latestStyle?.statusBarColor, Colors.green);
expect(SystemChrome.latestStyle?.systemNavigationBarColor, Colors.green);
}, variant: TargetPlatformVariant.only(TargetPlatform.android));
}); });
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment