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