Null safety

This commit is contained in:
Hillel Coren 2023-10-01 16:03:57 +03:00
parent fbd8b8f5ba
commit e22c4a5228
26 changed files with 131 additions and 132 deletions

View File

@ -186,7 +186,7 @@ class PaymentSidebar extends StatelessWidget {
itemCount: recentPayments.length, itemCount: recentPayments.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return PaymentListItem( return PaymentListItem(
payment: recentPayments[index], payment: recentPayments[index]!,
showSelected: false, showSelected: false,
); );
}, },

View File

@ -189,7 +189,7 @@ class ExpenseOverview extends StatelessWidget {
currencyId: expense.currencyId), currencyId: expense.currencyId),
), ),
ListDivider(), ListDivider(),
if ((expense.privateNotes ?? '').isNotEmpty) ...[ if (expense.privateNotes.isNotEmpty) ...[
IconMessage(expense.privateNotes, IconMessage(expense.privateNotes,
iconData: Icons.lock, copyToClipboard: true), iconData: Icons.lock, copyToClipboard: true),
ListDivider(), ListDivider(),
@ -223,7 +223,7 @@ class ExpenseOverview extends StatelessWidget {
.present(localization.active, localization.archived), .present(localization.active, localization.archived),
), ),
..._buildDetailsList(), ..._buildDetailsList(),
if ((expense.publicNotes ?? '').isNotEmpty) ...[ if (expense.publicNotes.isNotEmpty) ...[
IconMessage(expense.publicNotes, copyToClipboard: true), IconMessage(expense.publicNotes, copyToClipboard: true),
ListDivider() ListDivider()
], ],

View File

@ -250,9 +250,8 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
!item!.isEmpty && item.typeId == InvoiceItemEntity.TYPE_TASK) !item!.isEmpty && item.typeId == InvoiceItemEntity.TYPE_TASK)
.length; .length;
final showTasksTable = final showTasksTable = (invoice.hasTasks || company.showTasksTable) &&
(invoice.hasTasks || (company.showTasksTable ?? false)) && (invoice.isInvoice || invoice.isQuote);
(invoice.isInvoice || invoice.isQuote);
final settings = getClientSettings(state, client); final settings = getClientSettings(state, client);
final terms = entityType == EntityType.quote final terms = entityType == EntityType.quote
@ -376,7 +375,7 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
)) ))
.toList()), .toList()),
DatePicker( DatePicker(
labelText: (invoice.lastSentDate ?? '').isNotEmpty labelText: invoice.lastSentDate.isNotEmpty
? localization.nextSendDate ? localization.nextSendDate
: localization.startDate, : localization.startDate,
onSelected: (date, _) { onSelected: (date, _) {

View File

@ -223,7 +223,7 @@ class InvoiceEditDetailsState extends State<InvoiceEditDetails> {
)) ))
.toList()), .toList()),
DatePicker( DatePicker(
labelText: (invoice.lastSentDate ?? '').isNotEmpty labelText: invoice.lastSentDate.isNotEmpty
? localization.nextSendDate ? localization.nextSendDate
: localization.startDate, : localization.startDate,
onSelected: (date, _) => viewModel onSelected: (date, _) => viewModel

View File

@ -448,10 +448,10 @@ class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
..._columns ..._columns
.map((column) { .map((column) {
if (column == COLUMN_ITEM) { if (column == COLUMN_ITEM) {
return Text(item.productKey ?? ''); return Text(item.productKey);
} else if (column == COLUMN_DESCRIPTION) { } else if (column == COLUMN_DESCRIPTION) {
return Text( return Text(
item.notes ?? '', item.notes,
maxLines: 2, // TODO change to 1 maxLines: 2, // TODO change to 1
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
); );

View File

@ -57,7 +57,7 @@ class _InvoiceViewHistoryState extends State<InvoiceViewHistory> {
itemBuilder: (BuildContext context, index) { itemBuilder: (BuildContext context, index) {
final activity = activityList[index]; final activity = activityList[index];
final history = activity.history!; final history = activity.history!;
final activityId = history.activityId ?? ''; final activityId = history.activityId;
final state = viewModel.state!; final state = viewModel.state!;
final client = state.clientState.get(activity.clientId!); final client = state.clientState.get(activity.clientId!);

View File

@ -137,7 +137,7 @@ class InvoiceOverview extends StatelessWidget {
ListDivider(), ListDivider(),
]); ]);
if ((invoice.privateNotes ?? '').isNotEmpty) { if (invoice.privateNotes.isNotEmpty) {
widgets.addAll([ widgets.addAll([
IconMessage(invoice.privateNotes, IconMessage(invoice.privateNotes,
iconData: Icons.lock, copyToClipboard: true), iconData: Icons.lock, copyToClipboard: true),
@ -516,7 +516,7 @@ class InvoiceOverview extends StatelessWidget {
widgets.add(surchargeRow(localization.partialDue, invoice.partial)); widgets.add(surchargeRow(localization.partialDue, invoice.partial));
} }
if ((invoice.publicNotes ?? '').isNotEmpty) { if (invoice.publicNotes.isNotEmpty) {
widgets.addAll([ widgets.addAll([
ListDivider(), ListDivider(),
IconMessage(invoice.publicNotes, copyToClipboard: true), IconMessage(invoice.publicNotes, copyToClipboard: true),

View File

@ -625,7 +625,7 @@ class _PaymentableEditorState extends State<PaymentableEditor> {
// If a client isn't selected or a client is selected but the client // If a client isn't selected or a client is selected but the client
// doesn't have any more credits then don't show the picker // doesn't have any more credits then don't show the picker
if (widget.entityType == EntityType.credit && if (widget.entityType == EntityType.credit &&
((payment.clientId ?? '').isEmpty || (payment.clientId.isEmpty ||
(creditList.isEmpty && (paymentable.creditId ?? '').isEmpty))) { (creditList.isEmpty && (paymentable.creditId ?? '').isEmpty))) {
return SizedBox(); return SizedBox();
} else if (widget.entityType == EntityType.invoice && } else if (widget.entityType == EntityType.invoice &&

View File

@ -28,7 +28,7 @@ class PaymentListItem extends StatelessWidget {
this.showSelected = true, this.showSelected = true,
}); });
final PaymentEntity? payment; final PaymentEntity payment;
final String? filter; final String? filter;
final bool showCheckbox; final bool showCheckbox;
final bool isChecked; final bool isChecked;
@ -41,31 +41,31 @@ class PaymentListItem extends StatelessWidget {
final uiState = state.uiState; final uiState = state.uiState;
final paymentUIState = uiState.paymentUIState; final paymentUIState = uiState.paymentUIState;
final textStyle = TextStyle(fontSize: 16); final textStyle = TextStyle(fontSize: 16);
final client = state.clientState.get(payment!.clientId); final client = state.clientState.get(payment.clientId);
final localization = AppLocalization.of(context); final localization = AppLocalization.of(context);
final filterMatch = filter != null && filter!.isNotEmpty final filterMatch = filter != null && filter!.isNotEmpty
? (payment!.matchesFilterValue(filter) ?? ? (payment.matchesFilterValue(filter) ??
client.matchesFilterValue(filter)) client.matchesFilterValue(filter))
: null; : null;
final mobileSubtitle = filterMatch ?? final mobileSubtitle = filterMatch ??
(payment!.number ?? '') + '' + formatDate(payment!.date, context); payment.number + '' + formatDate(payment.date, context);
final textColor = Theme.of(context).textTheme.bodyLarge!.color; final textColor = Theme.of(context).textTheme.bodyLarge!.color;
String desktopSubtitle = ''; String desktopSubtitle = '';
if (payment!.date.isNotEmpty) { if (payment.date.isNotEmpty) {
desktopSubtitle = formatDate(payment!.date, context); desktopSubtitle = formatDate(payment.date, context);
} }
if (payment!.transactionReference.isNotEmpty) { if (payment.transactionReference.isNotEmpty) {
if (desktopSubtitle.isNotEmpty) { if (desktopSubtitle.isNotEmpty) {
desktopSubtitle += ''; desktopSubtitle += '';
} }
desktopSubtitle += payment!.transactionReference; desktopSubtitle += payment.transactionReference;
} }
return DismissibleEntity( return DismissibleEntity(
isSelected: isDesktop(context) && isSelected: isDesktop(context) &&
showSelected && showSelected &&
payment!.id == payment.id ==
(uiState.isEditing (uiState.isEditing
? paymentUIState.editing!.id ? paymentUIState.editing!.id
: paymentUIState.selectedId), : paymentUIState.selectedId),
@ -78,10 +78,10 @@ class PaymentListItem extends StatelessWidget {
? InkWell( ? InkWell(
onTap: () => onTap != null onTap: () => onTap != null
? onTap!() ? onTap!()
: selectEntity(entity: payment!, forceView: !showCheckbox), : selectEntity(entity: payment, forceView: !showCheckbox),
onLongPress: () => onTap != null onLongPress: () => onTap != null
? null ? null
: selectEntity(entity: payment!, longPress: true), : selectEntity(entity: payment, longPress: true),
child: Padding( child: Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 10, left: 10,
@ -105,7 +105,7 @@ class PaymentListItem extends StatelessWidget {
), ),
) )
: ActionMenuButton( : ActionMenuButton(
entityActions: payment!.getActions( entityActions: payment.getActions(
userCompany: state.userCompany, userCompany: state.userCompany,
client: client, client: client,
includeEdit: true, includeEdit: true,
@ -121,11 +121,11 @@ class PaymentListItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
payment!.number, payment.number,
style: textStyle, style: textStyle,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
if (!payment!.isActive) EntityStateLabel(payment) if (!payment.isActive) EntityStateLabel(payment)
], ],
), ),
), ),
@ -152,7 +152,7 @@ class PaymentListItem extends StatelessWidget {
), ),
SizedBox(width: 10), SizedBox(width: 10),
Text( Text(
formatNumber(payment!.amount, context, formatNumber(payment.amount, context,
clientId: client.id)!, clientId: client.id)!,
style: textStyle, style: textStyle,
textAlign: TextAlign.end, textAlign: TextAlign.end,
@ -166,10 +166,10 @@ class PaymentListItem extends StatelessWidget {
: ListTile( : ListTile(
onTap: () => onTap != null onTap: () => onTap != null
? onTap!() ? onTap!()
: selectEntity(entity: payment!, forceView: !showCheckbox), : selectEntity(entity: payment, forceView: !showCheckbox),
onLongPress: () => onTap != null onLongPress: () => onTap != null
? null ? null
: selectEntity(entity: payment!, longPress: true), : selectEntity(entity: payment, longPress: true),
leading: showCheckbox leading: showCheckbox
? IgnorePointer( ? IgnorePointer(
child: Checkbox( child: Checkbox(
@ -192,8 +192,8 @@ class PaymentListItem extends StatelessWidget {
), ),
), ),
Text( Text(
formatNumber(payment!.amount, context, formatNumber(payment.amount, context,
clientId: payment!.clientId)!, clientId: payment.clientId)!,
style: Theme.of(context).textTheme.titleMedium), style: Theme.of(context).textTheme.titleMedium),
], ],
), ),
@ -214,11 +214,11 @@ class PaymentListItem extends StatelessWidget {
), ),
Text( Text(
localization!.lookup( localization!.lookup(
'payment_status_${payment!.calculatedStatusId}')!, 'payment_status_${payment.calculatedStatusId}')!,
style: TextStyle( style: TextStyle(
color: PaymentStatusColors( color: PaymentStatusColors(
state.prefState.colorThemeModel) state.prefState.colorThemeModel)
.colors[payment!.calculatedStatusId], .colors[payment.calculatedStatusId],
)), )),
], ],
), ),

View File

@ -62,7 +62,7 @@ class _PaymentViewState extends State<PaymentView> {
if (payment.date.isNotEmpty) { if (payment.date.isNotEmpty) {
fields[PaymentFields.date] = formatDate(payment.date, context); fields[PaymentFields.date] = formatDate(payment.date, context);
} }
if ((payment.typeId ?? '').isNotEmpty) { if (payment.typeId.isNotEmpty) {
final paymentType = state.staticState.paymentTypeMap[payment.typeId]; final paymentType = state.staticState.paymentTypeMap[payment.typeId];
if (paymentType != null) { if (paymentType != null) {
fields[PaymentFields.typeId] = paymentType.name; fields[PaymentFields.typeId] = paymentType.name;
@ -133,7 +133,7 @@ class _PaymentViewState extends State<PaymentView> {
paymentable.createdAt), paymentable.createdAt),
context), context),
), ),
if ((payment.companyGatewayId ?? '').isNotEmpty) ...[ if (payment.companyGatewayId.isNotEmpty) ...[
ListTile( ListTile(
title: Text( title: Text(
'${localization.gateway} ${companyGateway.label}'), '${localization.gateway} ${companyGateway.label}'),

View File

@ -29,7 +29,7 @@ class ProjectListItem extends StatelessWidget {
final UserEntity? user; final UserEntity? user;
final GestureTapCallback? onTap; final GestureTapCallback? onTap;
final GestureTapCallback? onLongPress; final GestureTapCallback? onLongPress;
final ProjectEntity? project; final ProjectEntity project;
final String? filter; final String? filter;
final Function(bool?)? onCheckboxChanged; final Function(bool?)? onCheckboxChanged;
final bool isChecked; final bool isChecked;
@ -40,9 +40,9 @@ class ProjectListItem extends StatelessWidget {
final state = store.state; final state = store.state;
final uiState = state.uiState; final uiState = state.uiState;
final projectUIState = uiState.projectUIState; final projectUIState = uiState.projectUIState;
final client = state.clientState.get(project!.clientId); final client = state.clientState.get(project.clientId);
final filterMatch = filter != null && filter!.isNotEmpty final filterMatch = filter != null && filter!.isNotEmpty
? (project!.matchesFilterValue(filter) ?? ? (project.matchesFilterValue(filter) ??
client.matchesFilterValue(filter)) client.matchesFilterValue(filter))
: null; : null;
final listUIState = projectUIState.listUIState; final listUIState = projectUIState.listUIState;
@ -54,7 +54,7 @@ class ProjectListItem extends StatelessWidget {
return DismissibleEntity( return DismissibleEntity(
isSelected: isDesktop(context) && isSelected: isDesktop(context) &&
project!.id == project.id ==
(uiState.isEditing (uiState.isEditing
? projectUIState.editing!.id ? projectUIState.editing!.id
: projectUIState.selectedId), : projectUIState.selectedId),
@ -65,10 +65,10 @@ class ProjectListItem extends StatelessWidget {
return constraints.maxWidth > kTableListWidthCutoff return constraints.maxWidth > kTableListWidthCutoff
? InkWell( ? InkWell(
onTap: () => onTap: () =>
onTap != null ? onTap!() : selectEntity(entity: project!), onTap != null ? onTap!() : selectEntity(entity: project),
onLongPress: () => onLongPress != null onLongPress: () => onLongPress != null
? onLongPress!() ? onLongPress!()
: selectEntity(entity: project!, longPress: true), : selectEntity(entity: project, longPress: true),
child: Padding( child: Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 10, left: 10,
@ -97,7 +97,7 @@ class ProjectListItem extends StatelessWidget {
), ),
) )
: ActionMenuButton( : ActionMenuButton(
entityActions: project!.getActions( entityActions: project.getActions(
userCompany: state.userCompany, userCompany: state.userCompany,
client: client, client: client,
includeEdit: true, includeEdit: true,
@ -114,11 +114,11 @@ class ProjectListItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
project!.number ?? '', project.number ?? '',
style: textStyle, style: textStyle,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
if (!project!.isActive) EntityStateLabel(project) if (!project.isActive) EntityStateLabel(project)
], ],
), ),
), ),
@ -128,8 +128,8 @@ class ProjectListItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
project!.name + project.name +
(project!.documents.isNotEmpty (project.documents.isNotEmpty
? ' 📎' ? ' 📎'
: ''), : ''),
style: textStyle), style: textStyle),
@ -150,7 +150,7 @@ class ProjectListItem extends StatelessWidget {
Text( Text(
formatDuration( formatDuration(
Duration( Duration(
minutes: (project!.budgetedHours * 60).toInt()), minutes: (project.budgetedHours * 60).toInt()),
showSeconds: false), showSeconds: false),
style: textStyle, style: textStyle,
textAlign: TextAlign.end, textAlign: TextAlign.end,
@ -161,10 +161,10 @@ class ProjectListItem extends StatelessWidget {
) )
: ListTile( : ListTile(
onTap: () => onTap: () =>
onTap != null ? onTap!() : selectEntity(entity: project!), onTap != null ? onTap!() : selectEntity(entity: project),
onLongPress: () => onLongPress != null onLongPress: () => onLongPress != null
? onLongPress!() ? onLongPress!()
: selectEntity(entity: project!, longPress: true), : selectEntity(entity: project, longPress: true),
leading: showCheckbox leading: showCheckbox
? IgnorePointer( ? IgnorePointer(
ignoring: listUIState.isInMultiselect(), ignoring: listUIState.isInMultiselect(),
@ -183,8 +183,8 @@ class ProjectListItem extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: Text( child: Text(
project!.name + project.name +
(project!.documents.isNotEmpty ? ' 📎' : ''), (project.documents.isNotEmpty ? ' 📎' : ''),
style: Theme.of(context).textTheme.titleMedium, style: Theme.of(context).textTheme.titleMedium,
), ),
), ),
@ -192,7 +192,7 @@ class ProjectListItem extends StatelessWidget {
formatDuration( formatDuration(
Duration( Duration(
minutes: minutes:
(project!.budgetedHours * 60).toInt()), (project.budgetedHours * 60).toInt()),
showSeconds: false), showSeconds: false),
style: Theme.of(context).textTheme.titleMedium), style: Theme.of(context).textTheme.titleMedium),
], ],
@ -203,7 +203,7 @@ class ProjectListItem extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Text( Text(
filterMatch == null filterMatch == null
? project!.number + '' + client.displayName ? project.number + '' + client.displayName
: filterMatch, : filterMatch,
maxLines: 3, maxLines: 3,
overflow: TextOverflow.ellipsis), overflow: TextOverflow.ellipsis),

View File

@ -43,7 +43,7 @@ class ProjectListBuilder extends StatelessWidget {
itemBuilder: (BuildContext context, index) { itemBuilder: (BuildContext context, index) {
final state = viewModel.state; final state = viewModel.state;
final projectId = viewModel.projectList[index]; final projectId = viewModel.projectList[index];
final project = viewModel.projectMap[projectId]; final project = viewModel.projectMap[projectId]!;
final listState = state.getListState(EntityType.project); final listState = state.getListState(EntityType.project);
final isInMultiselect = listState.isInMultiselect(); final isInMultiselect = listState.isInMultiselect();
@ -51,7 +51,7 @@ class ProjectListBuilder extends StatelessWidget {
user: state.user, user: state.user,
filter: viewModel.filter, filter: viewModel.filter,
project: project, project: project,
isChecked: isInMultiselect && listState.isSelected(project!.id), isChecked: isInMultiselect && listState.isSelected(project.id),
); );
}); });
}, },

View File

@ -69,7 +69,7 @@ class ProjectPresenter extends EntityPresenter {
return Text(formatNumber(project.budgetedHours, context, return Text(formatNumber(project.budgetedHours, context,
formatNumberType: FormatNumberType.double)!); formatNumberType: FormatNumberType.double)!);
case ProjectFields.number: case ProjectFields.number:
return Text(project.number ?? ''); return Text(project.number);
case ProjectFields.customValue1: case ProjectFields.customValue1:
return Text(presentCustomField(context, project.customValue1)!); return Text(presentCustomField(context, project.customValue1)!);
case ProjectFields.customValue2: case ProjectFields.customValue2:

View File

@ -111,7 +111,7 @@ class _ProjectOverviewState extends State<ProjectOverview> {
Duration(minutes: (project.budgetedHours * 60).toInt())), Duration(minutes: (project.budgetedHours * 60).toInt())),
), ),
ListDivider(), ListDivider(),
if ((project.privateNotes ?? '').isNotEmpty) ...[ if (project.privateNotes.isNotEmpty) ...[
IconMessage(project.privateNotes, IconMessage(project.privateNotes,
iconData: Icons.lock, copyToClipboard: true), iconData: Icons.lock, copyToClipboard: true),
ListDivider() ListDivider()
@ -160,7 +160,7 @@ class _ProjectOverviewState extends State<ProjectOverview> {
FieldGrid(fields), FieldGrid(fields),
]); ]);
if ((project.publicNotes ?? '').isNotEmpty) { if (project.publicNotes.isNotEmpty) {
widgets.addAll([ widgets.addAll([
IconMessage(project.publicNotes, copyToClipboard: true), IconMessage(project.publicNotes, copyToClipboard: true),
ListDivider() ListDivider()

View File

@ -116,7 +116,7 @@ class PurchaseOrderListItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
(purchaseOrder.number ?? '').isEmpty purchaseOrder.number.isEmpty
? localization!.pending ? localization!.pending
: purchaseOrder.number, : purchaseOrder.number,
style: textStyle, style: textStyle,

View File

@ -206,7 +206,7 @@ class QuoteListItem extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: filterMatch == null child: filterMatch == null
? Text((((quote.number ?? '').isEmpty ? Text(((quote.number.isEmpty
? localization!.pending ? localization!.pending
: quote.number) + : quote.number) +
'' + '' +

View File

@ -70,9 +70,8 @@ class QuotePresenter extends EntityPresenter {
case QuoteFields.status: case QuoteFields.status:
return EntityStatusChip(entity: quote, showState: true); return EntityStatusChip(entity: quote, showState: true);
case QuoteFields.number: case QuoteFields.number:
return Text((quote.number ?? '').isEmpty return Text(
? localization!.pending quote.number.isEmpty ? localization!.pending : quote.number);
: quote.number);
case QuoteFields.client: case QuoteFields.client:
return LinkTextRelatedEntity(entity: client, relation: quote); return LinkTextRelatedEntity(entity: client, relation: quote);
case QuoteFields.date: case QuoteFields.date:

View File

@ -19,7 +19,7 @@ import 'package:invoiceninja_flutter/utils/platforms.dart';
class RecurringExpenseListItem extends StatelessWidget { class RecurringExpenseListItem extends StatelessWidget {
const RecurringExpenseListItem({ const RecurringExpenseListItem({
required this.expense, required this.expense,
this.filter, required this.filter,
this.onTap, this.onTap,
this.onCheckboxChanged, this.onCheckboxChanged,
this.showCheckbox = true, this.showCheckbox = true,
@ -29,7 +29,7 @@ class RecurringExpenseListItem extends StatelessWidget {
final Function(bool?)? onCheckboxChanged; final Function(bool?)? onCheckboxChanged;
final GestureTapCallback? onTap; final GestureTapCallback? onTap;
final ExpenseEntity? expense; final ExpenseEntity expense;
final String? filter; final String? filter;
final bool showCheckbox; final bool showCheckbox;
final bool isDismissible; final bool isDismissible;
@ -41,18 +41,18 @@ class RecurringExpenseListItem extends StatelessWidget {
final state = store.state; final state = store.state;
final uiState = state.uiState; final uiState = state.uiState;
final expenseUIState = uiState.recurringExpenseUIState; final expenseUIState = uiState.recurringExpenseUIState;
final client = state.clientState.get(expense!.clientId!); final client = state.clientState.get(expense.clientId!);
final vendor = state.vendorState.get(expense!.vendorId!); final vendor = state.vendorState.get(expense.vendorId!);
final category = state.expenseCategoryState.get(expense!.categoryId); final category = state.expenseCategoryState.get(expense.categoryId);
final filterMatch = filter != null && filter!.isNotEmpty final filterMatch = filter != null && filter!.isNotEmpty
? (expense!.matchesFilterValue(filter) ?? ? (expense.matchesFilterValue(filter) ??
client.matchesFilterValue(filter)) client.matchesFilterValue(filter))
: null; : null;
final listUIState = expenseUIState.listUIState; final listUIState = expenseUIState.listUIState;
final isInMultiselect = listUIState.isInMultiselect(); final isInMultiselect = listUIState.isInMultiselect();
final showCheckbox = onCheckboxChanged != null || isInMultiselect; final showCheckbox = onCheckboxChanged != null || isInMultiselect;
final isChecked = isDismissible final isChecked = isDismissible
? (isInMultiselect && listUIState.isSelected(expense!.id)) ? (isInMultiselect && listUIState.isSelected(expense.id))
: this.isChecked; : this.isChecked;
final textStyle = TextStyle(fontSize: 16); final textStyle = TextStyle(fontSize: 16);
final textColor = Theme.of(context).textTheme.bodyLarge!.color; final textColor = Theme.of(context).textTheme.bodyLarge!.color;
@ -62,8 +62,8 @@ class RecurringExpenseListItem extends StatelessWidget {
subtitle = filterMatch; subtitle = filterMatch;
} else if (client != null || vendor != null || category != null) { } else if (client != null || vendor != null || category != null) {
final parts = <String>[]; final parts = <String>[];
if (expense!.nextSendDate.isNotEmpty) { if (expense.nextSendDate.isNotEmpty) {
parts.add(formatDate(expense!.nextSendDate, context)); parts.add(formatDate(expense.nextSendDate, context));
} }
if (category.isOld) { if (category.isOld) {
parts.add(category.name); parts.add(category.name);
@ -81,7 +81,7 @@ class RecurringExpenseListItem extends StatelessWidget {
showMultiselect: this.showCheckbox, showMultiselect: this.showCheckbox,
isDismissible: isDismissible, isDismissible: isDismissible,
isSelected: isDesktop(context) && isSelected: isDesktop(context) &&
expense!.id == expense.id ==
(uiState.isEditing (uiState.isEditing
? expenseUIState.editing!.id ? expenseUIState.editing!.id
: expenseUIState.selectedId), : expenseUIState.selectedId),
@ -92,9 +92,9 @@ class RecurringExpenseListItem extends StatelessWidget {
return constraints.maxWidth > kTableListWidthCutoff return constraints.maxWidth > kTableListWidthCutoff
? InkWell( ? InkWell(
onTap: () => onTap: () =>
onTap != null ? onTap!() : selectEntity(entity: expense!), onTap != null ? onTap!() : selectEntity(entity: expense),
onLongPress: () => onLongPress: () =>
selectEntity(entity: expense!, longPress: true), selectEntity(entity: expense, longPress: true),
child: Padding( child: Padding(
padding: const EdgeInsets.only( padding: const EdgeInsets.only(
left: 10, left: 10,
@ -123,7 +123,7 @@ class RecurringExpenseListItem extends StatelessWidget {
), ),
) )
: ActionMenuButton( : ActionMenuButton(
entityActions: expense!.getActions( entityActions: expense.getActions(
userCompany: state.userCompany, userCompany: state.userCompany,
includeEdit: true, includeEdit: true,
), ),
@ -139,11 +139,11 @@ class RecurringExpenseListItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
expense!.number, expense.number,
style: textStyle, style: textStyle,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
), ),
if (!expense!.isActive) EntityStateLabel(expense) if (!expense.isActive) EntityStateLabel(expense)
], ],
), ),
), ),
@ -153,8 +153,8 @@ class RecurringExpenseListItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
(expense!.publicNotes ?? '') + (expense.publicNotes ?? '') +
(expense!.documents.isNotEmpty ? ' 📎' : ''), (expense.documents.isNotEmpty ? ' 📎' : ''),
style: textStyle, style: textStyle,
maxLines: 1, maxLines: 1,
), ),
@ -173,8 +173,8 @@ class RecurringExpenseListItem extends StatelessWidget {
), ),
SizedBox(width: 8), SizedBox(width: 8),
Text( Text(
formatNumber(expense!.convertedAmount, context, formatNumber(expense.convertedAmount, context,
currencyId: expense!.currencyId)!, currencyId: expense.currencyId)!,
style: textStyle, style: textStyle,
textAlign: TextAlign.end, textAlign: TextAlign.end,
), ),
@ -186,9 +186,9 @@ class RecurringExpenseListItem extends StatelessWidget {
) )
: ListTile( : ListTile(
onTap: () => onTap: () =>
onTap != null ? onTap!() : selectEntity(entity: expense!), onTap != null ? onTap!() : selectEntity(entity: expense),
onLongPress: () => onLongPress: () =>
selectEntity(entity: expense!, longPress: true), selectEntity(entity: expense, longPress: true),
leading: showCheckbox leading: showCheckbox
? IgnorePointer( ? IgnorePointer(
ignoring: listUIState.isInMultiselect(), ignoring: listUIState.isInMultiselect(),
@ -207,17 +207,17 @@ class RecurringExpenseListItem extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: Text( child: Text(
(expense!.publicNotes.isEmpty (expense.publicNotes.isEmpty
? expense!.number ? expense.number
: expense!.publicNotes) + : expense.publicNotes) +
(expense!.documents.isNotEmpty ? ' 📎' : ''), (expense.documents.isNotEmpty ? ' 📎' : ''),
style: Theme.of(context).textTheme.titleMedium, style: Theme.of(context).textTheme.titleMedium,
maxLines: 1, maxLines: 1,
), ),
), ),
Text( Text(
formatNumber(expense!.convertedAmount, context, formatNumber(expense.convertedAmount, context,
currencyId: expense!.currencyId)!, currencyId: expense.currencyId)!,
style: Theme.of(context).textTheme.titleMedium), style: Theme.of(context).textTheme.titleMedium),
], ],
), ),

View File

@ -44,7 +44,7 @@ class RecurringExpenseListBuilder extends StatelessWidget {
final state = viewModel.state; final state = viewModel.state;
final recurringExpenseId = viewModel.recurringExpenseList[index]; final recurringExpenseId = viewModel.recurringExpenseList[index];
final recurringExpense = final recurringExpense =
viewModel.recurringExpenseMap[recurringExpenseId]; viewModel.recurringExpenseMap[recurringExpenseId]!;
final listState = state.getListState(EntityType.recurringExpense); final listState = state.getListState(EntityType.recurringExpense);
final isInMultiselect = listState.isInMultiselect(); final isInMultiselect = listState.isInMultiselect();
@ -52,7 +52,7 @@ class RecurringExpenseListBuilder extends StatelessWidget {
filter: viewModel.filter, filter: viewModel.filter,
expense: recurringExpense, expense: recurringExpense,
isChecked: isInMultiselect && isChecked: isInMultiselect &&
listState.isSelected(recurringExpense!.id), listState.isSelected(recurringExpense.id),
); );
}); });
}, },

View File

@ -216,7 +216,7 @@ class RecurringInvoiceListItem extends StatelessWidget {
Expanded( Expanded(
child: filterMatch == null child: filterMatch == null
? Text( ? Text(
(((invoice.number ?? '').isEmpty ((invoice.number.isEmpty
? localization.pending ? localization.pending
: invoice.number) + : invoice.number) +
(invoice.nextSendDate.isNotEmpty (invoice.nextSendDate.isNotEmpty

View File

@ -52,76 +52,75 @@ class RecurringInvoicePresenter extends EntityPresenter {
Widget getField({String? field, required BuildContext context}) { Widget getField({String? field, required BuildContext context}) {
final localization = AppLocalization.of(context); final localization = AppLocalization.of(context);
final state = StoreProvider.of<AppState>(context).state; final state = StoreProvider.of<AppState>(context).state;
final invoice = entity as InvoiceEntity?; final invoice = entity as InvoiceEntity;
switch (field) { switch (field) {
case RecurringInvoiceFields.status: case RecurringInvoiceFields.status:
return EntityStatusChip(entity: invoice, showState: true); return EntityStatusChip(entity: invoice, showState: true);
case RecurringInvoiceFields.number: case RecurringInvoiceFields.number:
return Text((invoice!.number ?? '').isEmpty return Text(
? localization!.pending invoice.number.isEmpty ? localization!.pending : invoice.number);
: invoice.number);
case RecurringInvoiceFields.client: case RecurringInvoiceFields.client:
final client = state.clientState.get(invoice!.clientId); final client = state.clientState.get(invoice.clientId);
return LinkTextRelatedEntity(entity: client, relation: invoice); return LinkTextRelatedEntity(entity: client, relation: invoice);
case RecurringInvoiceFields.date: case RecurringInvoiceFields.date:
return Text(formatDate(invoice!.date, context)); return Text(formatDate(invoice.date, context));
case RecurringInvoiceFields.reminder1Sent: case RecurringInvoiceFields.reminder1Sent:
return Text(formatDate(invoice!.reminder1Sent, context)); return Text(formatDate(invoice.reminder1Sent, context));
case RecurringInvoiceFields.reminder2Sent: case RecurringInvoiceFields.reminder2Sent:
return Text(formatDate(invoice!.reminder2Sent, context)); return Text(formatDate(invoice.reminder2Sent, context));
case RecurringInvoiceFields.reminder3Sent: case RecurringInvoiceFields.reminder3Sent:
return Text(formatDate(invoice!.reminder3Sent, context)); return Text(formatDate(invoice.reminder3Sent, context));
case RecurringInvoiceFields.reminderLastSent: case RecurringInvoiceFields.reminderLastSent:
return Text(formatDate(invoice!.reminderLastSent, context)); return Text(formatDate(invoice.reminderLastSent, context));
case RecurringInvoiceFields.amount: case RecurringInvoiceFields.amount:
return Align( return Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: Text(formatNumber(invoice!.amount, context, child: Text(formatNumber(invoice.amount, context,
clientId: invoice.clientId)!), clientId: invoice.clientId)!),
); );
case RecurringInvoiceFields.customValue1: case RecurringInvoiceFields.customValue1:
return Text(presentCustomField(context, invoice!.customValue1)!); return Text(presentCustomField(context, invoice.customValue1)!);
case RecurringInvoiceFields.customValue2: case RecurringInvoiceFields.customValue2:
return Text(presentCustomField(context, invoice!.customValue2)!); return Text(presentCustomField(context, invoice.customValue2)!);
case RecurringInvoiceFields.customValue3: case RecurringInvoiceFields.customValue3:
return Text(presentCustomField(context, invoice!.customValue3)!); return Text(presentCustomField(context, invoice.customValue3)!);
case RecurringInvoiceFields.customValue4: case RecurringInvoiceFields.customValue4:
return Text(presentCustomField(context, invoice!.customValue4)!); return Text(presentCustomField(context, invoice.customValue4)!);
case RecurringInvoiceFields.publicNotes: case RecurringInvoiceFields.publicNotes:
return TableTooltip(message: invoice!.publicNotes); return TableTooltip(message: invoice.publicNotes);
case RecurringInvoiceFields.privateNotes: case RecurringInvoiceFields.privateNotes:
return TableTooltip(message: invoice!.privateNotes); return TableTooltip(message: invoice.privateNotes);
case RecurringInvoiceFields.discount: case RecurringInvoiceFields.discount:
return Text(invoice!.isAmountDiscount return Text(invoice.isAmountDiscount
? formatNumber(invoice.discount, context, ? formatNumber(invoice.discount, context,
formatNumberType: FormatNumberType.money, formatNumberType: FormatNumberType.money,
clientId: invoice.clientId)! clientId: invoice.clientId)!
: formatNumber(invoice.discount, context, : formatNumber(invoice.discount, context,
formatNumberType: FormatNumberType.percent)!); formatNumberType: FormatNumberType.percent)!);
case RecurringInvoiceFields.poNumber: case RecurringInvoiceFields.poNumber:
return Text(invoice!.poNumber); return Text(invoice.poNumber);
case RecurringInvoiceFields.documents: case RecurringInvoiceFields.documents:
return Text('${invoice!.documents.length}'); return Text('${invoice.documents.length}');
case RecurringInvoiceFields.taxAmount: case RecurringInvoiceFields.taxAmount:
return Text(formatNumber(invoice!.taxAmount, context, return Text(formatNumber(invoice.taxAmount, context,
clientId: invoice.clientId)!); clientId: invoice.clientId)!);
case RecurringInvoiceFields.exchangeRate: case RecurringInvoiceFields.exchangeRate:
return Text(formatNumber(invoice!.exchangeRate, context, return Text(formatNumber(invoice.exchangeRate, context,
formatNumberType: FormatNumberType.double)!); formatNumberType: FormatNumberType.double)!);
case RecurringInvoiceFields.remainingCycles: case RecurringInvoiceFields.remainingCycles:
return Text(invoice!.remainingCycles == -1 return Text(invoice.remainingCycles == -1
? localization!.endless ? localization!.endless
: '${invoice.remainingCycles}'); : '${invoice.remainingCycles}');
case RecurringInvoiceFields.nextSendDate: case RecurringInvoiceFields.nextSendDate:
return Text(invoice!.nextSendDatetime.isNotEmpty return Text(invoice.nextSendDatetime.isNotEmpty
? formatDate(invoice.nextSendDatetime, context, ? formatDate(invoice.nextSendDatetime, context,
showTime: true, showSeconds: false) showTime: true, showSeconds: false)
: formatDate(invoice.nextSendDate, context)); : formatDate(invoice.nextSendDate, context));
case RecurringInvoiceFields.frequency: case RecurringInvoiceFields.frequency:
return Text(localization!.lookup(kFrequencies[invoice!.frequencyId])!); return Text(localization!.lookup(kFrequencies[invoice.frequencyId])!);
case RecurringInvoiceFields.dueDateDays: case RecurringInvoiceFields.dueDateDays:
return Text(invoice!.dueDateDays == 'terms' return Text(invoice.dueDateDays == 'terms'
? localization!.paymentTerm ? localization!.paymentTerm
: invoice.dueDateDays == 'on_receipt' : invoice.dueDateDays == 'on_receipt'
? localization!.dueOnReceipt! ? localization!.dueOnReceipt!
@ -132,7 +131,7 @@ class RecurringInvoicePresenter extends EntityPresenter {
: localization!.dayCount : localization!.dayCount
.replaceFirst(':count', '${invoice.dueDateDays}')); .replaceFirst(':count', '${invoice.dueDateDays}'));
case RecurringInvoiceFields.autoBill: case RecurringInvoiceFields.autoBill:
return Text(localization!.lookup(invoice!.autoBill)!); return Text(localization!.lookup(invoice.autoBill)!);
} }
return super.getField(field: field, context: context); return super.getField(field: field, context: context);

View File

@ -722,7 +722,7 @@ class TotalsDataTable extends StatelessWidget {
reportResult.columns.length > reportSettings.sortTotalsIndex reportResult.columns.length > reportSettings.sortTotalsIndex
? reportSettings.sortTotalsIndex ? reportSettings.sortTotalsIndex
: null, : null,
sortAscending: reportSettings.sortTotalsAscending ?? true, sortAscending: reportSettings.sortTotalsAscending,
columns: reportResult.totalColumns( columns: reportResult.totalColumns(
context, context,
(index, ascending) => (index, ascending) =>
@ -925,7 +925,10 @@ class ReportResult {
return true; return true;
} }
static bool matchString({required String filter, String? value}) { static bool matchString({
required String filter,
String? value,
}) {
filter = filter.trim(); filter = filter.trim();
if (filter.isEmpty) { if (filter.isEmpty) {
@ -933,7 +936,7 @@ class ReportResult {
} }
value = (value ?? '').toLowerCase(); value = (value ?? '').toLowerCase();
filter = (filter ?? '').toLowerCase(); filter = filter.toLowerCase();
if (filter == 'null' && value.isEmpty) { if (filter == 'null' && value.isEmpty) {
return true; return true;
@ -1120,7 +1123,7 @@ class ReportResult {
)) ))
else if (getReportColumnType(column, context) == ReportColumnType.age) else if (getReportColumnType(column, context) == ReportColumnType.age)
DataCell(AppDropdownButton<String>( DataCell(AppDropdownButton<String>(
value: (textEditingControllers[column]!.text ?? '').isNotEmpty && value: (textEditingControllers[column]!.text).isNotEmpty &&
textEditingControllers[column]!.text != 'null' textEditingControllers[column]!.text != 'null'
? textEditingControllers[column]!.text ? textEditingControllers[column]!.text
: null, : null,

View File

@ -164,10 +164,10 @@ ReportResult taskReport(
value = task.description; value = task.description;
break; break;
case TaskReportFields.invoice: case TaskReportFields.invoice:
value = invoice.listDisplayName ?? ''; value = invoice.listDisplayName;
break; break;
case TaskReportFields.invoice_amount: case TaskReportFields.invoice_amount:
value = invoice.amount ?? ''; value = invoice.amount;
break; break;
case TaskReportFields.invoice_date: case TaskReportFields.invoice_date:
value = invoice.isNew ? '' : invoice.date; value = invoice.isNew ? '' : invoice.date;

View File

@ -152,7 +152,7 @@ class VendorOverview extends StatelessWidget {
vendor.id, state.transactionState.map) vendor.id, state.transactionState.map)
.present(localization.active, localization.archived), .present(localization.active, localization.archived),
), ),
if ((vendor.publicNotes ?? '').isNotEmpty) ...[ if (vendor.publicNotes.isNotEmpty) ...[
IconMessage(vendor.publicNotes, copyToClipboard: true), IconMessage(vendor.publicNotes, copyToClipboard: true),
ListDivider() ListDivider()
], ],

View File

@ -276,8 +276,7 @@ AppLayout calculateLayout(BuildContext context) {
} }
AppLayout getLayout(BuildContext context) => AppLayout getLayout(BuildContext context) =>
StoreProvider.of<AppState>(context).state.prefState.appLayout ?? StoreProvider.of<AppState>(context).state.prefState.appLayout;
AppLayout.mobile;
bool isMobile(BuildContext context) => getLayout(context) == AppLayout.mobile; bool isMobile(BuildContext context) => getLayout(context) == AppLayout.mobile;

View File

@ -34,7 +34,7 @@ String toCamelCase(String subject) {
} }
String toSpaceCase(String value) { String toSpaceCase(String value) {
if ((value ?? '').isEmpty) { if (value.isEmpty) {
return ''; return '';
} }