# How AppFlowy Implements Data Import from Notion, Todoist, and Other Tools

> Discover how AppFlowy seamlessly imports data from Notion, Todoist, and more. Explore the cross-layer pipeline connecting Flutter UI to a powerful Rust backend for efficient data transformation and workspace creation.

- Repository: [AppFlowy-IO/AppFlowy](https://github.com/AppFlowy-IO/AppFlowy)
- Tags: how-to-guide
- Published: 2026-03-03

---

**AppFlowy implements data import through a cross-layer pipeline where the Flutter UI packages raw files into protobuf-encoded requests and hands them to a Rust backend that parses the data and creates workspace views, with specific handlers for Notion ZIP archives and extensible support for CSV-based tools like Todoist.**

AppFlowy’s import architecture separates data ingestion from data processing to keep the codebase modular and extensible. According to the AppFlowy-IO/AppFlowy source code, the Flutter frontend never parses third-party formats directly; instead, it builds `ImportItemPayloadPB` or `ImportZipPB` objects and dispatches them via the `FolderEvent` API to Rust handlers that perform the heavy lifting.

## Architectural Overview of the Import Pipeline

The import system spans four distinct layers, each with specific responsibilities and well-defined interfaces:

- **Flutter UI Layer** – Displays the import dialog, handles file selection, and constructs protobuf payloads. Key components include `ImportPanel`, `ImportType` enum, and `NotionImporter` located in `frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/import/import_panel.dart`.
- **Flutter Service Layer** – Sends protobuf requests to the backend via the `FolderEvent` API. The `ImportBackendService` class in `frontend/appflowy_flutter/lib/workspace/application/settings/share/import_service.dart` provides `importPages()` and `importZipFiles()` methods.
- **Protobuf Definitions** – Define the wire format for all import payloads. The `ImportItemPayloadPB`, `ImportZipPB`, and `ImportTypePB` structs live in [`frontend/rust-lib/flowy-folder/src/entities/import.rs`](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/rust-lib/flowy-folder/src/entities/import.rs).
- **Rust Backend Layer** – Parses payloads, reads files or raw bytes, and converts data into internal `ImportItem` structs. The `import_pages_handler` and `import_zip_file_handler` functions in [`frontend/rust-lib/flowy-folder/src/event_handler.rs`](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/rust-lib/flowy-folder/src/event_handler.rs) receive these events and delegate to `FolderManager.import()` and `FolderManager.import_zip_file()` for insertion into the workspace hierarchy.

This separation ensures that adding a new third-party source requires only a new UI entry and potentially a new parser in Rust, without touching the core folder management logic.

## How Notion Data Import Works

Notion imports rely on the platform’s ZIP export format, which contains JSON page definitions and associated assets.

### ZIP File Processing Pipeline

When a user initiates a Notion import:

1. The `NotionImporter` widget in `_sidebar_import_notion.dart` captures the ZIP file path selected by the user.
2. The UI constructs an `ImportZipPB` protobuf message with the `file_path` field set to the selected archive.
3. `ImportBackendService.importZipFiles()` dispatches the `FolderEventImportZipFile` event to the Rust backend.
4. The `import_zip_file_handler` in [`event_handler.rs`](https://github.com/AppFlowy-IO/AppFlowy/blob/main/event_handler.rs) receives the payload and calls `FolderManager.import_zip_file(&data.file_path)`.
5. The Rust backend extracts the archive, traverses the Notion JSON structure, and transforms each page into an `ImportItem` with appropriate view layouts and metadata.
6. Each `ImportItem` is persisted as a `View` inside the user’s workspace. Errors are wrapped in `FlowyError` and propagated back to the UI for display.

### Key Source Files for Notion Import

- **UI Widget**: `_sidebar_import_notion.dart` – Handles file picker interaction and error display.
- **Service Interface**: `import_service.dart` – Defines `ImportBackendService.importZipFiles()`.
- **Protobuf Schema**: [`import.rs`](https://github.com/AppFlowy-IO/AppFlowy/blob/main/import.rs) – Contains `ImportZipPB` and related type definitions.
- **Event Handler**: [`event_handler.rs`](https://github.com/AppFlowy-IO/AppFlowy/blob/main/event_handler.rs) – Implements `import_zip_file_handler` and routes to the folder manager.

## Extending AppFlowy for Todoist Data Import

As of the current main branch, AppFlowy does not ship a built-in Todoist importer, but the existing CSV pipeline provides a foundation for implementing one with minimal changes:

- **UI Extension** – Add a `todoist` variant to the `ImportType` enum in `import_type.dart` and register a corresponding entry in `ImportPanel`.
- **Payload Construction** – Todoist exports data as CSV. The UI can reuse the existing CSV path by creating an `ImportItemPayloadPB` with `importType` set to `ImportTypePB.CSV` and encoding the file bytes.
- **Backend Parsing** – Extend the Rust `ImportItem` conversion logic in the folder manager to detect the Todoist CSV schema, mapping rows to checklist items and creating views with `TodoList` block layouts.

Because `FolderEventImportData` and `import_pages_handler` already handle CSV parsing, no new event handlers are required for basic Todoist support; only the schema-specific transformation logic needs implementation.

## Importing from Other Tools: CSV, Markdown, and AFDatabase

The generic import panel in `import_panel.dart` supports several additional sources through the same `ImportBackendService.importPages` endpoint, varying only the `ImportItemPayloadPB.import_type` field:

- **History Document (`.afdoc`)** – Migrates legacy AppFlowy documents via `ImportTypePB.HistoryDocument`.
- **History Database (`.afdb`)** – Imports archived database files as `ImportTypePB.HistoryDatabase`.
- **Markdown and Text (`.md`, `.txt`)** – Converts Markdown content to AppFlowy documents using `ImportTypePB.Markdown`.
- **CSV (`.csv`)** – Creates grid views from tabular data via `ImportTypePB.CSV`.
- **AFDatabase (`.afdb`)** – Imports native AppFlowy database exports.

For each type, the UI reads the file as a string or byte array, constructs the appropriate `ImportItemPayloadPB`, and sends it through the `FolderEventImportData` channel to the `import_pages_handler` in Rust.

## Practical Implementation: Code Examples

### Triggering a Notion Import Programmatically

To initiate a Notion import from Dart code without using the default UI:

```dart
final notionZipPath = '/path/to/notion-export.zip';

final result = await ImportBackendService.importZipFiles([
  ImportZipPB()..filePath = notionZipPath,
]);

result.fold(
  (_) => debugPrint('Notion import succeeded'),
  (err) => debugPrint('Notion import failed: ${err.msg}'),
);

```

This call creates an `ImportZipPB`, transmits it to the Rust backend, and returns a `FlowyResult` containing either void success or a `FlowyError` with diagnostic details.

### Extending the Import Type Enum for Todoist

To add Todoist support, modify the Dart layer as follows:

```dart
// In import_type.dart
enum ImportType {
  // existing entries...
  todoist,

  List<String> get allowedExtensions {
    switch (this) {
      case ImportType.todoist:
        return ['csv'];
      // other cases...
    }
  }
}

// In import_panel.dart
case ImportType.todoist:
  final data = await File(path).readAsString();
  importValues.add(
    ImportItemPayloadPB.create()
      ..name = name
      ..data = utf8.encode(data)
      ..viewLayout = ViewLayoutPB.Grid
      ..importType = ImportTypePB.CSV,
  );
  break;

```

Because the Rust backend already handles `ImportTypePB.CSV`, this implementation immediately enables basic Todoist CSV ingestion. For richer metadata mapping (e.g., labels to tags), extend `ImportTypePB` in [`import.rs`](https://github.com/AppFlowy-IO/AppFlowy/blob/main/import.rs) and add corresponding transformation logic in the folder manager.

## Summary

- **Notion imports** utilize a specialized ZIP pipeline: `ImportZipPB` → `FolderEventImportZipFile` → `import_zip_file_handler` → `FolderManager.import_zip_file`, which extracts JSON and assets to create workspace views.
- **Todoist and similar tools** can leverage the existing CSV infrastructure by adding UI variants and reusing `ImportItemPayloadPB` with `ImportTypePB.CSV`, minimizing backend changes.
- **Generic imports** for Markdown, CSV, and AppFlowy-specific formats flow through `ImportItemPayloadPB` → `FolderEventImportData` → `import_pages_handler` → `FolderManager.import`.
- The architecture’s separation of concerns allows new third-party integrations by adding protobuf enum values and Rust parsing logic without modifying core UI components.

## Frequently Asked Questions

### Does AppFlowy support native Todoist imports?

No, AppFlowy does not currently ship with a built-in Todoist importer. However, the existing CSV import pipeline in `import_service.dart` and the generic `ImportItemPayloadPB` structure provide the necessary infrastructure to add Todoist support by extending the `ImportType` enum and handling Todoist's CSV export format in the Rust backend.

### What file formats can AppFlowy import from other tools?

AppFlowy supports importing **Notion** (ZIP archives), **Markdown** (.md, .txt), **CSV** (.csv), **History Documents** (.afdoc), and **History Databases** (.afdb). Each format maps to a specific `ImportTypePB` enum value defined in [`frontend/rust-lib/flowy-folder/src/entities/import.rs`](https://github.com/AppFlowy-IO/AppFlowy/blob/main/frontend/rust-lib/flowy-folder/src/entities/import.rs), with the backend containing dedicated logic for extracting content from each file type.

### How does the Rust backend handle Notion's ZIP structure?

The `import_zip_file_handler` in [`event_handler.rs`](https://github.com/AppFlowy-IO/AppFlowy/blob/main/event_handler.rs) receives the ZIP file path, delegates extraction to `FolderManager.import_zip_file()`, and walks the nested JSON files to construct `ImportItem` structs. These structs capture page hierarchies, content blocks, and embedded assets, which are then persisted as native AppFlowy `View` objects with appropriate layout metadata.

### Can imports be triggered programmatically without the UI?

Yes, developers can call `ImportBackendService.importPages()` or `ImportBackendService.importZipFiles()` directly from Dart code, passing constructed `ImportItemPayloadPB` or `ImportZipPB` objects. This bypasses the `ImportPanel` UI and allows automated migration scripts or third-party integrations to ingest data into AppFlowy workspaces.