nav_bar.dart 4.94 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 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
  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;

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

75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
  @override
  Size get preferredSize => const Size.fromHeight(_kNavBarHeight);

  @override
  Widget build(BuildContext context) {
    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,
              ),
            ),
          ),
        ),
      ),
    );

136
    if (!opaque) {
137 138 139 140 141 142 143 144 145 146 147 148
      // 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;
  }
}