Null safety

This commit is contained in:
Hillel Coren 2023-10-01 14:57:32 +03:00
parent df7e3576b1
commit fbd8b8f5ba
18 changed files with 85 additions and 88 deletions

View File

@ -236,18 +236,17 @@ class _CompanyGatewayEditState extends State<CompanyGatewayEdit>
AppDropdownButton<String>( AppDropdownButton<String>(
labelText: localization.captureCard, labelText: localization.captureCard,
value: companyGateway.tokenBilling, value: companyGateway.tokenBilling,
selectedItemBuilder: selectedItemBuilder: companyGateway.tokenBilling.isEmpty
(companyGateway.tokenBilling ?? '').isEmpty ? null
? null : (context) => [
: (context) => [ SettingsEntity.AUTO_BILL_ALWAYS,
SettingsEntity.AUTO_BILL_ALWAYS, SettingsEntity.AUTO_BILL_OPT_OUT,
SettingsEntity.AUTO_BILL_OPT_OUT, SettingsEntity.AUTO_BILL_OPT_IN,
SettingsEntity.AUTO_BILL_OPT_IN, SettingsEntity.AUTO_BILL_OFF,
SettingsEntity.AUTO_BILL_OFF, ]
] .map((type) =>
.map((type) => Text(localization.lookup(type)!))
Text(localization.lookup(type)!)) .toList(),
.toList(),
onChanged: (dynamic value) => viewModel.onChanged( onChanged: (dynamic value) => viewModel.onChanged(
companyGateway companyGateway
.rebuild((b) => b..tokenBilling = value)), .rebuild((b) => b..tokenBilling = value)),
@ -767,11 +766,11 @@ class _LimitEditorState extends State<LimitEditor> {
_minController!.text = settings.minLimit == -1 _minController!.text = settings.minLimit == -1
? '' ? ''
: formatNumber((settings.minLimit ?? 0).toDouble(), context, : formatNumber(settings.minLimit.toDouble(), context,
formatNumberType: FormatNumberType.inputMoney)!; formatNumberType: FormatNumberType.inputMoney)!;
_maxController!.text = settings.maxLimit == -1 _maxController!.text = settings.maxLimit == -1
? '' ? ''
: formatNumber((settings.maxLimit ?? 0).toDouble(), context, : formatNumber(settings.maxLimit.toDouble(), context,
formatNumberType: FormatNumberType.inputMoney)!; formatNumberType: FormatNumberType.inputMoney)!;
_minController!.addListener(_onTextChange); _minController!.addListener(_onTextChange);

View File

@ -116,7 +116,7 @@ class CreditListItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
(credit.number ?? '').isEmpty credit.number.isEmpty
? localization!.pending ? localization!.pending
: credit.number, : credit.number,
style: textStyle, style: textStyle,
@ -211,7 +211,7 @@ class CreditListItem extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: filterMatch == null child: filterMatch == null
? Text((((credit.number ?? '').isEmpty ? Text(((credit.number.isEmpty
? localization!.pending ? localization!.pending
: credit.number) + : credit.number) +
'' + '' +

View File

@ -70,9 +70,8 @@ class CreditPresenter extends EntityPresenter {
case CreditFields.status: case CreditFields.status:
return EntityStatusChip(entity: credit, showState: true); return EntityStatusChip(entity: credit, showState: true);
case CreditFields.number: case CreditFields.number:
return Text((credit.number ?? '').isEmpty return Text(
? localization!.pending credit.number.isEmpty ? localization!.pending : credit.number);
: credit.number);
case CreditFields.client: case CreditFields.client:
return LinkTextRelatedEntity(entity: client, relation: credit); return LinkTextRelatedEntity(entity: client, relation: credit);
case CreditFields.date: case CreditFields.date:
@ -146,7 +145,7 @@ class CreditPresenter extends EntityPresenter {
return SizedBox(); return SizedBox();
} }
if (field == CreditFields.contactName) { if (field == CreditFields.contactName) {
return Text(contact.fullName ?? ''); return Text(contact.fullName);
} }
return CopyToClipboard( return CopyToClipboard(
value: contact.email, value: contact.email,

View File

@ -865,7 +865,7 @@ class __DashboardPanelState extends State<_DashboardPanel> {
title: widget.title, title: widget.title,
onDateSelected: widget.onDateSelected, onDateSelected: widget.onDateSelected,
onSelected: widget.onSelected as dynamic Function(), onSelected: widget.onSelected as dynamic Function(),
currencyId: (settings.currencyId ?? '').isNotEmpty currencyId: settings.currencyId.isNotEmpty
? settings.currencyId ? settings.currencyId
: state.company!.currencyId, : state.company!.currencyId,
); );
@ -996,7 +996,7 @@ class __OverviewPanelState extends State<_OverviewPanel> {
title: widget.title, title: widget.title,
onSelected: () => null, onSelected: () => null,
onDateSelected: widget.onDateSelected, onDateSelected: widget.onDateSelected,
currencyId: (settings.currencyId ?? '').isNotEmpty currencyId: settings.currencyId.isNotEmpty
? settings.currencyId ? settings.currencyId
: state.company!.currencyId, : state.company!.currencyId,
isOverview: true, isOverview: true,

View File

@ -405,7 +405,7 @@ class ExpenseSidbar extends StatelessWidget {
itemCount: recentExpenses.length, itemCount: recentExpenses.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
return ExpenseListItem( return ExpenseListItem(
expense: recentExpenses[index], expense: recentExpenses[index]!,
showCheckbox: false, showCheckbox: false,
showSelected: false, showSelected: false,
); );

View File

@ -140,7 +140,7 @@ class _DesignEditState extends State<DesignEdit>
kDesignBody: _bodyController.text.trim(), kDesignBody: _bodyController.text.trim(),
kDesignFooter: _footerController.text.trim(), kDesignFooter: _footerController.text.trim(),
kDesignProducts: _productsController.text.trim(), kDesignProducts: _productsController.text.trim(),
kDesignTasks: _tasksController.text.trim() ?? '', kDesignTasks: _tasksController.text.trim(),
kDesignIncludes: _includesController.text.trim() kDesignIncludes: _includesController.text.trim()
}))); })));

View File

@ -180,7 +180,7 @@ class ExpenseEditDetailsState extends State<ExpenseEditDetails> {
(client as ClientEntity).settings.currencyId ?? (client as ClientEntity).settings.currencyId ??
company.currencyId; company.currencyId;
viewModel.onChanged!(expense.rebuild((b) => b viewModel.onChanged!(expense.rebuild((b) => b
..clientId = client.id ?? '' ..clientId = client.id
..invoiceCurrencyId = currencyId)); ..invoiceCurrencyId = currencyId));
}, },
onAddPressed: (completer) { onAddPressed: (completer) {
@ -195,7 +195,7 @@ class ExpenseEditDetailsState extends State<ExpenseEditDetails> {
final project = store.state.projectState.get(selectedId); final project = store.state.projectState.get(selectedId);
viewModel.onChanged!(expense.rebuild((b) => b viewModel.onChanged!(expense.rebuild((b) => b
..projectId = project.id ..projectId = project.id
..clientId = (project.clientId ?? '').isNotEmpty ..clientId = project.clientId.isNotEmpty
? project.clientId ? project.clientId
: expense.clientId)); : expense.clientId));
}, },
@ -361,7 +361,7 @@ class ExpenseEditDetailsState extends State<ExpenseEditDetails> {
)) ))
.toList()), .toList()),
DatePicker( DatePicker(
labelText: (expense.lastSentDate ?? '').isNotEmpty labelText: expense.lastSentDate.isNotEmpty
? localization.nextSendDate ? localization.nextSendDate
: localization.startDate, : localization.startDate,
onSelected: (date, _) { onSelected: (date, _) {

View File

@ -33,7 +33,7 @@ class ExpenseListItem 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;
@ -48,11 +48,11 @@ class ExpenseListItem extends StatelessWidget {
final uiState = state.uiState; final uiState = state.uiState;
final expenseUIState = uiState.expenseUIState; final expenseUIState = uiState.expenseUIState;
final listUIState = expenseUIState.listUIState; final listUIState = expenseUIState.listUIState;
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 textStyle = TextStyle(fontSize: 16); final textStyle = TextStyle(fontSize: 16);
@ -63,7 +63,7 @@ class ExpenseListItem 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>[
formatDate(expense!.date, context), formatDate(expense.date, context),
]; ];
if (category.isOld) { if (category.isOld) {
parts.add(category.name); parts.add(category.name);
@ -82,7 +82,7 @@ class ExpenseListItem extends StatelessWidget {
isDismissible: isDismissible, isDismissible: isDismissible,
isSelected: isDesktop(context) && isSelected: isDesktop(context) &&
showSelected && showSelected &&
expense!.id == expense.id ==
(uiState.isEditing (uiState.isEditing
? expenseUIState.editing!.id ? expenseUIState.editing!.id
: expenseUIState.selectedId), : expenseUIState.selectedId),
@ -93,10 +93,10 @@ class ExpenseListItem 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: () => onTap != null onLongPress: () => onTap != null
? null ? null
: 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,
@ -125,7 +125,7 @@ class ExpenseListItem extends StatelessWidget {
), ),
) )
: ActionMenuButton( : ActionMenuButton(
entityActions: expense!.getActions( entityActions: expense.getActions(
userCompany: state.userCompany, userCompany: state.userCompany,
includeEdit: true, includeEdit: true,
), ),
@ -141,11 +141,11 @@ class ExpenseListItem 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)
], ],
), ),
), ),
@ -155,12 +155,12 @@ class ExpenseListItem 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,
), ),
Text(subtitle ?? filterMatch!, Text(subtitle,
maxLines: 3, maxLines: 3,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: Theme.of(context) style: Theme.of(context)
@ -175,8 +175,8 @@ class ExpenseListItem extends StatelessWidget {
), ),
SizedBox(width: 8), SizedBox(width: 8),
Text( Text(
formatNumber(expense!.grossAmount, context, formatNumber(expense.grossAmount, context,
currencyId: expense!.currencyId)!, currencyId: expense.currencyId)!,
style: textStyle, style: textStyle,
textAlign: TextAlign.end, textAlign: TextAlign.end,
), ),
@ -188,10 +188,10 @@ class ExpenseListItem extends StatelessWidget {
) )
: ListTile( : ListTile(
onTap: () => onTap: () =>
onTap != null ? onTap!() : selectEntity(entity: expense!), onTap != null ? onTap!() : selectEntity(entity: expense),
onLongPress: () => onTap != null onLongPress: () => onTap != null
? null ? null
: selectEntity(entity: expense!, longPress: true), : selectEntity(entity: expense, longPress: true),
leading: showCheckbox leading: showCheckbox
? IgnorePointer( ? IgnorePointer(
ignoring: listUIState.isInMultiselect(), ignoring: listUIState.isInMultiselect(),
@ -210,17 +210,17 @@ class ExpenseListItem 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!.amount, context, formatNumber(expense.amount, context,
currencyId: expense!.currencyId)!, currencyId: expense.currencyId)!,
style: Theme.of(context).textTheme.titleMedium), style: Theme.of(context).textTheme.titleMedium),
], ],
), ),
@ -247,14 +247,14 @@ class ExpenseListItem extends StatelessWidget {
), ),
Text( Text(
localization!.lookup( localization!.lookup(
kExpenseStatuses[expense!.calculatedStatusId])!, kExpenseStatuses[expense.calculatedStatusId])!,
style: TextStyle( style: TextStyle(
color: category.color.isNotEmpty && color: category.color.isNotEmpty &&
category.color != '#fff' category.color != '#fff'
? convertHexStringToColor(category.color) ? convertHexStringToColor(category.color)
: ExpenseStatusColors( : ExpenseStatusColors(
state.prefState.colorThemeModel) state.prefState.colorThemeModel)
.colors[expense!.calculatedStatusId])), .colors[expense.calculatedStatusId])),
], ],
), ),
); );

View File

@ -456,19 +456,19 @@ class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
); );
} else if (column == COLUMN_CUSTOM1) { } else if (column == COLUMN_CUSTOM1) {
return Text(item.customValue1 ?? ''); return Text(item.customValue1);
} else if (column == COLUMN_CUSTOM2) { } else if (column == COLUMN_CUSTOM2) {
return Text(item.customValue2 ?? ''); return Text(item.customValue2);
} else if (column == COLUMN_CUSTOM3) { } else if (column == COLUMN_CUSTOM3) {
return Text(item.customValue3 ?? ''); return Text(item.customValue3);
} else if (column == COLUMN_CUSTOM4) { } else if (column == COLUMN_CUSTOM4) {
return Text(item.customValue4 ?? ''); return Text(item.customValue4);
} else if (column == COLUMN_TAX1) { } else if (column == COLUMN_TAX1) {
return Text(item.taxName1 ?? ''); return Text(item.taxName1);
} else if (column == COLUMN_TAX2) { } else if (column == COLUMN_TAX2) {
return Text(item.taxName2 ?? ''); return Text(item.taxName2);
} else if (column == COLUMN_TAX3) { } else if (column == COLUMN_TAX3) {
return Text(item.taxName3 ?? ''); return Text(item.taxName3);
} else if (column == COLUMN_TAX_CATEGORY) { } else if (column == COLUMN_TAX_CATEGORY) {
return Text(localization return Text(localization
.lookup(kTaxCategories[item.taxCategoryId])!); .lookup(kTaxCategories[item.taxCategoryId])!);

View File

@ -126,7 +126,7 @@ class InvoiceEditVM extends AbstractInvoiceEditVM {
for (int i = 0; i < invoice.lineItems.length; i++) { for (int i = 0; i < invoice.lineItems.length; i++) {
final lineItem = invoice.lineItems[i]!; final lineItem = invoice.lineItems[i]!;
final task = state.taskState.get(lineItem.taskId ?? ''); final task = state.taskState.get(lineItem.taskId ?? '');
if ((task.clientId ?? '').isNotEmpty && task.clientId != clientId) { if (task.clientId.isNotEmpty && task.clientId != clientId) {
showDialog<ErrorDialog>( showDialog<ErrorDialog>(
context: navigatorKey.currentContext!, context: navigatorKey.currentContext!,
builder: (BuildContext context) { builder: (BuildContext context) {
@ -204,8 +204,8 @@ class InvoiceEditVM extends AbstractInvoiceEditVM {
if ((clientId ?? '').isNotEmpty || (projectId ?? '').isNotEmpty) { if ((clientId ?? '').isNotEmpty || (projectId ?? '').isNotEmpty) {
final client = state.clientState.get(clientId!); final client = state.clientState.get(clientId!);
store.dispatch(UpdateInvoice(invoice.rebuild((b) => b store.dispatch(UpdateInvoice(invoice.rebuild((b) => b
..clientId = clientId ?? '' ..clientId = clientId
..projectId = projectId ?? '' ..projectId = projectId
..invitations.replace(BuiltList<InvitationEntity>(client ..invitations.replace(BuiltList<InvitationEntity>(client
.emailContacts .emailContacts
.map( .map(

View File

@ -134,7 +134,7 @@ class InvoiceListItem extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
Text( Text(
(invoice.number ?? '').isEmpty invoice.number.isEmpty
? localization.pending ? localization.pending
: invoice.number, : invoice.number,
style: textStyle, style: textStyle,
@ -236,7 +236,7 @@ class InvoiceListItem extends StatelessWidget {
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: filterMatch == null child: filterMatch == null
? Text((((invoice.number ?? '').isEmpty ? Text(((invoice.number.isEmpty
? localization.pending ? localization.pending
: invoice.number) + : invoice.number) +
'' + '' +

View File

@ -79,9 +79,8 @@ class InvoicePresenter extends EntityPresenter {
case InvoiceFields.status: case InvoiceFields.status:
return EntityStatusChip(entity: invoice, showState: true); return EntityStatusChip(entity: invoice, showState: true);
case InvoiceFields.number: case InvoiceFields.number:
return Text((invoice.number ?? '').isEmpty return Text(
? localization!.pending invoice.number.isEmpty ? localization!.pending : invoice.number);
: invoice.number);
case InvoiceFields.client: case InvoiceFields.client:
return LinkTextRelatedEntity(entity: client, relation: invoice); return LinkTextRelatedEntity(entity: client, relation: invoice);
case InvoiceFields.project: case InvoiceFields.project:
@ -203,7 +202,7 @@ class InvoicePresenter extends EntityPresenter {
return Text(localization!.secondReminder); return Text(localization!.secondReminder);
} else if ((invoice.reminder1Sent ?? '').isNotEmpty) { } else if ((invoice.reminder1Sent ?? '').isNotEmpty) {
return Text(localization!.firstReminder); return Text(localization!.firstReminder);
} else if ((invoice.lastSentDate ?? '').isNotEmpty) { } else if (invoice.lastSentDate.isNotEmpty) {
return Text(localization!.initialEmail); return Text(localization!.initialEmail);
} else { } else {
return Text(''); return Text('');

View File

@ -264,12 +264,12 @@ class InvoiceOverview extends StatelessWidget {
); );
} }
if ((invoice.projectId ?? '').isNotEmpty) { if (invoice.projectId.isNotEmpty) {
final project = state.projectState.get(invoice.projectId); final project = state.projectState.get(invoice.projectId);
widgets.add(EntityListTile(entity: project, isFilter: isFilter)); widgets.add(EntityListTile(entity: project, isFilter: isFilter));
} }
if ((invoice.expenseId ?? '').isNotEmpty) { if (invoice.expenseId.isNotEmpty) {
final expense = state.vendorState.get(invoice.expenseId); final expense = state.vendorState.get(invoice.expenseId);
widgets.add(EntityListTile(entity: expense, isFilter: isFilter)); widgets.add(EntityListTile(entity: expense, isFilter: isFilter));
} }

View File

@ -133,7 +133,7 @@ class ProjectListItem extends StatelessWidget {
? ' 📎' ? ' 📎'
: ''), : ''),
style: textStyle), style: textStyle),
Text(subtitle ?? filterMatch!, Text(subtitle,
maxLines: 3, maxLines: 3,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: Theme.of(context) style: Theme.of(context)

View File

@ -158,7 +158,7 @@ class RecurringExpenseListItem extends StatelessWidget {
style: textStyle, style: textStyle,
maxLines: 1, maxLines: 1,
), ),
Text(subtitle ?? filterMatch!, Text(subtitle,
maxLines: 3, maxLines: 3,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: Theme.of(context) style: Theme.of(context)

View File

@ -706,28 +706,28 @@ class _ReportDataTableState extends State<ReportDataTable> {
class TotalsDataTable extends StatelessWidget { class TotalsDataTable extends StatelessWidget {
const TotalsDataTable({ const TotalsDataTable({
this.reportSettings, required this.reportSettings,
this.reportResult, required this.reportResult,
this.viewModel, required this.viewModel,
}); });
final ReportsScreenVM? viewModel; final ReportsScreenVM viewModel;
final ReportSettingsEntity? reportSettings; final ReportSettingsEntity reportSettings;
final ReportResult? reportResult; final ReportResult reportResult;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return mt.DataTable( return mt.DataTable(
sortColumnIndex: sortColumnIndex:
reportResult!.columns.length > reportSettings!.sortTotalsIndex reportResult.columns.length > reportSettings.sortTotalsIndex
? reportSettings!.sortTotalsIndex ? reportSettings.sortTotalsIndex
: null, : null,
sortAscending: reportSettings!.sortTotalsAscending ?? true, sortAscending: reportSettings.sortTotalsAscending ?? true,
columns: reportResult!.totalColumns( columns: reportResult.totalColumns(
context, context,
(index, ascending) => (index, ascending) =>
viewModel!.onReportTotalsSorted(index, ascending)), viewModel.onReportTotalsSorted(index, ascending)),
rows: reportResult!.totalRows(context), rows: reportResult.totalRows(context),
); );
} }
} }

View File

@ -190,7 +190,7 @@ class TaskListItem extends StatelessWidget {
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: textStyle), style: textStyle),
Text( Text(
subtitle ?? filterMatch!, subtitle,
maxLines: 3, maxLines: 3,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: Theme.of(context) style: Theme.of(context)

View File

@ -1047,7 +1047,7 @@ class _MatchWithdrawalsState extends State<_MatchWithdrawals> {
separatorBuilder: (context, index) => ListDivider(), separatorBuilder: (context, index) => ListDivider(),
itemCount: _expenses.length, itemCount: _expenses.length,
itemBuilder: (BuildContext context, int index) { itemBuilder: (BuildContext context, int index) {
final expense = _expenses[index]; final expense = _expenses[index]!;
return ExpenseListItem( return ExpenseListItem(
expense: expense, expense: expense,
showCheckbox: true, showCheckbox: true,