├── ios
├── Assets
│ └── .gitkeep
├── Classes
│ ├── FlutterContactsPlugin.h
│ ├── properties
│ │ ├── Note.swift
│ │ ├── Group.swift
│ │ ├── Account.swift
│ │ ├── Organization.swift
│ │ ├── Name.swift
│ │ ├── Website.swift
│ │ ├── Email.swift
│ │ └── Event.swift
│ └── FlutterContactsPlugin.m
├── .gitignore
└── flutter_contacts.podspec
├── doc
└── api
│ ├── categories.json
│ ├── static-assets
│ ├── favicon.png
│ ├── play_button.svg
│ ├── search.svg
│ ├── readme.md
│ └── github.css
│ ├── contact
│ ├── contact-library-sidebar.html
│ └── Contact-class-sidebar.html
│ ├── diacritics
│ └── diacritics-library-sidebar.html
│ ├── properties_name
│ ├── properties_name-library-sidebar.html
│ └── Name-class-sidebar.html
│ ├── properties_note
│ ├── properties_note-library-sidebar.html
│ └── Note-class-sidebar.html
│ ├── properties_group
│ ├── properties_group-library-sidebar.html
│ └── Group-class-sidebar.html
│ ├── properties_account
│ ├── properties_account-library-sidebar.html
│ └── Account-class-sidebar.html
│ ├── properties_organization
│ ├── properties_organization-library-sidebar.html
│ └── Organization-class-sidebar.html
│ ├── config
│ ├── config-library-sidebar.html
│ ├── VCardVersion-enum-sidebar.html
│ └── FlutterContactsConfig-class-sidebar.html
│ ├── properties_email
│ ├── properties_email-library-sidebar.html
│ ├── Email-class-sidebar.html
│ └── EmailLabel-enum-sidebar.html
│ ├── properties_event
│ ├── properties_event-library-sidebar.html
│ ├── Event-class-sidebar.html
│ └── EventLabel-enum-sidebar.html
│ ├── properties_phone
│ ├── properties_phone-library-sidebar.html
│ └── Phone-class-sidebar.html
│ ├── vcard
│ ├── vcard-library-sidebar.html
│ ├── Param-class-sidebar.html
│ └── VCardParser-class-sidebar.html
│ ├── properties_address
│ ├── properties_address-library-sidebar.html
│ ├── AddressLabel-enum-sidebar.html
│ └── Address-class-sidebar.html
│ ├── properties_website
│ ├── properties_website-library-sidebar.html
│ ├── Website-class-sidebar.html
│ └── WebsiteLabel-enum-sidebar.html
│ ├── properties_social_media
│ ├── properties_social_media-library-sidebar.html
│ └── SocialMedia-class-sidebar.html
│ └── flutter_contacts
│ ├── flutter_contacts-library-sidebar.html
│ └── FlutterContacts-class-sidebar.html
├── android
├── settings.gradle
├── src
│ └── main
│ │ ├── AndroidManifest.xml
│ │ └── kotlin
│ │ └── co
│ │ └── quis
│ │ └── flutter_contacts
│ │ ├── properties
│ │ ├── Note.kt
│ │ ├── Group.kt
│ │ ├── Website.kt
│ │ ├── Account.kt
│ │ ├── Email.kt
│ │ ├── Event.kt
│ │ ├── SocialMedia.kt
│ │ ├── Phone.kt
│ │ ├── Organization.kt
│ │ ├── Name.kt
│ │ └── Address.kt
│ │ ├── ContactChangeObserver.kt
│ │ └── Contact.kt
├── .gitignore
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── build.gradle
├── example_full
├── ios
│ ├── Flutter
│ │ ├── .last_build_id
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── AppFrameworkInfo.plist
│ ├── Runner
│ │ ├── Runner-Bridging-Header.h
│ │ ├── Assets.xcassets
│ │ │ ├── LaunchImage.imageset
│ │ │ │ ├── LaunchImage.png
│ │ │ │ ├── LaunchImage@2x.png
│ │ │ │ ├── LaunchImage@3x.png
│ │ │ │ ├── README.md
│ │ │ │ └── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ ├── Icon-App-20x20@1x.png
│ │ │ │ ├── Icon-App-20x20@2x.png
│ │ │ │ ├── Icon-App-20x20@3x.png
│ │ │ │ ├── Icon-App-29x29@1x.png
│ │ │ │ ├── Icon-App-29x29@2x.png
│ │ │ │ ├── Icon-App-29x29@3x.png
│ │ │ │ ├── Icon-App-40x40@1x.png
│ │ │ │ ├── Icon-App-40x40@2x.png
│ │ │ │ ├── Icon-App-40x40@3x.png
│ │ │ │ ├── Icon-App-60x60@2x.png
│ │ │ │ ├── Icon-App-60x60@3x.png
│ │ │ │ ├── Icon-App-76x76@1x.png
│ │ │ │ ├── Icon-App-76x76@2x.png
│ │ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ │ └── Contents.json
│ │ ├── AppDelegate.swift
│ │ ├── Base.lproj
│ │ │ ├── Main.storyboard
│ │ │ └── LaunchScreen.storyboard
│ │ └── Info.plist
│ ├── Runner.xcodeproj
│ │ └── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── .gitignore
│ ├── Podfile.lock
│ └── Podfile
├── README.md
├── android
│ ├── gradle.properties
│ ├── .gitignore
│ ├── app
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── res
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── values
│ │ │ │ │ │ └── styles.xml
│ │ │ │ │ └── drawable
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ ├── kotlin
│ │ │ │ │ └── co
│ │ │ │ │ │ └── quis
│ │ │ │ │ │ └── flutter_contacts_example
│ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── debug
│ │ │ │ └── AndroidManifest.xml
│ │ │ └── profile
│ │ │ │ └── AndroidManifest.xml
│ │ └── build.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ ├── build.gradle
│ └── settings.gradle
├── .metadata
├── pubspec.yaml
├── lib
│ ├── util
│ │ └── avatar.dart
│ ├── main.dart
│ └── pages
│ │ ├── form_components
│ │ ├── note_form.dart
│ │ ├── phone_form.dart
│ │ ├── email_form.dart
│ │ └── website_form.dart
│ │ └── groups_page.dart
└── .gitignore
├── example
├── ios
│ ├── Runner
│ │ ├── Runner-Bridging-Header.h
│ │ ├── Assets.xcassets
│ │ │ ├── LaunchImage.imageset
│ │ │ │ ├── LaunchImage.png
│ │ │ │ ├── LaunchImage@2x.png
│ │ │ │ ├── LaunchImage@3x.png
│ │ │ │ ├── README.md
│ │ │ │ └── Contents.json
│ │ │ └── AppIcon.appiconset
│ │ │ │ ├── Icon-App-20x20@1x.png
│ │ │ │ ├── Icon-App-20x20@2x.png
│ │ │ │ ├── Icon-App-20x20@3x.png
│ │ │ │ ├── Icon-App-29x29@1x.png
│ │ │ │ ├── Icon-App-29x29@2x.png
│ │ │ │ ├── Icon-App-29x29@3x.png
│ │ │ │ ├── Icon-App-40x40@1x.png
│ │ │ │ ├── Icon-App-40x40@2x.png
│ │ │ │ ├── Icon-App-40x40@3x.png
│ │ │ │ ├── Icon-App-60x60@2x.png
│ │ │ │ ├── Icon-App-60x60@3x.png
│ │ │ │ ├── Icon-App-76x76@1x.png
│ │ │ │ ├── Icon-App-76x76@2x.png
│ │ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ │ ├── Icon-App-83.5x83.5@2x.png
│ │ │ │ └── Contents.json
│ │ ├── AppDelegate.swift
│ │ ├── Base.lproj
│ │ │ ├── Main.storyboard
│ │ │ └── LaunchScreen.storyboard
│ │ └── Info.plist
│ ├── Flutter
│ │ ├── Debug.xcconfig
│ │ ├── Release.xcconfig
│ │ └── AppFrameworkInfo.plist
│ ├── Runner.xcodeproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata
│ │ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── WorkspaceSettings.xcsettings
│ │ │ └── IDEWorkspaceChecks.plist
│ ├── Podfile.lock
│ ├── .gitignore
│ └── Podfile
├── android
│ ├── gradle.properties
│ ├── app
│ │ ├── src
│ │ │ ├── main
│ │ │ │ ├── res
│ │ │ │ │ ├── mipmap-hdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-mdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── mipmap-xxxhdpi
│ │ │ │ │ │ └── ic_launcher.png
│ │ │ │ │ ├── drawable
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── drawable-v21
│ │ │ │ │ │ └── launch_background.xml
│ │ │ │ │ ├── values
│ │ │ │ │ │ └── styles.xml
│ │ │ │ │ └── values-night
│ │ │ │ │ │ └── styles.xml
│ │ │ │ ├── kotlin
│ │ │ │ │ └── co
│ │ │ │ │ │ └── quis
│ │ │ │ │ │ └── flutter_contacts_example
│ │ │ │ │ │ └── MainActivity.kt
│ │ │ │ └── AndroidManifest.xml
│ │ │ ├── debug
│ │ │ │ └── AndroidManifest.xml
│ │ │ └── profile
│ │ │ │ └── AndroidManifest.xml
│ │ └── build.gradle
│ ├── gradle
│ │ └── wrapper
│ │ │ └── gradle-wrapper.properties
│ ├── .gitignore
│ ├── build.gradle
│ └── settings.gradle
├── .metadata
├── pubspec.yaml
├── README.md
├── .gitignore
└── lib
│ └── main.dart
├── scripts
└── format.sh
├── .gitignore
├── analysis_options.yaml
├── .metadata
├── test
├── diacritics_test.dart
├── testdata
│ └── vcards
│ │ ├── bvcard.com.vcf
│ │ ├── qr-code-generator.com.vcf
│ │ ├── vcardmaker.com.vcf
│ │ ├── qr-code-generator.com.json
│ │ └── bvcard.com.json
└── contact_test.dart
├── lib
├── properties
│ ├── group.dart
│ ├── note.dart
│ ├── account.dart
│ ├── website.dart
│ └── organization.dart
└── config.dart
├── pubspec.yaml
└── LICENSE
/ios/Assets/.gitkeep:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/doc/api/categories.json:
--------------------------------------------------------------------------------
1 | []
2 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'flutter_contacts'
2 |
--------------------------------------------------------------------------------
/example_full/ios/Flutter/.last_build_id:
--------------------------------------------------------------------------------
1 | 822131e42e6e229d1550608792c18706
--------------------------------------------------------------------------------
/example/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/example_full/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
--------------------------------------------------------------------------------
/example_full/README.md:
--------------------------------------------------------------------------------
1 | # flutter_contacts_example
2 |
3 | Demonstrates how to use the flutter_contacts plugin.
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/doc/api/static-assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/doc/api/static-assets/favicon.png
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Classes/FlutterContactsPlugin.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface FlutterContactsPlugin : NSObject
4 | @end
5 |
--------------------------------------------------------------------------------
/scripts/format.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -ex
4 |
5 | dart format .
6 |
7 | swiftformat --swiftversion 5.2 .
8 |
9 | ktlint -F
10 |
--------------------------------------------------------------------------------
/example/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 | android.enableR8=true
5 |
--------------------------------------------------------------------------------
/example/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example_full/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example_full/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.enableR8=true
3 | android.useAndroidX=true
4 | android.enableJetifier=true
5 |
--------------------------------------------------------------------------------
/example_full/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/example_full/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 |
7 | build/
8 |
9 | pubspec.lock
10 |
11 | # IntelliJ related
12 | *.iml
13 | *.ipr
14 | *.iws
15 | .idea/
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example_full/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example_full/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example_full/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example_full/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/example_full/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:pedantic/analysis_options.yaml
2 |
3 | linter:
4 | rules:
5 | # set literals are not supported before dart 2.2
6 | prefer_collection_literals: false
7 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/QuisApp/flutter_contacts/HEAD/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/example/android/app/src/main/kotlin/co/quis/flutter_contacts_example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts_example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity : FlutterActivity()
6 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example_full/android/app/src/main/kotlin/co/quis/flutter_contacts_example/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts_example
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity : FlutterActivity()
6 |
--------------------------------------------------------------------------------
/example_full/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
6 |
--------------------------------------------------------------------------------
/doc/api/contact/contact-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | Contact
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
7 |
--------------------------------------------------------------------------------
/example_full/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
7 |
--------------------------------------------------------------------------------
/doc/api/diacritics/diacritics-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Functions
9 | removeDiacritics
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/doc/api/properties_name/properties_name-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | Name
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/doc/api/properties_note/properties_note-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | Note
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/doc/api/properties_group/properties_group-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | Group
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example_full/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/example_full/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/doc/api/properties_account/properties_account-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | Account
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/doc/api/static-assets/play_button.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example_full/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: f139b11009aeb8ed2a3a3aa8b0066e482709dde3
8 | channel: stable
9 |
10 | project_type: plugin
11 |
--------------------------------------------------------------------------------
/example/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: c1042314a94e693b4ea92404dab84852e9428442
8 | channel: master
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/example/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_contacts_example
2 | description: Demonstrates how to use the flutter_contacts plugin.
3 | publish_to: "none"
4 |
5 | environment:
6 | sdk: ">=2.12.0-0 <3.0.0"
7 |
8 | dependencies:
9 | flutter:
10 | sdk: flutter
11 | flutter_contacts:
12 | path: ../
13 |
14 | flutter:
15 | uses-material-design: true
16 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/co/quis/flutter_contacts/properties/Note.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts.properties
2 |
3 | data class Note(
4 | var note: String
5 | ) {
6 | companion object {
7 | fun fromMap(m: Map): Note = Note(m["note"] as String)
8 | }
9 |
10 | fun toMap(): Map = mapOf("note" to note)
11 | }
12 |
--------------------------------------------------------------------------------
/example_full/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: f139b11009aeb8ed2a3a3aa8b0066e482709dde3
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/doc/api/properties_organization/properties_organization-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | Organization
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/example/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example_full/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/example_full/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/example/android/build.gradle:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | rootProject.buildDir = '../build'
9 | subprojects {
10 | project.buildDir = "${rootProject.buildDir}/${project.name}"
11 | }
12 | subprojects {
13 | project.evaluationDependsOn(':app')
14 | }
15 |
16 | tasks.register("clean", Delete) {
17 | delete rootProject.buildDir
18 | }
19 |
--------------------------------------------------------------------------------
/example_full/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
--------------------------------------------------------------------------------
/example_full/android/build.gradle:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | rootProject.buildDir = '../build'
9 | subprojects {
10 | project.buildDir = "${rootProject.buildDir}/${project.name}"
11 | }
12 | subprojects {
13 | project.evaluationDependsOn(':app')
14 | }
15 |
16 | tasks.register("clean", Delete) {
17 | delete rootProject.buildDir
18 | }
19 |
--------------------------------------------------------------------------------
/test/diacritics_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_contacts/diacritics.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 |
4 | void main() {
5 | test('remove diacritics', () {
6 | expect(removeDiacritics('Édouard'), 'Edouard');
7 | expect(removeDiacritics("Être contesté, c'est être constaté"),
8 | "Etre conteste, c'est etre constate");
9 | expect(removeDiacritics('ÇƑⓃꝘNJǩꜩß'), 'CFNQNJktzss');
10 | });
11 | }
12 |
--------------------------------------------------------------------------------
/doc/api/config/config-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | FlutterContactsConfig
4 |
5 | Enums
6 | VCardVersion
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/example_full/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_contacts_example
2 | description: Demonstrates how to use the flutter_contacts plugin.
3 | publish_to: "none"
4 |
5 | environment:
6 | sdk: ">=2.12.0 <3.0.0"
7 |
8 | dependencies:
9 | flutter:
10 | sdk: flutter
11 | after_layout: ^1.2.0
12 | flutter_contacts:
13 | path: ../
14 | image_picker: ^1.1.1
15 | pretty_json: ^2.0.0
16 | prompt_dialog: ^1.0.16
17 |
18 | flutter:
19 | uses-material-design: true
20 |
--------------------------------------------------------------------------------
/doc/api/static-assets/search.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/ios/Classes/properties/Note.swift:
--------------------------------------------------------------------------------
1 | import Contacts
2 |
3 | @available(iOS 9.0, *)
4 | struct Note {
5 | var note: String
6 |
7 | init(fromMap m: [String: Any]) {
8 | note = m["note"] as! String
9 | }
10 |
11 | init(fromContact c: CNContact) {
12 | note = c.note
13 | }
14 |
15 | func toMap() -> [String: Any] { [
16 | "note": note,
17 | ]
18 | }
19 |
20 | func addTo(_ c: CNMutableContact) {
21 | c.note = note
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/co/quis/flutter_contacts/properties/Group.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts.properties
2 |
3 | data class Group(
4 | var id: String,
5 | var name: String
6 | ) {
7 |
8 | companion object {
9 | fun fromMap(m: Map): Group = Group(
10 | m["id"] as String,
11 | m["name"] as String
12 | )
13 | }
14 |
15 | fun toMap(): Map = mapOf(
16 | "id" to id,
17 | "name" to name
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/doc/api/properties_email/properties_email-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | Email
4 |
5 | Enums
6 | EmailLabel
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/doc/api/properties_event/properties_event-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | Event
4 |
5 | Enums
6 | EventLabel
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/doc/api/properties_phone/properties_phone-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | Phone
4 |
5 | Enums
6 | PhoneLabel
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/doc/api/vcard/vcard-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | Param
4 | VCardParser
5 |
6 |
7 |
8 |
9 |
10 |
11 | Functions
12 | vCardEncode
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/example/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test/testdata/vcards/bvcard.com.vcf:
--------------------------------------------------------------------------------
1 | BEGIN:VCARD
2 | VERSION:4.0
3 | PRODID:-//BBros.us llc//bvCard.com//EN
4 | N:Smith;John;J;Dr;Jr
5 | FN:Dr John J Smith, Jr
6 | EMAIL:johnsmith@example.com
7 | ORG:FlutterContacts
8 | TEL:555-123-4567
9 | TEL;type=FAX:555-999-9999
10 | URL;type=pref:http://www.example.com
11 | ADR:;123 Main St;Apt 3;Portland;TN;37148;USA
12 | URL:http://linked.com/profile
13 | URL:http://facebook.com/profile
14 | URL:http://google.plus/profile
15 | URL:http://twitter.com/profile
16 | END:VCARD
--------------------------------------------------------------------------------
/example_full/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Classes/properties/Group.swift:
--------------------------------------------------------------------------------
1 | import Contacts
2 |
3 | @available(iOS 9.0, *)
4 | struct Group {
5 | var id: String
6 | var name: String
7 |
8 | init(fromMap m: [String: Any]) {
9 | id = m["id"] as! String
10 | name = m["name"] as! String
11 | }
12 |
13 | init(fromGroup g: CNGroup) {
14 | id = g.identifier
15 | name = g.name
16 | }
17 |
18 | func toMap() -> [String: Any] { [
19 | "id": id,
20 | "name": name,
21 | ]
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example_full/lib/util/avatar.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_contacts/contact.dart';
3 |
4 | Widget avatar(Contact contact,
5 | [double radius = 48.0, IconData defaultIcon = Icons.person]) {
6 | if (contact.photoOrThumbnail != null) {
7 | return CircleAvatar(
8 | backgroundImage: MemoryImage(contact.photoOrThumbnail!),
9 | radius: radius,
10 | );
11 | }
12 | return CircleAvatar(
13 | radius: radius,
14 | child: Icon(defaultIcon),
15 | );
16 | }
17 |
--------------------------------------------------------------------------------
/test/testdata/vcards/qr-code-generator.com.vcf:
--------------------------------------------------------------------------------
1 | BEGIN:VCARD
2 | VERSION:3.0
3 | PRODID:-//QR-Code-Generator.com//EN
4 | N:Smith;John;;;
5 | FN:John Smith
6 | ORG:FlutterContacts;
7 | TITLE:Software Engineer
8 | EMAIL;TYPE=PREF,INTERNET:johnsmith@example.com
9 | TEL;TYPE=CELL,voice:555-123-4567
10 | TEL;TYPE=WORK,voice:555-999-9999
11 | TEL;TYPE=WORK,fax:555-888-8888
12 | ADR;TYPE=WORK;type=pref:;; ;;;;
13 | URL:www.example.com
14 |
15 | NOTE:Some notes line 1 Some notes line 2
16 |
17 | REV:2008-04-24T19:52:43Z
18 | END:VCARD
19 |
--------------------------------------------------------------------------------
/doc/api/properties_address/properties_address-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | Address
4 |
5 | Enums
6 | AddressLabel
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/doc/api/properties_website/properties_website-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | Website
4 |
5 | Enums
6 | WebsiteLabel
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example_full/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vagrant/
3 | .sconsign.dblite
4 | .svn/
5 |
6 | .DS_Store
7 | *.swp
8 | profile
9 |
10 | DerivedData/
11 | build/
12 | GeneratedPluginRegistrant.h
13 | GeneratedPluginRegistrant.m
14 |
15 | .generated/
16 |
17 | *.pbxuser
18 | *.mode1v3
19 | *.mode2v3
20 | *.perspectivev3
21 |
22 | !default.pbxuser
23 | !default.mode1v3
24 | !default.mode2v3
25 | !default.perspectivev3
26 |
27 | xcuserdata
28 |
29 | *.moved-aside
30 |
31 | *.pyc
32 | *sync/
33 | Icon?
34 | .tags*
35 |
36 | /Flutter/Generated.xcconfig
37 | /Flutter/flutter_export_environment.sh
--------------------------------------------------------------------------------
/android/src/main/kotlin/co/quis/flutter_contacts/ContactChangeObserver.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts
2 |
3 | import android.database.ContentObserver
4 | import android.os.Handler
5 | import io.flutter.plugin.common.EventChannel
6 |
7 | class ContactChangeObserver : ContentObserver {
8 | val _sink: EventChannel.EventSink
9 |
10 | constructor(handler: Handler, sink: EventChannel.EventSink) : super(handler) {
11 | this._sink = sink
12 | }
13 |
14 | override fun onChange(selfChange: Boolean) {
15 | _sink?.success(selfChange)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/lib/properties/group.dart:
--------------------------------------------------------------------------------
1 | /// Group (called label on Android and group on iOS).
2 | ///
3 | /// A contact may belong to zero, one or more groups.
4 | class Group {
5 | String id;
6 | String name;
7 |
8 | Group(this.id, this.name);
9 |
10 | factory Group.fromJson(Map json) => Group(
11 | (json['id'] as String?) ?? '',
12 | (json['name'] as String?) ?? '',
13 | );
14 |
15 | Map toJson() => {
16 | 'id': id,
17 | 'name': name,
18 | };
19 |
20 | @override
21 | String toString() => 'Group(id=$id, name=$name)';
22 | }
23 |
--------------------------------------------------------------------------------
/example/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - flutter_contacts (0.0.1):
4 | - Flutter
5 |
6 | DEPENDENCIES:
7 | - Flutter (from `Flutter`)
8 | - flutter_contacts (from `.symlinks/plugins/flutter_contacts/ios`)
9 |
10 | EXTERNAL SOURCES:
11 | Flutter:
12 | :path: Flutter
13 | flutter_contacts:
14 | :path: ".symlinks/plugins/flutter_contacts/ios"
15 |
16 | SPEC CHECKSUMS:
17 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
18 | flutter_contacts: edb1c5ce76aa433e20e6cb14c615f4c0b66e0983
19 |
20 | PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011
21 |
22 | COCOAPODS: 1.15.2
23 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # flutter_contacts_example
2 |
3 | Demonstrates how to use the flutter_contacts plugin.
4 |
5 | ## Getting Started
6 |
7 | This project is a starting point for a Flutter application.
8 |
9 | A few resources to get you started if this is your first Flutter project:
10 |
11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
13 |
14 | For help getting started with Flutter, view our
15 | [online documentation](https://flutter.dev/docs), which offers tutorials,
16 | samples, guidance on mobile development, and a full API reference.
17 |
--------------------------------------------------------------------------------
/example/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/ios/Classes/FlutterContactsPlugin.m:
--------------------------------------------------------------------------------
1 | #import "FlutterContactsPlugin.h"
2 | #if __has_include()
3 | #import
4 | #else
5 | // Support project import fallback if the generated compatibility header
6 | // is not copied when this plugin is created as a library.
7 | // https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816
8 | #import "flutter_contacts-Swift.h"
9 | #endif
10 |
11 | @implementation FlutterContactsPlugin
12 | + (void)registerWithRegistrar:(NSObject*)registrar {
13 | [SwiftFlutterContactsPlugin registerWithRegistrar:registrar];
14 | }
15 | @end
16 |
--------------------------------------------------------------------------------
/example_full/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/co/quis/flutter_contacts/properties/Website.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts.properties
2 |
3 | data class Website(
4 | var url: String,
5 | // one of: blog, ftp, home, homepage, profile, school, work, other, custom
6 | var label: String = "homepage",
7 | var customLabel: String = ""
8 | ) {
9 | companion object {
10 | fun fromMap(m: Map): Website = Website(
11 | m["url"] as String,
12 | m["label"] as String,
13 | m["customLabel"] as String
14 | )
15 | }
16 |
17 | fun toMap(): Map = mapOf(
18 | "url" to url,
19 | "label" to label,
20 | "customLabel" to customLabel
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/co/quis/flutter_contacts/properties/Account.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts.properties
2 |
3 | data class Account(
4 | var rawId: String,
5 | var type: String,
6 | var name: String,
7 | var mimetypes: List = listOf()
8 | ) {
9 |
10 | companion object {
11 | fun fromMap(m: Map): Account = Account(
12 | m["rawId"] as String,
13 | m["type"] as String,
14 | m["name"] as String,
15 | m["mimetypes"] as List
16 | )
17 | }
18 |
19 | fun toMap(): Map = mapOf(
20 | "rawId" to rawId,
21 | "type" to type,
22 | "name" to name,
23 | "mimetypes" to mimetypes
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_contacts
2 | description: Flutter plugin to read, create, update, delete and observe native contacts on Android and iOS, with group support, vCard support, and contact permission handling
3 | version: 1.1.9+2
4 | homepage: https://github.com/QuisApp/flutter_contacts
5 |
6 | environment:
7 | sdk: ">=2.12.0 <4.0.0"
8 | flutter: ">=1.12.0"
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 |
14 | dev_dependencies:
15 | flutter_test:
16 | sdk: flutter
17 | pedantic: ^1.11.0
18 | test: ^1.16.5
19 |
20 | flutter:
21 | plugin:
22 | platforms:
23 | android:
24 | package: co.quis.flutter_contacts
25 | pluginClass: FlutterContactsPlugin
26 | ios:
27 | pluginClass: FlutterContactsPlugin
28 |
--------------------------------------------------------------------------------
/example/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }()
9 |
10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
11 |
12 | repositories {
13 | google()
14 | mavenCentral()
15 | gradlePluginPortal()
16 | }
17 | }
18 |
19 | plugins {
20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0"
21 | id "com.android.application" version "8.3.1" apply false
22 | }
23 |
24 | include ":app"
25 |
--------------------------------------------------------------------------------
/example_full/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | def flutterSdkPath = {
3 | def properties = new Properties()
4 | file("local.properties").withInputStream { properties.load(it) }
5 | def flutterSdkPath = properties.getProperty("flutter.sdk")
6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
7 | return flutterSdkPath
8 | }()
9 |
10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
11 |
12 | repositories {
13 | google()
14 | mavenCentral()
15 | gradlePluginPortal()
16 | }
17 | }
18 |
19 | plugins {
20 | id "dev.flutter.flutter-plugin-loader" version "1.0.0"
21 | id "com.android.application" version "8.3.1" apply false
22 | }
23 |
24 | include ":app"
25 |
--------------------------------------------------------------------------------
/example_full/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | /build/
32 |
33 | # Web related
34 | lib/generated_plugin_registrant.dart
35 |
36 | # Exceptions to above rules.
37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
38 |
39 | pubspec.lock
--------------------------------------------------------------------------------
/android/src/main/kotlin/co/quis/flutter_contacts/properties/Email.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts.properties
2 |
3 | data class Email(
4 | var address: String,
5 | // one of: home, iCloud, mobile, school, work, other, custom
6 | var label: String = "home",
7 | var customLabel: String = "",
8 | var isPrimary: Boolean = false
9 | ) {
10 | companion object {
11 | fun fromMap(m: Map): Email = Email(
12 | m["address"] as String,
13 | m["label"] as String,
14 | m["customLabel"] as String,
15 | m["isPrimary"] as Boolean
16 | )
17 | }
18 |
19 | fun toMap(): Map = mapOf(
20 | "address" to address,
21 | "label" to label,
22 | "customLabel" to customLabel,
23 | "isPrimary" to isPrimary
24 | )
25 | }
26 |
--------------------------------------------------------------------------------
/doc/api/properties_social_media/properties_social_media-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | SocialMedia
4 |
5 | Enums
6 | SocialMediaLabel
7 |
8 |
9 |
10 |
11 | Properties
12 | lowerCaseStringToSocialMediaLabel
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/doc/api/static-assets/readme.md:
--------------------------------------------------------------------------------
1 | # Dart documentation generator
2 |
3 | This directory includes static sources used by the Dart documentation generator
4 | through the `dart doc` command.
5 |
6 | To learn more about generating and viewing the generated documentation,
7 | check out the [`dart doc` documentation][].
8 |
9 | [`dart doc` documentation]: https://dart.dev/tools/dart-doc
10 |
11 | ## Third-party resources
12 |
13 | ## highlight.js
14 |
15 | Generated from https://highlightjs.org/download/ on 2021-07-13.
16 |
17 | **License:** https://github.com/highlightjs/highlight.js/blob/main/LICENSE
18 |
19 | **Included languages:**
20 |
21 | * bash
22 | * c
23 | * css
24 | * dart
25 | * diff
26 | * html, xml
27 | * java
28 | * javascript
29 | * json
30 | * kotlin
31 | * markdown
32 | * objective-c
33 | * plaintext
34 | * shell
35 | * swift
36 | * yaml
37 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/co/quis/flutter_contacts/properties/Event.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts.properties
2 |
3 | data class Event(
4 | var year: Int?,
5 | var month: Int,
6 | var day: Int,
7 | // one of: anniversary, birthday, other, custom
8 | var label: String = "birthday",
9 | var customLabel: String = ""
10 | ) {
11 | companion object {
12 | fun fromMap(m: Map): Event = Event(
13 | m["year"] as Int?,
14 | m["month"] as Int,
15 | m["day"] as Int,
16 | m["label"] as String,
17 | m["customLabel"] as String
18 | )
19 | }
20 |
21 | fun toMap(): Map = mapOf(
22 | "year" to year,
23 | "month" to month,
24 | "day" to day,
25 | "label" to label,
26 | "customLabel" to customLabel
27 | )
28 | }
29 |
--------------------------------------------------------------------------------
/example_full/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import 'pages/contact_list_page.dart';
4 | import 'pages/contact_page.dart';
5 | import 'pages/edit_contact_page.dart';
6 | import 'pages/groups_page.dart';
7 |
8 | void main() => runApp(FlutterContactsExample());
9 |
10 | class FlutterContactsExample extends StatelessWidget {
11 | @override
12 | Widget build(BuildContext context) {
13 | return MaterialApp(
14 | title: 'flutter_contacts_example',
15 | theme: ThemeData(
16 | primarySwatch: Colors.blue,
17 | ),
18 | initialRoute: '/contactList',
19 | routes: {
20 | '/contactList': (context) => ContactListPage(),
21 | '/contact': (context) => ContactPage(),
22 | '/editContact': (context) => EditContactPage(),
23 | '/groups': (context) => GroupsPage(),
24 | },
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/example_full/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - Flutter (1.0.0)
3 | - flutter_contacts (0.0.1):
4 | - Flutter
5 | - image_picker_ios (0.0.1):
6 | - Flutter
7 |
8 | DEPENDENCIES:
9 | - Flutter (from `Flutter`)
10 | - flutter_contacts (from `.symlinks/plugins/flutter_contacts/ios`)
11 | - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
12 |
13 | EXTERNAL SOURCES:
14 | Flutter:
15 | :path: Flutter
16 | flutter_contacts:
17 | :path: ".symlinks/plugins/flutter_contacts/ios"
18 | image_picker_ios:
19 | :path: ".symlinks/plugins/image_picker_ios/ios"
20 |
21 | SPEC CHECKSUMS:
22 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
23 | flutter_contacts: edb1c5ce76aa433e20e6cb14c615f4c0b66e0983
24 | image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
25 |
26 | PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011
27 |
28 | COCOAPODS: 1.15.2
29 |
--------------------------------------------------------------------------------
/example/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 12.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example_full/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 12.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
43 | # Android Studio will place build artifacts here
44 | /android/app/debug
45 | /android/app/profile
46 | /android/app/release
47 |
--------------------------------------------------------------------------------
/ios/flutter_contacts.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
3 | # Run `pod lib lint flutter_contacts.podspec' to validate before publishing.
4 | #
5 | Pod::Spec.new do |s|
6 | s.name = 'flutter_contacts'
7 | s.version = '0.0.1'
8 | s.summary = 'A new flutter plugin project.'
9 | s.description = <<-DESC
10 | A new flutter plugin project.
11 | DESC
12 | s.homepage = 'http://example.com'
13 | s.license = { :file => '../LICENSE' }
14 | s.author = { 'Your Company' => 'email@example.com' }
15 | s.source = { :path => '.' }
16 | s.source_files = 'Classes/**/*'
17 | s.dependency 'Flutter'
18 | s.platform = :ios, '8.0'
19 |
20 | # Flutter.framework does not contain a i386 slice. Only x86_64 simulators are supported.
21 | s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
22 | s.swift_version = '5.0'
23 | end
24 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/co/quis/flutter_contacts/properties/SocialMedia.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts.properties
2 |
3 | data class SocialMedia(
4 | var userName: String,
5 | // one of: aim, baiduTieba, discord, facebook, flickr, gaduGadu, gameCenter,
6 | // googleTalk, icq, instagram, jabber, line, linkedIn, medium, messenger, msn,
7 | // mySpace, netmeeting, pinterest, qqchat, qzone, reddit, sinaWeibo, skype,
8 | // snapchat, telegram, tencentWeibo, tikTok, tumblr, twitter, viber, wechat,
9 | // whatsapp, yahoo, yelp, youtube, zoom, other, custom
10 | var label: String = "other",
11 | var customLabel: String = ""
12 | ) {
13 | companion object {
14 | fun fromMap(m: Map): SocialMedia = SocialMedia(
15 | m["userName"] as String,
16 | m["label"] as String,
17 | m["customLabel"] as String
18 | )
19 | }
20 |
21 | fun toMap(): Map = mapOf(
22 | "userName" to userName,
23 | "label" to label,
24 | "customLabel" to customLabel
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/ios/Classes/properties/Account.swift:
--------------------------------------------------------------------------------
1 | import Contacts
2 |
3 | @available(iOS 9.0, *)
4 | struct Account {
5 | var rawId: String
6 | // local, exchange, cardDAV or unassigned
7 | var type: String
8 | var name: String
9 |
10 | init(fromMap m: [String: Any]) {
11 | rawId = m["rawId"] as! String
12 | type = m["type"] as! String
13 | name = m["name"] as! String
14 | }
15 |
16 | init(fromContainer c: CNContainer) {
17 | rawId = c.identifier
18 | name = c.name
19 | switch c.type {
20 | case .local:
21 | type = "local"
22 | case .exchange:
23 | type = "exchange"
24 | case .cardDAV:
25 | type = "cardDAV"
26 | case .unassigned:
27 | type = "unassigned"
28 | default:
29 | type = "unassigned"
30 | }
31 | }
32 |
33 | func toMap() -> [String: Any] { [
34 | "rawId": rawId,
35 | "type": type,
36 | "name": name,
37 | "mimetypes": [String](), // not available on iOS
38 | ]
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/example/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/co/quis/flutter_contacts/properties/Phone.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts.properties
2 |
3 | data class Phone(
4 | var number: String,
5 | var normalizedNumber: String,
6 | // one of: assistant, callback, car, companyMain, faxHome, faxOther, faxWork, home,
7 | // iPhone, isdn, main, mms, mobile, pager, radio, school, telex, ttyTtd, work,
8 | // workMobile, workPager, other, custom
9 | var label: String = "mobile",
10 | var customLabel: String,
11 | var isPrimary: Boolean = false
12 | ) {
13 | companion object {
14 | fun fromMap(m: Map): Phone = Phone(
15 | m["number"] as String,
16 | m["normalizedNumber"] as String,
17 | m["label"] as String,
18 | m["customLabel"] as String,
19 | m["isPrimary"] as Boolean
20 | )
21 | }
22 |
23 | fun toMap(): Map = mapOf(
24 | "number" to number,
25 | "normalizedNumber" to normalizedNumber,
26 | "label" to label,
27 | "customLabel" to customLabel,
28 | "isPrimary" to isPrimary
29 | )
30 | }
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Joachim Valente
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/co/quis/flutter_contacts/properties/Organization.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts.properties
2 |
3 | data class Organization(
4 | var company: String = "",
5 | var title: String = "",
6 | var department: String = "",
7 | var jobDescription: String = "",
8 | var symbol: String = "",
9 | var phoneticName: String = "",
10 | var officeLocation: String = ""
11 | ) {
12 | companion object {
13 | fun fromMap(m: Map): Organization = Organization(
14 | m["company"] as String,
15 | m["title"] as String,
16 | m["department"] as String,
17 | m["jobDescription"] as String,
18 | m["symbol"] as String,
19 | m["phoneticName"] as String,
20 | m["officeLocation"] as String
21 | )
22 | }
23 |
24 | fun toMap(): Map = mapOf(
25 | "company" to company,
26 | "title" to title,
27 | "department" to department,
28 | "jobDescription" to jobDescription,
29 | "symbol" to symbol,
30 | "phoneticName" to phoneticName,
31 | "officeLocation" to officeLocation
32 | )
33 | }
34 |
--------------------------------------------------------------------------------
/lib/properties/note.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_contacts/vcard.dart';
2 |
3 | /// Note, i.e. a free-form string about the contact.
4 | ///
5 | /// Android allows multiple notes per contact, while iOS supports only one.
6 | ///
7 | /// On iOS13+ notes are no longer supported, as you need to request entitlement
8 | /// from Apple to use it in your app. See:
9 | /// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_contacts_notes
10 | class Note {
11 | /// Note content.
12 | String note;
13 |
14 | Note(this.note);
15 |
16 | factory Note.fromJson(Map json) =>
17 | Note((json['note'] as String?) ?? '');
18 |
19 | Map toJson() => {'note': note};
20 |
21 | @override
22 | int get hashCode => note.hashCode;
23 |
24 | @override
25 | bool operator ==(Object o) => o is Note && o.note == note;
26 |
27 | @override
28 | String toString() => 'Note(note=$note)';
29 |
30 | List toVCard() {
31 | // NOTE (V3): https://tools.ietf.org/html/rfc2426#section-3.6.2
32 | // NOTE (V4): https://tools.ietf.org/html/rfc6350#section-6.7.2
33 | return ['NOTE:${vCardEncode(note)}'];
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'co.quis.flutter_contacts'
2 | version '1.0-SNAPSHOT'
3 |
4 | buildscript {
5 | ext.kotlin_version = '1.7.22'
6 | repositories {
7 | google()
8 | mavenCentral()
9 | }
10 |
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:8.3.1'
13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14 | }
15 | }
16 |
17 | rootProject.allprojects {
18 | repositories {
19 | google()
20 | mavenCentral()
21 | }
22 | }
23 |
24 | apply plugin: 'com.android.library'
25 | apply plugin: 'kotlin-android'
26 |
27 | android {
28 | compileSdk 34
29 |
30 | namespace 'co.quis.flutter_contacts'
31 |
32 | compileOptions {
33 | sourceCompatibility JavaVersion.VERSION_17
34 | targetCompatibility JavaVersion.VERSION_17
35 | }
36 |
37 | kotlinOptions {
38 | jvmTarget = 17
39 | }
40 |
41 | defaultConfig {
42 | minSdk 19
43 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
44 | }
45 | lintOptions {
46 | disable 'InvalidPackage'
47 | }
48 |
49 | dependencies {
50 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/co/quis/flutter_contacts/properties/Name.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts.properties
2 |
3 | data class Name(
4 | var first: String = "",
5 | var last: String = "",
6 | var middle: String = "",
7 | var prefix: String = "",
8 | var suffix: String = "",
9 | var nickname: String = "",
10 | var firstPhonetic: String = "",
11 | var lastPhonetic: String = "",
12 | var middlePhonetic: String = ""
13 | ) {
14 | companion object {
15 | fun fromMap(m: Map): Name = Name(
16 | m["first"] as String,
17 | m["last"] as String,
18 | m["middle"] as String,
19 | m["prefix"] as String,
20 | m["suffix"] as String,
21 | m["nickname"] as String,
22 | m["firstPhonetic"] as String,
23 | m["lastPhonetic"] as String,
24 | m["middlePhonetic"] as String
25 | )
26 | }
27 |
28 | fun toMap(): Map = mapOf(
29 | "first" to first,
30 | "last" to last,
31 | "middle" to middle,
32 | "prefix" to prefix,
33 | "suffix" to suffix,
34 | "nickname" to nickname,
35 | "firstPhonetic" to firstPhonetic,
36 | "lastPhonetic" to lastPhonetic,
37 | "middlePhonetic" to middlePhonetic
38 | )
39 | }
40 |
--------------------------------------------------------------------------------
/doc/api/vcard/Param-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | Param
5 |
6 |
7 |
8 |
9 | Properties
10 |
11 | hashCode
12 | key
13 | runtimeType
14 | value
15 |
16 | Methods
17 | noSuchMethod
18 | toString
19 |
20 | Operators
21 | operator ==
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/lib/properties/account.dart:
--------------------------------------------------------------------------------
1 | /// Account information, which is exposed for information and debugging purposes
2 | /// and should be ignored in most cases.
3 | ///
4 | /// On Android this is the raw account, and there can be several accounts per
5 | /// unified contact (for example one for Gmail, one for Skype and one for
6 | /// WhatsApp). On iOS it is called container, and there can be only one
7 | /// container per contact.
8 | class Account {
9 | /// Raw account ID.
10 | String rawId;
11 |
12 | /// Account type, e.g. com.google or com.facebook.messenger.
13 | String type;
14 |
15 | /// Account name, e.g. john.doe@gmail.com.
16 | String name;
17 |
18 | /// Android mimetypes provided by this account.
19 | List mimetypes;
20 |
21 | Account(this.rawId, this.type, this.name, this.mimetypes);
22 |
23 | factory Account.fromJson(Map json) => Account(
24 | (json['rawId'] as String?) ?? '',
25 | (json['type'] as String?) ?? '',
26 | (json['name'] as String?) ?? '',
27 | (json['mimetypes'] as List?)?.map((e) => e as String).toList() ?? [],
28 | );
29 |
30 | Map toJson() => {
31 | 'rawId': rawId,
32 | 'type': type,
33 | 'name': name,
34 | 'mimetypes': mimetypes,
35 | };
36 |
37 | @override
38 | String toString() =>
39 | 'Account(rawId=$rawId, type=$type, name=$name, mimetypes=$mimetypes)';
40 | }
41 |
--------------------------------------------------------------------------------
/doc/api/properties_note/Note-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | Note
5 | fromJson
6 |
7 |
8 |
9 |
10 | Properties
11 |
12 | hashCode
13 | note
14 | runtimeType
15 |
16 | Methods
17 | noSuchMethod
18 | toJson
19 | toString
20 | toVCard
21 |
22 | Operators
23 | operator ==
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/example/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '12.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/example_full/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '12.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | flutter_additional_ios_build_settings(target)
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/doc/api/properties_group/Group-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | Group
5 | fromJson
6 |
7 |
8 |
9 |
10 | Properties
11 |
12 | hashCode
13 | id
14 | name
15 | runtimeType
16 |
17 | Methods
18 | noSuchMethod
19 | toJson
20 | toString
21 |
22 | Operators
23 | operator ==
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/doc/api/vcard/VCardParser-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | VCardParser
5 |
6 |
7 |
8 |
9 | Properties
10 |
11 | hashCode
12 | runtimeType
13 |
14 | Methods
15 | decode
16 | encode
17 | noSuchMethod
18 | parse
19 | toString
20 | unfold
21 |
22 | Operators
23 | operator ==
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/example_full/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/doc/api/static-assets/github.css:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | github.com style (c) Vasily Polovnyov
4 |
5 | */
6 |
7 | .hljs {
8 | display: block;
9 | overflow-x: auto;
10 | padding: 0.5em;
11 | color: #333;
12 | background: #f8f8f8;
13 | }
14 |
15 | .hljs-comment,
16 | .hljs-quote {
17 | color: #998;
18 | font-style: italic;
19 | }
20 |
21 | .hljs-keyword,
22 | .hljs-selector-tag,
23 | .hljs-subst {
24 | color: #333;
25 | font-weight: bold;
26 | }
27 |
28 | .hljs-number,
29 | .hljs-literal,
30 | .hljs-variable,
31 | .hljs-template-variable,
32 | .hljs-tag .hljs-attr {
33 | color: #008080;
34 | }
35 |
36 | .hljs-string,
37 | .hljs-doctag {
38 | color: #d14;
39 | }
40 |
41 | .hljs-title,
42 | .hljs-section,
43 | .hljs-selector-id {
44 | color: #900;
45 | font-weight: bold;
46 | }
47 |
48 | .hljs-subst {
49 | font-weight: normal;
50 | }
51 |
52 | .hljs-type,
53 | .hljs-class .hljs-title {
54 | color: #458;
55 | font-weight: bold;
56 | }
57 |
58 | .hljs-tag,
59 | .hljs-name,
60 | .hljs-attribute {
61 | color: #000080;
62 | font-weight: normal;
63 | }
64 |
65 | .hljs-regexp,
66 | .hljs-link {
67 | color: #009926;
68 | }
69 |
70 | .hljs-symbol,
71 | .hljs-bullet {
72 | color: #990073;
73 | }
74 |
75 | .hljs-built_in,
76 | .hljs-builtin-name {
77 | color: #0086b3;
78 | }
79 |
80 | .hljs-meta {
81 | color: #999;
82 | font-weight: bold;
83 | }
84 |
85 | .hljs-deletion {
86 | background: #fdd;
87 | }
88 |
89 | .hljs-addition {
90 | background: #dfd;
91 | }
92 |
93 | .hljs-emphasis {
94 | font-style: italic;
95 | }
96 |
97 | .hljs-strong {
98 | font-weight: bold;
99 | }
100 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/example_full/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Classes/properties/Organization.swift:
--------------------------------------------------------------------------------
1 | import Contacts
2 |
3 | @available(iOS 9.0, *)
4 | struct Organization {
5 | var company: String = ""
6 | var title: String = ""
7 | var department: String = ""
8 | var jobDescription: String = ""
9 | var symbol: String = ""
10 | var phoneticName: String = ""
11 | var officeLocation: String = ""
12 |
13 | init(fromMap m: [String: Any]) {
14 | company = m["company"] as! String
15 | title = m["title"] as! String
16 | department = m["department"] as! String
17 | jobDescription = m["jobDescription"] as! String
18 | symbol = m["symbol"] as! String
19 | phoneticName = m["phoneticName"] as! String
20 | officeLocation = m["officeLocation"] as! String
21 | }
22 |
23 | init(fromContact c: CNContact) {
24 | company = c.organizationName
25 | title = c.jobTitle
26 | department = c.departmentName
27 | // jobDescription, symbol and officeLocation not supported
28 | if #available(iOS 10, *) {
29 | phoneticName = c.phoneticOrganizationName
30 | }
31 | }
32 |
33 | func toMap() -> [String: Any] { [
34 | "company": company,
35 | "title": title,
36 | "department": department,
37 | "jobDescription": jobDescription,
38 | "symbol": symbol,
39 | "phoneticName": phoneticName,
40 | "officeLocation": officeLocation,
41 | ]
42 | }
43 |
44 | func addTo(_ c: CNMutableContact) {
45 | c.organizationName = company
46 | c.jobTitle = title
47 | c.departmentName = department
48 | if #available(iOS 10, *) {
49 | c.phoneticOrganizationName = phoneticName
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/doc/api/properties_website/Website-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | Website
5 | fromJson
6 |
7 |
8 |
9 |
10 | Properties
11 |
12 | customLabel
13 | hashCode
14 | label
15 | runtimeType
16 | url
17 |
18 | Methods
19 | noSuchMethod
20 | toJson
21 | toString
22 | toVCard
23 |
24 | Operators
25 | operator ==
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/doc/api/properties_email/Email-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | Email
5 | fromJson
6 |
7 |
8 |
9 |
10 | Properties
11 |
12 | address
13 | customLabel
14 | hashCode
15 | isPrimary
16 | label
17 | runtimeType
18 |
19 | Methods
20 | noSuchMethod
21 | toJson
22 | toString
23 | toVCard
24 |
25 | Operators
26 | operator ==
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/co/quis/flutter_contacts/properties/Address.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts.properties
2 |
3 | data class Address(
4 | var address: String,
5 | // one of: home, school, work, other, custom
6 | var label: String = "home",
7 | var customLabel: String = "",
8 | var street: String = "",
9 | var pobox: String = "",
10 | var neighborhood: String = "",
11 | var city: String = "",
12 | var state: String = "",
13 | var postalCode: String = "",
14 | var country: String = "",
15 | var isoCountry: String = "",
16 | var subAdminArea: String = "",
17 | var subLocality: String = ""
18 | ) {
19 |
20 | companion object {
21 | fun fromMap(m: Map): Address = Address(
22 | m["address"] as String,
23 | m["label"] as String,
24 | m["customLabel"] as String,
25 | m["street"] as String,
26 | m["pobox"] as String,
27 | m["neighborhood"] as String,
28 | m["city"] as String,
29 | m["state"] as String,
30 | m["postalCode"] as String,
31 | m["country"] as String,
32 | m["isoCountry"] as String,
33 | m["subAdminArea"] as String,
34 | m["subLocality"] as String
35 | )
36 | }
37 |
38 | fun toMap(): Map = mapOf(
39 | "address" to address,
40 | "label" to label,
41 | "customLabel" to customLabel,
42 | "street" to street,
43 | "pobox" to pobox,
44 | "neighborhood" to neighborhood,
45 | "city" to city,
46 | "state" to state,
47 | "postalCode" to postalCode,
48 | "country" to country,
49 | "isoCountry" to isoCountry,
50 | "subAdminArea" to subAdminArea,
51 | "subLocality" to subLocality
52 | )
53 | }
54 |
--------------------------------------------------------------------------------
/doc/api/properties_account/Account-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | Account
5 | fromJson
6 |
7 |
8 |
9 |
10 | Properties
11 |
12 | hashCode
13 | mimetypes
14 | name
15 | rawId
16 | runtimeType
17 | type
18 |
19 | Methods
20 | noSuchMethod
21 | toJson
22 | toString
23 |
24 | Operators
25 | operator ==
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/doc/api/properties_event/Event-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | Event
5 | fromJson
6 |
7 |
8 |
9 |
10 | Properties
11 |
12 | customLabel
13 | day
14 | hashCode
15 | label
16 | month
17 | runtimeType
18 | year
19 |
20 | Methods
21 | noSuchMethod
22 | toJson
23 | toString
24 | toVCard
25 |
26 | Operators
27 | operator ==
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/test/contact_test.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/services.dart';
2 | import 'package:flutter_test/flutter_test.dart';
3 | import 'package:flutter_contacts/flutter_contacts.dart';
4 |
5 | void main() {
6 | TestWidgetsFlutterBinding.ensureInitialized();
7 |
8 | test('new contact should have no null values except for photo', () {
9 | final contact = Contact();
10 | final json = contact.toJson();
11 | final nulls = nullValues(json);
12 | expect(nulls, ['/thumbnail', '/photo']);
13 | });
14 |
15 | test('add phone numbers', () async {
16 | TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
17 | .setMockMethodCallHandler(
18 | const MethodChannel('github.com/QuisApp/flutter_contacts'),
19 | (MethodCall methodCall) async {
20 | if (methodCall.method == 'insert') {
21 | final c = methodCall.arguments[0];
22 | c['displayName'] = '${c['name']['first']} ${c['name']['last']}';
23 | c['id'] = '12345';
24 | return c;
25 | }
26 | return null;
27 | },
28 | );
29 | final newContact = Contact()
30 | ..name = Name(first: 'John', last: 'Doe')
31 | ..phones = [
32 | Phone('555-123-4567'),
33 | Phone('555-999-9999', label: PhoneLabel.work)
34 | ];
35 | final returnedContact = await newContact.insert();
36 | expect(returnedContact.displayName, 'John Doe');
37 | expect(returnedContact.phones.length, 2);
38 | });
39 | }
40 |
41 | List nullValues(dynamic x, {String path = ''}) {
42 | if (x is Map) {
43 | var nulls = [];
44 | x.forEach((k, v) => nulls.addAll(nullValues(v, path: '$path/$k')));
45 | return nulls;
46 | } else if (x is List) {
47 | return nullValues(x.asMap(), path: path);
48 | } else if (x == null) {
49 | return [path];
50 | } else {
51 | return [];
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/doc/api/config/VCardVersion-enum-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | VCardVersion
5 |
6 | Values
7 | v3
8 | v4
9 |
10 |
11 |
12 | Properties
13 |
14 | hashCode
15 | index
16 | runtimeType
17 |
18 | Methods
19 | noSuchMethod
20 | toString
21 |
22 | Operators
23 | operator ==
24 |
25 |
26 |
27 |
28 |
29 |
30 | Constants
31 | values
32 |
33 |
--------------------------------------------------------------------------------
/doc/api/config/FlutterContactsConfig-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | FlutterContactsConfig
5 |
6 |
7 |
8 |
9 | Properties
10 |
11 | hashCode
12 | includeNonVisibleOnAndroid
13 | includeNotesOnIos13AndAbove
14 | returnUnifiedContacts
15 | runtimeType
16 | vCardVersion
17 |
18 | Methods
19 | noSuchMethod
20 | toString
21 |
22 | Operators
23 | operator ==
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/doc/api/properties_phone/Phone-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | Phone
5 | fromJson
6 |
7 |
8 |
9 |
10 | Properties
11 |
12 | customLabel
13 | hashCode
14 | isPrimary
15 | label
16 | normalizedNumber
17 | number
18 | runtimeType
19 |
20 | Methods
21 | noSuchMethod
22 | toJson
23 | toString
24 | toVCard
25 |
26 | Operators
27 | operator ==
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/doc/api/properties_social_media/SocialMedia-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | SocialMedia
5 | fromJson
6 |
7 |
8 |
9 |
10 | Properties
11 |
12 | customLabel
13 | hashCode
14 | label
15 | runtimeType
16 | userName
17 |
18 | Methods
19 | noSuchMethod
20 | toJson
21 | toString
22 | toVCard
23 |
24 | Operators
25 | operator ==
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/example/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | flutter_contacts_example
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 | NSContactsUsageDescription
45 | Access contact list
46 | CADisableMinimumFrameDurationOnPhone
47 |
48 | UIApplicationSupportsIndirectInputEvents
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/example_full/lib/pages/form_components/note_form.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_contacts/properties/note.dart';
3 |
4 | class NoteForm extends StatefulWidget {
5 | final Note note;
6 | final void Function(Note)? onUpdate;
7 | final void Function() onDelete;
8 |
9 | NoteForm(
10 | this.note, {
11 | required this.onUpdate,
12 | required this.onDelete,
13 | Key? key,
14 | }) : super(key: key);
15 |
16 | @override
17 | _NoteFormState createState() => _NoteFormState();
18 | }
19 |
20 | class _NoteFormState extends State {
21 | final _formKey = GlobalKey();
22 |
23 | late TextEditingController _noteController;
24 |
25 | @override
26 | void initState() {
27 | super.initState();
28 | _noteController = TextEditingController(text: widget.note.note);
29 | }
30 |
31 | void _onChanged() {
32 | final note = Note(
33 | _noteController.text,
34 | );
35 | if (widget.onUpdate != null) widget.onUpdate!(note);
36 | }
37 |
38 | @override
39 | Widget build(BuildContext context) {
40 | return ListTile(
41 | trailing: PopupMenuButton(
42 | itemBuilder: (context) =>
43 | [PopupMenuItem(value: 'Delete', child: Text('Delete'))],
44 | onSelected: (_) => widget.onDelete(),
45 | ),
46 | subtitle: Padding(
47 | padding: const EdgeInsets.all(8.0),
48 | child: Form(
49 | key: _formKey,
50 | onChanged: _onChanged,
51 | child: Column(
52 | children: [
53 | TextFormField(
54 | controller: _noteController,
55 | keyboardType: TextInputType.multiline,
56 | textCapitalization: TextCapitalization.sentences,
57 | maxLines: null,
58 | minLines: 3,
59 | decoration: InputDecoration(hintText: 'Note'),
60 | ),
61 | ],
62 | ),
63 | ),
64 | ),
65 | );
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/example/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "dev.flutter.flutter-gradle-plugin"
4 | }
5 |
6 | def localProperties = new Properties()
7 | def localPropertiesFile = rootProject.file('local.properties')
8 | if (localPropertiesFile.exists()) {
9 | localPropertiesFile.withReader('UTF-8') { reader ->
10 | localProperties.load(reader)
11 | }
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | android {
25 | compileSdk 34
26 |
27 | namespace 'co.quis.flutter_contacts_example'
28 |
29 | compileOptions {
30 | sourceCompatibility JavaVersion.VERSION_17
31 | targetCompatibility JavaVersion.VERSION_17
32 | }
33 |
34 | sourceSets {
35 | main.java.srcDirs += 'src/main/kotlin'
36 | }
37 |
38 | lintOptions {
39 | disable 'InvalidPackage'
40 | }
41 |
42 | defaultConfig {
43 | applicationId "co.quis.flutter_contacts_example"
44 | minSdk 21
45 | targetSdk 34
46 | versionCode flutterVersionCode.toInteger()
47 | versionName flutterVersionName
48 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
49 | }
50 |
51 | buildTypes {
52 | release {
53 | // TODO: Add your own signing config for the release build.
54 | // Signing with the debug keys for now, so `flutter run --release` works.
55 | signingConfig signingConfigs.debug
56 | }
57 | }
58 | }
59 |
60 | flutter {
61 | source '../..'
62 | }
63 |
64 | dependencies {
65 | testImplementation 'junit:junit:4.13.2'
66 | androidTestImplementation 'androidx.test:runner:1.6.1'
67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
68 | }
--------------------------------------------------------------------------------
/example_full/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "dev.flutter.flutter-gradle-plugin"
4 | }
5 |
6 | def localProperties = new Properties()
7 | def localPropertiesFile = rootProject.file('local.properties')
8 | if (localPropertiesFile.exists()) {
9 | localPropertiesFile.withReader('UTF-8') { reader ->
10 | localProperties.load(reader)
11 | }
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | android {
25 | compileSdk 34
26 |
27 | namespace 'co.quis.flutter_contacts_example'
28 |
29 | compileOptions {
30 | sourceCompatibility JavaVersion.VERSION_17
31 | targetCompatibility JavaVersion.VERSION_17
32 | }
33 |
34 | sourceSets {
35 | main.java.srcDirs += 'src/main/kotlin'
36 | }
37 |
38 | lintOptions {
39 | disable 'InvalidPackage'
40 | }
41 |
42 | defaultConfig {
43 | applicationId "co.quis.flutter_contacts_example"
44 | minSdk 21
45 | targetSdk 34
46 | versionCode flutterVersionCode.toInteger()
47 | versionName flutterVersionName
48 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
49 | }
50 |
51 | buildTypes {
52 | release {
53 | // TODO: Add your own signing config for the release build.
54 | // Signing with the debug keys for now, so `flutter run --release` works.
55 | signingConfig signingConfigs.debug
56 | }
57 | }
58 | }
59 |
60 | flutter {
61 | source '../..'
62 | }
63 |
64 | dependencies {
65 | testImplementation 'junit:junit:4.13.2'
66 | androidTestImplementation 'androidx.test:runner:1.6.1'
67 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
68 | }
--------------------------------------------------------------------------------
/lib/config.dart:
--------------------------------------------------------------------------------
1 | /// vCard version.
2 | enum VCardVersion {
3 | /// vCard version 3.0.
4 | ///
5 | /// This is described in https://tools.ietf.org/html/rfc2426 from 1998 and is
6 | /// the most commonly used format.
7 | v3,
8 |
9 | /// vCard version 4.0.
10 | ///
11 | /// This is described in https://tools.ietf.org/html/rfc6350 from 2011.
12 | v4,
13 |
14 | // V2.1 (described in https://tools.ietf.org/html/rfc2425 from 1998) is not
15 | // yet supported.
16 | }
17 |
18 | class FlutterContactsConfig {
19 | /// Whether to include notes in the properties on iOS13 and above.
20 | ///
21 | /// On iOS13+ the app needs to be explicitly approved by Apple (see
22 | /// https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_contacts_notes)
23 | /// and the app crashes when trying to read or write notes without
24 | /// entitlement. If your app got entitled, you can set this to true to access
25 | /// notes.
26 | bool includeNotesOnIos13AndAbove = false;
27 |
28 | /// Non-visible contacts on Android are contacts that are not part of a group,
29 | /// and are excluded by default.
30 | ///
31 | /// See https://stackoverflow.com/questions/28665587/what-does-contactscontract-contacts-in-visible-group-mean-in-android
32 | bool includeNonVisibleOnAndroid = false;
33 |
34 | /// Return unified contacts instead of raw contacts.
35 | ///
36 | /// On both iOS and Android there is a concept of raw and unified contacts. A
37 | /// single person might have two raw contacts (for example from Gmail and from
38 | /// iCloud) but will be merged into a single view called a unified contact. In
39 | /// a contact app you typically want unified contacts, so this is what's
40 | /// returned by default. When this is false, raw contacts are returned
41 | /// instead.
42 | bool returnUnifiedContacts = true;
43 |
44 | /// The vCard version to use when exporting to VCard. V4 is the most current,
45 | /// but V3 is the most commonly supported.
46 | VCardVersion vCardVersion = VCardVersion.v3;
47 | }
48 |
--------------------------------------------------------------------------------
/ios/Classes/properties/Name.swift:
--------------------------------------------------------------------------------
1 | import Contacts
2 |
3 | @available(iOS 9.0, *)
4 | struct Name {
5 | var first: String = ""
6 | var last: String = ""
7 | var middle: String = ""
8 | var prefix: String = ""
9 | var suffix: String = ""
10 | var nickname: String = ""
11 | var firstPhonetic: String = ""
12 | var lastPhonetic: String = ""
13 | var middlePhonetic: String = ""
14 |
15 | init() {}
16 |
17 | init(fromMap m: [String: Any]) {
18 | first = m["first"] as! String
19 | last = m["last"] as! String
20 | middle = m["middle"] as! String
21 | prefix = m["prefix"] as! String
22 | suffix = m["suffix"] as! String
23 | nickname = m["nickname"] as! String
24 | firstPhonetic = m["firstPhonetic"] as! String
25 | lastPhonetic = m["lastPhonetic"] as! String
26 | middlePhonetic = m["middlePhonetic"] as! String
27 | }
28 |
29 | init(fromContact c: CNContact) {
30 | first = c.givenName
31 | last = c.familyName
32 | middle = c.middleName
33 | prefix = c.namePrefix
34 | suffix = c.nameSuffix
35 | nickname = c.nickname
36 | firstPhonetic = c.phoneticGivenName
37 | lastPhonetic = c.phoneticFamilyName
38 | middlePhonetic = c.phoneticMiddleName
39 | }
40 |
41 | func toMap() -> [String: Any] { [
42 | "first": first,
43 | "last": last,
44 | "middle": middle,
45 | "prefix": prefix,
46 | "suffix": suffix,
47 | "nickname": nickname,
48 | "firstPhonetic": firstPhonetic,
49 | "lastPhonetic": lastPhonetic,
50 | "middlePhonetic": middlePhonetic,
51 | ]
52 | }
53 |
54 | func addTo(_ c: CNMutableContact) {
55 | c.givenName = first
56 | c.familyName = last
57 | c.middleName = middle
58 | c.namePrefix = prefix
59 | c.nameSuffix = suffix
60 | c.nickname = nickname
61 | c.phoneticGivenName = firstPhonetic
62 | c.phoneticFamilyName = lastPhonetic
63 | c.phoneticMiddleName = middlePhonetic
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/doc/api/flutter_contacts/flutter_contacts-library-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 | Classes
3 | Account
4 | Address
5 | Contact
6 | Email
7 | Event
8 | FlutterContacts
9 | Group
10 | Name
11 | Note
12 | Organization
13 | Phone
14 | SocialMedia
15 | Website
16 |
17 | Enums
18 | AddressLabel
19 | EmailLabel
20 | EventLabel
21 | PhoneLabel
22 | SocialMediaLabel
23 | WebsiteLabel
24 |
25 |
26 |
27 |
28 | Properties
29 | lowerCaseStringToSocialMediaLabel
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/example_full/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | flutter_contacts_example
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 | NSContactsUsageDescription
47 | Access contact list
48 |
49 |
50 | NSCameraUsageDescription
51 | Access camera to add contact pictures
52 | CADisableMinimumFrameDurationOnPhone
53 |
54 | UIApplicationSupportsIndirectInputEvents
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/doc/api/properties_event/EventLabel-enum-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | EventLabel
5 |
6 | Values
7 | anniversary
8 | birthday
9 | other
10 | custom
11 |
12 |
13 |
14 | Properties
15 |
16 | hashCode
17 | index
18 | runtimeType
19 |
20 | Methods
21 | noSuchMethod
22 | toString
23 |
24 | Operators
25 | operator ==
26 |
27 |
28 |
29 |
30 |
31 |
32 | Constants
33 | values
34 |
35 |
--------------------------------------------------------------------------------
/doc/api/properties_name/Name-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | Name
5 | fromJson
6 |
7 |
8 |
9 |
10 | Properties
11 |
12 | first
13 | firstPhonetic
14 | hashCode
15 | last
16 | lastPhonetic
17 | middle
18 | middlePhonetic
19 | nickname
20 | prefix
21 | runtimeType
22 | suffix
23 |
24 | Methods
25 | noSuchMethod
26 | toJson
27 | toString
28 | toVCard
29 |
30 | Operators
31 | operator ==
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/example/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/ios/Classes/properties/Website.swift:
--------------------------------------------------------------------------------
1 | import Contacts
2 |
3 | @available(iOS 9.0, *)
4 | struct Website {
5 | var url: String
6 | // one of: blog, ftp, home, homepage, profile, school, work, other, custom
7 | var label: String = "homepage"
8 | var customLabel: String = ""
9 |
10 | init(fromMap m: [String: Any]) {
11 | url = m["url"] as! String
12 | label = m["label"] as! String
13 | customLabel = m["customLabel"] as! String
14 | }
15 |
16 | init(fromWebsite w: CNLabeledValue) {
17 | url = w.value as String
18 | switch w.label {
19 | case CNLabelHome:
20 | label = "home"
21 | case CNLabelURLAddressHomePage:
22 | label = "homepage"
23 | case CNLabelWork:
24 | label = "work"
25 | case CNLabelOther:
26 | label = "other"
27 | default:
28 | if #available(iOS 13, *), w.label == CNLabelSchool {
29 | label = "school"
30 | } else {
31 | label = "custom"
32 | customLabel = w.label ?? ""
33 | }
34 | }
35 | }
36 |
37 | func toMap() -> [String: Any] { [
38 | "url": url,
39 | "label": label,
40 | "customLabel": customLabel,
41 | ]
42 | }
43 |
44 | func addTo(_ c: CNMutableContact) {
45 | var labelInv: String
46 | switch label {
47 | case "home":
48 | labelInv = CNLabelHome
49 | case "homepage":
50 | labelInv = CNLabelURLAddressHomePage
51 | case "school":
52 | if #available(iOS 13, *) {
53 | labelInv = CNLabelSchool
54 | } else {
55 | labelInv = "school"
56 | }
57 | case "work":
58 | labelInv = CNLabelWork
59 | case "other":
60 | labelInv = CNLabelOther
61 | case "custom":
62 | labelInv = customLabel
63 | default:
64 | labelInv = label
65 | }
66 | c.urlAddresses.append(
67 | CNLabeledValue(
68 | label: labelInv,
69 | value: url as NSString
70 | )
71 | )
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/doc/api/properties_address/AddressLabel-enum-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | AddressLabel
5 |
6 | Values
7 | home
8 | school
9 | work
10 | other
11 | custom
12 |
13 |
14 |
15 | Properties
16 |
17 | hashCode
18 | index
19 | runtimeType
20 |
21 | Methods
22 | noSuchMethod
23 | toString
24 |
25 | Operators
26 | operator ==
27 |
28 |
29 |
30 |
31 |
32 |
33 | Constants
34 | values
35 |
36 |
--------------------------------------------------------------------------------
/doc/api/properties_organization/Organization-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | Organization
5 | fromJson
6 |
7 |
8 |
9 |
10 | Properties
11 |
12 | company
13 | department
14 | hashCode
15 | jobDescription
16 | officeLocation
17 | phoneticName
18 | runtimeType
19 | symbol
20 | title
21 |
22 | Methods
23 | noSuchMethod
24 | toJson
25 | toString
26 | toVCard
27 |
28 | Operators
29 | operator ==
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/test/testdata/vcards/vcardmaker.com.vcf:
--------------------------------------------------------------------------------
1 | BEGIN:VCARD
2 | VERSION:3.0
3 | FN;CHARSET=UTF-8:John Jøhñ Smith
4 | N;CHARSET=UTF-8:Smith;John;Jøhñ;Dr;Jr
5 | NICKNAME;CHARSET=UTF-8:Nick
6 | GENDER:M
7 | UID;CHARSET=UTF-8:UID-1234
8 | BDAY:19830223
9 | ANNIVERSARY:20100323
10 | EMAIL;CHARSET=UTF-8;type=HOME,INTERNET:johnsmith@example.com
11 | EMAIL;CHARSET=UTF-8;type=WORK,INTERNET:john@smith.com
12 | LOGO;ENCODING=b;TYPE=JPEG:/9j/4AAQSkZJRgABAQAAAQABAAD//gAfQ29tcHJlc3NlZCBieSBqcGVnLXJlY29tcHJlc3P/2wCEAAQEBAQEBAQEBAQGBgUGBggHBwcHCAwJCQkJCQwTDA4MDA4MExEUEA8QFBEeFxUVFx4iHRsdIiolJSo0MjRERFwBBAQEBAQEBAQEBAYGBQYGCAcHBwcIDAkJCQkJDBMMDgwMDgwTERQQDxAUER4XFRUXHiIdGx0iKiUlKjQyNEREXP/CABEIAAgACAMBIgACEQEDEQH/xAAUAAEAAAAAAAAAAAAAAAAAAAAI/9oACAEBAAAAAFZ//8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAhAAAAB//8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAxAAAAB//8QAIhAAAQEGBwAAAAAAAAAAAAAAAgQDBQYSExQAAQcVMTNi/9oACAEBAAE/AGr5iEdTmKQXXFJQ/Y2RkKJJtF125Kq09fxxLj//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAECAQE/AH//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==
13 | PHOTO;ENCODING=b;TYPE=JPEG:/9j/4AAQSkZJRgABAQAAAQABAAD//gAfQ29tcHJlc3NlZCBieSBqcGVnLXJlY29tcHJlc3P/2wCEAAQEBAQEBAQEBAQGBgUGBggHBwcHCAwJCQkJCQwTDA4MDA4MExEUEA8QFBEeFxUVFx4iHRsdIiolJSo0MjRERFwBBAQEBAQEBAQEBAYGBQYGCAcHBwcIDAkJCQkJDBMMDgwMDgwTERQQDxAUER4XFRUXHiIdGx0iKiUlKjQyNEREXP/CABEIAAgACAMBIgACEQEDEQH/xAAUAAEAAAAAAAAAAAAAAAAAAAAI/9oACAEBAAAAAFZ//8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAhAAAAB//8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAxAAAAB//8QAIhAAAQEGBwAAAAAAAAAAAAAAAgQDBQYSExQAAQcVMTNi/9oACAEBAAE/AGr5iEdTmKQXXFJQ/Y2RkKJJtF125Kq09fxxLj//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAECAQE/AH//xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDAQE/AH//2Q==
14 | TEL;TYPE=CELL:555-123-4567
15 | TEL;TYPE=PAGER:555-888-8888
16 | TEL;TYPE=HOME,VOICE:+1-555-000-1111
17 | TEL;TYPE=WORK,VOICE:555-999-9999
18 | TEL;TYPE=HOME,FAX:555-777-7777
19 | TEL;TYPE=WORK,FAX:555-666-6666
20 | LABEL;CHARSET=UTF-8;TYPE=HOME:Home Label
21 | ADR;CHARSET=UTF-8;TYPE=HOME:;;123 Main St;Portland;TN;37148;USA
22 | ADR;CHARSET=UTF-8;TYPE=WORK:;;1600 Amphitheatre Pkwy\, Mountain View\, CA 94043;;;;
23 | TITLE;CHARSET=UTF-8:Software Engineer
24 | ROLE;CHARSET=UTF-8:Package maker
25 | ORG;CHARSET=UTF-8:FlutterContacts
26 | URL;CHARSET=UTF-8:www.example.com
27 | URL;type=WORK;CHARSET=UTF-8:https://pub.dev/packages/flutter_contacts
28 | NOTE;CHARSET=UTF-8:Some notes
29 | REV:2021-02-23T14:11:13.361Z
30 | END:VCARD
31 |
--------------------------------------------------------------------------------
/ios/Classes/properties/Email.swift:
--------------------------------------------------------------------------------
1 | import Contacts
2 |
3 | @available(iOS 9.0, *)
4 | struct Email {
5 | var address: String
6 | // one of: home, iCloud, mobile, school, work, other, custom
7 | var label: String = "home"
8 | var customLabel: String = ""
9 | var isPrimary: Bool = false // not supported on iOS
10 |
11 | init(fromMap m: [String: Any]) {
12 | address = m["address"] as! String
13 | label = m["label"] as! String
14 | customLabel = m["customLabel"] as! String
15 | isPrimary = m["isPrimary"] as! Bool
16 | }
17 |
18 | init(fromEmail e: CNLabeledValue) {
19 | address = e.value as String
20 | switch e.label {
21 | case CNLabelHome:
22 | label = "home"
23 | case CNLabelEmailiCloud:
24 | label = "iCloud"
25 | case CNLabelWork:
26 | label = "work"
27 | case CNLabelOther:
28 | label = "other"
29 | default:
30 | if #available(iOS 13, *), e.label == CNLabelSchool {
31 | label = "school"
32 | } else {
33 | label = "custom"
34 | customLabel = e.label ?? ""
35 | }
36 | }
37 | }
38 |
39 | func toMap() -> [String: Any] { [
40 | "address": address,
41 | "label": label,
42 | "customLabel": customLabel,
43 | "isPrimary": isPrimary,
44 | ]
45 | }
46 |
47 | func addTo(_ c: CNMutableContact) {
48 | var labelInv: String
49 | switch label {
50 | case "home":
51 | labelInv = CNLabelHome
52 | case "iCloud":
53 | labelInv = CNLabelEmailiCloud
54 | case "school":
55 | if #available(iOS 13, *) {
56 | labelInv = CNLabelSchool
57 | } else {
58 | labelInv = "school"
59 | }
60 | case "work":
61 | labelInv = CNLabelWork
62 | case "other":
63 | labelInv = CNLabelOther
64 | case "custom":
65 | labelInv = customLabel
66 | default:
67 | labelInv = label
68 | }
69 | c.emailAddresses.append(
70 | CNLabeledValue(
71 | label: labelInv,
72 | value: address as NSString
73 | )
74 | )
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/doc/api/properties_email/EmailLabel-enum-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | EmailLabel
5 |
6 | Values
7 | home
8 | iCloud
9 | mobile
10 | school
11 | work
12 | other
13 | custom
14 |
15 |
16 |
17 | Properties
18 |
19 | hashCode
20 | index
21 | runtimeType
22 |
23 | Methods
24 | noSuchMethod
25 | toString
26 |
27 | Operators
28 | operator ==
29 |
30 |
31 |
32 |
33 |
34 |
35 | Constants
36 | values
37 |
38 |
--------------------------------------------------------------------------------
/example/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_contacts/flutter_contacts.dart';
3 |
4 | void main() => runApp(FlutterContactsExample());
5 |
6 | class FlutterContactsExample extends StatefulWidget {
7 | @override
8 | _FlutterContactsExampleState createState() => _FlutterContactsExampleState();
9 | }
10 |
11 | class _FlutterContactsExampleState extends State {
12 | List? _contacts;
13 | bool _permissionDenied = false;
14 |
15 | @override
16 | void initState() {
17 | super.initState();
18 | _fetchContacts();
19 | }
20 |
21 | Future _fetchContacts() async {
22 | if (!await FlutterContacts.requestPermission(readonly: true)) {
23 | setState(() => _permissionDenied = true);
24 | } else {
25 | final contacts = await FlutterContacts.getContacts();
26 | setState(() => _contacts = contacts);
27 | }
28 | }
29 |
30 | @override
31 | Widget build(BuildContext context) => MaterialApp(
32 | home: Scaffold(
33 | appBar: AppBar(title: Text('flutter_contacts_example')),
34 | body: _body()));
35 |
36 | Widget _body() {
37 | if (_permissionDenied) return Center(child: Text('Permission denied'));
38 | if (_contacts == null) return Center(child: CircularProgressIndicator());
39 | return ListView.builder(
40 | itemCount: _contacts!.length,
41 | itemBuilder: (context, i) => ListTile(
42 | title: Text(_contacts![i].displayName),
43 | onTap: () async {
44 | final fullContact =
45 | await FlutterContacts.getContact(_contacts![i].id);
46 | await Navigator.of(context).push(
47 | MaterialPageRoute(builder: (_) => ContactPage(fullContact!)));
48 | }));
49 | }
50 | }
51 |
52 | class ContactPage extends StatelessWidget {
53 | final Contact contact;
54 | ContactPage(this.contact);
55 |
56 | @override
57 | Widget build(BuildContext context) => Scaffold(
58 | appBar: AppBar(title: Text(contact.displayName)),
59 | body: Column(children: [
60 | Text('First name: ${contact.name.first}'),
61 | Text('Last name: ${contact.name.last}'),
62 | Text(
63 | 'Phone number: ${contact.phones.isNotEmpty ? contact.phones.first.number : '(none)'}'),
64 | Text(
65 | 'Email address: ${contact.emails.isNotEmpty ? contact.emails.first.address : '(none)'}'),
66 | ]));
67 | }
68 |
--------------------------------------------------------------------------------
/example/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/example_full/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/test/testdata/vcards/qr-code-generator.com.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "",
3 | "displayName": "John Smith",
4 | "thumbnail": null,
5 | "photo": null,
6 | "isStarred": false,
7 | "name": {
8 | "first": "John",
9 | "last": "Smith",
10 | "middle": "",
11 | "prefix": "",
12 | "suffix": "",
13 | "nickname": "",
14 | "firstPhonetic": "",
15 | "lastPhonetic": "",
16 | "middlePhonetic": ""
17 | },
18 | "phones": [
19 | {
20 | "number": "555-123-4567",
21 | "normalizedNumber": "",
22 | "label": "mobile",
23 | "customLabel": "",
24 | "isPrimary": false
25 | },
26 | {
27 | "number": "555-999-9999",
28 | "normalizedNumber": "",
29 | "label": "work",
30 | "customLabel": "",
31 | "isPrimary": false
32 | },
33 | {
34 | "number": "555-888-8888",
35 | "normalizedNumber": "",
36 | "label": "faxWork",
37 | "customLabel": "",
38 | "isPrimary": false
39 | }
40 | ],
41 | "emails": [
42 | {
43 | "address": "johnsmith@example.com",
44 | "label": "home",
45 | "customLabel": "",
46 | "isPrimary": true
47 | }
48 | ],
49 | "addresses": [
50 | {
51 | "address": " ",
52 | "label": "work",
53 | "customLabel": "",
54 | "street": "",
55 | "pobox": "",
56 | "neighborhood": "",
57 | "city": "",
58 | "state": "",
59 | "postalCode": "",
60 | "country": "",
61 | "isoCountry": "",
62 | "subAdminArea": "",
63 | "subLocality": ""
64 | }
65 | ],
66 | "organizations": [
67 | {
68 | "company": "FlutterContacts",
69 | "title": "Software Engineer",
70 | "department": "",
71 | "jobDescription": "",
72 | "symbol": "",
73 | "phoneticName": "",
74 | "officeLocation": ""
75 | }
76 | ],
77 | "websites": [
78 | {
79 | "url": "www.example.com",
80 | "label": "homepage",
81 | "customLabel": ""
82 | }
83 | ],
84 | "socialMedias": [],
85 | "events": [],
86 | "notes": [
87 | {
88 | "note": "Some notes line 1 Some notes line 2"
89 | }
90 | ],
91 | "accounts": [],
92 | "groups": []
93 | }
--------------------------------------------------------------------------------
/doc/api/properties_website/WebsiteLabel-enum-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | WebsiteLabel
5 |
6 | Values
7 | blog
8 | ftp
9 | home
10 | homepage
11 | profile
12 | school
13 | work
14 | other
15 | custom
16 |
17 |
18 |
19 | Properties
20 |
21 | hashCode
22 | index
23 | runtimeType
24 |
25 | Methods
26 | noSuchMethod
27 | toString
28 |
29 | Operators
30 | operator ==
31 |
32 |
33 |
34 |
35 |
36 |
37 | Constants
38 | values
39 |
40 |
--------------------------------------------------------------------------------
/doc/api/properties_address/Address-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | Address
5 | fromJson
6 |
7 |
8 |
9 |
10 | Properties
11 |
12 | address
13 | city
14 | country
15 | customLabel
16 | hashCode
17 | isoCountry
18 | label
19 | neighborhood
20 | pobox
21 | postalCode
22 | runtimeType
23 | state
24 | street
25 | subAdminArea
26 | subLocality
27 |
28 | Methods
29 | noSuchMethod
30 | toJson
31 | toString
32 | toVCard
33 |
34 | Operators
35 | operator ==
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/lib/properties/website.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_contacts/vcard.dart';
2 |
3 | /// Labeled website.
4 | class Website {
5 | /// Website URL.
6 | String url;
7 |
8 | /// Label (default [WebsiteLabel.homepage]).
9 | WebsiteLabel label;
10 |
11 | /// Custom label, if [label] is [WebsiteLabel.custom].
12 | String customLabel;
13 |
14 | Website(this.url,
15 | {this.label = WebsiteLabel.homepage, this.customLabel = ''});
16 |
17 | factory Website.fromJson(Map json) => Website(
18 | (json['url'] as String?) ?? '',
19 | label: _stringToWebsiteLabel[json['label'] as String? ?? ''] ??
20 | WebsiteLabel.homepage,
21 | customLabel: (json['customLabel'] as String?) ?? '',
22 | );
23 |
24 | Map toJson() => {
25 | 'url': url,
26 | 'label': _websiteLabelToString[label],
27 | 'customLabel': customLabel,
28 | };
29 |
30 | @override
31 | int get hashCode => url.hashCode ^ label.hashCode ^ customLabel.hashCode;
32 |
33 | @override
34 | bool operator ==(Object o) =>
35 | o is Website &&
36 | o.url == url &&
37 | o.label == label &&
38 | o.customLabel == customLabel;
39 |
40 | @override
41 | String toString() =>
42 | 'Website(url=$url, label=$label, customLabel=$customLabel)';
43 |
44 | List toVCard() {
45 | // URL (V3): https://tools.ietf.org/html/rfc2426#section-3.6.8
46 | // URL (V4): https://tools.ietf.org/html/rfc6350#section-6.7.8
47 | return ['URL:${vCardEncode(url)}'];
48 | }
49 | }
50 |
51 | /// Website labels.
52 | ///
53 | /// | Label | Android | iOS |
54 | /// |----------|:-------:|:---:|
55 | /// | blog | ✔ | ⨯ |
56 | /// | ftp | ✔ | ⨯ |
57 | /// | home | ✔ | ✔ |
58 | /// | homepage | ✔ | ✔ |
59 | /// | profile | ✔ | ⨯ |
60 | /// | school | ⨯ | ✔ |
61 | /// | work | ✔ | ✔ |
62 | /// | other | ✔ | ✔ |
63 | /// | custom | ✔ | ✔ |
64 | enum WebsiteLabel {
65 | blog,
66 | ftp,
67 | home,
68 | homepage,
69 | profile,
70 | school,
71 | work,
72 | other,
73 | custom,
74 | }
75 |
76 | final _websiteLabelToString = {
77 | WebsiteLabel.blog: 'blog',
78 | WebsiteLabel.ftp: 'ftp',
79 | WebsiteLabel.home: 'home',
80 | WebsiteLabel.homepage: 'homepage',
81 | WebsiteLabel.profile: 'profile',
82 | WebsiteLabel.school: 'school',
83 | WebsiteLabel.work: 'work',
84 | WebsiteLabel.other: 'other',
85 | WebsiteLabel.custom: 'custom',
86 | };
87 |
88 | final _stringToWebsiteLabel = {
89 | 'blog': WebsiteLabel.blog,
90 | 'ftp': WebsiteLabel.ftp,
91 | 'home': WebsiteLabel.home,
92 | 'homepage': WebsiteLabel.homepage,
93 | 'profile': WebsiteLabel.profile,
94 | 'school': WebsiteLabel.school,
95 | 'work': WebsiteLabel.work,
96 | 'other': WebsiteLabel.other,
97 | 'custom': WebsiteLabel.custom,
98 | };
99 |
--------------------------------------------------------------------------------
/example_full/lib/pages/groups_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:after_layout/after_layout.dart';
2 | import 'package:flutter_contacts/flutter_contacts.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:prompt_dialog/prompt_dialog.dart';
5 |
6 | class GroupsPage extends StatefulWidget {
7 | @override
8 | _GroupsPageState createState() => _GroupsPageState();
9 | }
10 |
11 | class _GroupsPageState extends State
12 | with AfterLayoutMixin {
13 | List? _groups;
14 |
15 | @override
16 | void afterFirstLayout(BuildContext context) {
17 | _fetchGroups();
18 | }
19 |
20 | Future _fetchGroups() async {
21 | final groups = await FlutterContacts.getGroups();
22 | setState(() => _groups = groups);
23 | }
24 |
25 | @override
26 | Widget build(BuildContext context) => Scaffold(
27 | appBar: AppBar(
28 | title: Text('flutter_contacts_example - groups'),
29 | ),
30 | body: _body(),
31 | floatingActionButton: FloatingActionButton(
32 | onPressed: _newGroup,
33 | child: Icon(Icons.add),
34 | ),
35 | );
36 |
37 | Widget _body() {
38 | if (_groups == null) {
39 | return Center(child: CircularProgressIndicator());
40 | }
41 | return ListView.builder(
42 | itemCount: _groups!.length,
43 | itemBuilder: (context, i) {
44 | final group = _groups![i];
45 | return ListTile(
46 | title: Text('${group.name} (id=${group.id})'),
47 | trailing: Row(
48 | mainAxisSize: MainAxisSize.min,
49 | children: [
50 | IconButton(
51 | onPressed: () => _renameGroup(group),
52 | icon: Icon(Icons.edit),
53 | ),
54 | IconButton(
55 | onPressed: () => _deleteGroup(group),
56 | icon: Icon(Icons.delete),
57 | ),
58 | ],
59 | ),
60 | onTap: () => _renameGroup(group),
61 | );
62 | });
63 | }
64 |
65 | Future _newGroup() async {
66 | final name = await prompt(context);
67 | if (name != null && name.isNotEmpty) {
68 | final group = await FlutterContacts.insertGroup(Group('', name));
69 | print('Inserted group $group');
70 | await _fetchGroups();
71 | }
72 | }
73 |
74 | Future _renameGroup(Group group) async {
75 | final name = await prompt(context, initialValue: group.name);
76 | if (name != null && name.isNotEmpty) {
77 | final updatedGroup =
78 | await FlutterContacts.updateGroup(Group(group.id, name));
79 | print('Updated group $updatedGroup');
80 | await _fetchGroups();
81 | }
82 | }
83 |
84 | Future _deleteGroup(Group group) async {
85 | await FlutterContacts.deleteGroup(group);
86 | print('Deleted group $group');
87 | await _fetchGroups();
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/ios/Classes/properties/Event.swift:
--------------------------------------------------------------------------------
1 | import Contacts
2 |
3 | @available(iOS 9.0, *)
4 | struct Event {
5 | var year: Int?
6 | var month: Int
7 | var day: Int
8 | // one of: anniversary, birthday, other, custom
9 | var label: String = "birthday"
10 | var customLabel: String = ""
11 |
12 | init(fromMap m: [String: Any?]) {
13 | year = m["year"] as? Int
14 | month = m["month"] as! Int
15 | day = m["day"] as! Int
16 | label = m["label"] as! String
17 | customLabel = m["customLabel"] as! String
18 | }
19 |
20 | init(fromContact c: CNContact) {
21 | // It seems like NSDateComponents use 2^64-1 as a value for year when there is
22 | // no year. This should cover similar edge cases.
23 | let y = c.birthday!.year
24 | year = (y == nil || y! < -100_000 || y! > 100_000) ? nil : y
25 | year = c.birthday!.year
26 | month = c.birthday!.month ?? 1
27 | day = c.birthday!.day ?? 1
28 | label = "birthday"
29 | }
30 |
31 | init(fromDate d: CNLabeledValue) {
32 | // It seems like NSDateComponents use 2^64-1 as a value for year when there is
33 | // no year. This should cover similar edge cases.
34 | let y = d.value.year
35 | year = (y < -100_000 || y > 100_000) ? nil : y
36 | month = d.value.month
37 | day = d.value.day
38 | switch d.label {
39 | case CNLabelDateAnniversary:
40 | label = "anniversary"
41 | case CNLabelOther:
42 | label = "other"
43 | default:
44 | label = "custom"
45 | customLabel = d.label ?? ""
46 | }
47 | }
48 |
49 | func toMap() -> [String: Any?] { [
50 | "year": year,
51 | "month": month,
52 | "day": day,
53 | "label": label,
54 | "customLabel": customLabel,
55 | ]
56 | }
57 |
58 | func addTo(_ c: CNMutableContact) {
59 | var dateComponents: DateComponents
60 | if year == nil {
61 | dateComponents = DateComponents(month: month, day: day)
62 | } else {
63 | dateComponents = DateComponents(year: year, month: month, day: day)
64 | }
65 | if label == "birthday" {
66 | c.birthday = dateComponents
67 | } else {
68 | var labelInv: String
69 | switch label {
70 | case "anniversary":
71 | labelInv = CNLabelDateAnniversary
72 | case "other":
73 | labelInv = CNLabelOther
74 | case "custom":
75 | labelInv = customLabel
76 | default:
77 | labelInv = label
78 | }
79 | c.dates.append(
80 | CNLabeledValue(
81 | label: labelInv,
82 | value: dateComponents as NSDateComponents
83 | )
84 | )
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/test/testdata/vcards/bvcard.com.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "",
3 | "displayName": "Dr John J Smith, Jr",
4 | "thumbnail": null,
5 | "photo": null,
6 | "isStarred": false,
7 | "name": {
8 | "first": "John",
9 | "last": "Smith",
10 | "middle": "J",
11 | "prefix": "Dr",
12 | "suffix": "Jr",
13 | "nickname": "",
14 | "firstPhonetic": "",
15 | "lastPhonetic": "",
16 | "middlePhonetic": ""
17 | },
18 | "phones": [
19 | {
20 | "number": "555-123-4567",
21 | "normalizedNumber": "",
22 | "label": "mobile",
23 | "customLabel": "",
24 | "isPrimary": false
25 | },
26 | {
27 | "number": "555-999-9999",
28 | "normalizedNumber": "",
29 | "label": "faxHome",
30 | "customLabel": "",
31 | "isPrimary": false
32 | }
33 | ],
34 | "emails": [
35 | {
36 | "address": "johnsmith@example.com",
37 | "label": "home",
38 | "customLabel": "",
39 | "isPrimary": false
40 | }
41 | ],
42 | "addresses": [
43 | {
44 | "address": "123 Main St Apt 3 Portland TN 37148 USA",
45 | "label": "home",
46 | "customLabel": "",
47 | "street": "Apt 3",
48 | "pobox": "",
49 | "neighborhood": "",
50 | "city": "Portland",
51 | "state": "TN",
52 | "postalCode": "37148",
53 | "country": "USA",
54 | "isoCountry": "",
55 | "subAdminArea": "",
56 | "subLocality": ""
57 | }
58 | ],
59 | "organizations": [
60 | {
61 | "company": "FlutterContacts",
62 | "title": "",
63 | "department": "",
64 | "jobDescription": "",
65 | "symbol": "",
66 | "phoneticName": "",
67 | "officeLocation": ""
68 | }
69 | ],
70 | "websites": [
71 | {
72 | "url": "http://www.example.com",
73 | "label": "homepage",
74 | "customLabel": ""
75 | },
76 | {
77 | "url": "http://linked.com/profile",
78 | "label": "homepage",
79 | "customLabel": ""
80 | },
81 | {
82 | "url": "http://facebook.com/profile",
83 | "label": "homepage",
84 | "customLabel": ""
85 | },
86 | {
87 | "url": "http://google.plus/profile",
88 | "label": "homepage",
89 | "customLabel": ""
90 | },
91 | {
92 | "url": "http://twitter.com/profile",
93 | "label": "homepage",
94 | "customLabel": ""
95 | }
96 | ],
97 | "socialMedias": [],
98 | "events": [],
99 | "notes": [],
100 | "accounts": [],
101 | "groups": []
102 | }
--------------------------------------------------------------------------------
/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/example_full/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/doc/api/contact/Contact-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | Contact
5 | fromJson
6 | fromVCard
7 |
8 |
9 |
10 |
11 | Properties
12 |
13 | accounts
14 | addresses
15 | displayName
16 | emails
17 | events
18 | groups
19 | hashCode
20 | id
21 | isStarred
22 | isUnified
23 | name
24 | notes
25 | organizations
26 | phones
27 | photo
28 | photoFetched
29 | photoOrThumbnail
30 | propertiesFetched
31 | runtimeType
32 | socialMedias
33 | thumbnail
34 | thumbnailFetched
35 | websites
36 |
37 | Methods
38 | deduplicateProperties
39 | delete
40 | insert
41 | noSuchMethod
42 | toJson
43 | toString
44 | toVCard
45 | update
46 |
47 | Operators
48 | operator ==
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/example_full/lib/pages/form_components/phone_form.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_contacts/properties/phone.dart';
3 |
4 | class PhoneForm extends StatefulWidget {
5 | final Phone phone;
6 | final void Function(Phone) onUpdate;
7 | final void Function() onDelete;
8 |
9 | PhoneForm(
10 | this.phone, {
11 | required this.onUpdate,
12 | required this.onDelete,
13 | Key? key,
14 | }) : super(key: key);
15 |
16 | @override
17 | _PhoneFormState createState() => _PhoneFormState();
18 | }
19 |
20 | class _PhoneFormState extends State {
21 | final _formKey = GlobalKey();
22 | static final _validLabels = PhoneLabel.values;
23 |
24 | late TextEditingController _numberController;
25 | late PhoneLabel _label;
26 | late TextEditingController _customLabelController;
27 |
28 | @override
29 | void initState() {
30 | super.initState();
31 | _numberController = TextEditingController(text: widget.phone.number);
32 | _label = widget.phone.label;
33 | _customLabelController =
34 | TextEditingController(text: widget.phone.customLabel);
35 | }
36 |
37 | void _onChanged() {
38 | final phone = Phone(_numberController.text,
39 | label: _label,
40 | customLabel:
41 | _label == PhoneLabel.custom ? _customLabelController.text : '');
42 | widget.onUpdate(phone);
43 | }
44 |
45 | @override
46 | Widget build(BuildContext context) {
47 | return ListTile(
48 | trailing: PopupMenuButton(
49 | itemBuilder: (context) =>
50 | [PopupMenuItem(value: 'Delete', child: Text('Delete'))],
51 | onSelected: (_) => widget.onDelete(),
52 | ),
53 | subtitle: Padding(
54 | padding: const EdgeInsets.all(8.0),
55 | child: Form(
56 | key: _formKey,
57 | onChanged: _onChanged,
58 | child: Column(
59 | children: [
60 | TextFormField(
61 | controller: _numberController,
62 | keyboardType: TextInputType.phone,
63 | decoration: InputDecoration(hintText: 'Phone'),
64 | ),
65 | DropdownButtonFormField(
66 | isExpanded: true, // to avoid overflow
67 | items: _validLabels
68 | .map((e) => DropdownMenuItem(
69 | value: e, child: Text(e.toString())))
70 | .toList(),
71 | value: _label,
72 | onChanged: (PhoneLabel? label) {
73 | setState(() {
74 | if (label != null) _label = label;
75 | });
76 | // Unfortunately, the form's `onChanged` gets triggered before
77 | // the dropdown's `onChanged`, so it doesn't update the
78 | // contact when updating the dropdown, and we need to do it
79 | // explicitly here.
80 | _onChanged();
81 | },
82 | ),
83 | _label == PhoneLabel.custom
84 | ? TextFormField(
85 | controller: _customLabelController,
86 | textCapitalization: TextCapitalization.sentences,
87 | decoration: InputDecoration(hintText: 'Custom label'),
88 | )
89 | : Container(),
90 | ],
91 | ),
92 | ),
93 | ),
94 | );
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/example_full/lib/pages/form_components/email_form.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_contacts/properties/email.dart';
3 |
4 | class EmailForm extends StatefulWidget {
5 | final Email email;
6 | final void Function(Email) onUpdate;
7 | final void Function() onDelete;
8 |
9 | EmailForm(
10 | this.email, {
11 | required this.onUpdate,
12 | required this.onDelete,
13 | Key? key,
14 | }) : super(key: key);
15 |
16 | @override
17 | _EmailFormState createState() => _EmailFormState();
18 | }
19 |
20 | class _EmailFormState extends State {
21 | final _formKey = GlobalKey();
22 | static final _validLabels = EmailLabel.values;
23 |
24 | late TextEditingController _addressController;
25 | late EmailLabel _label;
26 | late TextEditingController _customLabelController;
27 |
28 | @override
29 | void initState() {
30 | super.initState();
31 | _addressController = TextEditingController(text: widget.email.address);
32 | _label = widget.email.label;
33 | _customLabelController =
34 | TextEditingController(text: widget.email.customLabel);
35 | }
36 |
37 | void _onChanged() {
38 | final email = Email(
39 | _addressController.text,
40 | label: _label,
41 | customLabel:
42 | _label == EmailLabel.custom ? _customLabelController.text : '',
43 | );
44 | widget.onUpdate(email);
45 | }
46 |
47 | @override
48 | Widget build(BuildContext context) {
49 | return ListTile(
50 | trailing: PopupMenuButton(
51 | itemBuilder: (context) =>
52 | [PopupMenuItem(value: 'Delete', child: Text('Delete'))],
53 | onSelected: (_) => widget.onDelete(),
54 | ),
55 | subtitle: Padding(
56 | padding: const EdgeInsets.all(8.0),
57 | child: Form(
58 | key: _formKey,
59 | onChanged: _onChanged,
60 | child: Column(
61 | children: [
62 | TextFormField(
63 | controller: _addressController,
64 | keyboardType: TextInputType.emailAddress,
65 | decoration: InputDecoration(hintText: 'Address'),
66 | ),
67 | DropdownButtonFormField(
68 | isExpanded: true, // to avoid overflow
69 | items: _validLabels
70 | .map((e) => DropdownMenuItem(
71 | value: e, child: Text(e.toString())))
72 | .toList(),
73 | value: _label,
74 | onChanged: (EmailLabel? label) {
75 | setState(() {
76 | if (label != null) _label = label;
77 | });
78 | // Unfortunately, the form's `onChanged` gets triggered before
79 | // the dropdown's `onChanged`, so it doesn't update the
80 | // contact when updating the dropdown, and we need to do it
81 | // explicitly here.
82 | _onChanged();
83 | },
84 | ),
85 | _label == EmailLabel.custom
86 | ? TextFormField(
87 | controller: _customLabelController,
88 | textCapitalization: TextCapitalization.sentences,
89 | decoration: InputDecoration(hintText: 'Custom label'),
90 | )
91 | : Container(),
92 | ],
93 | ),
94 | ),
95 | ),
96 | );
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/doc/api/flutter_contacts/FlutterContacts-class-sidebar.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Constructors
4 | FlutterContacts
5 |
6 |
7 |
8 |
9 | Properties
10 |
11 | hashCode
12 | runtimeType
13 |
14 | Methods
15 | noSuchMethod
16 | toString
17 |
18 | Operators
19 | operator ==
20 |
21 |
22 |
23 |
24 | Static properties
25 | config
26 |
27 | Static methods
28 | addListener
29 | deleteContact
30 | deleteContacts
31 | deleteGroup
32 | getContact
33 | getContacts
34 | getGroups
35 | insertContact
36 | insertGroup
37 | openExternalEdit
38 | openExternalInsert
39 | openExternalPick
40 | openExternalView
41 | removeListener
42 | requestPermission
43 | updateContact
44 | updateGroup
45 |
46 |
47 |
--------------------------------------------------------------------------------
/example_full/lib/pages/form_components/website_form.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_contacts/properties/website.dart';
3 |
4 | class WebsiteForm extends StatefulWidget {
5 | final Website website;
6 | final void Function(Website) onUpdate;
7 | final void Function() onDelete;
8 |
9 | WebsiteForm(
10 | this.website, {
11 | required this.onUpdate,
12 | required this.onDelete,
13 | Key? key,
14 | }) : super(key: key);
15 |
16 | @override
17 | _WebsiteFormState createState() => _WebsiteFormState();
18 | }
19 |
20 | class _WebsiteFormState extends State {
21 | final _formKey = GlobalKey();
22 | static final _validLabels = WebsiteLabel.values;
23 |
24 | late TextEditingController _urlController;
25 | late WebsiteLabel _label;
26 | late TextEditingController _customLabelController;
27 |
28 | @override
29 | void initState() {
30 | super.initState();
31 | _urlController = TextEditingController(text: widget.website.url);
32 | _label = widget.website.label;
33 | _customLabelController =
34 | TextEditingController(text: widget.website.customLabel);
35 | }
36 |
37 | void _onChanged() {
38 | final website = Website(
39 | _urlController.text,
40 | label: _label,
41 | customLabel:
42 | _label == WebsiteLabel.custom ? _customLabelController.text : '',
43 | );
44 | widget.onUpdate(website);
45 | }
46 |
47 | @override
48 | Widget build(BuildContext context) {
49 | return ListTile(
50 | trailing: PopupMenuButton(
51 | itemBuilder: (context) =>
52 | [PopupMenuItem(value: 'Delete', child: Text('Delete'))],
53 | onSelected: (_) => widget.onDelete(),
54 | ),
55 | subtitle: Padding(
56 | padding: const EdgeInsets.all(8.0),
57 | child: Form(
58 | key: _formKey,
59 | onChanged: _onChanged,
60 | child: Column(
61 | children: [
62 | TextFormField(
63 | controller: _urlController,
64 | keyboardType: TextInputType.url,
65 | decoration: InputDecoration(hintText: 'URL'),
66 | ),
67 | DropdownButtonFormField(
68 | isExpanded: true, // to avoid overflow
69 | items: _validLabels
70 | .map((e) => DropdownMenuItem(
71 | value: e, child: Text(e.toString())))
72 | .toList(),
73 | value: _label,
74 | onChanged: (WebsiteLabel? label) {
75 | setState(() {
76 | if (label != null) _label = label;
77 | });
78 | // Unfortunately, the form's `onChanged` gets triggered before
79 | // the dropdown's `onChanged`, so it doesn't update the
80 | // contact when updating the dropdown, and we need to do it
81 | // explicitly here.
82 | _onChanged();
83 | },
84 | ),
85 | _label == WebsiteLabel.custom
86 | ? TextFormField(
87 | controller: _customLabelController,
88 | textCapitalization: TextCapitalization.sentences,
89 | decoration: InputDecoration(hintText: 'Custom label'),
90 | )
91 | : Container(),
92 | ],
93 | ),
94 | ),
95 | ),
96 | );
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/android/src/main/kotlin/co/quis/flutter_contacts/Contact.kt:
--------------------------------------------------------------------------------
1 | package co.quis.flutter_contacts
2 |
3 | import co.quis.flutter_contacts.properties.Account
4 | import co.quis.flutter_contacts.properties.Address
5 | import co.quis.flutter_contacts.properties.Email
6 | import co.quis.flutter_contacts.properties.Event
7 | import co.quis.flutter_contacts.properties.Group
8 | import co.quis.flutter_contacts.properties.Name
9 | import co.quis.flutter_contacts.properties.Note
10 | import co.quis.flutter_contacts.properties.Organization
11 | import co.quis.flutter_contacts.properties.Phone
12 | import co.quis.flutter_contacts.properties.SocialMedia
13 | import co.quis.flutter_contacts.properties.Website
14 |
15 | data class Contact(
16 | var id: String,
17 | var displayName: String,
18 | var thumbnail: ByteArray? = null,
19 | var photo: ByteArray? = null,
20 | val isStarred: Boolean = false,
21 | var name: Name = Name(),
22 | var phones: List = listOf(),
23 | var emails: List = listOf(),
24 | var addresses: List = listOf(),
25 | var organizations: List = listOf(),
26 | var websites: List = listOf(),
27 | var socialMedias: List = listOf(),
28 | var events: List = listOf(),
29 | var notes: List = listOf(),
30 | var accounts: List = listOf(),
31 | var groups: List = listOf()
32 | ) {
33 | companion object {
34 | fun fromMap(m: Map): Contact {
35 | return Contact(
36 | m["id"] as String,
37 | m["displayName"] as String,
38 | m["thumbnail"] as? ByteArray,
39 | m["photo"] as? ByteArray,
40 | m["isStarred"] as Boolean,
41 | Name.fromMap(m["name"] as Map),
42 | (m["phones"] as List>).map { Phone.fromMap(it) },
43 | (m["emails"] as List>).map { Email.fromMap(it) },
44 | (m["addresses"] as List>).map { Address.fromMap(it) },
45 | (m["organizations"] as List>).map { Organization.fromMap(it) },
46 | (m["websites"] as List>).map { Website.fromMap(it) },
47 | (m["socialMedias"] as List>).map { SocialMedia.fromMap(it) },
48 | (m["events"] as List>).map { Event.fromMap(it) },
49 | (m["notes"] as List>).map { Note.fromMap(it) },
50 | (m["accounts"] as List>).map { Account.fromMap(it) },
51 | (m["groups"] as List>).map { Group.fromMap(it) }
52 | )
53 | }
54 | }
55 |
56 | fun toMap(): Map = mapOf(
57 | "id" to id,
58 | "displayName" to displayName,
59 | "thumbnail" to thumbnail,
60 | "photo" to photo,
61 | "isStarred" to isStarred,
62 | "name" to name.toMap(),
63 | "phones" to phones.map { it.toMap() },
64 | "emails" to emails.map { it.toMap() },
65 | "addresses" to addresses.map { it.toMap() },
66 | "organizations" to organizations.map { it.toMap() },
67 | "websites" to websites.map { it.toMap() },
68 | "socialMedias" to socialMedias.map { it.toMap() },
69 | "events" to events.map { it.toMap() },
70 | "notes" to notes.map { it.toMap() },
71 | "accounts" to accounts.map { it.toMap() },
72 | "groups" to groups.map { it.toMap() }
73 | )
74 | }
75 |
--------------------------------------------------------------------------------
/lib/properties/organization.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_contacts/vcard.dart';
2 |
3 | /// Organization / job.
4 | class Organization {
5 | /// Company name.
6 | String company;
7 |
8 | /// Job title.
9 | String title;
10 |
11 | /// Department.
12 | String department;
13 |
14 | /// Job description (Android only).
15 | String jobDescription;
16 |
17 | /// Ticker symbol (Android only).
18 | String symbol;
19 |
20 | /// Phonetic company name (Android and iOS10+ only).
21 | String phoneticName;
22 |
23 | /// Office location (Android only).
24 | String officeLocation;
25 |
26 | Organization({
27 | this.company = '',
28 | this.title = '',
29 | this.department = '',
30 | this.jobDescription = '',
31 | this.symbol = '',
32 | this.phoneticName = '',
33 | this.officeLocation = '',
34 | });
35 |
36 | factory Organization.fromJson(Map json) => Organization(
37 | company: (json['company'] as String?) ?? '',
38 | title: (json['title'] as String?) ?? '',
39 | department: (json['department'] as String?) ?? '',
40 | jobDescription: (json['jobDescription'] as String?) ?? '',
41 | symbol: (json['symbol'] as String?) ?? '',
42 | phoneticName: (json['phoneticName'] as String?) ?? '',
43 | officeLocation: (json['officeLocation'] as String?) ?? '',
44 | );
45 |
46 | Map toJson() => {
47 | 'company': company,
48 | 'title': title,
49 | 'department': department,
50 | 'jobDescription': jobDescription,
51 | 'symbol': symbol,
52 | 'phoneticName': phoneticName,
53 | 'officeLocation': officeLocation,
54 | };
55 |
56 | @override
57 | int get hashCode =>
58 | company.hashCode ^
59 | title.hashCode ^
60 | department.hashCode ^
61 | jobDescription.hashCode ^
62 | symbol.hashCode ^
63 | phoneticName.hashCode ^
64 | officeLocation.hashCode;
65 |
66 | @override
67 | bool operator ==(Object o) =>
68 | o is Organization &&
69 | o.company == company &&
70 | o.title == title &&
71 | o.department == department &&
72 | o.jobDescription == jobDescription &&
73 | o.symbol == symbol &&
74 | o.phoneticName == phoneticName &&
75 | o.officeLocation == officeLocation;
76 |
77 | @override
78 | String toString() =>
79 | 'Organization(company=$company, title=$title, department=$department, '
80 | 'jobDescription=$jobDescription, symbol=$symbol, '
81 | 'phoneticName=$phoneticName, officeLocation=$officeLocation)';
82 |
83 | List toVCard() {
84 | // ORG (V3): https://tools.ietf.org/html/rfc2426#section-3.5.5
85 | // TITLE (V3): https://tools.ietf.org/html/rfc2426#section-3.5.1
86 | // ROLE (V3): https://tools.ietf.org/html/rfc2426#section-3.5.2
87 | // ORG (V4): https://tools.ietf.org/html/rfc6350#section-6.6.4
88 | // TITLE (V4): https://tools.ietf.org/html/rfc6350#section-6.6.1
89 | // ROLE (V4): https://tools.ietf.org/html/rfc6350#section-6.6.2
90 | var lines = [];
91 | if (company.isNotEmpty || department.isNotEmpty) {
92 | var s = 'ORG:${vCardEncode(company)}';
93 | if (department.isNotEmpty) {
94 | s += ';${vCardEncode(department)}';
95 | }
96 | lines.add(s);
97 | }
98 | if (title.isNotEmpty) {
99 | lines.add('TITLE:${vCardEncode(title)}');
100 | }
101 | if (jobDescription.isNotEmpty) {
102 | lines.add('ROLE:${vCardEncode(jobDescription)}');
103 | }
104 | return lines;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------