radio.dart 3.43 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;

17
class Radio<T> extends StatelessWidget {
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
  @override
43
  Widget build(BuildContext context) {
44
    assert(debugCheckHasMaterial(context));
45
    ThemeData themeData = Theme.of(context);
Hixie's avatar
Hixie committed
46 47 48 49 50 51 52 53
    return new Semantics(
      checked: value == groupValue,
      child: new _RadioRenderObjectWidget(
        selected: value == groupValue,
        activeColor: activeColor ?? themeData.accentColor,
        inactiveColor: _getInactiveColor(themeData),
        onChanged: _enabled ? _handleChanged : null
      )
54 55 56
    );
  }
}
57 58 59 60 61

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

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

76
  @override
77
  _RenderRadio createRenderObject(BuildContext context) => new _RenderRadio(
78
    value: selected,
79
    activeColor: activeColor,
80 81 82 83
    inactiveColor: inactiveColor,
    onChanged: onChanged
  );

84
  @override
85
  void updateRenderObject(BuildContext context, _RenderRadio renderObject) {
86 87 88 89 90
    renderObject
      ..value = selected
      ..activeColor = activeColor
      ..inactiveColor = inactiveColor
      ..onChanged = onChanged;
91 92 93 94 95 96
  }
}

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

108
  @override
109 110
  bool get isInteractive => super.isInteractive && !value;

111
  @override
112 113 114 115 116 117
  void paint(PaintingContext context, Offset offset) {
    final Canvas canvas = context.canvas;

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

    Point center = (offset & size).center;
118
    Color radioColor = onChanged != null ? activeColor : inactiveColor;
119 120 121

    // Outer circle
    Paint paint = new Paint()
122
      ..color = Color.lerp(inactiveColor, radioColor, position.value)
123
      ..style = PaintingStyle.stroke
124 125 126 127 128
      ..strokeWidth = 2.0;
    canvas.drawCircle(center, _kOuterRadius, paint);

    // Inner circle
    if (!position.isDismissed) {
129
      paint.style = PaintingStyle.fill;
130 131 132 133
      canvas.drawCircle(center, _kInnerRadius * position.value, paint);
    }
  }
}