Build a Flutter Expense Tracker with Advanced Transaction Features

syncfusionsyncfusion
9 min read

TL;DR: Dive into the Flutter Expense Tracker’s Transaction page — add, edit, delete, and filter your financial data in real-time using a responsive UI powered by Syncfusion’s Flutter DataGrid. Enjoy Excel export, state management, adaptive layouts, and seamless CRUD operations, tailored for desktop, tablet, and mobile screens.

Welcome back to the Expense Tracker sample blog series! So far, we have explored the setup process, import pages, and dashboard features that enhance user experience and financial tracking.

Now, let’s look closer at the transaction page, which allows users to view, add, edit, and delete transactions seamlessly. This section plays a crucial role in keeping financial records organized and accessible.

Transaction overview

The transaction page offers a range of features designed for efficiency and ease of use:

  • Responsive layout: Adapts seamlessly across desktop, tablet, and mobile devices.

  • Advanced filtering: Quickly sort transactions by type and date range for better insights.

  • CRUD operations: Effortlessly add, update, or delete transactions as needed.

  • Dynamic DataGrid: Features height-based row pagination for a structured display.

  • Excel export: Enables easy data backup and financial analysis.

  • Real-time updates: Uses custom notifiers to ensure transactions reflect instantly.

This section ensures users have complete control over their financial records while maintaining a smooth and intuitive experience.

Desktop view

Desktop view of transaction

Desktop view of transaction

Mobile view

Mobile view of transaction

Mobile view of transaction

Transaction page layout

The Transaction Page is structured into three key sections to ensure a seamless user experience:

  • Action bar: This bar provides buttons and dropdown menus for filtering transactions by type and date, enabling quick and efficient navigation.

  • Filter options: This option offers additional filtering tools, particularly for mobile users, to enhance usability across different screen sizes.

  • Transaction DataGrid: This tool displays transaction records in a structured table format using the Syncfusion® Flutter DataGrid, making it easy to view, analyze, and manage financial data.

This well-organized layout ensures smooth interaction and efficient transaction tracking across all devices.

Widget _buildTransactionPage(TransactionNotifier transactionNotifier) {
  return ExpenseCard(
    child: Column(
      children: [
        _buildActionBar(transactionNotifier),

        // Use < and > for conditionals if that's your requirement
        if (isMobile(context)) 
          _buildMobileFilterAndExportRow(), // Only on smaller (mobile) screens

        Expanded(child: _buildTransactionDataGrid(context)),
      ],
    ),
  );
}

Transaction filtering system

The transaction page includes an intuitive filtering system that helps users refine transaction records based on type and date range, ensuring a seamless and efficient browsing experience.

Transaction type filter

Users can filter transactions by selecting specific types:

  • All: Displays all transactions without any filters.

  • Income: Shows only income-related transactions.

  • Expense: Displays only expense-related transactions.

In the transaction page of the Expense Tracker sample, we have optimized the filtering UI for different screen sizes to ensure a seamless user experience:

  • Desktop view: Use Flutter’s built-in SegmentedButton widget for quick and efficient filtering.

  • Tablet view: Use Flutter’s DropdownMenu widget, replacing the SegmentedButton for better accessibility.

  • Mobile view: Use Flutter’s PopupMenuButton widget, further improving usability on compact screens.

Widget _buildFilteringSegmentsOrDropDownButton(BuildContext context) {
  final TransactionNotifier transactionNotifier =
      context.watch<TransactionNotifier>();

  return isTablet(context)
      ? ChartsDropdownFilter(
          intervalFilters: const <String>['All', 'Income', 'Expense'],
          selectedDuration: transactionNotifier.selectedSegment,
          onTap: (String? value) =>
              _handleSegmentChange(value!, transactionNotifier),
        )
      : SegmentedFilterButtons(
          options: const <String>['All', 'Income', 'Expense'],
          onSelectionChanged: (Set<String> selections) =>
              _handleSegmentChange(selections.first, transactionNotifier),
          selectedSegment: transactionNotifier.selectedSegment,
        );
}

Date range filter

Users can also filter transactions based on predefined date ranges, allowing them to view records within a specific period. We have used Flutter’s built-in DropdownMenu widget to allow users to select a date interval, triggering real-time updates.

Widget _buildTransactionFilter(BuildContext context) {
  final TransactionNotifier transactionNotifier =
      context.watch<TransactionNotifier>();

  return ChartsDropdownFilter(
    intervalFilters: dateDuration,
    onTap: (String? newValue) {
      if (newValue != null) {
        transactionNotifier.updateSelectedDuration(newValue);
        transactionNotifier.updateTransactions(_transactions);
      }
    },
    selectedDuration: transactionNotifier.selectedDuration,
  );
}

Desktop view

Desktop view of the transaction filtering

Desktop view of the transaction filtering

Mobile view

Mobile view of the transaction filtering

Mobile view of the transaction filtering

State management in the transaction page

State management is essential for enabling dynamic filtering, real-time UI updates, and tracking user selections. The transaction page leverages two key notifiers to handle state efficiently:

  • TransactionNotifier: It manages transaction data, handles filtering logic, and ensures seamless state updates.

  • TransactionSelectedCountNotifier: It tracks the number of selected transactions to delete.

class TransactionNotifier extends ChangeNotifier {
  void updateTransactions(List<Transaction> transactions) {
    _transactions = transactions;
    _filteredTransactions = _applyFilters(transactions);
    notifyListeners();
  }

  void updateSelectedSegment(String segment) {
    _selectedSegment = segment;
    _filteredTransactions = _applyFilters(_transactions);
    notifyListeners();
  }
}

Excel export functionality

The transaction page includes an Excel export feature, allowing users to download their transaction data for backup, analysis, and easy sharing. This functionality offers several benefits:

  • Backup & record-keeping: Users can maintain a local copy of their financial transactions.

  • Advanced data analysis: Transactions can be analyzed using spreadsheet tools like Microsoft Excel or Google Sheets.

  • Simplified sharing: Users can effortlessly share financial records with accountants or advisors.

_buildExportButton() {
  child: ExportButton(
    onTap: () async {
      handleOnTapExportLogic(context, 'Transactions', 'Transaction');
    },
  ),
}

The handleOnTapExportLogic() method facilitates the export process by converting the current transaction data into an Excel-compatible format, ensuring:

  • Seamless data conversion without loss of transaction details.

  • Accurate preservation of financial records.

  • A user-friendly export process that requires minimal effort from the user.

Desktop view

Desktop view of Excel export

Desktop view of Excel export

Mobile view

Mobile view of Excel export

Mobile view of Excel export

Efficient data management with SfDataGrid

The Syncfusion Flutter DataGrid plays a central role in displaying and managing transaction data on the Transaction Page. This powerful Flutter widget provides a structured, interactive, and efficient way to handle large datasets with ease.

The SfDataGrid offers advanced capabilities, including:

  • Dynamic column creation based on provided headers.

  • Multiple row selection with checkbox support.

  • Adaptive column width modes for optimal data visibility.

  • Custom cell rendering to display different data types effectively.

  • Page-based data loading using CustomDataGridSource.

  • Responsive design that adjusts seamlessly across mobile, tablet, and desktop views.

The implementation combines SfDataGrid , DataGridController , and CustomDataGridSource, allowing for efficient data handling with minimal configuration. This ensures a smooth, user-friendly experience when navigating and interacting with transaction records.

SfDataGrid(
  source: dataSource,
  columns: _buildGridColumns(),
  selectionMode: SelectionMode.multiple,
  rowsPerPage: rowsPerPage,
  columnWidthMode: ColumnWidthMode.fill,
)

Data Paging mechanism

To ensure efficient data management and smooth navigation, the TransactionGridSource class implements an advanced data paging strategy. Key features include:

  • Efficient data loading, which prevents performance degradation when handling large datasets.

  • Smooth page navigation, allowing users to browse transactions seamlessly.

class TransactionGridSource extends CustomDataGridSource<Transaction> {
  final int rowsPerPage;

  TransactionGridSource({
    required BuildContext context,
    required List<Transaction> transactions,
    required int rowsPerPage,
  }) : super(
         rowsPerPage: rowsPerPage,
         data: transactions,
         columns: ['Date', 'Category', 'Type', 'Amount', 'Notes']
       );
}

CRUD operations in the transaction page

This Flutter application implements a comprehensive transaction management system with smooth CRUD (Create, Read, Update, Delete) operations. Let’s explore how these core features work:

Edit transaction

Editing functionality allows users to modify existing transaction records. The process is as follows:

  • Users select a transaction from the data grid using checkboxes.

  • The system detects the selection and displays action buttons.

  • When the edit button is pressed, a dialog with pre-populated fields appears.

  • Users can modify any field, including type, category, subcategory, amount, date, and notes.

  • Upon submission, the transaction is updated in memory and saved to storage.

void handleEditTransaction() {
  // Get selected transaction index
  int selectedIndex = getSelectedTransactionIndex();

  // Show edit dialog
  if (isMobile) {
    showMobileEditDialog(selectedIndex);
  } else {
    showDesktopEditDialog(selectedIndex);
  }

  // On save:
  // 1. Create an "updated transaction object"
  // 2. Update transaction list
  // 3. Close dialog
}

Desktop view

Desktop view of editing transactions

Desktop view of editing transactions

Mobile view

Mobile view of editing transactions

Mobile view of editing transactions

Delete transaction

The delete operation removes one or more transactions with confirmation:

  • Users select the transactions to delete.

  • Clicking the delete button displays a confirmation dialog.

  • Upon confirmation, the transactions are removed from the list and storage.

  • The user interface updates to reflect the changes and clears the selection.

void handleDeleteTransaction() {
  // Show confirmation dialog
  showDeleteConfirmationDialog(
    title: "Delete Transaction?",
    content: "Do you want to delete this transaction?",
    onConfirm: () {
      // Get selected transaction indexes
      List<int> selectedIndexes = getSelectedTransactionIndexes();

      // Remove from transaction list
      transactionNotifier.deleteTransactions(selectedIndexes);

      // Update storage
      updateTransactions(
        context,
        userDetails,
        transaction,
        UserInteractions.delete,
        selectedIndexes
      );

      // Reset selection state
      clearSelection();

      // Close dialog
      Navigator.pop(context);
    }
  );
}

Desktop view

Desktop view of deleting transactions

Desktop view of deleting transactions

Mobile view

Mobile view of deleting transactions

Mobile view of deleting transactions

Add transaction

The add functionality creates new transaction records as follows:

  • The user clicks an add button (which is not explicitly shown in the provided code).

  • A dialog appears with empty fields.

  • The user enters transaction details, including type, category, amount, date, etc.

  • Upon submission, a new transaction is created and added to the list.

Refer to the following code example.

void handleAddTransaction() {
  // Set interaction type
  userInteraction = UserInteractions.add;

  // Show add dialog
  showDialog(
    context: context,
    builder: (context) => TransactionCenterDialog(
      userInteraction: userInteraction,
      userDetails: userDetails,
      categories: categories,
      subCategories: subCategories,
      // Other parameters
    )
  );

  // On dialog submission:
  // 1. Create a new transaction from form data
  // 2. Add to transaction list
  // 3. Persist to storage
  // 4. Close dialog
}

Desktop view

Desktop view of adding transactions

Desktop view of adding transactions

Mobile view

Mobile view of adding transactions

Mobile view of adding transactions

The application uses responsive design principles to adapt the UI for different screen sizes, showing appropriate dialogs for mobile and desktop views. The transaction management system is integrated with a data grid for visual representation and selection capabilities, with proper validation to ensure data integrity.

Conclusion

Thanks for reading! In this blog, we’ve explored how developing an effective transaction management page requires careful consideration of filtering, data presentation, and user interactions. By leveraging Flutter’s flexibility and Syncfusion’s DataGrid component, we have created a powerful and user-friendly interface adaptable to different devices.

This implementation showcases:

  • Advanced filtering with multiple criteria.

  • Responsive layouts for various screen sizes.

  • Efficient data management with selection-based actions.

  • Visually distinct representations of transaction types.

  • Intuitive workflows for editing and deleting transactions.

By extending these functionalities, we can incorporate additional features such as analytics, reporting, and integration with external financial tools. What other features would you like to see in future updates? Please let us know in the comments below!

Existing Syncfusion® users can access the most recent Essential Studio® on the license and downloads page. If you’re new to Syncfusion®, we offer a 30-day free trial to test these exciting new features.

You can also contact us through our support forum, support portal, or feedback portal. We are always happy to help you!

This article was originally published at Syncfusion.com.

0
Subscribe to my newsletter

Read articles from syncfusion directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

syncfusion
syncfusion

Syncfusion provides third-party UI components for React, Vue, Angular, JavaScript, Blazor, .NET MAUI, ASP.NET MVC, Core, WinForms, WPF, UWP and Xamarin.