ink_well.dart 4.98 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 7
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
8

9 10
import 'material.dart';
import 'theme.dart';
11

12 13
class InkResponse extends StatefulComponent {
  InkResponse({
14 15 16
    Key key,
    this.child,
    this.onTap,
17
    this.onDoubleTap,
18
    this.onLongPress,
19
    this.onHighlightChanged
20 21 22 23
  }) : super(key: key);

  final Widget child;
  final GestureTapCallback onTap;
24
  final GestureTapCallback onDoubleTap;
25
  final GestureLongPressCallback onLongPress;
26
  final ValueChanged<bool> onHighlightChanged;
27

28
  _InkResponseState createState() => new _InkResponseState<InkResponse>();
29 30
}

31
class _InkResponseState<T extends InkResponse> extends State<T> {
32

33
  bool get containedInWell => false;
34

35 36
  Set<InkSplash> _splashes;
  InkSplash _currentSplash;
37

38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
  void _handleTapDown(Point position) {
    RenderBox referenceBox = context.findRenderObject();
    assert(Material.of(context) != null);
    InkSplash splash;
    splash = Material.of(context).splashAt(
      referenceBox: referenceBox,
      position: referenceBox.globalToLocal(position),
      containedInWell: containedInWell,
      onRemoved: () {
        if (_splashes != null) {
          assert(_splashes.contains(splash));
          _splashes.remove(splash);
          if (_currentSplash == splash)
            _currentSplash = null;
        } // else we're probably in deactivate()
      }
    );
    _splashes ??= new Set<InkSplash>();
    _splashes.add(splash);
    _currentSplash = splash;
58 59
  }

60 61 62 63 64
  void _handleTap() {
    _currentSplash?.confirm();
    _currentSplash = null;
    if (config.onTap != null)
      config.onTap();
65 66
  }

67 68 69
  void _handleTapCancel() {
    _currentSplash?.cancel();
    _currentSplash = null;
70 71
  }

72 73 74 75 76
  void _handleDoubleTap() {
    _currentSplash?.confirm();
    _currentSplash = null;
    if (config.onDoubleTap != null)
      config.onDoubleTap();
77 78
  }

79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
  void _handleLongPress() {
    _currentSplash?.confirm();
    _currentSplash = null;
    if (config.onLongPress != null)
      config.onLongPress();
  }

  void deactivate() {
    if (_splashes != null) {
      Set<InkSplash> splashes = _splashes;
      _splashes = null;
      for (InkSplash splash in splashes)
        splash.dispose();
      _currentSplash = null;
    }
    assert(_currentSplash == null);
    super.deactivate();
96 97
  }

98 99 100 101 102 103 104 105 106 107 108
  Widget build(BuildContext context) {
    final bool enabled = config.onTap != null || config.onDoubleTap != null || config.onLongPress != null;
    return new GestureDetector(
      onTapDown: enabled ? _handleTapDown : null,
      onTap: enabled ? _handleTap : null,
      onTapCancel: enabled ? _handleTapCancel : null,
      onDoubleTap: config.onDoubleTap != null ? _handleDoubleTap : null,
      onLongPress: config.onLongPress != null ? _handleLongPress : null,
      behavior: HitTestBehavior.opaque,
      child: config.child
    );
109 110
  }

111
}
112

113 114 115 116 117 118 119
/// An area of a Material that responds to touch.
///
/// Must have an ancestor Material widget in which to cause ink reactions.
class InkWell extends InkResponse {
  InkWell({
    Key key,
    Widget child,
120
    GestureTapCallback onTap,
121
    GestureTapCallback onDoubleTap,
122
    GestureLongPressCallback onLongPress,
123
    this.onHighlightChanged
124 125 126 127 128 129 130
  }) : super(
    key: key,
    child: child,
    onTap: onTap,
    onDoubleTap: onDoubleTap,
    onLongPress: onLongPress
  );
131

132
  final ValueChanged<bool> onHighlightChanged;
133

134 135
  _InkWellState createState() => new _InkWellState();
}
136

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
class _InkWellState extends _InkResponseState<InkWell> {

  bool get containedInWell => true;

  InkHighlight _lastHighlight;

  void updateHighlight(bool value) {
    if (value == (_lastHighlight != null && _lastHighlight.active))
      return;
    if (value) {
      if (_lastHighlight == null) {
        RenderBox referenceBox = context.findRenderObject();
        assert(Material.of(context) != null);
        _lastHighlight = Material.of(context).highlightRectAt(
          referenceBox: referenceBox,
          color: Theme.of(context).highlightColor,
          onRemoved: () {
            assert(_lastHighlight != null);
            _lastHighlight = null;
          }
        );
      } else {
        _lastHighlight.activate();
      }
161
    } else {
162
      _lastHighlight.deactivate();
163
    }
164 165
    if (config.onHighlightChanged != null)
      config.onHighlightChanged(value != null);
166 167
  }

Hixie's avatar
Hixie committed
168
  void _handleTapDown(Point position) {
169 170
    super._handleTapDown(position);
    updateHighlight(true);
171 172
  }

173
  void _handleTap() {
174 175
    super._handleTap();
    updateHighlight(false);
176 177 178
  }

  void _handleTapCancel() {
179 180
    super._handleTapCancel();
    updateHighlight(false);
181 182
  }

183 184 185 186
  void deactivate() {
    _lastHighlight?.dispose();
    _lastHighlight = null;
    super.deactivate();
187 188
  }

189 190 191
  void dependenciesChanged(Type affectedWidgetType) {
    if (affectedWidgetType == Theme && _lastHighlight != null)
      _lastHighlight.color = Theme.of(context).highlightColor;
192 193 194
  }

}