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: Bruno Leroux <bruno.leroux@gmail.com>
This commit is contained in:
parent
5628ebf662
commit
f1cdfa289d
@ -879,7 +879,13 @@ class _AppBarState extends State<AppBar> {
|
||||
final SystemUiOverlayStyle style = brightness == Brightness.dark
|
||||
? SystemUiOverlayStyle.light
|
||||
: 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
|
||||
|
@ -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
|
||||
/// 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
|
||||
/// reset, nor does setting it to true cause the app to update immediately.
|
||||
///
|
||||
@ -312,20 +317,47 @@ class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox>
|
||||
case TargetPlatform.windows:
|
||||
break;
|
||||
}
|
||||
// If there are no overlay styles in the UI don't bother updating.
|
||||
if (upperOverlayStyle != null || lowerOverlayStyle != null) {
|
||||
// If there are no overlay style in the UI don't bother updating.
|
||||
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(
|
||||
statusBarBrightness: upperOverlayStyle?.statusBarBrightness,
|
||||
statusBarIconBrightness: upperOverlayStyle?.statusBarIconBrightness,
|
||||
statusBarColor: upperOverlayStyle?.statusBarColor,
|
||||
systemStatusBarContrastEnforced: upperOverlayStyle?.systemStatusBarContrastEnforced,
|
||||
systemNavigationBarColor: lowerOverlayStyle?.systemNavigationBarColor,
|
||||
systemNavigationBarDividerColor: lowerOverlayStyle?.systemNavigationBarDividerColor,
|
||||
systemNavigationBarIconBrightness: lowerOverlayStyle?.systemNavigationBarIconBrightness,
|
||||
systemNavigationBarContrastEnforced: lowerOverlayStyle?.systemNavigationBarContrastEnforced,
|
||||
statusBarBrightness: upperOverlayStyle.statusBarBrightness,
|
||||
statusBarIconBrightness: upperOverlayStyle.statusBarIconBrightness,
|
||||
statusBarColor: upperOverlayStyle.statusBarColor,
|
||||
systemStatusBarContrastEnforced: upperOverlayStyle.systemStatusBarContrastEnforced,
|
||||
systemNavigationBarColor: lowerOverlayStyle.systemNavigationBarColor,
|
||||
systemNavigationBarDividerColor: lowerOverlayStyle.systemNavigationBarDividerColor,
|
||||
systemNavigationBarIconBrightness: lowerOverlayStyle.systemNavigationBarIconBrightness,
|
||||
systemNavigationBarContrastEnforced: lowerOverlayStyle.systemNavigationBarContrastEnforced,
|
||||
);
|
||||
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
|
||||
|
@ -558,7 +558,13 @@ class SystemChrome {
|
||||
/// 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
|
||||
/// 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}
|
||||
/// The following example creates a widget that changes the status bar color
|
||||
|
@ -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 {
|
||||
// Regression test for https://github.com/flutter/flutter/issues/17598
|
||||
const double appBarHeight = 256.0;
|
||||
|
@ -228,6 +228,72 @@ void main() {
|
||||
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));
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user