null safety

This commit is contained in:
phyothandar
2021-09-10 17:42:40 +06:30
parent 2c95ec7600
commit 93fcd473bb

View File

@@ -75,7 +75,7 @@ class MyDataRow {
this.key,
this.selected = false,
this.onSelectChanged,
@required this.cells,
required this.cells,
}) : assert(cells != null);
/// Creates the configuration for a row of a [MyDataTable], deriving
@@ -83,12 +83,12 @@ class MyDataRow {
///
/// The [cells] argument must not be null.
MyDataRow.byIndex({
int index,
int? index,
this.selected = false,
this.onSelectChanged,
@required this.cells,
required this.cells,
}) : assert(cells != null),
key = ValueKey<int>(index);
key = ValueKey<int>(index!);
/// A [Key] that uniquely identifies this row. This is used to
/// ensure that if a row is added or removed, any stateful widgets
@@ -96,7 +96,7 @@ class MyDataRow {
/// remain on the right row visually.
///
/// If the table never changes once created, no key is necessary.
final LocalKey key;
final LocalKey? key;
/// Called when the user selects or unselects a selectable row.
///
@@ -111,7 +111,7 @@ class MyDataRow {
/// A row whose [onSelectChanged] callback is null is ignored for
/// the purposes of determining the state of the "all" checkbox,
/// and its checkbox is disabled.
final ValueChanged<bool> onSelectChanged;
final ValueChanged<bool>? onSelectChanged;
/// Whether the row is selected.
///
@@ -193,7 +193,7 @@ class MyDataCell {
/// If non-null, tapping the cell will call this callback. If
/// null, tapping the cell will attempt to select the row (if
/// [MyDataRow.onSelectChanged] is provided).
final VoidCallback onTap;
final VoidCallback? onTap;
bool get _debugInteractive => onTap != null;
}
@@ -309,8 +309,8 @@ class MyDataTable extends StatelessWidget {
/// the sort order is ascending, this should be true (the default),
/// otherwise it should be false.
MyDataTable({
Key key,
@required this.columns,
Key? key,
required this.columns,
this.sortColumnIndex,
this.sortAscending = true,
this.onSelectAll,
@@ -320,7 +320,7 @@ class MyDataTable extends StatelessWidget {
this.columnSpacing = 56.0,
this.oddLine,
this.evenLine,
@required this.rows,
required this.rows,
}) : assert(columns != null),
assert(columns.isNotEmpty),
assert(sortColumnIndex == null ||
@@ -339,8 +339,8 @@ class MyDataTable extends StatelessWidget {
/// The configuration and labels for the columns in the table.
final List<MyDataColumn> columns;
final Decoration oddLine;
final Decoration evenLine;
final Decoration? oddLine;
final Decoration? evenLine;
/// The current primary sort key's column.
///
@@ -353,7 +353,7 @@ class MyDataTable extends StatelessWidget {
///
/// When this is null, it implies that the table's sort order does
/// not correspond to any of the columns.
final int sortColumnIndex;
final int? sortColumnIndex;
/// Whether the column mentioned in [sortColumnIndex], if any, is sorted
/// in ascending order.
@@ -376,7 +376,7 @@ class MyDataTable extends StatelessWidget {
/// To control whether a particular row is selectable or not, see
/// [MyDataRow.onSelectChanged]. This callback is only relevant if any
/// row is selectable.
final ValueSetter<bool> onSelectAll;
final ValueSetter<bool>? onSelectAll;
/// The height of each row (excluding the row that contains column headings).
///
@@ -413,11 +413,10 @@ class MyDataTable extends StatelessWidget {
// non-numeric, if there is exactly one, otherwise null.
final int _onlyTextColumn;
static int _initOnlyTextColumn(List<MyDataColumn> columns) {
int result;
int result = 0;
for (int index = 0; index < columns.length; index += 1) {
final MyDataColumn column = columns[index];
if (!column.numeric) {
if (result != null) return null;
result = index;
}
}
@@ -433,11 +432,11 @@ class MyDataTable extends StatelessWidget {
void _handleSelectAll(bool checked) {
if (onSelectAll != null) {
onSelectAll(checked);
onSelectAll!(checked);
} else {
for (MyDataRow row in rows) {
if ((row.onSelectChanged != null) && (row.selected != checked))
row.onSelectChanged(checked);
row.onSelectChanged!(checked);
}
}
}
@@ -452,10 +451,10 @@ class MyDataTable extends StatelessWidget {
Color(0x1E000000); // Dark theme variant is just a guess.
Widget _buildCheckbox({
Color color,
bool checked,
VoidCallback onRowTap,
ValueChanged<bool> onCheckboxChanged,
Color? color,
bool? checked,
VoidCallback? onRowTap,
ValueChanged<bool>? onCheckboxChanged,
}) {
Widget contents = Semantics(
container: true,
@@ -466,7 +465,9 @@ class MyDataTable extends StatelessWidget {
child: Checkbox(
activeColor: color,
value: checked,
onChanged: onCheckboxChanged,
onChanged: (bool? value) {
onCheckboxChanged!(value ?? false);
},
),
),
),
@@ -484,32 +485,33 @@ class MyDataTable extends StatelessWidget {
}
Widget _buildHeadingCell({
BuildContext context,
EdgeInsetsGeometry padding,
Widget label,
String tooltip,
bool numeric,
VoidCallback onSort,
bool sorted,
bool ascending,
required BuildContext context,
EdgeInsetsGeometry? padding,
Widget? label,
String? tooltip,
bool? numeric,
VoidCallback? onSort,
bool? sorted,
bool? ascending,
}) {
if (onSort != null) {
final Widget arrow = _SortArrow(
visible: sorted,
visible: sorted!,
down: sorted ? ascending : null,
duration: _sortArrowAnimationDuration,
);
const Widget arrowPadding = SizedBox(width: _sortArrowPadding);
label = Row(
textDirection: numeric ? TextDirection.rtl : null,
children: <Widget>[label, arrowPadding, arrow],
textDirection: (numeric ?? false) ? TextDirection.rtl : null,
children: <Widget>[label ?? Container(), arrowPadding, arrow],
);
}
label = Container(
padding: padding,
height: headingRowHeight,
alignment:
numeric ? Alignment.centerRight : AlignmentDirectional.centerStart,
alignment: (numeric ?? false)
? Alignment.centerRight
: AlignmentDirectional.centerStart,
child: AnimatedDefaultTextStyle(
style: TextStyle(
// TODO(ianh): font family should match Theme; see https://github.com/flutter/flutter/issues/3116
@@ -517,12 +519,16 @@ class MyDataTable extends StatelessWidget {
fontSize: _headingFontSize,
height: math.min(1.0, headingRowHeight / _headingFontSize),
color: (Theme.of(context).brightness == Brightness.light)
? ((onSort != null && sorted) ? Colors.black87 : Colors.black54)
: ((onSort != null && sorted) ? Colors.white : Colors.white70),
? ((onSort != null && (sorted ?? false))
? Colors.black87
: Colors.black54)
: ((onSort != null && (sorted ?? false))
? Colors.white
: Colors.white70),
),
softWrap: false,
duration: _sortArrowAnimationDuration,
child: label,
child: label ?? Container(),
),
);
if (tooltip != null) {
@@ -541,42 +547,43 @@ class MyDataTable extends StatelessWidget {
}
Widget _buildMyDataCell({
BuildContext context,
EdgeInsetsGeometry padding,
Widget label,
bool numeric,
bool placeholder,
bool showEditIcon,
VoidCallback onTap,
VoidCallback onSelectChanged,
required BuildContext context,
EdgeInsetsGeometry? padding,
Widget? label,
bool? numeric,
bool? placeholder,
bool? showEditIcon,
VoidCallback? onTap,
VoidCallback? onSelectChanged,
}) {
final bool isLightTheme = Theme.of(context).brightness == Brightness.light;
if (showEditIcon) {
if (showEditIcon ?? false) {
const Widget icon = Icon(Icons.edit, size: 18.0);
label = Expanded(child: label);
label = Expanded(child: label ?? Container());
label = Row(
textDirection: numeric ? TextDirection.rtl : null,
textDirection: (numeric ?? false) ? TextDirection.rtl : null,
children: <Widget>[label, icon],
);
}
label = Container(
padding: padding,
height: MyDataRowHeight,
alignment:
numeric ? Alignment.centerRight : AlignmentDirectional.centerStart,
alignment: (numeric ?? false)
? Alignment.centerRight
: AlignmentDirectional.centerStart,
child: DefaultTextStyle(
style: TextStyle(
// TODO(ianh): font family should be Roboto; see https://github.com/flutter/flutter/issues/3116
fontSize: 13.0,
color: isLightTheme
? (placeholder ? Colors.black38 : Colors.black87)
: (placeholder ? Colors.white38 : Colors.white70),
? ((placeholder ?? false) ? Colors.black38 : Colors.black87)
: ((placeholder ?? false) ? Colors.white38 : Colors.white70),
),
child: IconTheme.merge(
data: IconThemeData(
color: isLightTheme ? Colors.black54 : Colors.white70,
),
child: DropdownButtonHideUnderline(child: label),
child: DropdownButtonHideUnderline(child: label ?? Container()),
),
),
);
@@ -613,8 +620,9 @@ class MyDataTable extends StatelessWidget {
final bool showCheckboxColumn = false;
final bool allChecked = false;
final List<TableColumnWidth> tableColumns =
List<TableColumnWidth>(columns.length + (showCheckboxColumn ? 1 : 0));
final List<TableColumnWidth> tableColumns = (columns.length +
(showCheckboxColumn ? 1 : 0)) as List<TableColumnWidth>;
final List<TableRow> tableRows = List<TableRow>.generate(
rows.length + 1, // the +1 is for the header row
(int index) {
@@ -627,7 +635,7 @@ class MyDataTable extends StatelessWidget {
: index.isEven && evenLine != null
? evenLine
: _kUnselectedDecoration,
children: List<Widget>(tableColumns.length),
children: tableColumns.map((e) => Container()).toList(),
);
},
);
@@ -635,28 +643,28 @@ class MyDataTable extends StatelessWidget {
int rowIndex;
int displayColumnIndex = 0;
if (showCheckboxColumn) {
tableColumns[0] = FixedColumnWidth(
horizontalMargin + Checkbox.width + horizontalMargin / 2.0);
tableRows[0].children[0] = _buildCheckbox(
color: theme.accentColor,
checked: allChecked,
onCheckboxChanged: _handleSelectAll,
);
rowIndex = 1;
for (MyDataRow row in rows) {
tableRows[rowIndex].children[0] = _buildCheckbox(
color: theme.accentColor,
checked: row.selected,
onRowTap: () => row.onSelectChanged != null
? row.onSelectChanged(!row.selected)
: null,
onCheckboxChanged: row.onSelectChanged,
);
rowIndex += 1;
}
displayColumnIndex += 1;
}
// if (showCheckboxColumn) {
// tableColumns[0] = FixedColumnWidth(
// horizontalMargin + Checkbox.width + horizontalMargin / 2.0);
// tableRows[0].children![0] = _buildCheckbox(
// color: theme.accentColor,
// checked: allChecked,
// onCheckboxChanged: _handleSelectAll,
// );
// rowIndex = 1;
// for (MyDataRow row in rows) {
// tableRows[rowIndex].children[0] = _buildCheckbox(
// color: theme.accentColor,
// checked: row.selected,
// onRowTap: () => row.onSelectChanged != null
// ? row.onSelectChanged(!row.selected)
// : null,
// onCheckboxChanged: row.onSelectChanged,
// );
// rowIndex += 1;
// }
// displayColumnIndex += 1;
// }
for (int MyDataColumnIndex = 0;
MyDataColumnIndex < columns.length;
@@ -689,14 +697,14 @@ class MyDataTable extends StatelessWidget {
} else {
tableColumns[displayColumnIndex] = const IntrinsicColumnWidth();
}
tableRows[0].children[displayColumnIndex] = _buildHeadingCell(
tableRows[0].children![displayColumnIndex] = _buildHeadingCell(
context: context,
padding: padding,
label: column.label,
tooltip: column.tooltip,
numeric: column.numeric,
onSort: () => column.onSort != null
? column.onSort(MyDataColumnIndex,
? column.onSort!(MyDataColumnIndex,
sortColumnIndex != MyDataColumnIndex || !sortAscending)
: null,
sorted: MyDataColumnIndex == sortColumnIndex,
@@ -705,7 +713,7 @@ class MyDataTable extends StatelessWidget {
rowIndex = 1;
for (MyDataRow row in rows) {
final MyDataCell cell = row.cells[MyDataColumnIndex];
tableRows[rowIndex].children[displayColumnIndex] = _buildMyDataCell(
tableRows[rowIndex].children?[displayColumnIndex] = _buildMyDataCell(
context: context,
padding: padding,
label: cell.child,
@@ -714,7 +722,7 @@ class MyDataTable extends StatelessWidget {
showEditIcon: cell.showEditIcon,
onTap: cell.onTap,
onSelectChanged: () => row.onSelectChanged != null
? row.onSelectChanged(!row.selected)
? row.onSelectChanged!(!row.selected)
: null,
);
rowIndex += 1;
@@ -745,12 +753,12 @@ class MyDataTable extends StatelessWidget {
class TableRowInkWell extends InkResponse {
/// Creates an ink well for a table row.
const TableRowInkWell({
Key key,
Widget child,
GestureTapCallback onTap,
GestureTapCallback onDoubleTap,
GestureLongPressCallback onLongPress,
ValueChanged<bool> onHighlightChanged,
Key? key,
Widget? child,
GestureTapCallback? onTap,
GestureTapCallback? onDoubleTap,
GestureLongPressCallback? onLongPress,
ValueChanged<bool>? onHighlightChanged,
}) : super(
key: key,
child: child,
@@ -766,7 +774,7 @@ class TableRowInkWell extends InkResponse {
RectCallback getRectCallback(RenderBox referenceBox) {
return () {
RenderObject cell = referenceBox;
AbstractNode table = cell.parent;
AbstractNode? table = cell.parent;
final Matrix4 transform = Matrix4.identity();
while (table is RenderObject && table is! RenderTable) {
final RenderObject parentBox = table as RenderObject;
@@ -779,11 +787,11 @@ class TableRowInkWell extends InkResponse {
final TableCellParentData cellParentData =
cell.parentData as TableCellParentData;
assert(cellParentData.y != null);
final Rect rect = table.getRowBox(cellParentData.y);
final Rect rect = table.getRowBox(cellParentData.y!);
// The rect is in the table's coordinate space. We need to change it to the
// TableRowInkWell's coordinate space.
table.applyPaintTransform(cell, transform);
final Offset offset = MatrixUtils.getAsTranslation(transform);
final Offset? offset = MatrixUtils.getAsTranslation(transform);
if (offset != null) return rect.shift(-offset);
}
return Rect.zero;
@@ -799,31 +807,31 @@ class TableRowInkWell extends InkResponse {
class _SortArrow extends StatefulWidget {
const _SortArrow({
Key key,
Key? key,
this.visible,
this.down,
this.duration,
}) : super(key: key);
final bool visible;
final bool? visible;
final bool down;
final bool? down;
final Duration duration;
final Duration? duration;
@override
_SortArrowState createState() => _SortArrowState();
}
class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin {
AnimationController _opacityController;
Animation<double> _opacityAnimation;
AnimationController? _opacityController;
Animation<double>? _opacityAnimation;
AnimationController _orientationController;
Animation<double> _orientationAnimation;
AnimationController? _orientationController;
Animation<double>? _orientationAnimation;
double _orientationOffset = 0.0;
bool _down;
bool? _down;
static final Animatable<double> _turnTween =
Tween<double>(begin: 0.0, end: math.pi)
@@ -839,15 +847,16 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin {
),
curve: Curves.fastOutSlowIn,
)..addListener(_rebuild);
_opacityController.value = widget.visible ? 1.0 : 0.0;
_opacityController!.value = (widget.visible ?? false) ? 1.0 : 0.0;
_orientationController = AnimationController(
duration: widget.duration,
vsync: this,
);
_orientationAnimation = _orientationController.drive(_turnTween)
_orientationAnimation = _orientationController!.drive(_turnTween)
..addListener(_rebuild)
..addStatusListener(_resetOrientationAnimation);
if (widget.visible) _orientationOffset = widget.down ? 0.0 : math.pi;
if (widget.visible ?? false)
_orientationOffset = (widget.down ?? false) ? 0.0 : math.pi;
}
void _rebuild() {
@@ -858,9 +867,9 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin {
void _resetOrientationAnimation(AnimationStatus status) {
if (status == AnimationStatus.completed) {
assert(_orientationAnimation.value == math.pi);
assert(_orientationAnimation!.value == math.pi);
_orientationOffset += math.pi;
_orientationController.value =
_orientationController!.value =
0.0; // TODO(ianh): This triggers a pointless rebuild.
}
}
@@ -869,26 +878,26 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin {
void didUpdateWidget(_SortArrow oldWidget) {
super.didUpdateWidget(oldWidget);
bool skipArrow = false;
final bool newDown = widget.down ?? _down;
final bool newDown = widget.down ?? _down!;
if (oldWidget.visible != widget.visible) {
if (widget.visible &&
(_opacityController.status == AnimationStatus.dismissed)) {
_orientationController.stop();
_orientationController.value = 0.0;
if (widget.visible! &&
(_opacityController!.status == AnimationStatus.dismissed)) {
_orientationController!.stop();
_orientationController!.value = 0.0;
_orientationOffset = newDown ? 0.0 : math.pi;
skipArrow = true;
}
if (widget.visible) {
_opacityController.forward();
if ((widget.visible ?? false)) {
_opacityController!.forward();
} else {
_opacityController.reverse();
_opacityController!.reverse();
}
}
if ((_down != newDown) && !skipArrow) {
if (_orientationController.status == AnimationStatus.dismissed) {
_orientationController.forward();
if (_orientationController!.status == AnimationStatus.dismissed) {
_orientationController?.forward();
} else {
_orientationController.reverse();
_orientationController?.reverse();
}
}
_down = newDown;
@@ -896,8 +905,8 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin {
@override
void dispose() {
_opacityController.dispose();
_orientationController.dispose();
_opacityController?.dispose();
_orientationController?.dispose();
super.dispose();
}
@@ -907,10 +916,10 @@ class _SortArrowState extends State<_SortArrow> with TickerProviderStateMixin {
@override
Widget build(BuildContext context) {
return Opacity(
opacity: _opacityAnimation.value,
opacity: _opacityAnimation!.value,
child: Transform(
transform:
Matrix4.rotationZ(_orientationOffset + _orientationAnimation.value)
Matrix4.rotationZ(_orientationOffset + _orientationAnimation!.value)
..setTranslationRaw(0.0, _arrowIconBaselineOffset, 0.0),
alignment: Alignment.center,
child: Icon(