radio.dart 3.37 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
import 'package:flutter/rendering.dart';
6
import 'package:flutter/widgets.dart';
7

8
import 'constants.dart';
9
import 'debug.dart';
10
import 'theme.dart';
11
import 'toggleable.dart';
12

13 14 15 16
const double _kDiameter = 16.0;
const double _kOuterRadius = _kDiameter / 2.0;
const double _kInnerRadius = 5.0;

Hixie's avatar
Hixie committed
17
class Radio<T> extends StatelessComponent {
18
  Radio({
19
    Key key,
20 21
    this.value,
    this.groupValue,
22
    this.activeColor,
23
    this.onChanged
24
  }) : super(key: key);
25

Hixie's avatar
Hixie committed
26 27
  final T value;
  final T groupValue;
28
  final Color activeColor;
Hixie's avatar
Hixie committed
29
  final ValueChanged<T> onChanged;
30

31
  bool get _enabled => onChanged != null;
32

33
  Color _getInactiveColor(ThemeData themeData) {
34
    return _enabled ? themeData.unselectedColor : themeData.disabledColor;
35 36
  }

37 38 39 40 41
  void _handleChanged(bool selected) {
    if (selected)
      onChanged(value);
  }

42
  Widget build(BuildContext context) {
43
    assert(debugCheckHasMaterial(context));
44
    ThemeData themeData = Theme.of(context);
Hixie's avatar
Hixie committed
45 46 47 48 49 50 51 52
    return new Semantics(
      checked: value == groupValue,
      child: new _RadioRenderObjectWidget(
        selected: value == groupValue,
        activeColor: activeColor ?? themeData.accentColor,
        inactiveColor: _getInactiveColor(themeData),
        onChanged: _enabled ? _handleChanged : null
      )
53 54 55
    );
  }
}
56 57 58 59 60

class _RadioRenderObjectWidget extends LeafRenderObjectWidget {
  _RadioRenderObjectWidget({
    Key key,
    this.selected,
61
    this.activeColor,
62 63 64
    this.inactiveColor,
    this.onChanged
  }) : super(key: key) {
65 66
    assert(selected != null);
    assert(activeColor != null);
67 68 69 70 71
    assert(inactiveColor != null);
  }

  final bool selected;
  final Color inactiveColor;
72
  final Color activeColor;
73 74 75 76
  final ValueChanged<bool> onChanged;

  _RenderRadio createRenderObject() => new _RenderRadio(
    value: selected,
77
    activeColor: activeColor,
78 79 80 81 82
    inactiveColor: inactiveColor,
    onChanged: onChanged
  );

  void updateRenderObject(_RenderRadio renderObject, _RadioRenderObjectWidget oldWidget) {
83 84 85 86 87
    renderObject
      ..value = selected
      ..activeColor = activeColor
      ..inactiveColor = inactiveColor
      ..onChanged = onChanged;
88 89 90 91 92 93
  }
}

class _RenderRadio extends RenderToggleable {
  _RenderRadio({
    bool value,
94
    Color activeColor,
95 96
    Color inactiveColor,
    ValueChanged<bool> onChanged
97 98 99 100 101 102 103
  }): super(
    value: value,
    activeColor: activeColor,
    inactiveColor: inactiveColor,
    onChanged: onChanged,
    size: const Size(2 * kRadialReactionRadius, 2 * kRadialReactionRadius)
  );
104 105 106 107 108 109 110 111 112

  bool get isInteractive => super.isInteractive && !value;

  void paint(PaintingContext context, Offset offset) {
    final Canvas canvas = context.canvas;

    paintRadialReaction(canvas, offset + const Offset(kRadialReactionRadius, kRadialReactionRadius));

    Point center = (offset & size).center;
113
    Color radioColor = onChanged != null ? activeColor : inactiveColor;
114 115 116

    // Outer circle
    Paint paint = new Paint()
117
      ..color = Color.lerp(inactiveColor, radioColor, position.value)
118
      ..style = PaintingStyle.stroke
119 120 121 122 123
      ..strokeWidth = 2.0;
    canvas.drawCircle(center, _kOuterRadius, paint);

    // Inner circle
    if (!position.isDismissed) {
124
      paint.style = PaintingStyle.fill;
125 126 127 128
      canvas.drawCircle(center, _kInnerRadius * position.value, paint);
    }
  }
}