radio.dart 3.42 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 'dart:ui' as ui;
6

7
import 'package:flutter/rendering.dart';
8
import 'package:flutter/widgets.dart';
9

10
import 'constants.dart';
11
import 'debug.dart';
12
import 'theme.dart';
13
import 'toggleable.dart';
14

15 16 17 18
const double _kDiameter = 16.0;
const double _kOuterRadius = _kDiameter / 2.0;
const double _kInnerRadius = 5.0;

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

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

33
  bool get _enabled => onChanged != null;
34

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

39 40 41 42 43
  void _handleChanged(bool selected) {
    if (selected)
      onChanged(value);
  }

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

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

  final bool selected;
  final Color inactiveColor;
74
  final Color activeColor;
75 76 77 78
  final ValueChanged<bool> onChanged;

  _RenderRadio createRenderObject() => new _RenderRadio(
    value: selected,
79
    activeColor: activeColor,
80 81 82 83 84 85
    inactiveColor: inactiveColor,
    onChanged: onChanged
  );

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

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

  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;
114
    Color radioColor = onChanged != null ? activeColor : inactiveColor;
115 116 117

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

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