dialog.dart 5.16 KB
Newer Older
1 2 3 4
// Copyright 2015 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.

5 6
import 'dart:async';

7
import 'package:flutter/widgets.dart';
8

9
import 'button.dart';
10
import 'button_bar.dart';
11 12
import 'colors.dart';
import 'material.dart';
13
import 'theme.dart';
14 15 16

/// A material design dialog
///
17 18 19 20
/// Typically passed as the child widget to [showDialog], which displays the
/// dialog.
///
/// See also:
Ian Hickson's avatar
Ian Hickson committed
21
///
22 23
///  * [showDialog]
///  * <https://www.google.com/design/spec/components/dialogs.html>
24
class Dialog extends StatelessWidget {
25 26 27
  /// Creates a dialog.
  ///
  /// Typically used in conjunction with [showDialog].
28
  Dialog({
29
    Key key,
30
    this.title,
31
    this.titlePadding,
32
    this.content,
33
    this.contentPadding,
34
    this.actions
35
  }) : super(key: key);
36 37 38

  /// The (optional) title of the dialog is displayed in a large font at the top
  /// of the dialog.
Ian Hickson's avatar
Ian Hickson committed
39 40
  ///
  /// Typically a [Text] widget.
41 42
  final Widget title;

43 44 45 46
  /// Padding around the title.
  ///
  /// Uses material design default if none is supplied. If there is no title, no
  /// padding will be provided.
47
  final EdgeInsets titlePadding;
48

49 50
  /// The (optional) content of the dialog is displayed in the center of the
  /// dialog in a lighter font.
Ian Hickson's avatar
Ian Hickson committed
51 52 53 54
  ///
  /// Typically, this is a [Block] containing the contents of the dialog. Using
  /// a [Block] ensures that the contents can scroll if they are too big to fit
  /// on the display.
55 56
  final Widget content;

57 58 59
  /// Padding around the content.
  ///
  /// Uses material design default if none is supplied.
60
  final EdgeInsets contentPadding;
61

62 63
  /// The (optional) set of actions that are displayed at the bottom of the
  /// dialog.
Ian Hickson's avatar
Ian Hickson committed
64 65 66 67
  ///
  /// Typically this is a list of [FlatButton] widgets.
  ///
  /// These widgets will be wrapped in a [ButtonBar].
68 69
  final List<Widget> actions;

70
  Color _getColor(BuildContext context) {
pq's avatar
pq committed
71 72
    Brightness brightness = Theme.of(context).brightness;
    switch (brightness) {
73
      case Brightness.light:
74
        return Colors.white;
75
      case Brightness.dark:
76
        return Colors.grey[800];
77
    }
pq's avatar
pq committed
78
    assert(brightness != null);
pq's avatar
pq committed
79
    return null;
80 81
  }

82
  @override
83
  Widget build(BuildContext context) {
84
    List<Widget> dialogBody = new List<Widget>();
85 86

    if (title != null) {
87
      EdgeInsets padding = titlePadding;
88
      if (padding == null)
89
        padding = new EdgeInsets.fromLTRB(24.0, 24.0, 24.0, content == null ? 20.0 : 0.0);
90
      dialogBody.add(new Padding(
91
        padding: padding,
92
        child: new DefaultTextStyle(
93
          style: Theme.of(context).textTheme.title,
94 95 96 97 98 99
          child: title
        )
      ));
    }

    if (content != null) {
100
      EdgeInsets padding = contentPadding;
101
      if (padding == null)
102
        padding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0);
103
      dialogBody.add(new Padding(
104
        padding: padding,
105
        child: new DefaultTextStyle(
106
          style: Theme.of(context).textTheme.subhead,
107 108 109 110 111
          child: content
        )
      ));
    }

112
    if (actions != null) {
113
      dialogBody.add(new ButtonTheme.bar(
114 115 116
        child: new ButtonBar(
          alignment: MainAxisAlignment.end,
          children: actions
117 118
        )
      ));
119
    }
120

121 122
    return new Center(
      child: new Container(
123
        margin: new EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0),
124 125 126
        child: new ConstrainedBox(
          constraints: new BoxConstraints(minWidth: 280.0),
          child: new Material(
Hans Muller's avatar
Hans Muller committed
127
            elevation: 24,
128 129 130
            color: _getColor(context),
            type: MaterialType.card,
            child: new IntrinsicWidth(
131
              child: new Block(children: dialogBody)
132 133 134 135
            )
          )
        )
      )
136
    );
137 138
  }
}
139

Hixie's avatar
Hixie committed
140 141 142 143 144
class _DialogRoute<T> extends PopupRoute<T> {
  _DialogRoute({
    Completer<T> completer,
    this.child
  }) : super(completer: completer);
145

Adam Barth's avatar
Adam Barth committed
146
  final Widget child;
147

148
  @override
149
  Duration get transitionDuration => const Duration(milliseconds: 150);
150 151

  @override
Hixie's avatar
Hixie committed
152
  bool get barrierDismissable => true;
153 154

  @override
155 156
  Color get barrierColor => Colors.black54;

157
  @override
158
  Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> forwardAnimation) {
159 160
    return child;
  }
161

162
  @override
163
  Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> forwardAnimation, Widget child) {
164 165 166 167 168
    return new FadeTransition(
      opacity: new CurvedAnimation(
        parent: animation,
        curve: Curves.easeOut
      ),
169 170
      child: child
    );
171 172 173
  }
}

174 175 176 177 178 179 180 181 182 183 184
/// Displays a dialog above the current contents of the app.
///
/// This function typically receives a [Dialog] widget as its child argument.
/// Content below the dialog is dimmed with a [ModalBarrier].
///
/// Returns a `Future` that resolves to the value (if any) that was passed to
/// [Navigator.pop] when the dialog was closed.
///
/// See also:
///  * [Dialog]
///  * <https://www.google.com/design/spec/components/dialogs.html>
185 186 187
Future<dynamic/*=T*/> showDialog/*<T>*/({ BuildContext context, Widget child }) {
  Completer<dynamic/*=T*/> completer = new Completer<dynamic/*=T*/>();
  Navigator.push(context, new _DialogRoute<dynamic/*=T*/>(completer: completer, child: child));
188 189
  return completer.future;
}