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

import 'dart:async';

import 'package:sky/animation.dart';
import 'package:sky/material.dart';
import 'package:sky/src/fn3/basic.dart';
import 'package:sky/src/fn3/focus.dart';
import 'package:sky/src/fn3/framework.dart';
import 'package:sky/src/fn3/gesture_detector.dart';
import 'package:sky/src/fn3/material.dart';
import 'package:sky/src/fn3/navigator.dart';
import 'package:sky/src/fn3/scrollable.dart';
import 'package:sky/src/fn3/theme.dart';
import 'package:sky/src/fn3/transitions.dart';

19
typedef Dialog DialogBuilder(NavigatorState navigator);
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 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 72 73 74 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

/// A material design dialog
///
/// <https://www.google.com/design/spec/components/dialogs.html>
class Dialog extends StatelessComponent {
  Dialog({
    Key key,
    this.title,
    this.titlePadding,
    this.content,
    this.contentPadding,
    this.actions,
    this.onDismiss
  }): super(key: key);

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

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

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

  // Padding around the content; uses material design default if none is supplied
  final EdgeDims contentPadding;

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

  /// An (optional) callback that is called when the dialog is dismissed.
  final Function onDismiss;

  Color _getColor(BuildContext context) {
    switch (Theme.of(context).brightness) {
      case ThemeBrightness.light:
        return Colors.white;
      case ThemeBrightness.dark:
        return Colors.grey[800];
    }
  }

  Widget build(BuildContext context) {

    List<Widget> dialogBody = new List<Widget>();

    if (title != null) {
      EdgeDims padding = titlePadding;
      if (padding == null)
        padding = new EdgeDims(24.0, 24.0, content == null ? 20.0 : 0.0, 24.0);
      dialogBody.add(new Padding(
        padding: padding,
        child: new DefaultTextStyle(
          style: Theme.of(context).text.title,
          child: title
        )
      ));
    }

    if (content != null) {
      EdgeDims padding = contentPadding;
      if (padding == null)
        padding = const EdgeDims(20.0, 24.0, 24.0, 24.0);
      dialogBody.add(new Padding(
        padding: padding,
        child: new DefaultTextStyle(
          style: Theme.of(context).text.subhead,
          child: content
        )
      ));
    }

    if (actions != null) {
      dialogBody.add(new Container(
        child: new Row(actions,
          justifyContent: FlexJustifyContent.end
        )
      ));
    }

    return new Stack([
      new GestureDetector(
        onTap: onDismiss,
        child: new Container(
          decoration: const BoxDecoration(
            backgroundColor: const Color(0x7F000000)
          )
        )
      ),
      new Center(
        child: new Container(
          margin: new EdgeDims.symmetric(horizontal: 40.0, vertical: 24.0),
          child: new ConstrainedBox(
            constraints: new BoxConstraints(minWidth: 280.0),
            child: new Material(
              level: 4,
              color: _getColor(context),
              child: new IntrinsicWidth(
                child: new Block(dialogBody)
              )
            )
          )
        )
      )
    ]);

  }
}

const Duration _kTransitionDuration = const Duration(milliseconds: 150);

135
class DialogRoute extends Route {
136 137 138 139 140 141
  DialogRoute({ this.completer, this.builder });

  final Completer completer;
  final RouteBuilder builder;

  Duration get transitionDuration => _kTransitionDuration;
Hixie's avatar
Hixie committed
142 143
  bool get opaque => false;
  Widget build(Key key, NavigatorState navigator) {
144 145 146 147 148 149 150
    return new FadeTransition(
      performance: performance,
      opacity: new AnimatedValue<double>(0.0, end: 1.0, curve: easeOut),
      child: builder(navigator, this)
    );
  }

Hixie's avatar
Hixie committed
151
  void didPop([dynamic result]) {
152
    completer.complete(result);
Hixie's avatar
Hixie committed
153
    super.didPop(result);
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
  }
}

Future showDialog(NavigatorState navigator, DialogBuilder builder) {
  Completer completer = new Completer();
  navigator.push(new DialogRoute(
    completer: completer,
    builder: (navigator, route) {
      return new Focus(
        key: new GlobalObjectKey(route),
        autofocus: true,
        child: builder(navigator)
      );
    }
  ));
  return completer.future;
}