Null safety

This commit is contained in:
Hillel Coren 2023-09-19 13:16:56 +03:00
parent 3b3dd7c91d
commit 824a60da68
19 changed files with 68 additions and 57 deletions

View File

@ -130,7 +130,8 @@ abstract class ScheduleEntity extends Object
return actions..addAll(super.getActions(userCompany: userCompany)); return actions..addAll(super.getActions(userCompany: userCompany));
} }
int compareTo(ScheduleEntity? schedule, String sortField, bool sortAscending) { int compareTo(
ScheduleEntity? schedule, String sortField, bool sortAscending) {
int response = 0; int response = 0;
final scheduleA = sortAscending ? this : schedule; final scheduleA = sortAscending ? this : schedule;
final scheduleB = sortAscending ? schedule : this; final scheduleB = sortAscending ? schedule : this;
@ -174,9 +175,9 @@ abstract class ScheduleEntity extends Object
} }
@override @override
String? get listDisplayName { String get listDisplayName {
final localization = AppLocalization.of(navigatorKey.currentContext!)!; final localization = AppLocalization.of(navigatorKey.currentContext!)!;
return localization.lookup(template); return localization.lookup(template) ?? '';
} }
@override @override
@ -204,7 +205,7 @@ abstract class ScheduleParameters
showPaymentsTable: showPaymentsTable:
action == ScheduleEntity.TEMPLATE_EMAIL_STATEMENT ? true : null, action == ScheduleEntity.TEMPLATE_EMAIL_STATEMENT ? true : null,
onlyClientsWithInvoices: onlyClientsWithInvoices:
action == ScheduleEntity.TEMPLATE_EMAIL_STATEMENT ? false : null, action == ScheduleEntity.TEMPLATE_EMAIL_STATEMENT ? false : null,
showCreditsTable: showCreditsTable:
action == ScheduleEntity.TEMPLATE_EMAIL_STATEMENT ? true : null, action == ScheduleEntity.TEMPLATE_EMAIL_STATEMENT ? true : null,
status: action == ScheduleEntity.TEMPLATE_EMAIL_STATEMENT status: action == ScheduleEntity.TEMPLATE_EMAIL_STATEMENT

View File

@ -363,11 +363,12 @@ abstract class TransactionEntity extends Object
} }
@override @override
String? get listDisplayName { String get listDisplayName {
if (description.isNotEmpty) { if (description.isNotEmpty) {
return description.split('\n').first; return description.split('\n').first;
} else { } else {
return AppLocalization.of(navigatorKey.currentContext!)!.transaction; return AppLocalization.of(navigatorKey.currentContext!)!.transaction ??
'';
} }
} }

View File

@ -49,7 +49,7 @@ class AppBottomBar extends StatefulWidget {
final List<String> sortFields; final List<String> sortFields;
final List<EntityStatus> statuses; final List<EntityStatus> statuses;
final Function onCheckboxPressed; final Function onCheckboxPressed;
final Function(String?)? onSelectedSortField; final Function(String)? onSelectedSortField;
final Function(EntityState, bool?)? onSelectedState; final Function(EntityState, bool?)? onSelectedState;
final Function(EntityStatus, bool?)? onSelectedStatus; final Function(EntityStatus, bool?)? onSelectedStatus;
final Function(String)? onSelectedCustom1; final Function(String)? onSelectedCustom1;
@ -258,7 +258,7 @@ class _AppBottomBarState extends State<AppBottomBar> {
listUIState.sortField == field) { listUIState.sortField == field) {
// Is re-selecting // Is re-selecting
widget.onSelectedSortField!(field); widget.onSelectedSortField!(field);
} else { } else if (value != null) {
widget.onSelectedSortField!(value); widget.onSelectedSortField!(value);
} }
}, },

View File

@ -393,7 +393,7 @@ class _EntityDropdownState extends State<EntityDropdown> {
: kDefaultLightSelectedColor) : kDefaultLightSelectedColor)
: Theme.of(context).cardColor, : Theme.of(context).cardColor,
child: EntityAutocompleteListTile( child: EntityAutocompleteListTile(
onTap: (entity) => onSelected(entity), onTap: (entity) => onSelected(entity!),
entity: options.elementAt(index), entity: options.elementAt(index),
filter: _filter, filter: _filter,
overrideSuggestedAmount: overrideSuggestedAmount:
@ -550,7 +550,7 @@ class _EntityDropdownDialogState extends State<EntityDropdownDialog> {
itemCount: matches.length, itemCount: matches.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
final entityId = matches[index]; final entityId = matches[index];
final entity = widget.entityMap![entityId]; final entity = widget.entityMap![entityId]!;
return EntityAutocompleteListTile( return EntityAutocompleteListTile(
entity: entity, entity: entity,
filter: _filter, filter: _filter,
@ -586,12 +586,12 @@ class EntityAutocompleteListTile extends StatelessWidget {
this.onTap, this.onTap,
this.subtitle}); this.subtitle});
final SelectableEntity? entity; final SelectableEntity entity;
final Function(SelectableEntity? entity)? onTap; final Function(SelectableEntity entity)? onTap;
final String? filter; final String? filter;
final String? subtitle; final String? subtitle;
final Function(SelectableEntity?)? overrideSuggestedAmount; final Function(SelectableEntity)? overrideSuggestedAmount;
final Function(SelectableEntity?)? overrideSuggestedLabel; final Function(SelectableEntity)? overrideSuggestedLabel;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {

View File

@ -29,7 +29,7 @@ class ClientPicker extends StatelessWidget {
final String? clientId; final String? clientId;
final ClientState clientState; final ClientState clientState;
final Function(SelectableEntity) onSelected; final Function(SelectableEntity?) onSelected;
final Function(Completer<SelectableEntity> completer)? onAddPressed; final Function(Completer<SelectableEntity> completer)? onAddPressed;
final bool? autofocus; final bool? autofocus;
final List<String> excludeIds; final List<String> excludeIds;

View File

@ -93,10 +93,12 @@ class _CustomFieldState extends State<CustomField> {
case kFieldTypeSwitch: case kFieldTypeSwitch:
return BoolDropdownButton( return BoolDropdownButton(
onChanged: (value) { onChanged: (value) {
_controller!.text = value ? kSwitchValueYes : kSwitchValueNo; _controller!.text =
value == true ? kSwitchValueYes : kSwitchValueNo;
Debouncer.complete(); Debouncer.complete();
if (widget.onChanged != null) { if (widget.onChanged != null) {
widget.onChanged!(value ? kSwitchValueYes : kSwitchValueNo); widget
.onChanged!(value == true ? kSwitchValueYes : kSwitchValueNo);
} }
}, },
value: widget.value == null ? null : widget.value == kSwitchValueYes, value: widget.value == null ? null : widget.value == kSwitchValueYes,

View File

@ -117,7 +117,7 @@ class _DatePickerState extends State<DatePicker> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var label = widget.labelText; var label = widget.labelText ?? '';
if (widget.message != null && (widget.selectedDate ?? '').isEmpty) { if (widget.message != null && (widget.selectedDate ?? '').isEmpty) {
label += '${widget.message}'; label += '${widget.message}';
} }
@ -130,7 +130,7 @@ class _DatePickerState extends State<DatePicker> {
keyboardType: TextInputType.text, keyboardType: TextInputType.text,
decoration: InputDecoration( decoration: InputDecoration(
hintText: widget.hint ?? '', hintText: widget.hint ?? '',
labelText: _pendingValue ?? label ?? '', labelText: _pendingValue ?? label,
suffixIcon: suffixIcon:
widget.allowClearing && (widget.selectedDate ?? '').isNotEmpty widget.allowClearing && (widget.selectedDate ?? '').isNotEmpty
? IconButton( ? IconButton(

View File

@ -67,7 +67,7 @@ class DynamicSelector extends StatelessWidget {
return EntityDropdown( return EntityDropdown(
labelText: labelText ?? localization!.lookup('$entityType'), labelText: labelText ?? localization!.lookup('$entityType'),
entityType: entityType, entityType: entityType,
onSelected: (entity) => onChanged!(entity?.id), onSelected: (entity) => onChanged!(entity?.id ?? ''),
onAddPressed: onAddPressed, onAddPressed: onAddPressed,
entityId: entityId, entityId: entityId,
entityList: entityIds, entityList: entityIds,

View File

@ -28,7 +28,7 @@ class VendorPicker extends StatelessWidget {
final String vendorId; final String vendorId;
final VendorState vendorState; final VendorState vendorState;
final Function(SelectableEntity) onSelected; final Function(SelectableEntity?) onSelected;
final Function(Completer<SelectableEntity> completer) onAddPressed; final Function(Completer<SelectableEntity> completer) onAddPressed;
final bool? autofocus; final bool? autofocus;

View File

@ -1,4 +1,6 @@
// Flutter imports: // Flutter imports:
import 'dart:async';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -138,7 +140,7 @@ class MenuDrawerVM {
final completer = snackBarCompleter<Null>( final completer = snackBarCompleter<Null>(
context, AppLocalization.of(context)!.addedCompany, context, AppLocalization.of(context)!.addedCompany,
shouldPop: true) shouldPop: true)
..future.then((value) { ..future.then<Null>(() {
AppBuilder.of(navigatorKey.currentContext!)!.rebuild(); AppBuilder.of(navigatorKey.currentContext!)!.rebuild();
} as FutureOr<Null> Function(Null)); } as FutureOr<Null> Function(Null));

View File

@ -141,7 +141,7 @@ class AppPaginatedDataTable extends StatefulWidget {
/// checkbox in the heading row. /// checkbox in the heading row.
/// ///
/// See [DataTable.onSelectAll]. /// See [DataTable.onSelectAll].
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).
/// ///

View File

@ -3,6 +3,7 @@ import 'dart:async';
import 'dart:math'; import 'dart:math';
// Flutter imports: // Flutter imports:
import 'package:built_collection/built_collection.dart';
import 'package:collection/collection.dart' show IterableNullableExtension; import 'package:collection/collection.dart' show IterableNullableExtension;
import 'package:flutter/material.dart' hide DataRow, DataCell, DataColumn; import 'package:flutter/material.dart' hide DataRow, DataCell, DataColumn;
@ -116,7 +117,8 @@ class _EntityListState extends State<EntityList> {
final uiState = state.getUIState(widget.entityType)!; final uiState = state.getUIState(widget.entityType)!;
dataTableSource.editingId = uiState.editingId; dataTableSource.editingId = uiState.editingId;
dataTableSource.entityList = widget.entityList; dataTableSource.entityList = widget.entityList;
dataTableSource.entityMap = state.getEntityMap(widget.entityType) as BuiltMap<String?, BaseEntity?>?; dataTableSource.entityMap = state.getEntityMap(widget.entityType)
as BuiltMap<String?, BaseEntity?>?;
// ignore: invalid_use_of_visible_for_testing_member, invalid_use_of_protected_member // ignore: invalid_use_of_visible_for_testing_member, invalid_use_of_protected_member
dataTableSource.notifyListeners(); dataTableSource.notifyListeners();
@ -254,8 +256,8 @@ class _EntityListState extends State<EntityList> {
min(_firstRowIndex + rowsPerPage, entityList.length); min(_firstRowIndex + rowsPerPage, entityList.length);
final entities = entityList final entities = entityList
.sublist(startIndex, endIndex) .sublist(startIndex, endIndex)
.map<BaseEntity?>( .map<BaseEntity?>((String? entityId) =>
(String? entityId) => entityMap![entityId] as BaseEntity?) entityMap![entityId] as BaseEntity?)
.where((invoice) => .where((invoice) =>
value != listUIState.isSelected(invoice!.id)) value != listUIState.isSelected(invoice!.id))
.toList(); .toList();
@ -285,16 +287,18 @@ class _EntityListState extends State<EntityList> {
}), }),
], ],
source: dataTableSource, source: dataTableSource,
sortColumnIndex: sortColumnIndex: widget.tableColumns!
widget.tableColumns!.contains(listUIState.sortField) .contains(listUIState.sortField)
? widget.tableColumns!.indexOf(listUIState.sortField) ? widget.tableColumns!.indexOf(listUIState.sortField)
: 0, : 0,
sortAscending: listUIState.sortAscending, sortAscending: listUIState.sortAscending,
rowsPerPage: state.prefState.rowsPerPage, rowsPerPage: state.prefState.rowsPerPage,
onPageChanged: (row) { onPageChanged: (row) {
_firstRowIndex = row; if (row != null) {
store.dispatch(UpdateLastHistory( _firstRowIndex = row;
(row / state.prefState.rowsPerPage).floor())); store.dispatch(UpdateLastHistory(
(row / state.prefState.rowsPerPage).floor()));
}
}, },
initialFirstRowIndex: _firstRowIndex, initialFirstRowIndex: _firstRowIndex,
availableRowsPerPage: [ availableRowsPerPage: [
@ -358,8 +362,8 @@ class _EntityListState extends State<EntityList> {
min(entityList.length, kMaxEntitiesPerBulkAction); min(entityList.length, kMaxEntitiesPerBulkAction);
final entities = entityList final entities = entityList
.sublist(0, endIndex) .sublist(0, endIndex)
.map<BaseEntity?>( .map<BaseEntity?>((entityId) =>
(entityId) => entityMap![entityId] as BaseEntity?) entityMap![entityId] as BaseEntity?)
.toList(); .toList();
handleEntitiesActions( handleEntitiesActions(
entities, EntityAction.toggleMultiselect); entities, EntityAction.toggleMultiselect);
@ -466,8 +470,9 @@ class _EntityListState extends State<EntityList> {
entities: entities, entities: entities,
multiselect: true, multiselect: true,
completer: Completer<Null>() completer: Completer<Null>()
..future.then<dynamic>( ..future.then<Null>((() =>
((_) => widget.onClearMultiselect()) as FutureOr<dynamic> Function(Null)), widget.onClearMultiselect())
as FutureOr<Null> Function(Null)),
); );
}, },
onCancelPressed: (_) => widget.onClearMultiselect(), onCancelPressed: (_) => widget.onClearMultiselect(),

View File

@ -185,11 +185,11 @@ class _LoginState extends State<LoginView> {
final Completer<Null> completer = Completer<Null>(); final Completer<Null> completer = Completer<Null>();
completer.future completer.future
.then((_) { .then<Null>(() {
setState(() { setState(() {
_loginError = ''; _loginError = '';
}); });
} as FutureOr<_> Function(Null)) } as FutureOr<Null> Function(Null))
.catchError((Object error) { .catchError((Object error) {
setState(() { setState(() {
_buttonController.reset(); _buttonController.reset();

View File

@ -115,10 +115,8 @@ class LoginVM {
onMicrosoftSignUpPressed; onMicrosoftSignUpPressed;
final Function(BuildContext, Completer<Null> completer, final Function(BuildContext, Completer<Null> completer,
{String url, {String url, String secret, String oneTimePassword}) onAppleLoginPressed;
String? secret, final Function(BuildContext, Completer<Null> completer, String url)
String? oneTimePassword}) onAppleLoginPressed;
final Function(BuildContext, Completer<Null> completer, String? url)
onAppleSignUpPressed; onAppleSignUpPressed;
static LoginVM fromStore(Store<AppState> store) { static LoginVM fromStore(Store<AppState> store) {
@ -164,9 +162,9 @@ class LoginVM {
onGoogleLoginPressed: ( onGoogleLoginPressed: (
BuildContext context, BuildContext context,
Completer<Null> completer, { Completer<Null> completer, {
required String url, String url = '',
required String secret, String secret = '',
required String oneTimePassword, String oneTimePassword = '',
}) async { }) async {
try { try {
await GoogleOAuth.signOut(); await GoogleOAuth.signOut();
@ -286,9 +284,9 @@ class LoginVM {
onAppleLoginPressed: ( onAppleLoginPressed: (
BuildContext context, BuildContext context,
Completer<Null> completer, { Completer<Null> completer, {
required String url, String url = '',
required String secret, String secret = '',
required String oneTimePassword, String oneTimePassword = '',
}) async { }) async {
try { try {
final credentials = await SignInWithApple.getAppleIDCredential( final credentials = await SignInWithApple.getAppleIDCredential(

View File

@ -72,8 +72,9 @@ class CompanyGatewayScreen extends StatelessWidget {
entities: companyGateways, entities: companyGateways,
multiselect: true, multiselect: true,
completer: Completer<Null>() completer: Completer<Null>()
..future.then<dynamic>(((_) => ..future.then<Null>((() => store
store.dispatch(ClearCompanyGatewayMultiselect())) as FutureOr<dynamic> Function(Null)), .dispatch(ClearCompanyGatewayMultiselect()))
as FutureOr<Null> Function(Null)),
); );
}, },
onCancelPressed: (context) => onCancelPressed: (context) =>

View File

@ -68,7 +68,7 @@ class EntityEditDetailsVM {
final Function(InvoiceEntity)? onChanged; final Function(InvoiceEntity)? onChanged;
final Function(BuildContext context, InvoiceEntity, ClientEntity?)? final Function(BuildContext context, InvoiceEntity, ClientEntity?)?
onClientChanged; onClientChanged;
final Function(BuildContext context, InvoiceEntity, VendorEntity)? final Function(BuildContext context, InvoiceEntity, VendorEntity?)?
onVendorChanged; onVendorChanged;
final BuiltMap<String?, ClientEntity?>? clientMap; final BuiltMap<String?, ClientEntity?>? clientMap;
final BuiltList<String>? clientList; final BuiltList<String>? clientList;

View File

@ -67,7 +67,7 @@ class EntityEditItemsVM {
final int? invoiceItemIndex; final int? invoiceItemIndex;
final Function? addLineItem; final Function? addLineItem;
final Function? deleteLineItem; final Function? deleteLineItem;
final Function(int?)? onRemoveInvoiceItemPressed; final Function(int)? onRemoveInvoiceItemPressed;
final Function? clearSelectedInvoiceItem; final Function? clearSelectedInvoiceItem;
final Function(InvoiceItemEntity, int?)? onChangedInvoiceItem; final Function(InvoiceItemEntity, int?)? onChangedInvoiceItem;
final Function(int, int)? onMovedInvoiceItem; final Function(int, int)? onMovedInvoiceItem;

View File

@ -75,14 +75,14 @@ class LocalizationSettingsVM {
final appBuilder = AppBuilder.of(context); final appBuilder = AppBuilder.of(context);
final completer = snackBarCompleter<Null>( final completer = snackBarCompleter<Null>(
context, AppLocalization.of(context)!.savedSettings) context, AppLocalization.of(context)!.savedSettings)
..future.then<dynamic>((value) { ..future.then<Null>(() {
appBuilder!.rebuild(); appBuilder!.rebuild();
store.dispatch(RefreshData( store.dispatch(RefreshData(
includeStatic: true, includeStatic: true,
completer: Completer<dynamic>() completer: Completer<dynamic>()
..future ..future
.then((dynamic value) => appBuilder.rebuild()))); .then((dynamic value) => appBuilder.rebuild())));
} as FutureOr<dynamic> Function(Null)); } as FutureOr<Null> Function(Null));
store.dispatch(SaveCompanyRequest( store.dispatch(SaveCompanyRequest(
completer: completer, company: settingsUIState.company)); completer: completer, company: settingsUIState.company));
break; break;

View File

@ -78,8 +78,9 @@ class TaskStatusScreen extends StatelessWidget {
entities: taskStatusIds, entities: taskStatusIds,
multiselect: true, multiselect: true,
completer: Completer<Null>() completer: Completer<Null>()
..future.then<dynamic>( ..future.then<Null>((() =>
((_) => store.dispatch(ClearTaskStatusMultiselect())) as FutureOr<dynamic> Function(Null)), store.dispatch(ClearTaskStatusMultiselect()))
as FutureOr<Null> Function(Null)),
); );
}, },
label: localization!.actions, label: localization!.actions,