使用某些插件访问设备联系人时,应用程序崩溃且没有任何日志
App crashes without any logs when using some plugins to access device Contacts
我想在我的应用程序中显示我尝试使用 contacts_service and contacts_plugin 的联系人,但是当我 运行 安装软件包后我的应用程序崩溃了。我刚刚导入了文件并且没有编写任何代码。我也没有看到任何日志。
在我的应用程序中集成联系人的任何其他方式也会有很大帮助!
您确实需要学习和掌握开发 Flutter 应用程序所需的工具。
当你 post 关于 SO 的问题时,你真的需要更好地解释你的需求。
也就是说我已经快速试用了 contacts_plugin and contact_service。
不要同时使用它们,它们是为同一目的而制作的。选择一个或另一个。
似乎contact_service
更稳定:加权得分92超过82。
Android
正在尝试使用 contacts_plugin
我猜你不喜欢这个错误:
* What went wrong:
The Android Gradle plugin supports only Kotlin Gradle plugin version 1.2.51 and higher. Project 'contacts_plugin' is using version 1.2.30.
那是因为 contacts_plugin
使用 kotlin 版本 1.2.30
并且你像我一样拥有 AS >= 3.2。
ext.kotlin_version = '1.2.30'
.
如果您想尝试一下,可以克隆项目并以这种方式包含依赖项:
contacts_plugin:
path: ../flutter-contacts-plugin/
并在插件中更改 build.gradle 这一行:
ext.kotlin_version = '1.2.30'
有了这个
ext.kotlin_version = '1.2.51'
如果您使用 contact_plugins
,即使 iOS 项目也会出现问题。
contact_serivce
相反,它在 Android 和 iOS 上工作正常。
无论如何永远记住在 Android 上你需要在 AndroidManifest.xml
中添加这个权限
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
和 iOS 确保在 Info.plist
文件中设置 NSContactsUsageDescription
<key>NSContactsUsageDescription</key>
<string>This app requires contacts access to function properly.</string>
并创建一个具有 Swift 支持的 Flutter 项目。
可以使用contact_service
插件提供的default example开始学习:
import 'package:flutter/material.dart';
import 'package:contacts_service/contacts_service.dart';
void main() => runApp(ContactsExampleApp());
class ContactsExampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(routes: <String, WidgetBuilder>{
'/add': (BuildContext context) => AddContactPage()
}, home: ContactListPage());
}
}
class ContactListPage extends StatefulWidget {
@override
_ContactListPageState createState() => _ContactListPageState();
}
class _ContactListPageState extends State<ContactListPage> {
Iterable<Contact> _contacts;
@override
initState() {
super.initState();
refreshContacts();
}
refreshContacts() async {
var contacts = await ContactsService.getContacts();
setState(() {
_contacts = contacts;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Contacts Plugin Example')),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.of(context).pushNamed("/add").then((_) {
refreshContacts();
});
}),
body: SafeArea(
child: _contacts != null
? ListView.builder(
itemCount: _contacts?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
Contact c = _contacts?.elementAt(index);
return ListTile(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) =>
ContactDetailsPage(c)));
},
leading: (c.avatar != null && c.avatar.length > 0)
? CircleAvatar(backgroundImage: MemoryImage(c.avatar))
: CircleAvatar(
child: Text(c.displayName.length > 1
? c.displayName?.substring(0, 2)
: "")),
title: Text(c.displayName ?? ""),
);
},
)
: Center(child: CircularProgressIndicator()),
),
);
}
}
class ContactDetailsPage extends StatelessWidget {
ContactDetailsPage(this._contact);
final Contact _contact;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:
AppBar(title: Text(_contact.displayName ?? ""), actions: <Widget>[
FlatButton(
child: Icon(Icons.delete),
onPressed: () {
ContactsService.deleteContact(_contact);
})
]),
body: SafeArea(
child: ListView(
children: <Widget>[
ListTile(
title: Text("Name"),
trailing: Text(_contact.givenName ?? "")),
ListTile(
title: Text("Middle name"),
trailing: Text(_contact.middleName ?? "")),
ListTile(
title: Text("Family name"),
trailing: Text(_contact.familyName ?? "")),
ListTile(
title: Text("Prefix"), trailing: Text(_contact.prefix ?? "")),
ListTile(
title: Text("Suffix"), trailing: Text(_contact.suffix ?? "")),
ListTile(
title: Text("Company"),
trailing: Text(_contact.company ?? "")),
ListTile(
title: Text("Job"), trailing: Text(_contact.jobTitle ?? "")),
AddressesTile(_contact.postalAddresses),
ItemsTile("Phones", _contact.phones),
ItemsTile("Emails", _contact.emails)
],
),
));
}
}
class AddressesTile extends StatelessWidget {
AddressesTile(this._addresses);
final Iterable<PostalAddress> _addresses;
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ListTile(title: Text("Addresses")),
Column(
children: _addresses
.map((a) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: <Widget>[
ListTile(
title: Text("Street"),
trailing: Text(a.street)),
ListTile(
title: Text("Postcode"),
trailing: Text(a.postcode)),
ListTile(
title: Text("City"), trailing: Text(a.city)),
ListTile(
title: Text("Region"),
trailing: Text(a.region)),
ListTile(
title: Text("Country"),
trailing: Text(a.country)),
],
),
))
.toList())
]);
}
}
class ItemsTile extends StatelessWidget {
ItemsTile(this._title, this._items);
final Iterable<Item> _items;
final String _title;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ListTile(title: Text(_title)),
Column(
children: _items
.map((i) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ListTile(
title: Text(i.label ?? ""),
trailing: Text(i.value ?? ""))))
.toList())
]);
}
}
class AddContactPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => _AddContactPageState();
}
class _AddContactPageState extends State<AddContactPage> {
Contact contact = Contact();
PostalAddress address = PostalAddress(label: "Home");
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Add a contact"),
actions: <Widget>[
FlatButton(
onPressed: () {
_formKey.currentState.save();
contact.postalAddresses = [address];
ContactsService.addContact(contact);
Navigator.of(context).pop();
},
child: Icon(Icons.save, color: Colors.white))
],
),
body: Container(
padding: EdgeInsets.all(12.0),
child: Form(
key: _formKey,
child: ListView(
children: <Widget>[
TextFormField(
decoration: const InputDecoration(labelText: 'First name'),
onSaved: (v) => contact.givenName = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Middle name'),
onSaved: (v) => contact.middleName = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Last name'),
onSaved: (v) => contact.familyName = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Prefix'),
onSaved: (v) => contact.prefix = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Suffix'),
onSaved: (v) => contact.suffix = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Phone'),
onSaved: (v) =>
contact.phones = [Item(label: "mobile", value: v)],
keyboardType: TextInputType.phone),
TextFormField(
decoration: const InputDecoration(labelText: 'E-mail'),
onSaved: (v) =>
contact.emails = [Item(label: "work", value: v)],
keyboardType: TextInputType.emailAddress),
TextFormField(
decoration: const InputDecoration(labelText: 'Company'),
onSaved: (v) => contact.company = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Job'),
onSaved: (v) => contact.jobTitle = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Street'),
onSaved: (v) => address.street = v),
TextFormField(
decoration: const InputDecoration(labelText: 'City'),
onSaved: (v) => address.city = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Region'),
onSaved: (v) => address.region = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Postal code'),
onSaved: (v) => address.postcode = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Country'),
onSaved: (v) => address.country = v),
],
)),
),
);
}
}
我想在我的应用程序中显示我尝试使用 contacts_service and contacts_plugin 的联系人,但是当我 运行 安装软件包后我的应用程序崩溃了。我刚刚导入了文件并且没有编写任何代码。我也没有看到任何日志。
在我的应用程序中集成联系人的任何其他方式也会有很大帮助!
您确实需要学习和掌握开发 Flutter 应用程序所需的工具。
当你 post 关于 SO 的问题时,你真的需要更好地解释你的需求。
也就是说我已经快速试用了 contacts_plugin and contact_service。
不要同时使用它们,它们是为同一目的而制作的。选择一个或另一个。
似乎contact_service
更稳定:加权得分92超过82。
Android
正在尝试使用 contacts_plugin
我猜你不喜欢这个错误:
* What went wrong:
The Android Gradle plugin supports only Kotlin Gradle plugin version 1.2.51 and higher. Project 'contacts_plugin' is using version 1.2.30.
那是因为 contacts_plugin
使用 kotlin 版本 1.2.30
并且你像我一样拥有 AS >= 3.2。
ext.kotlin_version = '1.2.30'
.
如果您想尝试一下,可以克隆项目并以这种方式包含依赖项:
contacts_plugin:
path: ../flutter-contacts-plugin/
并在插件中更改 build.gradle 这一行:
ext.kotlin_version = '1.2.30'
有了这个
ext.kotlin_version = '1.2.51'
如果您使用 contact_plugins
,即使 iOS 项目也会出现问题。
contact_serivce
相反,它在 Android 和 iOS 上工作正常。
无论如何永远记住在 Android 上你需要在 AndroidManifest.xml
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
和 iOS 确保在 Info.plist
文件中设置 NSContactsUsageDescription
<key>NSContactsUsageDescription</key>
<string>This app requires contacts access to function properly.</string>
并创建一个具有 Swift 支持的 Flutter 项目。
可以使用contact_service
插件提供的default example开始学习:
import 'package:flutter/material.dart';
import 'package:contacts_service/contacts_service.dart';
void main() => runApp(ContactsExampleApp());
class ContactsExampleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(routes: <String, WidgetBuilder>{
'/add': (BuildContext context) => AddContactPage()
}, home: ContactListPage());
}
}
class ContactListPage extends StatefulWidget {
@override
_ContactListPageState createState() => _ContactListPageState();
}
class _ContactListPageState extends State<ContactListPage> {
Iterable<Contact> _contacts;
@override
initState() {
super.initState();
refreshContacts();
}
refreshContacts() async {
var contacts = await ContactsService.getContacts();
setState(() {
_contacts = contacts;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Contacts Plugin Example')),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
Navigator.of(context).pushNamed("/add").then((_) {
refreshContacts();
});
}),
body: SafeArea(
child: _contacts != null
? ListView.builder(
itemCount: _contacts?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
Contact c = _contacts?.elementAt(index);
return ListTile(
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (BuildContext context) =>
ContactDetailsPage(c)));
},
leading: (c.avatar != null && c.avatar.length > 0)
? CircleAvatar(backgroundImage: MemoryImage(c.avatar))
: CircleAvatar(
child: Text(c.displayName.length > 1
? c.displayName?.substring(0, 2)
: "")),
title: Text(c.displayName ?? ""),
);
},
)
: Center(child: CircularProgressIndicator()),
),
);
}
}
class ContactDetailsPage extends StatelessWidget {
ContactDetailsPage(this._contact);
final Contact _contact;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:
AppBar(title: Text(_contact.displayName ?? ""), actions: <Widget>[
FlatButton(
child: Icon(Icons.delete),
onPressed: () {
ContactsService.deleteContact(_contact);
})
]),
body: SafeArea(
child: ListView(
children: <Widget>[
ListTile(
title: Text("Name"),
trailing: Text(_contact.givenName ?? "")),
ListTile(
title: Text("Middle name"),
trailing: Text(_contact.middleName ?? "")),
ListTile(
title: Text("Family name"),
trailing: Text(_contact.familyName ?? "")),
ListTile(
title: Text("Prefix"), trailing: Text(_contact.prefix ?? "")),
ListTile(
title: Text("Suffix"), trailing: Text(_contact.suffix ?? "")),
ListTile(
title: Text("Company"),
trailing: Text(_contact.company ?? "")),
ListTile(
title: Text("Job"), trailing: Text(_contact.jobTitle ?? "")),
AddressesTile(_contact.postalAddresses),
ItemsTile("Phones", _contact.phones),
ItemsTile("Emails", _contact.emails)
],
),
));
}
}
class AddressesTile extends StatelessWidget {
AddressesTile(this._addresses);
final Iterable<PostalAddress> _addresses;
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ListTile(title: Text("Addresses")),
Column(
children: _addresses
.map((a) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: <Widget>[
ListTile(
title: Text("Street"),
trailing: Text(a.street)),
ListTile(
title: Text("Postcode"),
trailing: Text(a.postcode)),
ListTile(
title: Text("City"), trailing: Text(a.city)),
ListTile(
title: Text("Region"),
trailing: Text(a.region)),
ListTile(
title: Text("Country"),
trailing: Text(a.country)),
],
),
))
.toList())
]);
}
}
class ItemsTile extends StatelessWidget {
ItemsTile(this._title, this._items);
final Iterable<Item> _items;
final String _title;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
ListTile(title: Text(_title)),
Column(
children: _items
.map((i) => Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: ListTile(
title: Text(i.label ?? ""),
trailing: Text(i.value ?? ""))))
.toList())
]);
}
}
class AddContactPage extends StatefulWidget {
@override
State<StatefulWidget> createState() => _AddContactPageState();
}
class _AddContactPageState extends State<AddContactPage> {
Contact contact = Contact();
PostalAddress address = PostalAddress(label: "Home");
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Add a contact"),
actions: <Widget>[
FlatButton(
onPressed: () {
_formKey.currentState.save();
contact.postalAddresses = [address];
ContactsService.addContact(contact);
Navigator.of(context).pop();
},
child: Icon(Icons.save, color: Colors.white))
],
),
body: Container(
padding: EdgeInsets.all(12.0),
child: Form(
key: _formKey,
child: ListView(
children: <Widget>[
TextFormField(
decoration: const InputDecoration(labelText: 'First name'),
onSaved: (v) => contact.givenName = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Middle name'),
onSaved: (v) => contact.middleName = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Last name'),
onSaved: (v) => contact.familyName = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Prefix'),
onSaved: (v) => contact.prefix = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Suffix'),
onSaved: (v) => contact.suffix = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Phone'),
onSaved: (v) =>
contact.phones = [Item(label: "mobile", value: v)],
keyboardType: TextInputType.phone),
TextFormField(
decoration: const InputDecoration(labelText: 'E-mail'),
onSaved: (v) =>
contact.emails = [Item(label: "work", value: v)],
keyboardType: TextInputType.emailAddress),
TextFormField(
decoration: const InputDecoration(labelText: 'Company'),
onSaved: (v) => contact.company = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Job'),
onSaved: (v) => contact.jobTitle = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Street'),
onSaved: (v) => address.street = v),
TextFormField(
decoration: const InputDecoration(labelText: 'City'),
onSaved: (v) => address.city = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Region'),
onSaved: (v) => address.region = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Postal code'),
onSaved: (v) => address.postcode = v),
TextFormField(
decoration: const InputDecoration(labelText: 'Country'),
onSaved: (v) => address.country = v),
],
)),
),
);
}
}