CupertinoTabBar (#10264)
Move some Cupertino colors to a common file. Create a CupertinoTabBar widget to mimic iOS looks
This commit is contained in:
parent
f53d0fece2
commit
69c2542458
@ -8,7 +8,9 @@
|
|||||||
library cupertino;
|
library cupertino;
|
||||||
|
|
||||||
export 'src/cupertino/activity_indicator.dart';
|
export 'src/cupertino/activity_indicator.dart';
|
||||||
|
export 'src/cupertino/bottom_tab_bar.dart';
|
||||||
export 'src/cupertino/button.dart';
|
export 'src/cupertino/button.dart';
|
||||||
|
export 'src/cupertino/colors.dart';
|
||||||
export 'src/cupertino/dialog.dart';
|
export 'src/cupertino/dialog.dart';
|
||||||
export 'src/cupertino/page.dart';
|
export 'src/cupertino/page.dart';
|
||||||
export 'src/cupertino/slider.dart';
|
export 'src/cupertino/slider.dart';
|
||||||
|
@ -6,6 +6,8 @@ import 'dart:math' as math;
|
|||||||
|
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'colors.dart';
|
||||||
|
|
||||||
/// An iOS-style activity indicator.
|
/// An iOS-style activity indicator.
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
@ -80,7 +82,7 @@ class _CupertinoActivityIndicatorState extends State<CupertinoActivityIndicator>
|
|||||||
const double _kTwoPI = math.PI * 2.0;
|
const double _kTwoPI = math.PI * 2.0;
|
||||||
const int _kTickCount = 12;
|
const int _kTickCount = 12;
|
||||||
const int _kHalfTickCount = _kTickCount ~/ 2;
|
const int _kHalfTickCount = _kTickCount ~/ 2;
|
||||||
const Color _kTickColor = const Color(0xFFE0E0E0);
|
const Color _kTickColor = CupertinoColors.lightBackgroundGray;
|
||||||
const Color _kActiveTickColor = const Color(0xFF9D9D9D);
|
const Color _kActiveTickColor = const Color(0xFF9D9D9D);
|
||||||
final RRect _kTickFundamentalRRect = new RRect.fromLTRBXY(-10.0, 1.0, -5.0, -1.0, 1.0, 1.0);
|
final RRect _kTickFundamentalRRect = new RRect.fromLTRBXY(-10.0, 1.0, -5.0, -1.0, 1.0, 1.0);
|
||||||
|
|
||||||
|
161
packages/flutter/lib/src/cupertino/bottom_tab_bar.dart
Normal file
161
packages/flutter/lib/src/cupertino/bottom_tab_bar.dart
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
// 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 tab bar height.
|
||||||
|
const double _kTabBarHeight = 50.0;
|
||||||
|
|
||||||
|
const Color _kDefaultTabBarBackgroundColor = const Color(0xCCF8F8F8);
|
||||||
|
|
||||||
|
class CupertinoTabBar extends StatelessWidget {
|
||||||
|
CupertinoTabBar({
|
||||||
|
Key key,
|
||||||
|
@required this.items,
|
||||||
|
this.onTap,
|
||||||
|
this.currentIndex: 0,
|
||||||
|
this.backgroundColor: _kDefaultTabBarBackgroundColor,
|
||||||
|
this.activeColor: CupertinoColors.activeBlue,
|
||||||
|
this.inactiveColor: CupertinoColors.inactiveGray,
|
||||||
|
this.iconSize: 24.0,
|
||||||
|
}) : super(key: key) {
|
||||||
|
assert(items != null);
|
||||||
|
assert(items.length >= 2);
|
||||||
|
assert(0 <= currentIndex && currentIndex < items.length);
|
||||||
|
assert(iconSize != null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The interactive items laid out within the bottom navigation bar.
|
||||||
|
final List<BottomNavigationBarItem> items;
|
||||||
|
|
||||||
|
/// The callback that is called when a item is tapped.
|
||||||
|
///
|
||||||
|
/// The widget creating the bottom navigation bar needs to keep track of the
|
||||||
|
/// current index and call `setState` to rebuild it with the newly provided
|
||||||
|
/// index.
|
||||||
|
final ValueChanged<int> onTap;
|
||||||
|
|
||||||
|
/// The index into [items] of the current active item.
|
||||||
|
final int currentIndex;
|
||||||
|
|
||||||
|
/// The background color of the tab bar. If it contains transparency, the
|
||||||
|
/// tab bar will automatically produce a blurring effect to the content
|
||||||
|
/// behind it.
|
||||||
|
final Color backgroundColor;
|
||||||
|
|
||||||
|
/// The foreground color of the icon and title for the [BottomNavigationBarItem]
|
||||||
|
/// of the selected tab.
|
||||||
|
final Color activeColor;
|
||||||
|
|
||||||
|
/// The foreground color of the icon and title for the [BottomNavigationBarItem]s
|
||||||
|
/// in the unselected state.
|
||||||
|
final Color inactiveColor;
|
||||||
|
|
||||||
|
/// The size of all of the [BottomNavigationBarItem] icons.
|
||||||
|
///
|
||||||
|
/// This value is used to to configure the [IconTheme] for the navigation
|
||||||
|
/// bar. When a [BottomNavigationBarItem.icon] widget is not an [Icon] the widget
|
||||||
|
/// should configure itself to match the icon theme's size and color.
|
||||||
|
final double iconSize;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final bool addBlur = backgroundColor.alpha != 0xFF;
|
||||||
|
|
||||||
|
Widget result = new DecoratedBox(
|
||||||
|
decoration: new BoxDecoration(
|
||||||
|
border: const Border(
|
||||||
|
top: const BorderSide(
|
||||||
|
color: const Color(0x4C000000),
|
||||||
|
width: 0.0, // One physical pixel.
|
||||||
|
style: BorderStyle.solid,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
color: backgroundColor,
|
||||||
|
),
|
||||||
|
// TODO(xster): allow icons-only versions of the tab bar too.
|
||||||
|
child: new SizedBox(
|
||||||
|
height: _kTabBarHeight,
|
||||||
|
child: IconTheme.merge( // Default with the inactive state.
|
||||||
|
data: new IconThemeData(
|
||||||
|
color: inactiveColor,
|
||||||
|
size: iconSize,
|
||||||
|
),
|
||||||
|
child: DefaultTextStyle.merge( // Default with the inactive state.
|
||||||
|
style: new TextStyle(
|
||||||
|
fontSize: 10.0,
|
||||||
|
letterSpacing: 0.12,
|
||||||
|
color: inactiveColor,
|
||||||
|
),
|
||||||
|
child: new Row(
|
||||||
|
// Align bottom since we want the labels to be aligned.
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: _buildTabItems(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> _buildTabItems() {
|
||||||
|
final List<Widget> result = <Widget>[];
|
||||||
|
|
||||||
|
for (int index = 0; index < items.length; ++index) {
|
||||||
|
result.add(
|
||||||
|
_wrapActiveItem(
|
||||||
|
new Expanded(
|
||||||
|
child: new GestureDetector(
|
||||||
|
onTap: onTap == null ? null : () { onTap(index); },
|
||||||
|
child: new Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 4.0),
|
||||||
|
child: new Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: <Widget> [
|
||||||
|
new Expanded(child: new Center(child: items[index].icon)),
|
||||||
|
items[index].title,
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
active: index == currentIndex,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change the active tab item's icon and title colors to active.
|
||||||
|
Widget _wrapActiveItem(Widget item, { bool active }) {
|
||||||
|
if (!active)
|
||||||
|
return item;
|
||||||
|
|
||||||
|
return IconTheme.merge(
|
||||||
|
data: new IconThemeData(color: activeColor),
|
||||||
|
child: DefaultTextStyle.merge(
|
||||||
|
style: new TextStyle(color: activeColor),
|
||||||
|
child: item,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -5,9 +5,8 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
// TODO(xster): move this to a common Cupertino color palette with the next yak.
|
import 'colors.dart';
|
||||||
const Color _kBlue = const Color(0xFF007AFF);
|
|
||||||
const Color _kWhite = const Color(0xFFFFFFFF);
|
|
||||||
const Color _kDisabledBackground = const Color(0xFFA9A9A9);
|
const Color _kDisabledBackground = const Color(0xFFA9A9A9);
|
||||||
const Color _kDisabledForeground = const Color(0xFFC4C4C4);
|
const Color _kDisabledForeground = const Color(0xFFC4C4C4);
|
||||||
|
|
||||||
@ -16,7 +15,7 @@ const TextStyle _kButtonTextStyle = const TextStyle(
|
|||||||
inherit: false,
|
inherit: false,
|
||||||
fontSize: 15.0,
|
fontSize: 15.0,
|
||||||
fontWeight: FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
color: _kBlue,
|
color: CupertinoColors.activeBlue,
|
||||||
textBaseline: TextBaseline.alphabetic,
|
textBaseline: TextBaseline.alphabetic,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -25,7 +24,7 @@ final TextStyle _kDisabledButtonTextStyle = _kButtonTextStyle.copyWith(
|
|||||||
);
|
);
|
||||||
|
|
||||||
final TextStyle _kBackgroundButtonTextStyle = _kButtonTextStyle.copyWith(
|
final TextStyle _kBackgroundButtonTextStyle = _kButtonTextStyle.copyWith(
|
||||||
color: _kWhite,
|
color: CupertinoColors.white,
|
||||||
);
|
);
|
||||||
|
|
||||||
const EdgeInsets _kButtonPadding = const EdgeInsets.all(16.0);
|
const EdgeInsets _kButtonPadding = const EdgeInsets.all(16.0);
|
||||||
|
40
packages/flutter/lib/src/cupertino/colors.dart
Normal file
40
packages/flutter/lib/src/cupertino/colors.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// 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 Color;
|
||||||
|
|
||||||
|
class CupertinoColors {
|
||||||
|
CupertinoColors._();
|
||||||
|
|
||||||
|
/// iOS 10's default blue color. Used to indicate active elements such as
|
||||||
|
/// buttons, selected tabs and your own chat bubbles.
|
||||||
|
static const Color activeBlue = const Color(0xFF007AFF);
|
||||||
|
|
||||||
|
/// iOS 10's default green color. Used to indicate active accents such as
|
||||||
|
/// the switch in its on state and some accent buttons such as the call button
|
||||||
|
/// and Apple Map's 'Go' button.
|
||||||
|
static const Color activeGreen = const Color(0xFF4CD964);
|
||||||
|
|
||||||
|
/// Opaque white color. Used for backgrounds and fonts against dark backgrounds.
|
||||||
|
static const Color white = const Color(0xFFFFFFFF);
|
||||||
|
|
||||||
|
/// Opaque black color. Used for texts against light backgrounds.
|
||||||
|
static const Color black = const Color(0xFF000000);
|
||||||
|
|
||||||
|
/// Used in iOS 10 for light background fills such as the chat bubble background.
|
||||||
|
static const Color lightBackgroundGray = const Color(0xFFE5E5EA);
|
||||||
|
|
||||||
|
/// Used in iOS 10 for unselected selectables such as tab bar items in their
|
||||||
|
/// inactive state.
|
||||||
|
///
|
||||||
|
/// Not the same gray as disabled buttons etc.
|
||||||
|
static const Color inactiveGray = const Color(0xFF929292);
|
||||||
|
|
||||||
|
/// Used for iOS 10 for destructive actions such as the delete actions in
|
||||||
|
/// table view cells and dialogs.
|
||||||
|
///
|
||||||
|
/// Not the same red as the camera shutter or springboard icon notifications
|
||||||
|
/// or the foreground red theme in various native apps such as HealthKit.
|
||||||
|
static const Color destructiveRed = const Color(0xFFFF3B30);
|
||||||
|
}
|
@ -6,6 +6,8 @@ import 'dart:ui' show ImageFilter;
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'colors.dart';
|
||||||
|
|
||||||
// TODO(abarth): These constants probably belong somewhere more general.
|
// TODO(abarth): These constants probably belong somewhere more general.
|
||||||
|
|
||||||
const TextStyle _kCupertinoDialogTitleStyle = const TextStyle(
|
const TextStyle _kCupertinoDialogTitleStyle = const TextStyle(
|
||||||
@ -13,7 +15,7 @@ const TextStyle _kCupertinoDialogTitleStyle = const TextStyle(
|
|||||||
inherit: false,
|
inherit: false,
|
||||||
fontSize: 17.5,
|
fontSize: 17.5,
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
color: const Color(0xFF000000),
|
color: CupertinoColors.black,
|
||||||
height: 1.25,
|
height: 1.25,
|
||||||
textBaseline: TextBaseline.alphabetic,
|
textBaseline: TextBaseline.alphabetic,
|
||||||
);
|
);
|
||||||
@ -23,7 +25,7 @@ const TextStyle _kCupertinoDialogContentStyle = const TextStyle(
|
|||||||
inherit: false,
|
inherit: false,
|
||||||
fontSize: 12.4,
|
fontSize: 12.4,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: const Color(0xFF000000),
|
color: CupertinoColors.black,
|
||||||
height: 1.35,
|
height: 1.35,
|
||||||
textBaseline: TextBaseline.alphabetic,
|
textBaseline: TextBaseline.alphabetic,
|
||||||
);
|
);
|
||||||
@ -33,7 +35,7 @@ const TextStyle _kCupertinoDialogActionStyle = const TextStyle(
|
|||||||
inherit: false,
|
inherit: false,
|
||||||
fontSize: 16.8,
|
fontSize: 16.8,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
color: const Color(0xFF027AFF),
|
color: CupertinoColors.activeBlue,
|
||||||
textBaseline: TextBaseline.alphabetic,
|
textBaseline: TextBaseline.alphabetic,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -180,7 +182,6 @@ class CupertinoAlertDialog extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Color _kDestructiveActionColor = const Color(0xFFFF3B30);
|
|
||||||
|
|
||||||
/// A button typically used in a [CupertinoAlertDialog].
|
/// A button typically used in a [CupertinoAlertDialog].
|
||||||
///
|
///
|
||||||
@ -228,7 +229,7 @@ class CupertinoDialogAction extends StatelessWidget {
|
|||||||
style = style.copyWith(fontWeight: FontWeight.w600);
|
style = style.copyWith(fontWeight: FontWeight.w600);
|
||||||
|
|
||||||
if (isDestructiveAction)
|
if (isDestructiveAction)
|
||||||
style = style.copyWith(color: _kDestructiveActionColor);
|
style = style.copyWith(color: CupertinoColors.destructiveRed);
|
||||||
|
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
style = style.copyWith(color: style.color.withOpacity(0.5));
|
style = style.copyWith(color: style.color.withOpacity(0.5));
|
||||||
|
@ -10,6 +10,7 @@ import 'package:flutter/gestures.dart';
|
|||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'colors.dart';
|
||||||
import 'thumb_painter.dart';
|
import 'thumb_painter.dart';
|
||||||
|
|
||||||
/// An iOS-style slider.
|
/// An iOS-style slider.
|
||||||
@ -48,7 +49,7 @@ class CupertinoSlider extends StatefulWidget {
|
|||||||
this.min: 0.0,
|
this.min: 0.0,
|
||||||
this.max: 1.0,
|
this.max: 1.0,
|
||||||
this.divisions,
|
this.divisions,
|
||||||
this.activeColor: const Color(0xFF027AFF),
|
this.activeColor: CupertinoColors.activeBlue,
|
||||||
}) : assert(value != null),
|
}) : assert(value != null),
|
||||||
assert(min != null),
|
assert(min != null),
|
||||||
assert(max != null),
|
assert(max != null),
|
||||||
|
@ -10,6 +10,7 @@ import 'package:flutter/gestures.dart';
|
|||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'colors.dart';
|
||||||
import 'thumb_painter.dart';
|
import 'thumb_painter.dart';
|
||||||
|
|
||||||
/// An iOS-style switch.
|
/// An iOS-style switch.
|
||||||
@ -30,7 +31,7 @@ class CupertinoSwitch extends StatefulWidget {
|
|||||||
Key key,
|
Key key,
|
||||||
@required this.value,
|
@required this.value,
|
||||||
@required this.onChanged,
|
@required this.onChanged,
|
||||||
this.activeColor: const Color(0xFF4CD964),
|
this.activeColor: CupertinoColors.activeGreen,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
|
|
||||||
/// Whether this switch is on or off.
|
/// Whether this switch is on or off.
|
||||||
@ -130,7 +131,7 @@ const double _kTrackInnerLength = _kTrackInnerEnd - _kTrackInnerStart;
|
|||||||
const double _kSwitchWidth = 59.0;
|
const double _kSwitchWidth = 59.0;
|
||||||
const double _kSwitchHeight = 39.0;
|
const double _kSwitchHeight = 39.0;
|
||||||
|
|
||||||
const Color _kTrackColor = const Color(0xFFE5E5E5);
|
const Color _kTrackColor = CupertinoColors.lightBackgroundGray;
|
||||||
const Duration _kReactionDuration = const Duration(milliseconds: 300);
|
const Duration _kReactionDuration = const Duration(milliseconds: 300);
|
||||||
const Duration _kToggleDuration = const Duration(milliseconds: 200);
|
const Duration _kToggleDuration = const Duration(milliseconds: 200);
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
import 'package:flutter/painting.dart';
|
import 'package:flutter/painting.dart';
|
||||||
|
|
||||||
|
import 'colors.dart';
|
||||||
|
|
||||||
final MaskFilter _kShadowMaskFilter = new MaskFilter.blur(BlurStyle.normal, BoxShadow.convertRadiusToSigma(1.0));
|
final MaskFilter _kShadowMaskFilter = new MaskFilter.blur(BlurStyle.normal, BoxShadow.convertRadiusToSigma(1.0));
|
||||||
|
|
||||||
/// Paints an iOS-style slider thumb.
|
/// Paints an iOS-style slider thumb.
|
||||||
@ -12,7 +14,7 @@ final MaskFilter _kShadowMaskFilter = new MaskFilter.blur(BlurStyle.normal, BoxS
|
|||||||
class CupertinoThumbPainter {
|
class CupertinoThumbPainter {
|
||||||
/// Creates an object that paints an iOS-style slider thumb.
|
/// Creates an object that paints an iOS-style slider thumb.
|
||||||
CupertinoThumbPainter({
|
CupertinoThumbPainter({
|
||||||
this.color: const Color(0xFFFFFFFF),
|
this.color: CupertinoColors.white,
|
||||||
this.shadowColor: const Color(0x2C000000),
|
this.shadowColor: const Color(0x2C000000),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -35,44 +35,6 @@ enum BottomNavigationBarType {
|
|||||||
shifting,
|
shifting,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An interactive destination label within [BottomNavigationBar] with an icon
|
|
||||||
/// and title.
|
|
||||||
///
|
|
||||||
/// See also:
|
|
||||||
///
|
|
||||||
/// * [BottomNavigationBar]
|
|
||||||
/// * <https://material.google.com/components/bottom-navigation.html>
|
|
||||||
class BottomNavigationBarItem {
|
|
||||||
/// Creates an item that is used with [BottomNavigationBar.items].
|
|
||||||
///
|
|
||||||
/// The arguments [icon] and [title] should not be null.
|
|
||||||
BottomNavigationBarItem({
|
|
||||||
@required this.icon,
|
|
||||||
@required this.title,
|
|
||||||
this.backgroundColor
|
|
||||||
}) {
|
|
||||||
assert(icon != null);
|
|
||||||
assert(title != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The icon of the item.
|
|
||||||
///
|
|
||||||
/// Typically the icon is an [Icon] or an [ImageIcon] widget. If another type
|
|
||||||
/// of widget is provided then it should configure itself to match the current
|
|
||||||
/// [IconTheme] size and color.
|
|
||||||
final Widget icon;
|
|
||||||
|
|
||||||
/// The title of the item.
|
|
||||||
final Widget title;
|
|
||||||
|
|
||||||
/// The color of the background radial animation.
|
|
||||||
///
|
|
||||||
/// If the navigation bar's type is [BottomNavigationBarType.shifting], then
|
|
||||||
/// the entire bar is flooded with the [backgroundColor] when this item is
|
|
||||||
/// tapped.
|
|
||||||
final Color backgroundColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A material widget displayed at the bottom of an app for selecting among a
|
/// A material widget displayed at the bottom of an app for selecting among a
|
||||||
/// small number of views.
|
/// small number of views.
|
||||||
///
|
///
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
// 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 Color;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import 'framework.dart';
|
||||||
|
|
||||||
|
/// An interactive button within either material's [BottomNavigationBar]
|
||||||
|
/// or the iOS themed [CupertinoTabBar] with an icon and title.
|
||||||
|
///
|
||||||
|
/// This calss is rarely used in isolation. Commonly embedded in one of the
|
||||||
|
/// bottom navigation widgets above.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [BottomNavigationBar]
|
||||||
|
/// * <https://material.google.com/components/bottom-navigation.html>
|
||||||
|
/// * [CupertinoTabBar]
|
||||||
|
/// * <https://developer.apple.com/ios/human-interface-guidelines/ui-bars/tab-bars>
|
||||||
|
class BottomNavigationBarItem {
|
||||||
|
/// Creates an item that is used with [BottomNavigationBar.items].
|
||||||
|
///
|
||||||
|
/// The arguments [icon] and [title] should not be null.
|
||||||
|
const BottomNavigationBarItem({
|
||||||
|
@required this.icon,
|
||||||
|
@required this.title,
|
||||||
|
this.backgroundColor,
|
||||||
|
}) : assert(icon != null),
|
||||||
|
assert(title != null);
|
||||||
|
|
||||||
|
/// The icon of the item.
|
||||||
|
///
|
||||||
|
/// Typically the icon is an [Icon] or an [ImageIcon] widget. If another type
|
||||||
|
/// of widget is provided then it should configure itself to match the current
|
||||||
|
/// [IconTheme] size and color.
|
||||||
|
final Widget icon;
|
||||||
|
|
||||||
|
/// The title of the item.
|
||||||
|
final Widget title;
|
||||||
|
|
||||||
|
/// The color of the background radial animation for material [BottomNavigationBar].
|
||||||
|
///
|
||||||
|
/// If the navigation bar's type is [BottomNavigationBarType.shifting], then
|
||||||
|
/// the entire bar is flooded with the [backgroundColor] when this item is
|
||||||
|
/// tapped.
|
||||||
|
///
|
||||||
|
/// Not used for [CupertinoTabBar]. Control the invariant bar color directly
|
||||||
|
/// via [CupertinoTabBar.backgroundColor].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [Icon.color] and [ImageIcon.color] to control the foreground color of
|
||||||
|
/// the icons themselves.
|
||||||
|
final Color backgroundColor;
|
||||||
|
}
|
@ -22,6 +22,7 @@ export 'src/widgets/async.dart';
|
|||||||
export 'src/widgets/banner.dart';
|
export 'src/widgets/banner.dart';
|
||||||
export 'src/widgets/basic.dart';
|
export 'src/widgets/basic.dart';
|
||||||
export 'src/widgets/binding.dart';
|
export 'src/widgets/binding.dart';
|
||||||
|
export 'src/widgets/bottom_navigation_bar_item.dart';
|
||||||
export 'src/widgets/container.dart';
|
export 'src/widgets/container.dart';
|
||||||
export 'src/widgets/debug.dart';
|
export 'src/widgets/debug.dart';
|
||||||
export 'src/widgets/dismissible.dart';
|
export 'src/widgets/dismissible.dart';
|
||||||
|
112
packages/flutter/test/cupertino/bottom_tab_bar_test.dart
Normal file
112
packages/flutter/test/cupertino/bottom_tab_bar_test.dart
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// 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/widgets.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
|
import '../services/mocks_for_image_cache.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('Need at least 2 tabs', (WidgetTester tester) async {
|
||||||
|
try {
|
||||||
|
await tester.pumpWidget(new CupertinoTabBar(
|
||||||
|
items: <BottomNavigationBarItem>[
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
icon: const ImageIcon(const TestImageProvider(24, 24)),
|
||||||
|
title: const Text('Tab 1'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
fail('Should not be possible to create a tab bar with just one item');
|
||||||
|
} on AssertionError {
|
||||||
|
// Exception expected.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Active and inactive colors', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(new CupertinoTabBar(
|
||||||
|
items: <BottomNavigationBarItem>[
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
icon: const ImageIcon(const TestImageProvider(24, 24)),
|
||||||
|
title: const Text('Tab 1'),
|
||||||
|
),
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
icon: const ImageIcon(const TestImageProvider(24, 24)),
|
||||||
|
title: const Text('Tab 2'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
currentIndex: 1,
|
||||||
|
activeColor: const Color(0xFF123456),
|
||||||
|
inactiveColor: const Color(0xFF654321),
|
||||||
|
));
|
||||||
|
|
||||||
|
final RichText actualInactive = tester.widget(find.descendant(
|
||||||
|
of: find.text('Tab 1'),
|
||||||
|
matching: find.byType(RichText),
|
||||||
|
));
|
||||||
|
expect(actualInactive.text.style.color, const Color(0xFF654321));
|
||||||
|
|
||||||
|
final RichText actualActive = tester.widget(find.descendant(
|
||||||
|
of: find.text('Tab 2'),
|
||||||
|
matching: find.byType(RichText),
|
||||||
|
));
|
||||||
|
expect(actualActive.text.style.color, const Color(0xFF123456));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Opaque background does not add blur effects', (WidgetTester tester) async {
|
||||||
|
await tester.pumpWidget(new CupertinoTabBar(
|
||||||
|
items: <BottomNavigationBarItem>[
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
icon: const ImageIcon(const TestImageProvider(24, 24)),
|
||||||
|
title: const Text('Tab 1'),
|
||||||
|
),
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
icon: const ImageIcon(const TestImageProvider(24, 24)),
|
||||||
|
title: const Text('Tab 2'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(find.byType(BackdropFilter), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.pumpWidget(new CupertinoTabBar(
|
||||||
|
items: <BottomNavigationBarItem>[
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
icon: const ImageIcon(const TestImageProvider(24, 24)),
|
||||||
|
title: const Text('Tab 1'),
|
||||||
|
),
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
icon: const ImageIcon(const TestImageProvider(24, 24)),
|
||||||
|
title: const Text('Tab 2'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
backgroundColor: const Color(0xFFFFFFFF), // Opaque white.
|
||||||
|
));
|
||||||
|
|
||||||
|
expect(find.byType(BackdropFilter), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Tap callback', (WidgetTester tester) async {
|
||||||
|
int callbackTab;
|
||||||
|
|
||||||
|
await tester.pumpWidget(new CupertinoTabBar(
|
||||||
|
items: <BottomNavigationBarItem>[
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
icon: const ImageIcon(const TestImageProvider(24, 24)),
|
||||||
|
title: const Text('Tab 1'),
|
||||||
|
),
|
||||||
|
const BottomNavigationBarItem(
|
||||||
|
icon: const ImageIcon(const TestImageProvider(24, 24)),
|
||||||
|
title: const Text('Tab 2'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
currentIndex: 1,
|
||||||
|
onTap: (int tab) { callbackTab = tab; },
|
||||||
|
));
|
||||||
|
|
||||||
|
await tester.tap(find.text('Tab 1'));
|
||||||
|
expect(callbackTab, 0);
|
||||||
|
});
|
||||||
|
}
|
@ -14,11 +14,11 @@ void main() {
|
|||||||
home: new Scaffold(
|
home: new Scaffold(
|
||||||
bottomNavigationBar: new BottomNavigationBar(
|
bottomNavigationBar: new BottomNavigationBar(
|
||||||
items: <BottomNavigationBarItem>[
|
items: <BottomNavigationBarItem>[
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.ac_unit),
|
icon: const Icon(Icons.ac_unit),
|
||||||
title: const Text('AC')
|
title: const Text('AC')
|
||||||
),
|
),
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.access_alarm),
|
icon: const Icon(Icons.access_alarm),
|
||||||
title: const Text('Alarm')
|
title: const Text('Alarm')
|
||||||
)
|
)
|
||||||
@ -42,11 +42,11 @@ void main() {
|
|||||||
home: new Scaffold(
|
home: new Scaffold(
|
||||||
bottomNavigationBar: new BottomNavigationBar(
|
bottomNavigationBar: new BottomNavigationBar(
|
||||||
items: <BottomNavigationBarItem>[
|
items: <BottomNavigationBarItem>[
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.ac_unit),
|
icon: const Icon(Icons.ac_unit),
|
||||||
title: const Text('AC')
|
title: const Text('AC')
|
||||||
),
|
),
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.access_alarm),
|
icon: const Icon(Icons.access_alarm),
|
||||||
title: const Text('Alarm')
|
title: const Text('Alarm')
|
||||||
)
|
)
|
||||||
@ -69,11 +69,11 @@ void main() {
|
|||||||
bottomNavigationBar: new BottomNavigationBar(
|
bottomNavigationBar: new BottomNavigationBar(
|
||||||
type: BottomNavigationBarType.shifting,
|
type: BottomNavigationBarType.shifting,
|
||||||
items: <BottomNavigationBarItem>[
|
items: <BottomNavigationBarItem>[
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.ac_unit),
|
icon: const Icon(Icons.ac_unit),
|
||||||
title: const Text('AC')
|
title: const Text('AC')
|
||||||
),
|
),
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.access_alarm),
|
icon: const Icon(Icons.access_alarm),
|
||||||
title: const Text('Alarm')
|
title: const Text('Alarm')
|
||||||
)
|
)
|
||||||
@ -95,11 +95,11 @@ void main() {
|
|||||||
currentIndex: 1,
|
currentIndex: 1,
|
||||||
type: BottomNavigationBarType.shifting,
|
type: BottomNavigationBarType.shifting,
|
||||||
items: <BottomNavigationBarItem>[
|
items: <BottomNavigationBarItem>[
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.ac_unit),
|
icon: const Icon(Icons.ac_unit),
|
||||||
title: const Text('AC')
|
title: const Text('AC')
|
||||||
),
|
),
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.access_alarm),
|
icon: const Icon(Icons.access_alarm),
|
||||||
title: const Text('Alarm')
|
title: const Text('Alarm')
|
||||||
)
|
)
|
||||||
@ -124,19 +124,19 @@ void main() {
|
|||||||
bottomNavigationBar: new BottomNavigationBar(
|
bottomNavigationBar: new BottomNavigationBar(
|
||||||
type: BottomNavigationBarType.shifting,
|
type: BottomNavigationBarType.shifting,
|
||||||
items: <BottomNavigationBarItem>[
|
items: <BottomNavigationBarItem>[
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.ac_unit),
|
icon: const Icon(Icons.ac_unit),
|
||||||
title: const Text('AC')
|
title: const Text('AC')
|
||||||
),
|
),
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.access_alarm),
|
icon: const Icon(Icons.access_alarm),
|
||||||
title: const Text('Alarm')
|
title: const Text('Alarm')
|
||||||
),
|
),
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.access_time),
|
icon: const Icon(Icons.access_time),
|
||||||
title: const Text('Time')
|
title: const Text('Time')
|
||||||
),
|
),
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
title: const Text('Add')
|
title: const Text('Add')
|
||||||
)
|
)
|
||||||
@ -185,19 +185,19 @@ void main() {
|
|||||||
bottomNavigationBar: new BottomNavigationBar(
|
bottomNavigationBar: new BottomNavigationBar(
|
||||||
type: BottomNavigationBarType.shifting,
|
type: BottomNavigationBarType.shifting,
|
||||||
items: <BottomNavigationBarItem>[
|
items: <BottomNavigationBarItem>[
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.ac_unit),
|
icon: const Icon(Icons.ac_unit),
|
||||||
title: const Text('AC')
|
title: const Text('AC')
|
||||||
),
|
),
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.access_alarm),
|
icon: const Icon(Icons.access_alarm),
|
||||||
title: const Text('Alarm')
|
title: const Text('Alarm')
|
||||||
),
|
),
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.access_time),
|
icon: const Icon(Icons.access_time),
|
||||||
title: const Text('Time')
|
title: const Text('Time')
|
||||||
),
|
),
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
title: const Text('Add')
|
title: const Text('Add')
|
||||||
)
|
)
|
||||||
@ -223,19 +223,19 @@ void main() {
|
|||||||
bottomNavigationBar: new BottomNavigationBar(
|
bottomNavigationBar: new BottomNavigationBar(
|
||||||
type: BottomNavigationBarType.fixed,
|
type: BottomNavigationBarType.fixed,
|
||||||
items: <BottomNavigationBarItem>[
|
items: <BottomNavigationBarItem>[
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.ac_unit),
|
icon: const Icon(Icons.ac_unit),
|
||||||
title: const Text('AC')
|
title: const Text('AC')
|
||||||
),
|
),
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.access_alarm),
|
icon: const Icon(Icons.access_alarm),
|
||||||
title: const Text('Alarm')
|
title: const Text('Alarm')
|
||||||
),
|
),
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.access_time),
|
icon: const Icon(Icons.access_time),
|
||||||
title: const Text('Time')
|
title: const Text('Time')
|
||||||
),
|
),
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
title: const Text('Add')
|
title: const Text('Add')
|
||||||
)
|
)
|
||||||
@ -259,7 +259,7 @@ void main() {
|
|||||||
bottomNavigationBar: new BottomNavigationBar(
|
bottomNavigationBar: new BottomNavigationBar(
|
||||||
iconSize: 12.0,
|
iconSize: 12.0,
|
||||||
items: <BottomNavigationBarItem>[
|
items: <BottomNavigationBarItem>[
|
||||||
new BottomNavigationBarItem(
|
const BottomNavigationBarItem(
|
||||||
title: const Text('A'),
|
title: const Text('A'),
|
||||||
icon: const Icon(Icons.ac_unit),
|
icon: const Icon(Icons.ac_unit),
|
||||||
),
|
),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user