dialog.dart 4.76 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 11
import 'colors.dart';
import 'material.dart';
12
import 'theme.dart';
13 14 15

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

  /// The (optional) title of the dialog is displayed in a large font at the top
  /// of the dialog.
  final Widget title;

39 40
  // Padding around the title; uses material design default if none is supplied
  // If there is no title, no padding will be provided
41
  final EdgeInsets titlePadding;
42

43 44 45 46
  /// The (optional) content of the dialog is displayed in the center of the
  /// dialog in a lighter font.
  final Widget content;

47
  // Padding around the content; uses material design default if none is supplied
48
  final EdgeInsets contentPadding;
49

50 51 52 53
  /// The (optional) set of actions that are displayed at the bottom of the
  /// dialog.
  final List<Widget> actions;

54 55
  Color _getColor(BuildContext context) {
    switch (Theme.of(context).brightness) {
56
      case ThemeBrightness.light:
57
        return Colors.white;
58
      case ThemeBrightness.dark:
59
        return Colors.grey[800];
60 61 62
    }
  }

63
  @override
64
  Widget build(BuildContext context) {
65
    List<Widget> dialogBody = new List<Widget>();
66 67

    if (title != null) {
68
      EdgeInsets padding = titlePadding;
69
      if (padding == null)
70
        padding = new EdgeInsets.fromLTRB(24.0, 24.0, 24.0, content == null ? 20.0 : 0.0);
71
      dialogBody.add(new Padding(
72
        padding: padding,
73
        child: new DefaultTextStyle(
74
          style: Theme.of(context).textTheme.title,
75 76 77 78 79 80
          child: title
        )
      ));
    }

    if (content != null) {
81
      EdgeInsets padding = contentPadding;
82
      if (padding == null)
83
        padding = const EdgeInsets.fromLTRB(24.0, 20.0, 24.0, 24.0);
84
      dialogBody.add(new Padding(
85
        padding: padding,
86
        child: new DefaultTextStyle(
87
          style: Theme.of(context).textTheme.subhead,
88 89 90 91 92
          child: content
        )
      ));
    }

93
    if (actions != null) {
94 95 96
      dialogBody.add(new ButtonTheme(
        color: ButtonColor.accent,
        child: new Container(
97 98
          child: new Row(
            children: actions,
99
            mainAxisAlignment: MainAxisAlignment.end
100
          )
101 102
        )
      ));
103
    }
104

105 106
    return new Center(
      child: new Container(
107
        margin: new EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0),
108 109 110
        child: new ConstrainedBox(
          constraints: new BoxConstraints(minWidth: 280.0),
          child: new Material(
Hans Muller's avatar
Hans Muller committed
111
            elevation: 24,
112 113 114
            color: _getColor(context),
            type: MaterialType.card,
            child: new IntrinsicWidth(
115
              child: new Block(children: dialogBody)
116 117 118 119
            )
          )
        )
      )
120
    );
121 122
  }
}
123

Hixie's avatar
Hixie committed
124 125 126 127 128
class _DialogRoute<T> extends PopupRoute<T> {
  _DialogRoute({
    Completer<T> completer,
    this.child
  }) : super(completer: completer);
129

Adam Barth's avatar
Adam Barth committed
130
  final Widget child;
131

132
  @override
133
  Duration get transitionDuration => const Duration(milliseconds: 150);
134 135

  @override
Hixie's avatar
Hixie committed
136
  bool get barrierDismissable => true;
137 138

  @override
139 140
  Color get barrierColor => Colors.black54;

141
  @override
142
  Widget buildPage(BuildContext context, Animation<double> animation, Animation<double> forwardAnimation) {
143 144
    return child;
  }
145

146
  @override
147
  Widget buildTransitions(BuildContext context, Animation<double> animation, Animation<double> forwardAnimation, Widget child) {
148 149 150 151 152
    return new FadeTransition(
      opacity: new CurvedAnimation(
        parent: animation,
        curve: Curves.easeOut
      ),
153 154
      child: child
    );
155 156 157
  }
}

158 159 160 161 162 163 164 165 166 167 168
/// 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>
169 170 171
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));
172 173
  return completer.future;
}