From 69c2542458aafa4dbfe9b3b682668b768272bf33 Mon Sep 17 00:00:00 2001 From: xster Date: Wed, 24 May 2017 12:23:15 -0700 Subject: [PATCH] CupertinoTabBar (#10264) Move some Cupertino colors to a common file. Create a CupertinoTabBar widget to mimic iOS looks --- packages/flutter/lib/cupertino.dart | 2 + .../lib/src/cupertino/activity_indicator.dart | 4 +- .../lib/src/cupertino/bottom_tab_bar.dart | 161 ++++++++++++++++++ .../flutter/lib/src/cupertino/button.dart | 9 +- .../flutter/lib/src/cupertino/colors.dart | 40 +++++ .../flutter/lib/src/cupertino/dialog.dart | 11 +- .../flutter/lib/src/cupertino/slider.dart | 3 +- .../flutter/lib/src/cupertino/switch.dart | 5 +- .../lib/src/cupertino/thumb_painter.dart | 4 +- .../src/material/bottom_navigation_bar.dart | 38 ----- .../widgets/bottom_navigation_bar_item.dart | 58 +++++++ packages/flutter/lib/widgets.dart | 1 + .../test/cupertino/bottom_tab_bar_test.dart | 112 ++++++++++++ .../material/bottom_navigation_bar_test.dart | 42 ++--- 14 files changed, 416 insertions(+), 74 deletions(-) create mode 100644 packages/flutter/lib/src/cupertino/bottom_tab_bar.dart create mode 100644 packages/flutter/lib/src/cupertino/colors.dart create mode 100644 packages/flutter/lib/src/widgets/bottom_navigation_bar_item.dart create mode 100644 packages/flutter/test/cupertino/bottom_tab_bar_test.dart diff --git a/packages/flutter/lib/cupertino.dart b/packages/flutter/lib/cupertino.dart index 831e42e217..684c2fe4db 100644 --- a/packages/flutter/lib/cupertino.dart +++ b/packages/flutter/lib/cupertino.dart @@ -8,7 +8,9 @@ library cupertino; export 'src/cupertino/activity_indicator.dart'; +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/page.dart'; export 'src/cupertino/slider.dart'; diff --git a/packages/flutter/lib/src/cupertino/activity_indicator.dart b/packages/flutter/lib/src/cupertino/activity_indicator.dart index e978abafc3..af17b533ae 100644 --- a/packages/flutter/lib/src/cupertino/activity_indicator.dart +++ b/packages/flutter/lib/src/cupertino/activity_indicator.dart @@ -6,6 +6,8 @@ import 'dart:math' as math; import 'package:flutter/widgets.dart'; +import 'colors.dart'; + /// An iOS-style activity indicator. /// /// See also: @@ -80,7 +82,7 @@ class _CupertinoActivityIndicatorState extends State const double _kTwoPI = math.PI * 2.0; const int _kTickCount = 12; const int _kHalfTickCount = _kTickCount ~/ 2; -const Color _kTickColor = const Color(0xFFE0E0E0); +const Color _kTickColor = CupertinoColors.lightBackgroundGray; const Color _kActiveTickColor = const Color(0xFF9D9D9D); final RRect _kTickFundamentalRRect = new RRect.fromLTRBXY(-10.0, 1.0, -5.0, -1.0, 1.0, 1.0); diff --git a/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart b/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart new file mode 100644 index 0000000000..8a0ddded09 --- /dev/null +++ b/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart @@ -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 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 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 _buildTabItems() { + final List result = []; + + 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: [ + 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, + ), + ); + } +} diff --git a/packages/flutter/lib/src/cupertino/button.dart b/packages/flutter/lib/src/cupertino/button.dart index 0e87e16b2d..f46bac6673 100644 --- a/packages/flutter/lib/src/cupertino/button.dart +++ b/packages/flutter/lib/src/cupertino/button.dart @@ -5,9 +5,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; -// TODO(xster): move this to a common Cupertino color palette with the next yak. -const Color _kBlue = const Color(0xFF007AFF); -const Color _kWhite = const Color(0xFFFFFFFF); +import 'colors.dart'; + const Color _kDisabledBackground = const Color(0xFFA9A9A9); const Color _kDisabledForeground = const Color(0xFFC4C4C4); @@ -16,7 +15,7 @@ const TextStyle _kButtonTextStyle = const TextStyle( inherit: false, fontSize: 15.0, fontWeight: FontWeight.normal, - color: _kBlue, + color: CupertinoColors.activeBlue, textBaseline: TextBaseline.alphabetic, ); @@ -25,7 +24,7 @@ final TextStyle _kDisabledButtonTextStyle = _kButtonTextStyle.copyWith( ); final TextStyle _kBackgroundButtonTextStyle = _kButtonTextStyle.copyWith( - color: _kWhite, + color: CupertinoColors.white, ); const EdgeInsets _kButtonPadding = const EdgeInsets.all(16.0); diff --git a/packages/flutter/lib/src/cupertino/colors.dart b/packages/flutter/lib/src/cupertino/colors.dart new file mode 100644 index 0000000000..cb546cb564 --- /dev/null +++ b/packages/flutter/lib/src/cupertino/colors.dart @@ -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); +} diff --git a/packages/flutter/lib/src/cupertino/dialog.dart b/packages/flutter/lib/src/cupertino/dialog.dart index dd2b61b065..399e021af0 100644 --- a/packages/flutter/lib/src/cupertino/dialog.dart +++ b/packages/flutter/lib/src/cupertino/dialog.dart @@ -6,6 +6,8 @@ import 'dart:ui' show ImageFilter; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; +import 'colors.dart'; + // TODO(abarth): These constants probably belong somewhere more general. const TextStyle _kCupertinoDialogTitleStyle = const TextStyle( @@ -13,7 +15,7 @@ const TextStyle _kCupertinoDialogTitleStyle = const TextStyle( inherit: false, fontSize: 17.5, fontWeight: FontWeight.w600, - color: const Color(0xFF000000), + color: CupertinoColors.black, height: 1.25, textBaseline: TextBaseline.alphabetic, ); @@ -23,7 +25,7 @@ const TextStyle _kCupertinoDialogContentStyle = const TextStyle( inherit: false, fontSize: 12.4, fontWeight: FontWeight.w500, - color: const Color(0xFF000000), + color: CupertinoColors.black, height: 1.35, textBaseline: TextBaseline.alphabetic, ); @@ -33,7 +35,7 @@ const TextStyle _kCupertinoDialogActionStyle = const TextStyle( inherit: false, fontSize: 16.8, fontWeight: FontWeight.w400, - color: const Color(0xFF027AFF), + color: CupertinoColors.activeBlue, textBaseline: TextBaseline.alphabetic, ); @@ -180,7 +182,6 @@ class CupertinoAlertDialog extends StatelessWidget { } } -const Color _kDestructiveActionColor = const Color(0xFFFF3B30); /// A button typically used in a [CupertinoAlertDialog]. /// @@ -228,7 +229,7 @@ class CupertinoDialogAction extends StatelessWidget { style = style.copyWith(fontWeight: FontWeight.w600); if (isDestructiveAction) - style = style.copyWith(color: _kDestructiveActionColor); + style = style.copyWith(color: CupertinoColors.destructiveRed); if (!enabled) style = style.copyWith(color: style.color.withOpacity(0.5)); diff --git a/packages/flutter/lib/src/cupertino/slider.dart b/packages/flutter/lib/src/cupertino/slider.dart index 94428b0922..e787ee09ca 100644 --- a/packages/flutter/lib/src/cupertino/slider.dart +++ b/packages/flutter/lib/src/cupertino/slider.dart @@ -10,6 +10,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; +import 'colors.dart'; import 'thumb_painter.dart'; /// An iOS-style slider. @@ -48,7 +49,7 @@ class CupertinoSlider extends StatefulWidget { this.min: 0.0, this.max: 1.0, this.divisions, - this.activeColor: const Color(0xFF027AFF), + this.activeColor: CupertinoColors.activeBlue, }) : assert(value != null), assert(min != null), assert(max != null), diff --git a/packages/flutter/lib/src/cupertino/switch.dart b/packages/flutter/lib/src/cupertino/switch.dart index 00fe0b0acd..e62cac092a 100644 --- a/packages/flutter/lib/src/cupertino/switch.dart +++ b/packages/flutter/lib/src/cupertino/switch.dart @@ -10,6 +10,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/widgets.dart'; +import 'colors.dart'; import 'thumb_painter.dart'; /// An iOS-style switch. @@ -30,7 +31,7 @@ class CupertinoSwitch extends StatefulWidget { Key key, @required this.value, @required this.onChanged, - this.activeColor: const Color(0xFF4CD964), + this.activeColor: CupertinoColors.activeGreen, }) : super(key: key); /// Whether this switch is on or off. @@ -130,7 +131,7 @@ const double _kTrackInnerLength = _kTrackInnerEnd - _kTrackInnerStart; const double _kSwitchWidth = 59.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 _kToggleDuration = const Duration(milliseconds: 200); diff --git a/packages/flutter/lib/src/cupertino/thumb_painter.dart b/packages/flutter/lib/src/cupertino/thumb_painter.dart index ad486696a7..90642be1f5 100644 --- a/packages/flutter/lib/src/cupertino/thumb_painter.dart +++ b/packages/flutter/lib/src/cupertino/thumb_painter.dart @@ -4,6 +4,8 @@ import 'package:flutter/painting.dart'; +import 'colors.dart'; + final MaskFilter _kShadowMaskFilter = new MaskFilter.blur(BlurStyle.normal, BoxShadow.convertRadiusToSigma(1.0)); /// Paints an iOS-style slider thumb. @@ -12,7 +14,7 @@ final MaskFilter _kShadowMaskFilter = new MaskFilter.blur(BlurStyle.normal, BoxS class CupertinoThumbPainter { /// Creates an object that paints an iOS-style slider thumb. CupertinoThumbPainter({ - this.color: const Color(0xFFFFFFFF), + this.color: CupertinoColors.white, this.shadowColor: const Color(0x2C000000), }); diff --git a/packages/flutter/lib/src/material/bottom_navigation_bar.dart b/packages/flutter/lib/src/material/bottom_navigation_bar.dart index 6ea761621f..5fba485268 100644 --- a/packages/flutter/lib/src/material/bottom_navigation_bar.dart +++ b/packages/flutter/lib/src/material/bottom_navigation_bar.dart @@ -35,44 +35,6 @@ enum BottomNavigationBarType { shifting, } -/// An interactive destination label within [BottomNavigationBar] with an icon -/// and title. -/// -/// See also: -/// -/// * [BottomNavigationBar] -/// * -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 /// small number of views. /// diff --git a/packages/flutter/lib/src/widgets/bottom_navigation_bar_item.dart b/packages/flutter/lib/src/widgets/bottom_navigation_bar_item.dart new file mode 100644 index 0000000000..0cbd25ea2b --- /dev/null +++ b/packages/flutter/lib/src/widgets/bottom_navigation_bar_item.dart @@ -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] +/// * +/// * [CupertinoTabBar] +/// * +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; +} diff --git a/packages/flutter/lib/widgets.dart b/packages/flutter/lib/widgets.dart index 80660c5c15..a411ff948b 100644 --- a/packages/flutter/lib/widgets.dart +++ b/packages/flutter/lib/widgets.dart @@ -22,6 +22,7 @@ export 'src/widgets/async.dart'; export 'src/widgets/banner.dart'; export 'src/widgets/basic.dart'; export 'src/widgets/binding.dart'; +export 'src/widgets/bottom_navigation_bar_item.dart'; export 'src/widgets/container.dart'; export 'src/widgets/debug.dart'; export 'src/widgets/dismissible.dart'; diff --git a/packages/flutter/test/cupertino/bottom_tab_bar_test.dart b/packages/flutter/test/cupertino/bottom_tab_bar_test.dart new file mode 100644 index 0000000000..ad1bdf15a3 --- /dev/null +++ b/packages/flutter/test/cupertino/bottom_tab_bar_test.dart @@ -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: [ + 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: [ + 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: [ + 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: [ + 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: [ + 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); + }); +} \ No newline at end of file diff --git a/packages/flutter/test/material/bottom_navigation_bar_test.dart b/packages/flutter/test/material/bottom_navigation_bar_test.dart index 0feb895525..10de837196 100644 --- a/packages/flutter/test/material/bottom_navigation_bar_test.dart +++ b/packages/flutter/test/material/bottom_navigation_bar_test.dart @@ -14,11 +14,11 @@ void main() { home: new Scaffold( bottomNavigationBar: new BottomNavigationBar( items: [ - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.ac_unit), title: const Text('AC') ), - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.access_alarm), title: const Text('Alarm') ) @@ -42,11 +42,11 @@ void main() { home: new Scaffold( bottomNavigationBar: new BottomNavigationBar( items: [ - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.ac_unit), title: const Text('AC') ), - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.access_alarm), title: const Text('Alarm') ) @@ -69,11 +69,11 @@ void main() { bottomNavigationBar: new BottomNavigationBar( type: BottomNavigationBarType.shifting, items: [ - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.ac_unit), title: const Text('AC') ), - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.access_alarm), title: const Text('Alarm') ) @@ -95,11 +95,11 @@ void main() { currentIndex: 1, type: BottomNavigationBarType.shifting, items: [ - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.ac_unit), title: const Text('AC') ), - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.access_alarm), title: const Text('Alarm') ) @@ -124,19 +124,19 @@ void main() { bottomNavigationBar: new BottomNavigationBar( type: BottomNavigationBarType.shifting, items: [ - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.ac_unit), title: const Text('AC') ), - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.access_alarm), title: const Text('Alarm') ), - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.access_time), title: const Text('Time') ), - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.add), title: const Text('Add') ) @@ -185,19 +185,19 @@ void main() { bottomNavigationBar: new BottomNavigationBar( type: BottomNavigationBarType.shifting, items: [ - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.ac_unit), title: const Text('AC') ), - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.access_alarm), title: const Text('Alarm') ), - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.access_time), title: const Text('Time') ), - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.add), title: const Text('Add') ) @@ -223,19 +223,19 @@ void main() { bottomNavigationBar: new BottomNavigationBar( type: BottomNavigationBarType.fixed, items: [ - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.ac_unit), title: const Text('AC') ), - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.access_alarm), title: const Text('Alarm') ), - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.access_time), title: const Text('Time') ), - new BottomNavigationBarItem( + const BottomNavigationBarItem( icon: const Icon(Icons.add), title: const Text('Add') ) @@ -259,7 +259,7 @@ void main() { bottomNavigationBar: new BottomNavigationBar( iconSize: 12.0, items: [ - new BottomNavigationBarItem( + const BottomNavigationBarItem( title: const Text('A'), icon: const Icon(Icons.ac_unit), ),