关闭应用程序后保留变量值但遇到数据库问题
Keeping variable value after closing the application but having trouble with database
我正在尝试导入设备联系人并使用复选框列出它们,然后用户选择并将它们发送到其他屏幕并对其进行操作。
我使用了 contacts_service 插件并在 init
上使用了以下代码
var selectedContacts = List<Contact>();
List<Contact> _contacts;
Future<void> refreshContacts() async {
var contacts = (await ContactsService.getContacts(
withThumbnails: false, iOSLocalizedLabels: true))
.toList();
setState(() {
_contacts = contacts;
});
}
和列表视图:
_contacts != null
? ListView.builder(
itemCount: _contacts?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
Contact c = _contacts?.elementAt(index);
return CheckboxListTile(
value: selectedContacts.contains(c),
onChanged: (bool value) {
setState(() {
value
? selectedContacts.add(c)//
: selectedContacts.remove(c);//
});
},
title: Text(c.displayName ?? ""),
secondary: CircleAvatar(child: Text(c.initials())),
);
},
)
: Center(
child: CircularProgressIndicator(),
),
我的问题是当应用程序关闭并重新打开时复选框消失并且 selectedContacts 为空。我花了很多时间试图让数据库工作,但我无法让它工作,主要是因为 'Contact' class 有 phone 变量作为可迭代的,我无法将它保存到数据库。
我自己创建了 class,其中包含 ID、名称、phone,因此将它们分配给数据库会容易得多。
但后来我无法解决 value: selectedContacts.contains(c),
并且复选框不起作用。我尝试创建自己的 containst 函数,我在其中获取 Contact c 及其 phone 值并将其与我的列表进行比较并查找 phones 是否匹配并发送 true 和 false 仍然不起作用。
我的想法是 var selectedContacts =
我可以做到 database empty ? List<Contact> : import Contacts from database
然后我想我仍然不知道如何将 selectedContacts 发送到另一个文件和另一个主页,但我可以找到它我假设。
我没有尝试过 sharedpref,因为据我所知,它可以存储小尺寸,如果我的用户从 'Contact' class 中选择很多对象,那将是一个问题。
如果我的解释不好,我很抱歉。这不是那么重要,我只是想学习,在此先感谢
更新:我尝试再次使用数据库,我的代码如下所示。
database.dart
import 'dart:io' as io;
import 'package:contacts_service/contacts_service.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
class DatabaseHelper{
static final DatabaseHelper _instance = new DatabaseHelper.internal();
factory DatabaseHelper() => _instance;
static Database _db;
DatabaseHelper.internal();
static initDb() async{
io.Directory documentDirectory = await getApplicationDocumentsDirectory();
String path = join(documentDirectory.path,"contactDatabase.db");
var taskDb= await openDatabase(path,version: 1,onCreate: _onCreate);
return taskDb;
}
static void _onCreate(Database db,int version) async{
await db.execute(
"CREATE TABLE contacts(identifier TEXT PRIMARY KEY, displayName TEXT, givenName TEXT, middleName TEXT, familyName TEXT, prefix TEXT, suffix TEXT, company TEXT, jobTitle TEXT, androidAccountType TEXT, androidAccountName TEXT, emails TEXT, phones TEXT, postalAddresses TEXT, avatar TEXT, birthday TEXT, )"
);
}
static Future<void> addContact(Contact c) async{
final Database db = initDb();
await db.insert('contacts', c.toMap(),conflictAlgorithm: ConflictAlgorithm.replace,);
}
static Future<void> deleteContact(Contact c) async{
final Database db = initDb();
await db.delete('contacts', where: "id = ?", whereArgs: [c.identifier],);
}
static Future<List<Contact>> importContacts() async{
final Database db = initDb();
final List<Map<String, dynamic>> maps = await db.query('contacts');
return List.generate(maps.length, (index) => Contact.fromMap(maps[index]));
}
static Future<bool> dbEmpty() async{
final Database db = initDb();
int count = Sqflite.firstIntValue(await db.rawQuery('SELECT COUNT(*) FROM contacts'));
if (count>0){
return true;
}
else
return false;
}
}
我的联系人屏幕是这样的。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:contacts_service/contacts_service.dart';
import 'database.dart';
class ContactScreen extends StatefulWidget {
@override
_ContactScreenState createState() => _ContactScreenState();
}
class _ContactScreenState extends State<ContactScreen> {
void checkDb() async{
List<Contact> selectedContacts = await DatabaseHelper.dbEmpty()?DatabaseHelper.importContacts():List<Contact>();
}
List<Contact> selectedContacts;
List<Contact> _contacts;
DatabaseHelper databaseHelper = new DatabaseHelper();
@override
void initState() {
super.initState();
refreshContacts();
checkDb();
}
Future<void> refreshContacts() async {
// Load without thumbnails initially.
var contacts = (await ContactsService.getContacts(
withThumbnails: false, iOSLocalizedLabels: true))
.toList();
setState(() {
_contacts = contacts;
});
}
void addDb(Contact c){
selectedContacts.add(c);
DatabaseHelper.addContact(c);
}
void deleteDb(Contact c){
selectedContacts.remove(c);
DatabaseHelper.deleteContact(c);
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: _contacts != null
? ListView.builder(
itemCount: _contacts?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
Contact c = _contacts?.elementAt(index);
return CheckboxListTile(
value: selectedContacts.contains(c),
onChanged: (bool value) {
setState(() {
value
? addDb(c)//selectedContacts.add(c)
: deleteDb(c);//selectedContacts.remove(c);
});
},
title: Text(c.displayName ?? ""),
secondary: CircleAvatar(child: Text(c.initials())),
);
},
)
: Center(
child: CircularProgressIndicator(),
),
);
}
}
我无法使这段代码工作。
可以用所有文本创建我的数据库吗?根据我的理解 contact_service 像它的文本一样得到它的 fromMap 所以我也跟着它。
另外 contact_service 代码如下所示:
String identifier, displayName, givenName, middleName, prefix, suffix, familyName, company, jobTitle;
String androidAccountTypeRaw, androidAccountName;
AndroidAccountType androidAccountType;
Iterable<Item> emails = [];
Iterable<Item> phones = [];
Iterable<PostalAddress> postalAddresses = [];
Uint8List avatar;
DateTime birthday;
String initials() {
return ((this.givenName?.isNotEmpty == true ? this.givenName[0] : "") +
(this.familyName?.isNotEmpty == true ? this.familyName[0] : ""))
.toUpperCase();
}
Contact.fromMap(Map m) {
identifier = m["identifier"];
displayName = m["displayName"];
givenName = m["givenName"];
middleName = m["middleName"];
familyName = m["familyName"];
prefix = m["prefix"];
suffix = m["suffix"];
company = m["company"];
jobTitle = m["jobTitle"];
androidAccountTypeRaw = m["androidAccountType"];
androidAccountType = accountTypeFromString(androidAccountTypeRaw);
androidAccountName = m["androidAccountName"];
emails = (m["emails"] as Iterable)?.map((m) => Item.fromMap(m));
phones = (m["phones"] as Iterable)?.map((m) => Item.fromMap(m));
postalAddresses = (m["postalAddresses"] as Iterable)
?.map((m) => PostalAddress.fromMap(m));
avatar = m["avatar"];
try {
birthday = DateTime.parse(m["birthday"]);
} catch (e) {
birthday = null;
}
}
static Map _toMap(Contact contact) {
var emails = [];
for (Item email in contact.emails ?? []) {
emails.add(Item._toMap(email));
}
var phones = [];
for (Item phone in contact.phones ?? []) {
phones.add(Item._toMap(phone));
}
var postalAddresses = [];
for (PostalAddress address in contact.postalAddresses ?? []) {
postalAddresses.add(PostalAddress._toMap(address));
}
final birthday = contact.birthday == null
? null
: "${contact.birthday.year.toString()}-${contact.birthday.month.toString().padLeft(2, '0')}-${contact.birthday.day.toString().padLeft(2, '0')}";
return {
"identifier": contact.identifier,
"displayName": contact.displayName,
"givenName": contact.givenName,
"middleName": contact.middleName,
"familyName": contact.familyName,
"prefix": contact.prefix,
"suffix": contact.suffix,
"company": contact.company,
"jobTitle": contact.jobTitle,
"androidAccountType": contact.androidAccountTypeRaw,
"androidAccountName": contact.androidAccountName,
"emails": emails,
"phones": phones,
"postalAddresses": postalAddresses,
"avatar": contact.avatar,
"birthday": birthday
};
}
Map toMap() {
return Contact._toMap(this);
}
本节中:
void checkDb() async{
List<Contact> selectedContacts = await DatabaseHelper.dbEmpty() ? DatabaseHelper.importContacts() : List<Contact>();
}
你无法从数据库获取局部变量的联系,你是新创建的并且不使用它(因为 checkDb()
是一个方法和方法范围内可用的变量)。在你的情况下,你可以使用 FutureBuilder
小部件(阅读更多 here),它必须是这样的:
@override
Widget build(BuildContext context) {
return FutureBuilder<List<Contact>>(
future: DatabaseHelper.importContacts(), // You can do not check contacts availability in database, just return empty list by default
builder (context, snapshot) {
// Contacts list doesn't loaded, you need to show progress loader to a user
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(); // Here your code for list
}
);
}
我正在尝试导入设备联系人并使用复选框列出它们,然后用户选择并将它们发送到其他屏幕并对其进行操作。 我使用了 contacts_service 插件并在 init
上使用了以下代码 var selectedContacts = List<Contact>();
List<Contact> _contacts;
Future<void> refreshContacts() async {
var contacts = (await ContactsService.getContacts(
withThumbnails: false, iOSLocalizedLabels: true))
.toList();
setState(() {
_contacts = contacts;
});
}
和列表视图:
_contacts != null
? ListView.builder(
itemCount: _contacts?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
Contact c = _contacts?.elementAt(index);
return CheckboxListTile(
value: selectedContacts.contains(c),
onChanged: (bool value) {
setState(() {
value
? selectedContacts.add(c)//
: selectedContacts.remove(c);//
});
},
title: Text(c.displayName ?? ""),
secondary: CircleAvatar(child: Text(c.initials())),
);
},
)
: Center(
child: CircularProgressIndicator(),
),
我的问题是当应用程序关闭并重新打开时复选框消失并且 selectedContacts 为空。我花了很多时间试图让数据库工作,但我无法让它工作,主要是因为 'Contact' class 有 phone 变量作为可迭代的,我无法将它保存到数据库。
我自己创建了 class,其中包含 ID、名称、phone,因此将它们分配给数据库会容易得多。
但后来我无法解决 value: selectedContacts.contains(c),
并且复选框不起作用。我尝试创建自己的 containst 函数,我在其中获取 Contact c 及其 phone 值并将其与我的列表进行比较并查找 phones 是否匹配并发送 true 和 false 仍然不起作用。
我的想法是 var selectedContacts =
我可以做到 database empty ? List<Contact> : import Contacts from database
然后我想我仍然不知道如何将 selectedContacts 发送到另一个文件和另一个主页,但我可以找到它我假设。
我没有尝试过 sharedpref,因为据我所知,它可以存储小尺寸,如果我的用户从 'Contact' class 中选择很多对象,那将是一个问题。
如果我的解释不好,我很抱歉。这不是那么重要,我只是想学习,在此先感谢
更新:我尝试再次使用数据库,我的代码如下所示。 database.dart
import 'dart:io' as io;
import 'package:contacts_service/contacts_service.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path_provider/path_provider.dart';
class DatabaseHelper{
static final DatabaseHelper _instance = new DatabaseHelper.internal();
factory DatabaseHelper() => _instance;
static Database _db;
DatabaseHelper.internal();
static initDb() async{
io.Directory documentDirectory = await getApplicationDocumentsDirectory();
String path = join(documentDirectory.path,"contactDatabase.db");
var taskDb= await openDatabase(path,version: 1,onCreate: _onCreate);
return taskDb;
}
static void _onCreate(Database db,int version) async{
await db.execute(
"CREATE TABLE contacts(identifier TEXT PRIMARY KEY, displayName TEXT, givenName TEXT, middleName TEXT, familyName TEXT, prefix TEXT, suffix TEXT, company TEXT, jobTitle TEXT, androidAccountType TEXT, androidAccountName TEXT, emails TEXT, phones TEXT, postalAddresses TEXT, avatar TEXT, birthday TEXT, )"
);
}
static Future<void> addContact(Contact c) async{
final Database db = initDb();
await db.insert('contacts', c.toMap(),conflictAlgorithm: ConflictAlgorithm.replace,);
}
static Future<void> deleteContact(Contact c) async{
final Database db = initDb();
await db.delete('contacts', where: "id = ?", whereArgs: [c.identifier],);
}
static Future<List<Contact>> importContacts() async{
final Database db = initDb();
final List<Map<String, dynamic>> maps = await db.query('contacts');
return List.generate(maps.length, (index) => Contact.fromMap(maps[index]));
}
static Future<bool> dbEmpty() async{
final Database db = initDb();
int count = Sqflite.firstIntValue(await db.rawQuery('SELECT COUNT(*) FROM contacts'));
if (count>0){
return true;
}
else
return false;
}
}
我的联系人屏幕是这样的。
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:contacts_service/contacts_service.dart';
import 'database.dart';
class ContactScreen extends StatefulWidget {
@override
_ContactScreenState createState() => _ContactScreenState();
}
class _ContactScreenState extends State<ContactScreen> {
void checkDb() async{
List<Contact> selectedContacts = await DatabaseHelper.dbEmpty()?DatabaseHelper.importContacts():List<Contact>();
}
List<Contact> selectedContacts;
List<Contact> _contacts;
DatabaseHelper databaseHelper = new DatabaseHelper();
@override
void initState() {
super.initState();
refreshContacts();
checkDb();
}
Future<void> refreshContacts() async {
// Load without thumbnails initially.
var contacts = (await ContactsService.getContacts(
withThumbnails: false, iOSLocalizedLabels: true))
.toList();
setState(() {
_contacts = contacts;
});
}
void addDb(Contact c){
selectedContacts.add(c);
DatabaseHelper.addContact(c);
}
void deleteDb(Contact c){
selectedContacts.remove(c);
DatabaseHelper.deleteContact(c);
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: _contacts != null
? ListView.builder(
itemCount: _contacts?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
Contact c = _contacts?.elementAt(index);
return CheckboxListTile(
value: selectedContacts.contains(c),
onChanged: (bool value) {
setState(() {
value
? addDb(c)//selectedContacts.add(c)
: deleteDb(c);//selectedContacts.remove(c);
});
},
title: Text(c.displayName ?? ""),
secondary: CircleAvatar(child: Text(c.initials())),
);
},
)
: Center(
child: CircularProgressIndicator(),
),
);
}
}
我无法使这段代码工作。 可以用所有文本创建我的数据库吗?根据我的理解 contact_service 像它的文本一样得到它的 fromMap 所以我也跟着它。
另外 contact_service 代码如下所示:
String identifier, displayName, givenName, middleName, prefix, suffix, familyName, company, jobTitle;
String androidAccountTypeRaw, androidAccountName;
AndroidAccountType androidAccountType;
Iterable<Item> emails = [];
Iterable<Item> phones = [];
Iterable<PostalAddress> postalAddresses = [];
Uint8List avatar;
DateTime birthday;
String initials() {
return ((this.givenName?.isNotEmpty == true ? this.givenName[0] : "") +
(this.familyName?.isNotEmpty == true ? this.familyName[0] : ""))
.toUpperCase();
}
Contact.fromMap(Map m) {
identifier = m["identifier"];
displayName = m["displayName"];
givenName = m["givenName"];
middleName = m["middleName"];
familyName = m["familyName"];
prefix = m["prefix"];
suffix = m["suffix"];
company = m["company"];
jobTitle = m["jobTitle"];
androidAccountTypeRaw = m["androidAccountType"];
androidAccountType = accountTypeFromString(androidAccountTypeRaw);
androidAccountName = m["androidAccountName"];
emails = (m["emails"] as Iterable)?.map((m) => Item.fromMap(m));
phones = (m["phones"] as Iterable)?.map((m) => Item.fromMap(m));
postalAddresses = (m["postalAddresses"] as Iterable)
?.map((m) => PostalAddress.fromMap(m));
avatar = m["avatar"];
try {
birthday = DateTime.parse(m["birthday"]);
} catch (e) {
birthday = null;
}
}
static Map _toMap(Contact contact) {
var emails = [];
for (Item email in contact.emails ?? []) {
emails.add(Item._toMap(email));
}
var phones = [];
for (Item phone in contact.phones ?? []) {
phones.add(Item._toMap(phone));
}
var postalAddresses = [];
for (PostalAddress address in contact.postalAddresses ?? []) {
postalAddresses.add(PostalAddress._toMap(address));
}
final birthday = contact.birthday == null
? null
: "${contact.birthday.year.toString()}-${contact.birthday.month.toString().padLeft(2, '0')}-${contact.birthday.day.toString().padLeft(2, '0')}";
return {
"identifier": contact.identifier,
"displayName": contact.displayName,
"givenName": contact.givenName,
"middleName": contact.middleName,
"familyName": contact.familyName,
"prefix": contact.prefix,
"suffix": contact.suffix,
"company": contact.company,
"jobTitle": contact.jobTitle,
"androidAccountType": contact.androidAccountTypeRaw,
"androidAccountName": contact.androidAccountName,
"emails": emails,
"phones": phones,
"postalAddresses": postalAddresses,
"avatar": contact.avatar,
"birthday": birthday
};
}
Map toMap() {
return Contact._toMap(this);
}
本节中:
void checkDb() async{
List<Contact> selectedContacts = await DatabaseHelper.dbEmpty() ? DatabaseHelper.importContacts() : List<Contact>();
}
你无法从数据库获取局部变量的联系,你是新创建的并且不使用它(因为 checkDb()
是一个方法和方法范围内可用的变量)。在你的情况下,你可以使用 FutureBuilder
小部件(阅读更多 here),它必须是这样的:
@override
Widget build(BuildContext context) {
return FutureBuilder<List<Contact>>(
future: DatabaseHelper.importContacts(), // You can do not check contacts availability in database, just return empty list by default
builder (context, snapshot) {
// Contacts list doesn't loaded, you need to show progress loader to a user
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(); // Here your code for list
}
);
}