nav_bar.dart 5.07 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// 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);

18
/// An iOS-styled navigation bar.
19 20 21 22 23 24 25 26 27 28 29 30
///
/// 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.
31
//
32 33 34 35
// 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 {
36
  /// Creates a navigation bar in the iOS style.
37 38 39 40 41 42 43
  const CupertinoNavigationBar({
    Key key,
    this.leading,
    @required this.middle,
    this.trailing,
    this.backgroundColor: _kDefaultNavBarBackgroundColor,
    this.actionsForegroundColor: CupertinoColors.activeBlue,
44
  }) : assert(middle != null, 'There must be a middle widget, usually a title.'),
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
      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.
  ///
69 70
  /// The default color for text in the [middle] slot is always black, as per
  /// iOS standard design.
71 72
  final Color actionsForegroundColor;

73 74 75
  /// True if the nav bar's background color has no transparency.
  bool get opaque => backgroundColor.alpha == 0xFF;

76 77 78 79 80
  @override
  Size get preferredSize => const Size.fromHeight(_kNavBarHeight);

  @override
  Widget build(BuildContext context) {
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
    final TextStyle actionsStyle = new TextStyle(
      fontSize: 17.0,
      letterSpacing: -0.24,
      color: actionsForegroundColor,
    );

    final Widget styledLeading = leading == null ? null : DefaultTextStyle.merge(
      style: actionsStyle,
      child: leading,
    );

    final Widget styledTrailing = trailing == null ? null : DefaultTextStyle.merge(
      style: actionsStyle,
      child: trailing,
    );

    // Let the middle be black rather than `actionsForegroundColor` in case
    // it's a plain text title.
    final Widget styledMiddle = middle == null ? null : DefaultTextStyle.merge(
      style: actionsStyle.copyWith(color: CupertinoColors.black),
      child: middle,
    );
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

    // 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,
          ),
124 125 126 127 128 129 130
          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,
131
            ),
132 133 134 135 136
            child: new NavigationToolbar(
              leading: styledLeading,
              middle: styledMiddle,
              trailing: styledTrailing,
              centerMiddle: true,
137 138 139 140 141 142
            ),
          ),
        ),
      ),
    );

143
    if (!opaque) {
144 145 146 147 148 149 150 151 152 153 154 155
      // 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;
  }
}