CupertinoNavigationBar part 2 - create a bare bone CupertinoNavigationBar (#10423)
Create a CupertinoNavigationBar without automatic adding of the lead button
This commit is contained in:
parent
a8777ce6b0
commit
2aaa7f88b3
@ -12,6 +12,7 @@ export 'src/cupertino/bottom_tab_bar.dart';
|
||||
export 'src/cupertino/button.dart';
|
||||
export 'src/cupertino/colors.dart';
|
||||
export 'src/cupertino/dialog.dart';
|
||||
export 'src/cupertino/nav_bar.dart';
|
||||
export 'src/cupertino/page.dart';
|
||||
export 'src/cupertino/slider.dart';
|
||||
export 'src/cupertino/switch.dart';
|
||||
|
@ -13,7 +13,24 @@ import 'colors.dart';
|
||||
const double _kTabBarHeight = 50.0;
|
||||
|
||||
const Color _kDefaultTabBarBackgroundColor = const Color(0xCCF8F8F8);
|
||||
const Color _kDefaultTabBarBorderColor = const Color(0x4C000000);
|
||||
|
||||
/// An iOS styled bottom navigation tab bar.
|
||||
///
|
||||
/// Displays multiple tabs using [BottomNavigationBarItem] with one tab being
|
||||
/// active, the first tab by default.
|
||||
///
|
||||
/// This [StatelessWidget] doesn't store the active tab itself. You must
|
||||
/// listen to the [onTap] callbacks and call `setState` with a new [currentIndex]
|
||||
/// for the new selection to reflect.
|
||||
///
|
||||
/// Tab changes typically trigger a switch between [Navigator]s, each with its
|
||||
/// own navigation stack, per standard iOS design.
|
||||
///
|
||||
/// If the given [backgroundColor]'s opacity is not 1.0 (which is the case by
|
||||
/// default), it will produce a blurring effect to the content behind it.
|
||||
///
|
||||
// TODO(xster): document using with a CupertinoScaffold.
|
||||
class CupertinoTabBar extends StatelessWidget {
|
||||
CupertinoTabBar({
|
||||
Key key,
|
||||
@ -72,7 +89,7 @@ class CupertinoTabBar extends StatelessWidget {
|
||||
decoration: new BoxDecoration(
|
||||
border: const Border(
|
||||
top: const BorderSide(
|
||||
color: const Color(0x4C000000),
|
||||
color: _kDefaultTabBarBorderColor,
|
||||
width: 0.0, // One physical pixel.
|
||||
style: BorderStyle.solid,
|
||||
),
|
||||
|
146
packages/flutter/lib/src/cupertino/nav_bar.dart
Normal file
146
packages/flutter/lib/src/cupertino/nav_bar.dart
Normal file
@ -0,0 +1,146 @@
|
||||
// Copyright 2017 The Chromium 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 'dart:ui' show ImageFilter;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'colors.dart';
|
||||
|
||||
// Standard iOS 10 nav bar height without the status bar.
|
||||
const double _kNavBarHeight = 44.0;
|
||||
|
||||
const Color _kDefaultNavBarBackgroundColor = const Color(0xCCF8F8F8);
|
||||
const Color _kDefaultNavBarBorderColor = const Color(0x4C000000);
|
||||
|
||||
/// An iOS styled navigation bar.
|
||||
///
|
||||
/// The navigation bar is a toolbar that minimally consists of a widget, normally
|
||||
/// a page title, in the [middle] of the toolbar.
|
||||
///
|
||||
/// It also supports a [leading] and [trailing] widget before and after the
|
||||
/// [middle] widget while keeping the [middle] widget centered.
|
||||
///
|
||||
/// It should be placed at top of the screen and automatically accounts for
|
||||
/// the OS's status bar.
|
||||
///
|
||||
/// If the given [backgroundColor]'s opacity is not 1.0 (which is the case by
|
||||
/// default), it will produce a blurring effect to the content behind it.
|
||||
///
|
||||
// TODO(xster): document automatic addition of a CupertinoBackButton.
|
||||
// TODO(xster): add sample code using icons.
|
||||
// TODO(xster): document integration into a CupertinoScaffold.
|
||||
class CupertinoNavigationBar extends StatelessWidget implements PreferredSizeWidget {
|
||||
const CupertinoNavigationBar({
|
||||
Key key,
|
||||
this.leading,
|
||||
@required this.middle,
|
||||
this.trailing,
|
||||
this.backgroundColor: _kDefaultNavBarBackgroundColor,
|
||||
this.actionsForegroundColor: CupertinoColors.activeBlue,
|
||||
}) : assert(middle != null, 'There must be a middle widget, usually a title'),
|
||||
super(key: key);
|
||||
|
||||
/// Widget to place at the start of the nav bar. Normally a back button
|
||||
/// for a normal page or a cancel button for full page dialogs.
|
||||
final Widget leading;
|
||||
|
||||
/// Widget to place in the middle of the nav bar. Normally a title or
|
||||
/// a segmented control.
|
||||
final Widget middle;
|
||||
|
||||
/// Widget to place at the end of the nav bar. Normally additional actions
|
||||
/// taken on the page such as a search or edit function.
|
||||
final Widget trailing;
|
||||
|
||||
// TODO(xster): implement support for double row nav bars.
|
||||
|
||||
/// The background color of the nav bar. If it contains transparency, the
|
||||
/// tab bar will automatically produce a blurring effect to the content
|
||||
/// behind it.
|
||||
final Color backgroundColor;
|
||||
|
||||
/// Default color used for text and icons of the [leading] and [trailing]
|
||||
/// widgets in the nav bar.
|
||||
///
|
||||
/// The [title] remains black if it's a text as per iOS standard design.
|
||||
final Color actionsForegroundColor;
|
||||
|
||||
@override
|
||||
Size get preferredSize => const Size.fromHeight(_kNavBarHeight);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool addBlur = backgroundColor.alpha != 0xFF;
|
||||
|
||||
Widget styledMiddle = middle;
|
||||
if (styledMiddle.runtimeType == Text || styledMiddle.runtimeType == DefaultTextStyle) {
|
||||
// Let the middle be black rather than `actionsForegroundColor` in case
|
||||
// it's a plain text title.
|
||||
styledMiddle = DefaultTextStyle.merge(
|
||||
style: const TextStyle(color: CupertinoColors.black),
|
||||
child: middle,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO(xster): automatically build a CupertinoBackButton.
|
||||
|
||||
Widget result = new DecoratedBox(
|
||||
decoration: new BoxDecoration(
|
||||
border: const Border(
|
||||
bottom: const BorderSide(
|
||||
color: _kDefaultNavBarBorderColor,
|
||||
width: 0.0, // One physical pixel.
|
||||
style: BorderStyle.solid,
|
||||
),
|
||||
),
|
||||
color: backgroundColor,
|
||||
),
|
||||
child: new SizedBox(
|
||||
height: _kNavBarHeight + MediaQuery.of(context).padding.top,
|
||||
child: IconTheme.merge(
|
||||
data: new IconThemeData(
|
||||
color: actionsForegroundColor,
|
||||
size: 22.0,
|
||||
),
|
||||
child: DefaultTextStyle.merge(
|
||||
style: new TextStyle(
|
||||
fontSize: 17.0,
|
||||
letterSpacing: -0.24,
|
||||
color: actionsForegroundColor,
|
||||
),
|
||||
child: new Padding(
|
||||
padding: new EdgeInsets.only(
|
||||
top: MediaQuery.of(context).padding.top,
|
||||
// TODO(xster): dynamically reduce padding when an automatic
|
||||
// CupertinoBackButton is present.
|
||||
left: 16.0,
|
||||
right: 16.0,
|
||||
),
|
||||
child: new NavigationToolbar(
|
||||
leading: leading,
|
||||
middle: styledMiddle,
|
||||
trailing: trailing,
|
||||
centerMiddle: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (addBlur) {
|
||||
// For non-opaque backgrounds, apply a blur effect.
|
||||
result = new ClipRect(
|
||||
child: new BackdropFilter(
|
||||
filter: new ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0),
|
||||
child: result,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
71
packages/flutter/test/cupertino/nav_bar_test.dart
Normal file
71
packages/flutter/test/cupertino/nav_bar_test.dart
Normal file
@ -0,0 +1,71 @@
|
||||
// Copyright 2017 The Chromium 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:flutter/cupertino.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('Middle still in center with asymmetrical actions', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
new WidgetsApp(
|
||||
color: const Color(0xFFFFFFFF),
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return new PageRouteBuilder<Null>(
|
||||
settings: settings,
|
||||
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
|
||||
return const CupertinoNavigationBar(
|
||||
leading: const CupertinoButton(child: const Text('Something'), onPressed: null,),
|
||||
middle: const Text('Title'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// Expect the middle of the title to be exactly in the middle of the screen.
|
||||
expect(tester.getCenter(find.text('Title')).dx, 400.0);
|
||||
});
|
||||
|
||||
testWidgets('Opaque background does not add blur effects', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
new WidgetsApp(
|
||||
color: const Color(0xFFFFFFFF),
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return new PageRouteBuilder<Null>(
|
||||
settings: settings,
|
||||
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
|
||||
return const CupertinoNavigationBar(
|
||||
middle: const Text('Title'),
|
||||
backgroundColor: const Color(0xFFE5E5E5),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(find.byType(BackdropFilter), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('Non-opaque background adds blur effects', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(
|
||||
new WidgetsApp(
|
||||
color: const Color(0xFFFFFFFF),
|
||||
onGenerateRoute: (RouteSettings settings) {
|
||||
return new PageRouteBuilder<Null>(
|
||||
settings: settings,
|
||||
pageBuilder: (BuildContext context, Animation<double> animation, Animation<double> secondaryAnimation) {
|
||||
return const CupertinoNavigationBar(
|
||||
middle: const Text('Title'),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(find.byType(BackdropFilter), findsOneWidget);
|
||||
});
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user