Use `@required` for onPressed and onChanged (#4534)
We now use the `@required` annotation to encourage developers to
explicitly set onPressed and onChanged callbacks to null when that would
disable the widget.
Fixes #287
diff --git a/dev/manual_tests/pageable_list.dart b/dev/manual_tests/pageable_list.dart
index 49ad79b..5f5c60a 100644
--- a/dev/manual_tests/pageable_list.dart
+++ b/dev/manual_tests/pageable_list.dart
@@ -103,7 +103,8 @@
child: new Row(
children: <Widget>[
new Flexible(child: new Text('Scrolling wraps around')),
- new Checkbox(value: itemsWrap)
+ // TODO(abarth): Actually make this checkbox change this value.
+ new Checkbox(value: itemsWrap, onChanged: null)
]
)
)
diff --git a/examples/flutter_gallery/lib/demo/buttons_demo.dart b/examples/flutter_gallery/lib/demo/buttons_demo.dart
index 70b9922..eecc902 100644
--- a/examples/flutter_gallery/lib/demo/buttons_demo.dart
+++ b/examples/flutter_gallery/lib/demo/buttons_demo.dart
@@ -106,7 +106,8 @@
}
),
new RaisedButton(
- child: new Text('DISABLED')
+ child: new Text('DISABLED'),
+ onPressed: null
)
]
)
@@ -126,7 +127,8 @@
}
),
new FlatButton(
- child: new Text('DISABLED')
+ child: new Text('DISABLED'),
+ onPressed: null
)
]
)
@@ -173,7 +175,8 @@
color: iconButtonToggle ? Theme.of(context).primaryColor : null
),
new IconButton(
- icon: Icons.thumb_up
+ icon: Icons.thumb_up,
+ onPressed: null
)
]
.map((Widget button) => new SizedBox(width: 64.0, height: 64.0, child: button))
diff --git a/examples/flutter_gallery/lib/demo/persistent_bottom_sheet_demo.dart b/examples/flutter_gallery/lib/demo/persistent_bottom_sheet_demo.dart
index cb3c3b0..b99af8e 100644
--- a/examples/flutter_gallery/lib/demo/persistent_bottom_sheet_demo.dart
+++ b/examples/flutter_gallery/lib/demo/persistent_bottom_sheet_demo.dart
@@ -59,8 +59,9 @@
key: _scaffoldKey,
appBar: new AppBar(title: new Text('Persistent bottom sheet')),
floatingActionButton: new FloatingActionButton(
- child: new Icon(icon: Icons.add),
- backgroundColor: Colors.redAccent[200]
+ onPressed: null,
+ backgroundColor: Colors.redAccent[200],
+ child: new Icon(icon: Icons.add)
),
body: new Center(
child: new RaisedButton(
diff --git a/examples/flutter_gallery/lib/demo/selection_controls_demo.dart b/examples/flutter_gallery/lib/demo/selection_controls_demo.dart
index 29213d7..e79e229 100644
--- a/examples/flutter_gallery/lib/demo/selection_controls_demo.dart
+++ b/examples/flutter_gallery/lib/demo/selection_controls_demo.dart
@@ -101,8 +101,8 @@
mainAxisAlignment: MainAxisAlignment.collapse,
children: <Widget>[
// Disabled checkboxes
- new Checkbox(value: true),
- new Checkbox(value: false)
+ new Checkbox(value: true, onChanged: null),
+ new Checkbox(value: false, onChanged: null)
]
)
]
@@ -142,15 +142,18 @@
children: <Widget>[
new Radio<int>(
value: 0,
- groupValue: 0
+ groupValue: 0,
+ onChanged: null
),
new Radio<int>(
value: 1,
- groupValue: 0
+ groupValue: 0,
+ onChanged: null
),
new Radio<int>(
value: 2,
- groupValue: 0
+ groupValue: 0,
+ onChanged: null
)
]
)
@@ -171,8 +174,8 @@
});
}),
// Disabled switches
- new Switch(value: true),
- new Switch(value: false)
+ new Switch(value: true, onChanged: null),
+ new Switch(value: false, onChanged: null)
]
)
);
diff --git a/examples/flutter_gallery/lib/demo/slider_demo.dart b/examples/flutter_gallery/lib/demo/slider_demo.dart
index 2ebe9dd..7ed55b7 100644
--- a/examples/flutter_gallery/lib/demo/slider_demo.dart
+++ b/examples/flutter_gallery/lib/demo/slider_demo.dart
@@ -34,7 +34,7 @@
}
)
),
- new Center(child: new Slider(value: 0.25)),
+ new Center(child: new Slider(value: 0.25, onChanged: null)),
new Center(
child: new Slider(
value: _discreteValue,
diff --git a/examples/flutter_gallery/lib/gallery/example_code.dart b/examples/flutter_gallery/lib/gallery/example_code.dart
index f444059..3538a6a 100644
--- a/examples/flutter_gallery/lib/gallery/example_code.dart
+++ b/examples/flutter_gallery/lib/gallery/example_code.dart
@@ -26,7 +26,8 @@
// Buttons are disabled when onPressed isn't
// specified or is null.
new RaisedButton(
- child: new Text('BUTTON TITLE')
+ child: new Text('BUTTON TITLE'),
+ onPressed: null
);
// END
@@ -44,7 +45,8 @@
// Buttons are disabled when onPressed isn't
// specified or is null.
new FlatButton(
- child: new Text('BUTTON TITLE')
+ child: new Text('BUTTON TITLE'),
+ onPressed: null
);
// END
@@ -97,7 +99,8 @@
title: new Text('Demo')
),
floatingActionButton: new FloatingActionButton(
- child: new Icon(icon: Icons.add)
+ child: new Icon(icon: Icons.add),
+ onPressed: null
)
);
// END
@@ -127,7 +130,7 @@
// Create a disabled checkbox.
// Checkboxes are disabled when onChanged isn't
// specified or null.
-new Checkbox(value: false);
+new Checkbox(value: false, onChanged: null);
// END
@@ -166,7 +169,8 @@
// Creates a disabled radio button.
new Radio<int>(
value: 0,
- groupValue: 0
+ groupValue: 0,
+ onChanged: null
);
// END
@@ -188,7 +192,7 @@
// Create a disabled switch.
// Switches are disabled when onChanged isn't
// specified or null.
-new Switch(value: false);
+new Switch(value: false, onChanged: null);
// END
}
}
diff --git a/examples/layers/widgets/media_query.dart b/examples/layers/widgets/media_query.dart
index 48bc064..fc2f51d 100644
--- a/examples/layers/widgets/media_query.dart
+++ b/examples/layers/widgets/media_query.dart
@@ -52,7 +52,8 @@
child: new Text(name)
),
new IconButton(
- icon: Icons.more_vert
+ icon: Icons.more_vert,
+ onPressed: null
)
]
)
diff --git a/packages/flutter/lib/src/material/button.dart b/packages/flutter/lib/src/material/button.dart
index d9f824d..c968743 100644
--- a/packages/flutter/lib/src/material/button.dart
+++ b/packages/flutter/lib/src/material/button.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
+import 'package:meta/meta.dart';
import 'colors.dart';
import 'constants.dart';
@@ -128,7 +129,7 @@
this.minWidth,
this.height,
this.padding,
- this.onPressed,
+ @required this.onPressed,
this.child
}) : super(key: key);
diff --git a/packages/flutter/lib/src/material/checkbox.dart b/packages/flutter/lib/src/material/checkbox.dart
index 3856acf..c490f93 100644
--- a/packages/flutter/lib/src/material/checkbox.dart
+++ b/packages/flutter/lib/src/material/checkbox.dart
@@ -6,6 +6,7 @@
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
+import 'package:meta/meta.dart';
import 'constants.dart';
import 'debug.dart';
@@ -42,19 +43,14 @@
/// * [onChanged] is called when the value of the checkbox should change.
Checkbox({
Key key,
- this.value,
- this.activeColor,
- this.onChanged
+ @required this.value,
+ @required this.onChanged,
+ this.activeColor
}) : super(key: key);
/// Whether this checkbox is checked.
final bool value;
- /// The color to use when this checkbox is checked.
- ///
- /// Defaults to accent color of the current [Theme].
- final Color activeColor;
-
/// Called when the value of the checkbox should change.
///
/// The checkbox passes the new value to the callback but does not actually
@@ -64,6 +60,11 @@
/// If null, the checkbox will be displayed as disabled.
final ValueChanged<bool> onChanged;
+ /// The color to use when this checkbox is checked.
+ ///
+ /// Defaults to accent color of the current [Theme].
+ final Color activeColor;
+
/// The width of a checkbox widget.
static const double width = 18.0;
diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart
index 8035d01..04081ff 100644
--- a/packages/flutter/lib/src/material/date_picker.dart
+++ b/packages/flutter/lib/src/material/date_picker.dart
@@ -8,6 +8,7 @@
import 'package:flutter/widgets.dart';
import 'package:intl/date_symbols.dart';
import 'package:intl/intl.dart';
+import 'package:meta/meta.dart';
import 'colors.dart';
import 'debug.dart';
@@ -37,10 +38,10 @@
/// [showDatePicker] to show a date picker in a dialog.
DatePicker({
Key key,
- this.selectedDate,
- this.onChanged,
- this.firstDate,
- this.lastDate
+ @required this.selectedDate,
+ @required this.onChanged,
+ @required this.firstDate,
+ @required this.lastDate
}) : super(key: key) {
assert(selectedDate != null);
assert(firstDate != null);
@@ -134,9 +135,9 @@
class _DatePickerHeader extends StatelessWidget {
_DatePickerHeader({
Key key,
- this.selectedDate,
- this.mode,
- this.onModeChanged
+ @required this.selectedDate,
+ @required this.mode,
+ @required this.onModeChanged
}) : super(key: key) {
assert(selectedDate != null);
assert(mode != null);
@@ -241,10 +242,10 @@
/// Rarely used directly. Instead, typically used as part of a [DatePicker].
DayPicker({
Key key,
- this.selectedDate,
- this.currentDate,
- this.onChanged,
- this.displayedMonth
+ @required this.selectedDate,
+ @required this.currentDate,
+ @required this.onChanged,
+ @required this.displayedMonth
}) : super(key: key) {
assert(selectedDate != null);
assert(currentDate != null);
@@ -365,10 +366,10 @@
/// Rarely used directly. Instead, typically used as part of a [DatePicker].
MonthPicker({
Key key,
- this.selectedDate,
- this.onChanged,
- this.firstDate,
- this.lastDate
+ @required this.selectedDate,
+ @required this.onChanged,
+ @required this.firstDate,
+ @required this.lastDate
}) : super(key: key) {
assert(selectedDate != null);
assert(onChanged != null);
@@ -515,10 +516,10 @@
/// Rarely used directly. Instead, typically used as part of a [DatePicker].
YearPicker({
Key key,
- this.selectedDate,
- this.onChanged,
- this.firstDate,
- this.lastDate
+ @required this.selectedDate,
+ @required this.onChanged,
+ @required this.firstDate,
+ @required this.lastDate
}) : super(key: key) {
assert(selectedDate != null);
assert(onChanged != null);
diff --git a/packages/flutter/lib/src/material/drop_down.dart b/packages/flutter/lib/src/material/drop_down.dart
index c3e47f3..933193c 100644
--- a/packages/flutter/lib/src/material/drop_down.dart
+++ b/packages/flutter/lib/src/material/drop_down.dart
@@ -6,6 +6,7 @@
import 'dart:math' as math;
import 'package:flutter/widgets.dart';
+import 'package:meta/meta.dart';
import 'debug.dart';
import 'icon.dart';
@@ -376,9 +377,9 @@
/// defaults, so do not need to be specified).
DropDownButton({
Key key,
- this.items,
- this.value,
- this.onChanged,
+ @required this.items,
+ @required this.value,
+ @required this.onChanged,
this.elevation: 8,
this.style,
this.iconSize: 36.0
diff --git a/packages/flutter/lib/src/material/flat_button.dart b/packages/flutter/lib/src/material/flat_button.dart
index c6528b7..18a0d3d 100644
--- a/packages/flutter/lib/src/material/flat_button.dart
+++ b/packages/flutter/lib/src/material/flat_button.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
+import 'package:meta/meta.dart';
import 'button.dart';
import 'theme.dart';
@@ -40,7 +41,7 @@
/// caps.
FlatButton({
Key key,
- this.onPressed,
+ @required this.onPressed,
this.textColor,
this.disabledTextColor,
this.color,
diff --git a/packages/flutter/lib/src/material/floating_action_button.dart b/packages/flutter/lib/src/material/floating_action_button.dart
index df791fc..a14461b 100644
--- a/packages/flutter/lib/src/material/floating_action_button.dart
+++ b/packages/flutter/lib/src/material/floating_action_button.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
+import 'package:meta/meta.dart';
import 'colors.dart';
import 'icon_theme_data.dart';
@@ -49,7 +50,7 @@
this.backgroundColor,
this.elevation: 6,
this.highlightElevation: 12,
- this.onPressed,
+ @required this.onPressed,
this.mini: false
}) : super(key: key);
diff --git a/packages/flutter/lib/src/material/icon_button.dart b/packages/flutter/lib/src/material/icon_button.dart
index a07d6c1..ab283cd 100644
--- a/packages/flutter/lib/src/material/icon_button.dart
+++ b/packages/flutter/lib/src/material/icon_button.dart
@@ -50,7 +50,7 @@
@required this.icon,
this.color,
this.disabledColor,
- this.onPressed,
+ @required this.onPressed,
this.tooltip
}) : super(key: key);
diff --git a/packages/flutter/lib/src/material/radio.dart b/packages/flutter/lib/src/material/radio.dart
index 3adf64e..1bb8143 100644
--- a/packages/flutter/lib/src/material/radio.dart
+++ b/packages/flutter/lib/src/material/radio.dart
@@ -4,6 +4,7 @@
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
+import 'package:meta/meta.dart';
import 'constants.dart';
import 'debug.dart';
@@ -47,10 +48,10 @@
/// * [onChanged] is when the user selects this radio button.
Radio({
Key key,
- this.value,
- this.groupValue,
- this.activeColor,
- this.onChanged
+ @required this.value,
+ @required this.groupValue,
+ @required this.onChanged,
+ this.activeColor
}) : super(key: key);
/// The value represented by this radio button.
@@ -62,11 +63,6 @@
/// [groupValue].
final T groupValue;
- /// The color to use when this radio button is selected.
- ///
- /// Defaults to accent color of the current [Theme].
- final Color activeColor;
-
/// Called when the user selects this radio button.
///
/// The radio button passes [value] as a parameter to this callback. The radio
@@ -76,6 +72,11 @@
/// If null, the radio button will be displayed as disabled.
final ValueChanged<T> onChanged;
+ /// The color to use when this radio button is selected.
+ ///
+ /// Defaults to accent color of the current [Theme].
+ final Color activeColor;
+
bool get _enabled => onChanged != null;
Color _getInactiveColor(ThemeData themeData) {
diff --git a/packages/flutter/lib/src/material/raised_button.dart b/packages/flutter/lib/src/material/raised_button.dart
index f6e24a3..df7e24e 100644
--- a/packages/flutter/lib/src/material/raised_button.dart
+++ b/packages/flutter/lib/src/material/raised_button.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
+import 'package:meta/meta.dart';
import 'button.dart';
import 'colors.dart';
@@ -38,7 +39,7 @@
/// caps.
RaisedButton({
Key key,
- this.onPressed,
+ @required this.onPressed,
this.color,
this.disabledColor,
this.elevation: 2,
diff --git a/packages/flutter/lib/src/material/slider.dart b/packages/flutter/lib/src/material/slider.dart
index f474a31..ba72f23 100644
--- a/packages/flutter/lib/src/material/slider.dart
+++ b/packages/flutter/lib/src/material/slider.dart
@@ -7,6 +7,7 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
+import 'package:meta/meta.dart';
import 'colors.dart';
import 'constants.dart';
@@ -50,13 +51,13 @@
/// * [onChanged] is called when the user selects a new value for the slider.
Slider({
Key key,
- this.value,
+ @required this.value,
+ @required this.onChanged,
this.min: 0.0,
this.max: 1.0,
this.divisions,
this.label,
- this.activeColor,
- this.onChanged
+ this.activeColor
}) : super(key: key) {
assert(value != null);
assert(min != null);
@@ -70,6 +71,15 @@
/// The slider's thumb is drawn at a position that corresponds to this value.
final double value;
+ /// Called when the user selects a new value for the slider.
+ ///
+ /// The slider passes the new value to the callback but does not actually
+ /// change state until the parent widget rebuilds the slider with the new
+ /// value.
+ ///
+ /// If null, the slider will be displayed as disabled.
+ final ValueChanged<double> onChanged;
+
/// The minium value the user can select.
///
/// Defaults to 0.0.
@@ -97,15 +107,6 @@
/// Defaults to accent color of the current [Theme].
final Color activeColor;
- /// Called when the user selects a new value for the slider.
- ///
- /// The slider passes the new value to the callback but does not actually
- /// change state until the parent widget rebuilds the slider with the new
- /// value.
- ///
- /// If null, the slider will be displayed as disabled.
- final ValueChanged<double> onChanged;
-
void _handleChanged(double value) {
assert(onChanged != null);
onChanged(value * (max - min) + min);
diff --git a/packages/flutter/lib/src/material/snack_bar.dart b/packages/flutter/lib/src/material/snack_bar.dart
index e0a0b33..646dec8 100644
--- a/packages/flutter/lib/src/material/snack_bar.dart
+++ b/packages/flutter/lib/src/material/snack_bar.dart
@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'package:flutter/widgets.dart';
+import 'package:meta/meta.dart';
import 'button.dart';
import 'flat_button.dart';
@@ -46,7 +47,11 @@
/// Creates an action for a [SnackBar].
///
/// The [label] and [onPressed] arguments must be non-null.
- SnackBarAction({Key key, this.label, this.onPressed }) : super(key: key) {
+ SnackBarAction({
+ Key key,
+ this.label,
+ @required this.onPressed
+ }) : super(key: key) {
assert(label != null);
assert(onPressed != null);
}
diff --git a/packages/flutter/lib/src/material/switch.dart b/packages/flutter/lib/src/material/switch.dart
index f2db893..3b5ee8a 100644
--- a/packages/flutter/lib/src/material/switch.dart
+++ b/packages/flutter/lib/src/material/switch.dart
@@ -5,6 +5,7 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
+import 'package:meta/meta.dart';
import 'colors.dart';
import 'constants.dart';
@@ -42,16 +43,25 @@
/// * [onChanged] is called when the user toggles with switch on or off.
Switch({
Key key,
- this.value,
+ @required this.value,
+ @required this.onChanged,
this.activeColor,
this.activeThumbDecoration,
- this.inactiveThumbDecoration,
- this.onChanged
+ this.inactiveThumbDecoration
}) : super(key: key);
/// Whether this switch is on or off.
final bool value;
+ /// Called when the user toggles with switch on or off.
+ ///
+ /// The switch passes the new value to the callback but does not actually
+ /// change state until the parent widget rebuilds the switch with the new
+ /// value.
+ ///
+ /// If null, the switch will be displayed as disabled.
+ final ValueChanged<bool> onChanged;
+
/// The color to use when this switch is on.
///
/// Defaults to accent color of the current [Theme].
@@ -67,15 +77,6 @@
/// Defaults to a circular piece of material.
final Decoration inactiveThumbDecoration;
- /// Called when the user toggles with switch on or off.
- ///
- /// The switch passes the new value to the callback but does not actually
- /// change state until the parent widget rebuilds the switch with the new
- /// value.
- ///
- /// If null, the switch will be displayed as disabled.
- final ValueChanged<bool> onChanged;
-
@override
Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context));
diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart
index 7eb1719..56a5bf6 100644
--- a/packages/flutter/lib/src/material/tabs.dart
+++ b/packages/flutter/lib/src/material/tabs.dart
@@ -8,6 +8,7 @@
import 'package:flutter/physics.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
+import 'package:meta/meta.dart';
import 'colors.dart';
import 'debug.dart';
@@ -455,9 +456,9 @@
TabBarSelection({
Key key,
this.value,
- this.values,
+ @required this.values,
this.onChanged,
- this.child
+ @required this.child
}) : super(key: key) {
assert(values != null && values.length > 0);
assert(new Set<T>.from(values).length == values.length);
diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart
index db6536b..3f42dde 100644
--- a/packages/flutter/lib/src/material/time_picker.dart
+++ b/packages/flutter/lib/src/material/time_picker.dart
@@ -6,6 +6,7 @@
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
+import 'package:meta/meta.dart';
import 'colors.dart';
import 'theme.dart';
@@ -119,8 +120,8 @@
/// creates a time picker dialog.
TimePicker({
Key key,
- this.selectedTime,
- this.onChanged
+ @required this.selectedTime,
+ @required this.onChanged
}) : super(key: key) {
assert(selectedTime != null);
}
@@ -180,10 +181,10 @@
// TODO(ianh): Localize!
class _TimePickerHeader extends StatelessWidget {
_TimePickerHeader({
- this.selectedTime,
- this.mode,
- this.onModeChanged,
- this.onChanged
+ @required this.selectedTime,
+ @required this.mode,
+ @required this.onModeChanged,
+ @required this.onChanged
}) {
assert(selectedTime != null);
assert(mode != null);
@@ -393,9 +394,9 @@
class _Dial extends StatefulWidget {
_Dial({
- this.selectedTime,
- this.mode,
- this.onChanged
+ @required this.selectedTime,
+ @required this.mode,
+ @required this.onChanged
}) {
assert(selectedTime != null);
}
diff --git a/packages/flutter/test/material/switch_test.dart b/packages/flutter/test/material/switch_test.dart
index 50677de..ad11b09 100644
--- a/packages/flutter/test/material/switch_test.dart
+++ b/packages/flutter/test/material/switch_test.dart
@@ -65,6 +65,7 @@
child: new Center(
child: new Switch(
value: false,
+ onChanged: null,
activeThumbDecoration: activeDecoration,
inactiveThumbDecoration: inactiveDecoration
)
diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart
index d7cd511..2aeb7fe 100644
--- a/packages/flutter/test/material/tabs_test.dart
+++ b/packages/flutter/test/material/tabs_test.dart
@@ -31,6 +31,7 @@
child: new TabBarSelection<String>(
value: value,
values: tabs,
+ onChanged: null,
child: new TabBar<String>(
key: tabBarKey,
labels: new Map<String, TabLabel>.fromIterable(tabs, value: (String tab) => new TabLabel(text: tab)),