{"id":50942532,"url":"https://github.com/xcc3641/flutter_native_data_detector","last_synced_at":"2026-06-17T16:15:38.735Z","repository":{"id":363671864,"uuid":"1264398757","full_name":"xcc3641/flutter_native_data_detector","owner":"xcc3641","description":"Cross-platform text data detection for Flutter — NSDataDetector (iOS) \u0026 ML Kit Entity Extraction (Android). Phones, links, emails, addresses, dates.","archived":false,"fork":false,"pushed_at":"2026-06-09T21:10:52.000Z","size":1326,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-09T23:08:43.412Z","etag":null,"topics":["data-detector","entity-extraction","flutter","flutter-plugin","mlkit","nsdatadetector"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/flutter_native_data_detector","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xcc3641.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-09T21:09:26.000Z","updated_at":"2026-06-09T21:10:56.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/xcc3641/flutter_native_data_detector","commit_stats":null,"previous_names":["xcc3641/flutter_native_data_detector"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/xcc3641/flutter_native_data_detector","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xcc3641%2Fflutter_native_data_detector","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xcc3641%2Fflutter_native_data_detector/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xcc3641%2Fflutter_native_data_detector/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xcc3641%2Fflutter_native_data_detector/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xcc3641","download_url":"https://codeload.github.com/xcc3641/flutter_native_data_detector/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xcc3641%2Fflutter_native_data_detector/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34453860,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-17T02:00:05.408Z","response_time":127,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["data-detector","entity-extraction","flutter","flutter-plugin","mlkit","nsdatadetector"],"created_at":"2026-06-17T16:15:37.719Z","updated_at":"2026-06-17T16:15:38.724Z","avatar_url":"https://github.com/xcc3641.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"# flutter_native_data_detector\n\n[![pub package](https://img.shields.io/pub/v/flutter_native_data_detector.svg)](https://pub.dev/packages/flutter_native_data_detector)\n[![pub points](https://img.shields.io/pub/points/flutter_native_data_detector)](https://pub.dev/packages/flutter_native_data_detector/score)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![Platform](https://img.shields.io/badge/platform-android%20%7C%20ios-blue.svg)](https://pub.dev/packages/flutter_native_data_detector)\n\nEnglish | [简体中文](README.zh-CN.md)\n\nCross-platform text data detection for Flutter. Uses **NSDataDetector** on iOS and **ML Kit Entity Extraction** on Android to detect phone numbers, URLs, emails, dates, and addresses — returning structured results to Dart.\n\nA Flutter port of [react-native-data-detector](https://github.com/pablogdcr/react-native-data-detector).\n\n| Live detection demo | Entity pills |\n| :---: | :---: |\n| \u003cimg src=\"https://raw.githubusercontent.com/xcc3641/flutter_native_data_detector/main/images/demo.gif\" width=\"320\" alt=\"Typing live detection demo with animated entity pills\" /\u003e | \u003cimg src=\"https://raw.githubusercontent.com/xcc3641/flutter_native_data_detector/main/images/screenshot.png\" width=\"320\" alt=\"All five entity types rendered as glowing pills\" /\u003e |\n\nIt is built on top of:\n\n- **iOS**: `NSDataDetector` (built into the OS, no model download)\n- **Android**: Google ML Kit Entity Extraction (~5.6MB on-device model per language)\n\n## Features\n\n- **Phone numbers** — Detect and extract phone numbers\n- **URLs** — Detect web links\n- **Emails** — Detect email addresses\n- **Addresses** — Detect street addresses with parsed components (iOS)\n- **Dates** — Detect dates and times with ISO 8601 output\n\n- **Native accuracy** — Uses native APIs instead of regex\n- **Controllers** — `DataDetectorController` (imperative) and `DetectedEntitiesController` (reactive, as-you-type); both track model readiness and auto-download on Android\n- **Inline highlighting** — `DataDetectorTextEditingController` lights up entities in a `TextField` as the user types\n- **Entity pills** — `EntityRichText` + `EntityPill` render detected entities as glowing inline pills for read-only surfaces; fully restylable or replaceable\n- **Multiple languages** — Choose from 15 ML Kit language models on Android\n\n## Installation\n\n```bash\nflutter pub add flutter_native_data_detector\n```\n\n### Android\n\nThe ML Kit entity extraction model (~5.6MB per language) is downloaded on the user's device at runtime. You can control when this happens using `NativeDataDetector.prepareModel()` or `DataDetectorController` (which downloads automatically on construction) — for example, to ensure `detect()` works offline later. If you don't trigger it explicitly, the model is downloaded automatically on the first `detect()` call.\n\nRequires `minSdkVersion 26` (ML Kit Entity Extraction).\n\n## Usage\n\n### Functions\n\n```dart\nimport 'package:flutter_native_data_detector/flutter_native_data_detector.dart';\n\n// Pre-download the ML Kit model at app startup (Android only, no-op on iOS)\nawait NativeDataDetector.prepareModel();\n\n// Detect all entity types\nfinal entities = await NativeDataDetector.detect(\n  'Call me at 555-1234 or email john@example.com',\n);\n// [\n//   DetectedEntity(phoneNumber, \"555-1234\", 11..19, {phoneNumber: 555-1234}),\n//   DetectedEntity(email, \"john@example.com\", 29..45, {email: john@example.com}),\n// ]\n\n// Detect only specific types\nfinal phones = await NativeDataDetector.detect(\n  'Call 555-1234 or visit https://example.com',\n  types: [DetectionType.phoneNumber],\n);\n\n// Use a specific language model (Android only, ignored on iOS)\nfinal fr = await NativeDataDetector.detect(\n  'Appelez-moi au 01 23 45 67 89',\n  language: ModelLanguage.fr,\n);\n```\n\n### Controllers\n\nTwo `ChangeNotifier` controllers for two situations:\n\n- **`DataDetectorController`** — *imperative*. Tracks model readiness and hands you a\n  `detect` method you call yourself (e.g. once per chat message).\n- **`DetectedEntitiesController`** — *reactive*. Feed it a (changing) string via `text` and it\n  exposes the detected entities, debounced and recomputed as the text changes — ideal for\n  as-you-type input.\n\nBoth download the model automatically on Android and are no-ops on iOS (always ready).\n\n```dart\nfinal detector = DataDetectorController();\n\n// status: ModelStatus.notDownloaded | downloading | ready | error\nif (detector.isReady) {\n  final entities = await detector.detect(text, types: [DetectionType.email]);\n}\n```\n\n```dart\nfinal live = DetectedEntitiesController(debounce: Duration(milliseconds: 250));\n\n// From a TextField:\nTextField(onChanged: (text) =\u003e live.text = text);\n\n// Rebuild on changes:\nListenableBuilder(\n  listenable: live,\n  builder: (context, _) =\u003e Text(\n    '${live.entities.length} detected${live.isDetecting ? '…' : ''}',\n  ),\n);\n```\n\n### Inline highlighting\n\n`DataDetectorTextEditingController` is a `TextEditingController` that detects\nentities as the user types and highlights them inline — each entity type in\nits own color with a soft glow — while the field stays fully editable:\n\n```dart\nfinal controller = DataDetectorTextEditingController();\n\nTextField(controller: controller);\n\n// Structured results live on the embedded reactive controller:\ncontroller.detection.addListener(() {\n  print(controller.entities);\n});\n```\n\nNewly detected entities fade in over `highlightDuration` (default 350ms).\nThe package only drives the **appearance progress** `t` (linear `0.0 → 1.0`);\nhow an entity looks at any `t` is entirely yours via `entityStyleBuilder` —\nbring your own curve, colors, or ignore `t` for a static style:\n\n```dart\nDataDetectorTextEditingController(\n  highlightDuration: const Duration(milliseconds: 500),\n  entityStyleBuilder: (entity, base, t) =\u003e base.copyWith(\n    color: Color.lerp(base.color, Colors.amber, Curves.easeOutCubic.transform(t)),\n    decoration: TextDecoration.underline,\n  ),\n);\n```\n\nEscape hatches, from light to total control:\n\n1. `highlightDuration: Duration.zero` — no transition, `t` is always `1.0`.\n2. `entityStyleBuilder` — full control of the inline style at any progress.\n3. Subclass and override `buildTextSpan`, building from the public\n   `validEntities` (stale-range-guarded, sorted) — replace the rendering\n   entirely.\n\n### Entity pills (read-only display)\n\nFor display surfaces — message bubbles, previews — `EntityRichText` renders\ntext with each detected entity drawn inline, by default as the built-in\n`EntityPill`: a glowing rounded pill with the entity-type icon, fading in\nwhen the entity first appears. (`WidgetSpan` pills can't live inside an\neditable `TextField`, which is why the editable surface uses style-only\nhighlighting instead.)\n\n```dart\nEntityRichText(\n  text: message,\n  entities: entities, // from any detect() / controller\n  style: const TextStyle(fontSize: 17, height: 2.0),\n)\n```\n\nUse it as-is, restyle the pill, or replace it entirely:\n\n```dart\n// Tweak the built-in pill…\nEntityRichText(\n  text: message,\n  entities: entities,\n  entityBuilder: (context, entity) =\u003e EntityPill(\n    entity: entity,\n    color: Colors.teal,\n    icon: '', // hide the icon\n    appearDuration: Duration.zero,\n  ),\n);\n\n// …or bring your own widget.\nEntityRichText(\n  text: message,\n  entities: entities,\n  entityBuilder: (context, entity) =\u003e Chip(label: Text(entity.text)),\n);\n```\n\nEntity ranges are validated against `text` before rendering (the\n`entities.validIn(text)` extension, also public), so results that lag the\ntext never mis-render.\n\n## API\n\n### `NativeDataDetector.prepareModel({language})`\n\nPre-downloads the entity-detection model so `detect()` can run offline afterwards. On iOS, this is a no-op that resolves immediately — `NSDataDetector` is built into the OS and requires no model download.\n\nReturns `Future\u003cbool\u003e` — `true` when the model is ready.\n\n| Platform | Behavior |\n|----------|----------|\n| **iOS** | No-op, resolves `true` immediately |\n| **Android** | Downloads the ML Kit model (~5.6MB) for the language if not already cached. Requires internet on first call. |\n\n### `NativeDataDetector.getModelStatus({language})`\n\nReturns the download status of the model for the given language: `ModelStatus.ready` or `ModelStatus.notDownloaded`. On iOS always resolves `ready`. (A pure query never returns `downloading` or `error` — those are only surfaced by `DataDetectorController`.)\n\n### `NativeDataDetector.isModelReady({language})`\n\nConvenience wrapper around `getModelStatus`. Resolves `true` when the model for the given language is available.\n\n### `NativeDataDetector.detect(text, {types, language})`\n\nDetects entities in the given text using native platform APIs.\n\n| Parameter | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `text` | `String` | — | The text to analyze |\n| `types` | `List\u003cDetectionType\u003e?` | All types | Which entity types to detect |\n| `language` | `ModelLanguage` | `en` | Which language model to use (Android only). Ignored on iOS. |\n\nReturns `Future\u003cList\u003cDetectedEntity\u003e\u003e`.\n\n### `DataDetectorController({language, autoPrepare})`\n\n`ChangeNotifier` that tracks model availability and, on Android, downloads the language model automatically. On iOS the model is always available, so `status` settles on `ready`.\n\n| Member | Type | Description |\n|--------|------|-------------|\n| `detect(text, {types})` | `Future\u003cList\u003cDetectedEntity\u003e\u003e` | Detect entities using the configured language. |\n| `prepare()` | `Future\u003cvoid\u003e` | Manually (re)download the configured language model. |\n| `status` | `ModelStatus` | `notDownloaded` / `downloading` / `ready` / `error`. |\n| `isReady` | `bool` | `true` when `status == ModelStatus.ready`. |\n| `error` | `Object?` | The last preparation error, or `null`. |\n| `language` | `ModelLanguage` | Mutable; changing it re-checks/prepares the new model. |\n\n### `DetectedEntitiesController({text, debounce, types, language, enabled, autoPrepare})`\n\nReactive `ChangeNotifier`: set `text` as it changes and read back the detected entities, debounced and cancellation-safe (the latest text wins). Manages model readiness internally.\n\n| Member | Type | Description |\n|--------|------|-------------|\n| `text` | `String` | Mutable; setting it (re)starts the debounce timer. |\n| `entities` | `List\u003cDetectedEntity\u003e` | Entities detected in the debounced `text`. |\n| `isDetecting` | `bool` | `true` while a detection for the latest text is in flight. |\n| `status` | `ModelStatus` | Current model download state. |\n| `error` | `Object?` | The last detection or model error, or `null`. |\n| `enabled` | `bool` | Mutable; when `false`, detection pauses and the last result is kept. |\n| `debounce` | `Duration` | Debounce applied to `text` (default 300ms). |\n| `types` | `List\u003cDetectionType\u003e?` | Mutable; which entity types to detect (`null` = all). |\n| `language` | `ModelLanguage` | Mutable; selects the Android model and re-runs detection. |\n\n### `DataDetectorTextEditingController({text, debounce, types, language, enabled, autoPrepare, highlightDuration, entityStyleBuilder})`\n\nA `TextEditingController` that highlights detected entities inline while staying fully editable (only styles change, never characters, so cursor and selection are unaffected). Detection results that lag the text by the debounce interval are range-checked before styling, so edits never mis-highlight.\n\n| Member | Type | Description |\n|--------|------|-------------|\n| `detection` | `DetectedEntitiesController` | The embedded reactive detection state. |\n| `entities` | `List\u003cDetectedEntity\u003e` | Entities currently detected in the text. |\n| `validEntities` | `List\u003cDetectedEntity\u003e` | Stale-range-guarded, sorted entities — the safe basis for custom `buildTextSpan` overrides. |\n| `highlightDuration` | `Duration` | Fade-in length for newly detected entities (default 350ms; `Duration.zero` disables). |\n| `entityStyleBuilder` | `TextStyle Function(DetectedEntity, TextStyle base, double t)?` | Full control of the inline style at appearance progress `t` (linear 0→1). |\n\n### `DetectedEntity`\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `type` | `DetectionType` | The type of detected entity |\n| `text` | `String` | The matched text substring |\n| `start` | `int` | Start index in the original string (UTF-16 code units, i.e. Dart string indices) |\n| `end` | `int` | End index (exclusive) in the original string |\n| `data` | `Map\u003cString, String\u003e` | Additional structured data (see below) |\n\n### Entity Data by Type\n\n| Type | Data fields |\n|------|-------------|\n| `phoneNumber` | `{ phoneNumber }` |\n| `link` | `{ url }` |\n| `email` | `{ email }` |\n| `address` | `{ street, city, state, zip, country }` (iOS) / `{ address }` (Android) |\n| `date` | `{ date }` ISO 8601 string |\n\n## Supported Languages\n\nThe `language` option (enum `ModelLanguage`) selects which **Android** ML Kit model is used. It is a no-op on iOS, where `NSDataDetector` is language-agnostic. Each language is a separate ~5.6MB on-device model, downloaded on demand.\n\n| Code | Language | Code | Language   | Code | Language |\n| ---- | -------- | ---- | ---------- | ---- | -------- |\n| `ar` | Arabic   | `it` | Italian    | `ru` | Russian  |\n| `nl` | Dutch    | `ja` | Japanese   | `es` | Spanish  |\n| `en` | English  | `ko` | Korean     | `th` | Thai     |\n| `fr` | French   | `pl` | Polish     | `tr` | Turkish  |\n| `de` | German   | `pt` | Portuguese | `zh` | Chinese  |\n\n## Platform Differences\n\n| Feature | iOS | Android |\n|---------|-----|---------|\n| Engine | NSDataDetector | ML Kit Entity Extraction |\n| Offline | Always | After `prepareModel()` or first `detect()` call |\n| Model download | Not needed | ~5.6MB per language, on-device at runtime |\n| Language selection | Language-agnostic (option ignored) | 15 selectable language models |\n| Address parsing | Structured components | Raw string |\n| Date output | ISO 8601 | ISO 8601 |\n\n## Requirements\n\n- iOS 13.0+\n- Android API 26+ (minSdk) — required by ML Kit Entity Extraction\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxcc3641%2Fflutter_native_data_detector","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxcc3641%2Fflutter_native_data_detector","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxcc3641%2Fflutter_native_data_detector/lists"}