Привет, Хабр! Представляем вашему вниманию перевод статьи «Использование SQLite во Flutter» .
Сохранение данных очень важно для пользователей, поскольку загружать одни и те же данные из сети непрактично.
Разумнее хранить их локально.
В этой статье я покажу, как это сделать, используя SQLite во Flutter-e.
Почему SQLite?
SQLite — самый популярный способ хранения данных на мобильных устройствах.
В этой статье мы будем использовать пакет sqflite для использования SQLite. Sqflite — одна из наиболее часто используемых и подходящих библиотек для подключения базы данных SQLite во Flutter.
1. Добавление зависимостей
В нашем проекте мы открываем файл pubspec.yaml .В зависимости добавляем последнюю версию sqflite и path_provider.
dependencies: flutter: sdk: flutter sqflite: any path_provider: any
2. Создадим Клиент БД
Теперь давайте создадим новый файл Database.dart. Давайте создадим в нем синглтон.Зачем нам нужен синглтон: мы используем этот шаблон, чтобы гарантировать, что у нас есть только одна сущность класса, и предоставить глобальную точку входа в нее.
1. Давайте создадим приватный конструктор, который можно будет использовать только внутри этого класса.
class DBProvider {
DBProvider._();
static final DBProvider db = DBProvider._();
}
2. Настройте базу данных
Следующим шагом будет создание объекта базы данных и предоставление метода получения, в котором мы создадим объект базы данных, если он еще не был создан (ленивая инициализация).
static Database _database;
Future<Database> get database async {
if (_database != null)
return _database;
// if _database is null we instantiate it
_database = await initDB();
return _database;
}
Если базе данных не назначен объект, мы вызовем функцию initDB для создания базы данных.
В этой функции мы получим путь для сохранения базы данных и создания нужных таблиц.
initDB() async {
Directory documentsDirectory = await getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "TestDB.db");
return await openDatabase(path, version: 1, onOpen: (db) {
}, onCreate: (Database db, int version) async {
await db.execute("CREATE TABLE Client ("
"id INTEGER PRIMARY KEY,"
"first_name TEXT,"
"last_name TEXT,"
"blocked BIT"
")");
});
}
3. Создайте класс модели
Данные внутри базы данных будут преобразованы в карты Dart. Нам нужно создать классы модели с методами toMap и fromMap. Чтобы создать классы моделей, я собираюсь использовать это Веб-сайт Наша модель: /// ClientModel.dart
import 'dart:convert';
Client clientFromJson(String str) {
final jsonData = json.decode(str);
return Client.fromJson(jsonData);
}
String clientToJson(Client data) {
final dyn = data.toJson();
return json.encode(dyn);
}
class Client {
int id;
String firstName;
String lastName;
bool blocked;
Client({
this.id,
this.firstName,
this.lastName,
this.blocked,
});
factory Client.fromJson(Map<String, dynamic> json) => new Client(
id: json["id"],
firstName: json["first_name"],
lastName: json["last_name"],
blocked: json["blocked"],
);
Map<String, dynamic> toJson() => {
"id": id,
"first_name": firstName,
"last_name": lastName,
"blocked": blocked,
};
}
4. CRUD-операции
Создавать Использование rawInsert: newClient(Client newClient) async {
final db = await database;
var res = await db.rawInsert(
"INSERT Into Client (id,first_name)"
" VALUES (${newClient.id},${newClient.firstName})");
return res;
}
Использование вставки:
newClient(Client newClient) async {
final db = await database;
var res = await db.insert("Client", newClient.toMap());
return res;
}
Другой пример использования большого идентификатора в качестве нового идентификатора.
newClient(Client newClient) async {
final db = await database;
//get the biggest id in the table
var table = await db.rawQuery("SELECT MAX(id)+1 as id FROM Client");
int id = table.first["id"];
//insert to the table using the new id
var raw = await db.rawInsert(
"INSERT Into Client (id,first_name,last_name,blocked)"
" VALUES (?,?,?,?)",
[id, newClient.firstName, newClient.lastName, newClient.blocked]);
return raw;
}
Читать
Получить клиента по идентификатору getClient(int id) async {
final db = await database;
var res =await db.query("Client", where: "id = ?", whereArgs: [id]);
return res.isNotEmpty ? Client.fromMap(res.first) : Null ;
}
Получить всех клиентов с условием getAllClients() async {
final db = await database;
var res = await db.query("Client");
List<Client> list =
res.isNotEmpty ? res.map((c) => Client.fromMap(c)).
toList() : [];
return list;
}
Получить только заблокированных клиентов getBlockedClients() async {
final db = await database;
var res = await db.rawQuery("SELECT * FROM Client WHERE blocked=1");
List<Client> list =
res.isNotEmpty ? res.toList().
map((c) => Client.fromMap(c)) : null;
return list;
}
Обновлять
Обновить существующий клиент updateClient(Client newClient) async {
final db = await database;
var res = await db.update("Client", newClient.toMap(),
where: "id = ?", whereArgs: [newClient.id]);
return res;
}
Блокировка/разблокировка клиента
blockOrUnblock(Client client) async {
final db = await database;
Client blocked = Client(
id: client.id,
firstName: client.firstName,
lastName: client.lastName,
blocked: !client.blocked);
var res = await db.update("Client", blocked.toMap(),
where: "id = ?", whereArgs: [client.id]);
return res;
}
Удалить
Удалить одного клиента deleteClient(int id) async {
final db = await database;
db.delete("Client", where: "id = ?", whereArgs: [id]);
}
Удалить всех клиентов deleteAll() async {
final db = await database;
db.rawDelete("Delete * from Client");
}
Демо
Для нашей демонстрации мы создадим простое приложение, отображающее нашу базу данных.
Для начала давайте выложим экран Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Flutter SQLite")),
body: FutureBuilder<List<Client>>(
future: DBProvider.db.getAllClients(),
builder: (BuildContext context, AsyncSnapshot<List<Client>> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
Client item = snapshot.data[index];
return ListTile(
title: Text(item.lastName),
leading: Text(item.id.toString()),
trailing: Checkbox(
onChanged: (bool value) {
DBProvider.db.blockClient(item);
setState(() {});
},
value: item.blocked,
),
);
},
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () async {
Client rnd = testClients[math.Random().
nextInt(testClients.length)];
await DBProvider.db.newClient(rnd);
setState(() {});
},
),
);
}
Примечания:
1. FutureBuilder используется для получения данных из базы данных.
2. FAB для инициализации тестовых клиентов List<Client> testClients = [
Client(firstName: "Raouf", lastName: "Rahiche", blocked: false),
Client(firstName: "Zaki", lastName: "oun", blocked: true),
Client(firstName: "oussama", lastName: "ali", blocked: false),
];
3. Индикатор CircularProgressIndicator отображается при отсутствии данных.
4. При нажатии пользователем на галочки клиент блокируется/разблокируется.
Теперь очень легко добавлять новые функции.
Например, если мы хотим удалить клиента в тот момент, когда он проведет пальцем по экрану, просто оберните ListTile в виджет Dismissible, например: return Dismissible(
key: UniqueKey(),
background: Container(color: Colors.red),
onDismissed: (direction) {
DBProvider.db.deleteClient(item.id);
},
child: ListTile(.
),
);
Рефакторинг для использования шаблона BLoC
В этой статье мы многое сделали, но в реальных приложениях инициализация состояний на уровне пользовательского интерфейса — не лучшая идея.Давайте отделим логику от пользовательского интерфейса.
Во Flutter существует множество шаблонов, но мы будем использовать BLoC, поскольку он наиболее гибок в настройке.
Давайте создадим BLoC class ClientsBloc {
ClientsBloc() {
getClients();
}
final _clientController = StreamController<List<Client>>.
broadcast();
get clients => _clientController.stream;
dispose() {
_clientController.close();
}
getClients() async {
_clientController.sink.add(await DBProvider.db.getAllClients());
}
}
Примечания:
Примечания:
1. getClients получает данные из базы данных (таблицы Client) асинхронно.
Мы будем использовать этот метод всякий раз, когда нам понадобится обновить таблицу, поэтому его стоит поместить в тело конструктора.
2. Мы создали StreamController.broadcast для того, чтобы прослушивать транслируемые события более одного раза.
В нашем примере это не имеет особого значения, поскольку мы слушаем их только один раз, но, возможно, было бы неплохо реализовать это в будущем.
3. Не забудьте закрыть стримы.
Таким образом мы предотвратим мемориальные лица.
В нашем примере мы закрываем их с помощью метода Dispose в StatefulWidget.
Теперь давайте посмотрим на код blockUnblock(Client client) {
DBProvider.db.blockOrUnblock(client);
getClients();
}
delete(int id) {
DBProvider.db.deleteClient(id);
getClients();
}
add(Client client) {
DBProvider.db.newClient(client);
getClients();
}
И наконец окончательный результат
Исходники можно посмотреть здесь - Гитхаб
Теги: #iOS #Android #разработка iOS #разработка Android #разработка мобильных приложений #разработка iOS #разработка Android #flutter #SQLite
-
Кто Такие Ит-Аналитики И Чем Они Занимаются?
19 Oct, 24 -
Кража Контента В Китайской Разработке Игр
19 Oct, 24 -
Интернет Будущего Внутри Отдельной Услуги
19 Oct, 24