Customized Cupertino navigation bar. (#14307)
- Add override of border color of CupertinoNavigationBar - Add background color to CupertinoPageScaffold
This commit is contained in:
parent
e810bee6e5
commit
b88ba79c04
@ -79,7 +79,7 @@ class CupertinoIcons {
|
|||||||
/// A checkmark in a circle.
|
/// A checkmark in a circle.
|
||||||
static const IconData check_mark_circled = const IconData(0xf3fe, fontFamily: iconFont, fontPackage: iconFontPackage);
|
static const IconData check_mark_circled = const IconData(0xf3fe, fontFamily: iconFont, fontPackage: iconFontPackage);
|
||||||
|
|
||||||
/// A thicker left chevron used in iOS for the nav bar back button.
|
/// A thicker left chevron used in iOS for the navigation bar back button.
|
||||||
static const IconData back = const IconData(0xf3cf, fontFamily: iconFont, fontPackage: iconFontPackage, matchTextDirection: true);
|
static const IconData back = const IconData(0xf3cf, fontFamily: iconFont, fontPackage: iconFontPackage, matchTextDirection: true);
|
||||||
|
|
||||||
/// Outline of a simple front-facing house.
|
/// Outline of a simple front-facing house.
|
||||||
|
@ -37,6 +37,14 @@ const Duration _kNavBarTitleFadeDuration = const Duration(milliseconds: 150);
|
|||||||
const Color _kDefaultNavBarBackgroundColor = const Color(0xCCF8F8F8);
|
const Color _kDefaultNavBarBackgroundColor = const Color(0xCCF8F8F8);
|
||||||
const Color _kDefaultNavBarBorderColor = const Color(0x4C000000);
|
const Color _kDefaultNavBarBorderColor = const Color(0x4C000000);
|
||||||
|
|
||||||
|
const Border _kDefaultNavBarBorder = const Border(
|
||||||
|
bottom: const BorderSide(
|
||||||
|
color: _kDefaultNavBarBorderColor,
|
||||||
|
width: 0.0, // One physical pixel.
|
||||||
|
style: BorderStyle.solid,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const TextStyle _kLargeTitleTextStyle = const TextStyle(
|
const TextStyle _kLargeTitleTextStyle = const TextStyle(
|
||||||
fontFamily: '.SF UI Text',
|
fontFamily: '.SF UI Text',
|
||||||
fontSize: 34.0,
|
fontSize: 34.0,
|
||||||
@ -77,6 +85,7 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
|
|||||||
this.automaticallyImplyLeading: true,
|
this.automaticallyImplyLeading: true,
|
||||||
this.middle,
|
this.middle,
|
||||||
this.trailing,
|
this.trailing,
|
||||||
|
this.border: _kDefaultNavBarBorder,
|
||||||
this.backgroundColor: _kDefaultNavBarBackgroundColor,
|
this.backgroundColor: _kDefaultNavBarBackgroundColor,
|
||||||
this.actionsForegroundColor: CupertinoColors.activeBlue,
|
this.actionsForegroundColor: CupertinoColors.activeBlue,
|
||||||
}) : assert(automaticallyImplyLeading != null),
|
}) : assert(automaticallyImplyLeading != null),
|
||||||
@ -109,6 +118,11 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
|
|||||||
/// behind it.
|
/// behind it.
|
||||||
final Color backgroundColor;
|
final Color backgroundColor;
|
||||||
|
|
||||||
|
/// The border of the navigation bar. By default renders a single pixel bottom border side.
|
||||||
|
///
|
||||||
|
/// If a border is null, the navigation bar will not display a border.
|
||||||
|
final Border border;
|
||||||
|
|
||||||
/// Default color used for text and icons of the [leading] and [trailing]
|
/// Default color used for text and icons of the [leading] and [trailing]
|
||||||
/// widgets in the navigation bar.
|
/// widgets in the navigation bar.
|
||||||
///
|
///
|
||||||
@ -128,6 +142,7 @@ class CupertinoNavigationBar extends StatelessWidget implements ObstructingPrefe
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return _wrapWithBackground(
|
return _wrapWithBackground(
|
||||||
|
border: border,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
child: new _CupertinoPersistentNavigationBar(
|
child: new _CupertinoPersistentNavigationBar(
|
||||||
leading: leading,
|
leading: leading,
|
||||||
@ -265,16 +280,14 @@ class CupertinoSliverNavigationBar extends StatelessWidget {
|
|||||||
|
|
||||||
/// Returns `child` wrapped with background and a bottom border if background color
|
/// Returns `child` wrapped with background and a bottom border if background color
|
||||||
/// is opaque. Otherwise, also blur with [BackdropFilter].
|
/// is opaque. Otherwise, also blur with [BackdropFilter].
|
||||||
Widget _wrapWithBackground({Color backgroundColor, Widget child}) {
|
Widget _wrapWithBackground({
|
||||||
|
Border border,
|
||||||
|
Color backgroundColor,
|
||||||
|
Widget child,
|
||||||
|
}) {
|
||||||
final DecoratedBox childWithBackground = new DecoratedBox(
|
final DecoratedBox childWithBackground = new DecoratedBox(
|
||||||
decoration: new BoxDecoration(
|
decoration: new BoxDecoration(
|
||||||
border: const Border(
|
border: border,
|
||||||
bottom: const BorderSide(
|
|
||||||
color: _kDefaultNavBarBorderColor,
|
|
||||||
width: 0.0, // One physical pixel.
|
|
||||||
style: BorderStyle.solid,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
color: backgroundColor,
|
color: backgroundColor,
|
||||||
),
|
),
|
||||||
child: child,
|
child: child,
|
||||||
@ -282,7 +295,8 @@ Widget _wrapWithBackground({Color backgroundColor, Widget child}) {
|
|||||||
|
|
||||||
final bool darkBackground = backgroundColor.computeLuminance() < 0.179;
|
final bool darkBackground = backgroundColor.computeLuminance() < 0.179;
|
||||||
SystemChrome.setSystemUIOverlayStyle(
|
SystemChrome.setSystemUIOverlayStyle(
|
||||||
darkBackground ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark);
|
darkBackground ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark
|
||||||
|
);
|
||||||
|
|
||||||
if (backgroundColor.alpha == 0xFF)
|
if (backgroundColor.alpha == 0xFF)
|
||||||
return childWithBackground;
|
return childWithBackground;
|
||||||
@ -424,7 +438,8 @@ class _CupertinoLargeTitleNavigationBarSliverDelegate
|
|||||||
this.automaticallyImplyLeading,
|
this.automaticallyImplyLeading,
|
||||||
this.middle,
|
this.middle,
|
||||||
this.trailing,
|
this.trailing,
|
||||||
this.backgroundColor,
|
this.border: _kDefaultNavBarBorder,
|
||||||
|
this.backgroundColor: _kDefaultNavBarBackgroundColor,
|
||||||
this.actionsForegroundColor,
|
this.actionsForegroundColor,
|
||||||
}) : assert(persistentHeight != null);
|
}) : assert(persistentHeight != null);
|
||||||
|
|
||||||
@ -442,6 +457,8 @@ class _CupertinoLargeTitleNavigationBarSliverDelegate
|
|||||||
|
|
||||||
final Color backgroundColor;
|
final Color backgroundColor;
|
||||||
|
|
||||||
|
final Border border;
|
||||||
|
|
||||||
final Color actionsForegroundColor;
|
final Color actionsForegroundColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -467,6 +484,7 @@ class _CupertinoLargeTitleNavigationBarSliverDelegate
|
|||||||
);
|
);
|
||||||
|
|
||||||
return _wrapWithBackground(
|
return _wrapWithBackground(
|
||||||
|
border: border,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
child: new Stack(
|
child: new Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
|
@ -22,6 +22,7 @@ class CupertinoPageScaffold extends StatelessWidget {
|
|||||||
const CupertinoPageScaffold({
|
const CupertinoPageScaffold({
|
||||||
Key key,
|
Key key,
|
||||||
this.navigationBar,
|
this.navigationBar,
|
||||||
|
this.backgroundColor: CupertinoColors.white,
|
||||||
@required this.child,
|
@required this.child,
|
||||||
}) : assert(child != null),
|
}) : assert(child != null),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
@ -32,7 +33,7 @@ class CupertinoPageScaffold extends StatelessWidget {
|
|||||||
/// If translucent, the main content may slide behind it.
|
/// If translucent, the main content may slide behind it.
|
||||||
/// Otherwise, the main content's top margin will be offset by its height.
|
/// Otherwise, the main content's top margin will be offset by its height.
|
||||||
///
|
///
|
||||||
/// The scaffold assumes the nav bar will consume the [MediaQuery] top padding.
|
/// The scaffold assumes the navigation bar will consume the [MediaQuery] top padding.
|
||||||
// TODO(xster): document its page transition animation when ready
|
// TODO(xster): document its page transition animation when ready
|
||||||
final ObstructingPreferredSizeWidget navigationBar;
|
final ObstructingPreferredSizeWidget navigationBar;
|
||||||
|
|
||||||
@ -44,6 +45,11 @@ class CupertinoPageScaffold extends StatelessWidget {
|
|||||||
/// [navigationBar].
|
/// [navigationBar].
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
|
/// The color of the widget that underlies the entire scaffold.
|
||||||
|
///
|
||||||
|
/// By default uses [CupertinoColors.white] color.
|
||||||
|
final Color backgroundColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<Widget> stacked = <Widget>[];
|
final List<Widget> stacked = <Widget>[];
|
||||||
@ -57,8 +63,8 @@ class CupertinoPageScaffold extends StatelessWidget {
|
|||||||
final double topPadding = navigationBar.preferredSize.height
|
final double topPadding = navigationBar.preferredSize.height
|
||||||
+ existingMediaQuery.padding.top;
|
+ existingMediaQuery.padding.top;
|
||||||
|
|
||||||
// If nav bar is opaquely obstructing, directly shift the main content
|
// If navigation bar is opaquely obstructing, directly shift the main content
|
||||||
// down. If translucent, let main content draw behind nav bar but hint the
|
// down. If translucent, let main content draw behind navigation bar but hint the
|
||||||
// obstructed area.
|
// obstructed area.
|
||||||
if (navigationBar.fullObstruction) {
|
if (navigationBar.fullObstruction) {
|
||||||
paddedContent = new Padding(
|
paddedContent = new Padding(
|
||||||
@ -90,7 +96,7 @@ class CupertinoPageScaffold extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return new DecoratedBox(
|
return new DecoratedBox(
|
||||||
decoration: const BoxDecoration(color: CupertinoColors.white),
|
decoration: new BoxDecoration(color: backgroundColor),
|
||||||
child: new Stack(
|
child: new Stack(
|
||||||
children: stacked,
|
children: stacked,
|
||||||
),
|
),
|
||||||
|
@ -395,6 +395,92 @@ void main() {
|
|||||||
|
|
||||||
expect(find.text('Home page'), findsOneWidget);
|
expect(find.text('Home page'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Border should be displayed by default', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new WidgetsApp(
|
||||||
|
color: const Color(0xFFFFFFFF),
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return new CupertinoPageRoute<Null>(
|
||||||
|
settings: settings,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return const CupertinoNavigationBar(
|
||||||
|
middle: const Text('Title'),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final DecoratedBox decoratedBox = tester.widgetList(find.byType(DecoratedBox)).elementAt(1);
|
||||||
|
expect(decoratedBox.decoration.runtimeType, BoxDecoration);
|
||||||
|
|
||||||
|
final BoxDecoration decoration = decoratedBox.decoration;
|
||||||
|
expect(decoration.border, isNotNull);
|
||||||
|
|
||||||
|
final BorderSide side = decoration.border.bottom;
|
||||||
|
expect(side, isNotNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Overrides border color', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new WidgetsApp(
|
||||||
|
color: const Color(0xFFFFFFFF),
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return new CupertinoPageRoute<Null>(
|
||||||
|
settings: settings,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return const CupertinoNavigationBar(
|
||||||
|
middle: const Text('Title'),
|
||||||
|
border: const Border(
|
||||||
|
bottom: const BorderSide(
|
||||||
|
color: const Color(0xFFAABBCC),
|
||||||
|
width: 0.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final DecoratedBox decoratedBox = tester.widgetList(find.byType(DecoratedBox)).elementAt(1);
|
||||||
|
expect(decoratedBox.decoration.runtimeType, BoxDecoration);
|
||||||
|
|
||||||
|
final BoxDecoration decoration = decoratedBox.decoration;
|
||||||
|
expect(decoration.border, isNotNull);
|
||||||
|
|
||||||
|
final BorderSide side = decoration.border.bottom;
|
||||||
|
expect(side, isNotNull);
|
||||||
|
expect(side.color, const Color(0xFFAABBCC));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Border should not be displayed when null', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new WidgetsApp(
|
||||||
|
color: const Color(0xFFFFFFFF),
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return new CupertinoPageRoute<Null>(
|
||||||
|
settings: settings,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return const CupertinoNavigationBar(
|
||||||
|
middle: const Text('Title'),
|
||||||
|
border: null,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final DecoratedBox decoratedBox = tester.widgetList(find.byType(DecoratedBox)).elementAt(1);
|
||||||
|
expect(decoratedBox.decoration.runtimeType, BoxDecoration);
|
||||||
|
|
||||||
|
final BoxDecoration decoration = decoratedBox.decoration;
|
||||||
|
expect(decoration.border, isNull);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ExpectStyles extends StatelessWidget {
|
class _ExpectStyles extends StatelessWidget {
|
||||||
|
@ -254,4 +254,53 @@ void main() {
|
|||||||
expect(find.text('Page 1 of tab 2'), isOnstage);
|
expect(find.text('Page 1 of tab 2'), isOnstage);
|
||||||
expect(find.text('Page 2 of tab 1', skipOffstage: false), isOffstage);
|
expect(find.text('Page 2 of tab 1', skipOffstage: false), isOffstage);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('Decorated with white background by default', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new WidgetsApp(
|
||||||
|
color: const Color(0xFFFFFFFF),
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return new CupertinoPageRoute<Null>(
|
||||||
|
settings: settings,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return const CupertinoPageScaffold(
|
||||||
|
child: const Center(),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final DecoratedBox decoratedBox = tester.widgetList(find.byType(DecoratedBox)).elementAt(1);
|
||||||
|
expect(decoratedBox.decoration.runtimeType, BoxDecoration);
|
||||||
|
|
||||||
|
final BoxDecoration decoration = decoratedBox.decoration;
|
||||||
|
expect(decoration.color, CupertinoColors.white);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Overrides background color', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(
|
||||||
|
new WidgetsApp(
|
||||||
|
color: const Color(0xFFFFFFFF),
|
||||||
|
onGenerateRoute: (RouteSettings settings) {
|
||||||
|
return new CupertinoPageRoute<Null>(
|
||||||
|
settings: settings,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return const CupertinoPageScaffold(
|
||||||
|
child: const Center(),
|
||||||
|
backgroundColor: const Color(0xFF010203),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final DecoratedBox decoratedBox = tester.widgetList(find.byType(DecoratedBox)).elementAt(1);
|
||||||
|
expect(decoratedBox.decoration.runtimeType, BoxDecoration);
|
||||||
|
|
||||||
|
final BoxDecoration decoration = decoratedBox.decoration;
|
||||||
|
expect(decoration.color, const Color(0xFF010203));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user