From 93fcd473bbe1481b52ca6be7eb6db2e0592e2b40 Mon Sep 17 00:00:00 2001 From: phyothandar Date: Fri, 10 Sep 2021 17:42:40 +0630 Subject: [PATCH] null safety --- lib/pages/widgets/my_data_table.dart | 255 ++++++++++++++------------- 1 file changed, 132 insertions(+), 123 deletions(-) diff --git a/lib/pages/widgets/my_data_table.dart b/lib/pages/widgets/my_data_table.dart index 3015c50..f86925f 100644 --- a/lib/pages/widgets/my_data_table.dart +++ b/lib/pages/widgets/my_data_table.dart @@ -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(index); + key = ValueKey(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 onSelectChanged; + final ValueChanged? 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 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 onSelectAll; + final ValueSetter? 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 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 onCheckboxChanged, + Color? color, + bool? checked, + VoidCallback? onRowTap, + ValueChanged? 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: [label, arrowPadding, arrow], + textDirection: (numeric ?? false) ? TextDirection.rtl : null, + children: [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: [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 tableColumns = - List(columns.length + (showCheckboxColumn ? 1 : 0)); + final List tableColumns = (columns.length + + (showCheckboxColumn ? 1 : 0)) as List; + final List tableRows = List.generate( rows.length + 1, // the +1 is for the header row (int index) { @@ -627,36 +635,36 @@ class MyDataTable extends StatelessWidget { : index.isEven && evenLine != null ? evenLine : _kUnselectedDecoration, - children: List(tableColumns.length), + children: tableColumns.map((e) => Container()).toList(), ); }, ); 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; - } + 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; + // } 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 onHighlightChanged, + Key? key, + Widget? child, + GestureTapCallback? onTap, + GestureTapCallback? onDoubleTap, + GestureLongPressCallback? onLongPress, + ValueChanged? 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 _opacityAnimation; + AnimationController? _opacityController; + Animation? _opacityAnimation; - AnimationController _orientationController; - Animation _orientationAnimation; + AnimationController? _orientationController; + Animation? _orientationAnimation; double _orientationOffset = 0.0; - bool _down; + bool? _down; static final Animatable _turnTween = Tween(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(