798.23K
Category: informaticsinformatics

Работа с файлами и данными

1.

Работа с файлами и
данными

2.

File class
var myFile = File('file.txt');
Класс File содержит методы для управления
файлами и их содержимым. Используя методы
этого класса, вы можете открывать и закрывать
файлы, выполнять чтение и запись из них, создавать
и удалять их, а также проверять их существование.

3.

4.

Read from a file
import 'dart:async';
import 'dart:io';
void main() {
File('file.txt').readAsString().then((String
contents) {
print(contents);
});
}

5.

import 'dart:io';
import 'dart:convert';
import 'dart:async';
void main() async {
final file = File('file.txt');
Stream<String> lines = file.openRead()
.transform(utf8.decoder)
// Decode bytes to UTF-8.
.transform(LineSplitter()); // Convert stream to individual lines.
try {
await for (var line in lines) {
print('$line: ${line.length} characters');
}
print('File is now closed.');
} catch (e) {
print('Error: $e');
}

6.

Write to a file
import 'dart:io';
void main() async {
final filename = 'file.txt';
var file = await
File(filename).writeAsString('some content');
// Do something with the file.
}

7.

IOSink
IOSink предназначен для записи байтов.
Строки, записанные с помощью write или
writeCharCode, будут преобразованы в
байты с использованием кодировки.
Целочисленные данные, добавленные с
помощью add или addStream, будут
обрабатываться как байтовые данные и
будут усечены до 8-разрядных значений
без знака, как при использовании
int.toUnsigned.

8.

import 'dart:io';
void main() {
var file = File('file.txt');
var sink = file.openWrite();
sink.write('FILE ACCESSED ${DateTime.now()}\n');
// Close the IOSink to free system resources.
sink.close();
}

9.

Использование асинхронных методов
import 'dart:io';
void main() async {
final file = File('file.txt');
var length = await file.length();
print(length);
}

10.

path_provider

11.

Поддерживаемые платформы и пути
Directory
Android
macOS
Windows
Temporary





Application
Support





Application
Library





Application
Documents





Application
Cache





External Storage





External Cache
Directories





External Storage
Directories





✔ (❌ 2,12)




Downloads
iOS
Linux

12.

Тестирование
path_provider теперь использует PlatformInterface, что
означает, что не все платформы используют единую
реализацию на основе PlatformChannel. С учетом этого
изменения тесты должны быть обновлены, чтобы
имитировать PathProviderPlatform, а не
PlatformChannel.

13.

const String kTemporaryPath = 'temporaryPath';
setUp(() async {
PathProviderPlatform.instance = FakePathProviderPlatform();
});
test('getTemporaryDirectory', () async {
final Directory result = await getTemporaryDirectory();
expect(result.path, kTemporaryPath);
});

14.

class FakePathProviderPlatform extends Fake
with MockPlatformInterfaceMixin
implements PathProviderPlatform {
@override
Future<String?> getTemporaryPath() async {
return kTemporaryPath;
}

15.

TemporaryDirectory
Future<Directory> getTemporaryDirectory() async {
final String? path = await
_platform.getTemporaryPath();
if (path == null) {
throw MissingPlatformDirectoryException(
'Unable to get temporary directory');
}
return Directory(path);
}

16.

ApplicationDocumentsDirectory
Future<Directory> getApplicationDocumentsDirectory()
async {
final String? path = await
_platform.getApplicationDocumentsPath();
if (path == null) {
throw MissingPlatformDirectoryException(
'Unable to get application documents
directory');
}
return Directory(path);
}

17.

ApplicationSupportDirectory
Future<Directory> getApplicationSupportDirectory() async
{
final String? path = await
_platform.getApplicationSupportPath();
if (path == null) {
throw MissingPlatformDirectoryException(
'Unable to get application support directory');
}
return Directory(path);
}

18.

LibraryDirectory
Future<Directory> getLibraryDirectory() async {
final String? path = await
_platform.getLibraryPath();
if (path == null) {
throw
MissingPlatformDirectoryException('Unable to get
library directory');
}
return Directory(path);
}

19.

ExternalStorageDirectory
Future<Directory?> getExternalStorageDirectory()
async {
final String? path = await
_platform.getExternalStoragePath();
if (path == null) {
return null;
}
return Directory(path);
}

20.

ExternalStorageDirectories
Future<List<Directory>?> getExternalStorageDirectories({
/// Optional parameter. See [StorageDirectory] for more
informations on
/// how this type translates to Android storage directories.
StorageDirectory? type,
}) async {
final List<String>? paths =
await _platform.getExternalStoragePaths(type: type);
if (paths == null) {
return null;
}
return paths.map((String path) => Directory(path)).toList();
}

21.

ExternalCacheDirectories
Future<List<Directory>?>
getExternalCacheDirectories() async {
final List<String>? paths = await
_platform.getExternalCachePaths();
if (paths == null) {
return null;
}
return paths.map((String path) =>
Directory(path)).toList();
}

22.

ApplicationCacheDirectory
Future<Directory> getApplicationCacheDirectory()
async {
final String? path = await
_platform.getApplicationCachePath();
if (path == null) {
throw MissingPlatformDirectoryException(
'Unable to get application cache
directory');
}
return Directory(path);
}

23.

DownloadsDirectory
Future<Directory?> getDownloadsDirectory() async
{
final String? path = await
_platform.getDownloadsPath();
if (path == null) {
return null;
}
return Directory(path);
}

24.

shared_preferences

25.

SharedPreferences и его варианты
• SharedPreferences
• SharedPreferencesAsync
• SharedPreferencesWithCache

26.

Cache and async or sync getters
[SharedPreferences] и [SharedPreferencesWithCache]
используют локальный кэш для хранения настроек.
Это позволяет выполнять синхронные вызовы get
после того, как при первоначальном вызове setup
настройки будут получены с платформы.

27.

Cache and async or sync getters
Однако с кэшем также могут возникнуть проблемы:
• Если вы используете shared_preferences из нескольких изолятов,
поскольку каждый изолят имеет свой собственный синглтон и кэш.
• Если вы используете shared_preferences в нескольких экземплярах
(включая те, которые созданы плагинами, создающими фоновые
контексты на мобильных устройствах, такими как firebase_messaging).
• Если вы изменяете базовое хранилище системных настроек с помощью
чего-то другого, кроме плагина shared_preferences, например, нативного
кода.

28.

Cache and async or sync getters
[SharedPreferencesAsync]
не
использует
локальный кэш, из-за чего все вызовы становятся
асинхронными для решения для хранения данных
на хост-платформах. Это может быть менее
производительным,
но
всегда
должно
предоставлять самые свежие данные, хранящиеся
на собственной платформе, независимо от того,
какой процесс использовался для их хранения.

29.

Storage location by platform
Platform
Location
Android
SharedPreferences
iOS
NSUserDefaults
Linux
In the XDG_DATA_HOME
directory
macOS
NSUserDefaults
Web
LocalStorage
Windows
In the roaming AppData directory

30.

Запись данных
// Obtain shared preferences.
final SharedPreferences prefs = await
SharedPreferences.getInstance();
// Save an integer value to 'counter' key.
await prefs.setInt('counter', 10);
// Save an boolean value to 'repeat' key.
await prefs.setBool('repeat', true);
// Save an double value to 'decimal' key.
await prefs.setDouble('decimal', 1.5);
// Save an String value to 'action' key.
await prefs.setString('action', 'Start');
// Save an list of strings to 'items' key.
await prefs.setStringList('items', <String>['Earth', 'Moon',
'Sun']);

31.

Чтение данных
// Try reading data from the 'counter' key. If it doesn't exist,
returns null.
final int? counter = prefs.getInt('counter');
// Try reading data from the 'repeat' key. If it doesn't exist,
returns null.
final bool? repeat = prefs.getBool('repeat');
// Try reading data from the 'decimal' key. If it doesn't exist,
returns null.
final double? decimal = prefs.getDouble('decimal');
// Try reading data from the 'action' key. If it doesn't exist,
returns null.
final String? action = prefs.getString('action');
// Try reading data from the 'items' key. If it doesn't exist,
returns null.
final List<String>? items = prefs.getStringList('items');

32.

Удаление данных
// Remove data for the 'counter' key.
await prefs.remove('counter');

33.

SharedPreferencesAsync
final SharedPreferencesAsync asyncPrefs = SharedPreferencesAsync();
await asyncPrefs.setBool('repeat', true);
await asyncPrefs.setString('action', 'Start');
final bool? repeat = await asyncPrefs.getBool('repeat');
final String? action = await asyncPrefs.getString('action');
await asyncPrefs.remove('repeat');
await asyncPrefs.clear(allowList: <String>{'action', 'repeat'});

34.

final SharedPreferencesWithCache prefsWithCache =
await SharedPreferencesWithCache.create(
cacheOptions: const SharedPreferencesWithCacheOptions(
// When an allowlist is included, any keys that aren't included cannot be used.
allowList: <String>{'repeat', 'action'},
),
);
await prefsWithCache.setBool('repeat', true);
await prefsWithCache.setString('action', 'Start');
final bool? repeat = prefsWithCache.getBool('repeat');
final String? action = prefsWithCache.getString('action');
await prefsWithCache.remove('repeat');
await prefsWithCache.clear();

35.

Несколько экземпляров
• Если вы используете shared_preferences из нескольких изолятов,
поскольку каждый изолят имеет свой собственный синглтон
SharedPreferences и кэш.
• Если вы используете shared_preferences в нескольких engine
instances (включая те, которые созданы плагинами, создающими
фоновые контексты на мобильных устройствах, такими как
firebase_messaging).
• Если вы изменяете базовое хранилище системных настроек с
помощью чего-то другого, кроме плагина shared_preferences,
например, машинного кода.

36.

Migrating from SharedPreferences to
SharedPreferencesAsync/WithCache
В настоящее время переход с более старого API
[SharedPreferences] на более новый SharedPreferencesAsync]
или [SharedPreferencesWithCache] необходимо выполнять
вручную. Простой формой этого может быть получение всех
настроек с помощью [SharedPreferences] и добавление их
обратно с помощью [SharedPreferencesAsync], а затем
сохранение предпочтений, указывающих на то, что миграция
была выполнена, чтобы в будущих запусках миграция не
повторялась.

37.

Adding, Removing, or changing prefixes on
SharedPreferences
По умолчанию класс SharedPreferences будет считывать (и
записывать) только те настройки, которые начинаются с префикса
flutter.. Все это обрабатывается плагином самостоятельно и не
требует добавления этого префикса вручную. В качестве
альтернативы,
SharedPreferences
можно
настроить
на
использование любого префикса, добавив вызов setPrefix перед
созданием любых экземпляров SharedPreferences. Вызов setPrefix
после создания экземпляра SharedPreferences приведет к сбою.
Установка префикса в пустую строку " позволит получить доступ
ко всем настройкам, созданным любыми версиями приложения,
отличными от flutter (для перехода с нативного приложения на
flutter).

38.

Adding, Removing, or changing prefixes on
SharedPreferences
Если префиксу присвоено значение, такое как "", которое
приводит к считыванию значений, которые изначально не были
сохранены в SharedPreferences, инициализация SharedPreferences
может завершиться ошибкой, если какое-либо из значений
относится
к
типам,
которые
не
поддерживаются
SharedPreferences. В этом случае вы можете установить список
разрешений, содержащий только настройки поддерживаемых
типов. Если вы решите полностью удалить префикс, вы все равно
сможете получить доступ к ранее созданным настройкам,
вручную добавив предыдущий префикс flutter. в начало клавиши
настройки.

39.

Testing
В тестах вы можете заменить стандартную
реализацию SharedPreferences на имитационную
реализацию с начальными значениями. Эта
реализация доступна только в памяти и не будет
сохранять значения в обычном хранилище
предпочтений.
final Map<String, Object> values = <String, Object>{'counter': 1};
SharedPreferences.setMockInitialValues(values);

40.

SQFLITE
import 'package:sqflite/sqflite.dart';

41.

Открытие и закрытие БД
var db = await openDatabase('my_db.db');
Многие приложения используют одну базу данных,
и им никогда не потребуется ее закрывать (она
будет закрыта при завершении работы
приложения). Если вы хотите освободить ресурсы,
вы можете закрыть базу данных.
await db.close();

42.

Raw SQL queries
var databasesPath = await getDatabasesPath();
String path = join(databasesPath, 'demo.db');
await deleteDatabase(path);
Database database = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
await db.execute(
'CREATE TABLE Test (id INTEGER PRIMARY KEY, name TEXT, value
INTEGER, num REAL)');
});

43.

Raw SQL queries
await database.transaction((txn) async {
int id1 = await txn.rawInsert(
'INSERT INTO Test(name, value, num) VALUES("some name", 1234,
456.789)');
print('inserted1: $id1');
int id2 = await txn.rawInsert(
'INSERT INTO Test(name, value, num) VALUES(?, ?, ?)',
['another name', 12345678, 3.1416]);
print('inserted2: $id2');
});

44.

Raw SQL queries
int count = await database.rawUpdate(
'UPDATE Test SET name = ?, value = ? WHERE name = ?',
['updated name', '9876', 'some name']);
print('updated: $count');

45.

Raw SQL queries
List<Map> list = await database.rawQuery('SELECT * FROM Test');
List<Map> expectedList = [
{'name': 'updated name', 'id': 1, 'value': 9876, 'num': 456.789},
{'name': 'another name', 'id': 2, 'value': 12345678, 'num':
3.1416}
];
print(list);
print(expectedList);
assert(const DeepCollectionEquality().equals(list, expectedList));

46.

Raw SQL queries
count = Sqflite
.firstIntValue(await database.rawQuery('SELECT COUNT(*) FROM
Test'));
assert(count == 2);
count = await database
.rawDelete('DELETE FROM Test WHERE name = ?', ['another
name']);
assert(count == 1);
await database.close();

47.

SQL helpers
final String tableTodo =
'todo';
final String columnId = '_id';
final String columnTitle =
'title';
final String columnDone =
'done'
class Todo {
int id;
String title;
bool done;
toMap() …..
fromMap(…)…
class TodoProvider {
Database db;
Future open(String path) async {
db = await openDatabase(path, version: 1,
onCreate: (Database db, int version) async {
await db.execute('''
create table $tableTodo (
$columnId integer primary key autoincrement,
$columnTitle text not null,
$columnDone integer not null)
''');
});
}

48.

SQL helpers
Future<int> delete(int id)
async {
return await
db.delete(tableTodo,
where: '$columnId = ?',
whereArgs: [id]);
}
Future<int> update(Todo
todo) async {
return await
db.update(tableTodo,
todo.toMap(),
where: '$columnId =
?', whereArgs: [todo.id]);
}
Future<Todo> insert(Todo todo) async {
todo.id = await db.insert(tableTodo, todo.toMap());
return todo;
}
Future<Todo> getTodo(int id) async {
List<Map> maps = await db.query(tableTodo,
columns: [columnId, columnDone, columnTitle],
where: '$columnId = ?',
whereArgs: [id]);
if (maps.length > 0) {
return Todo.fromMap(maps.first);
}
return null;
}
Future close() async => db.close();

49.

Read results
List<Map<String, Object?>> records = await db.query('my_table');
Результирующие элементы доступны только для чтения
// get the first record
Map<String, Object?> mapRead = records.first;
// Update it in memory...this will throw an
exception
mapRead['my_column'] = 1;
// Crash... `mapRead` is read-only

50.

Transaction
await database.transaction((txn) async {
// Ok
await txn.execute('CREATE TABLE Test1 (id INTEGER PRIMARY KEY)');
// DON'T use the database object in a transaction
// this will deadlock!
await database.execute('CREATE TABLE Test2 (id INTEGER PRIMARY
KEY)');
});

51.

Batch support
batch = db.batch();
batch.insert('Test', {'name': 'item'});
batch.update('Test', {'name': 'new_item'}, where: 'name = ?',
whereArgs: ['item']);
batch.delete('Test', where: 'name = ?', whereArgs: ['item']);
results = await batch.commit();

52.

await database.transaction((txn) async {
var batch = txn.batch();
// ...
// commit but the actual commit will happen when the transaction is committed
// however the data is available in this transaction
await batch.commit();
// ...
});
await batch.commit(continueOnError: true);

53.

Table and column names
В общем, лучше избегать использования ключевых слов SQLite для
имен сущностей. Если используется любое из следующих имен:
"add","all","alter","and","as","autoincrement","between","case","chec
k","collate","commit","constraint","create","default","deferrable","dele
te","distinct","drop","else","escape","except","exists","foreign","from",
"group","having","if","in","index","insert","intersect","into","is","isnull"
,"join","limit","not","notnull","null","on","or","order","primary","refere
nces","select","set","table","then","to","transaction","union","unique",
"update","using","values","when","where"

54.

db.rawQuery('SELECT * FROM "table"');
db.query('table', columns: ['group'], where: '"group" = ?',
whereArgs: ['my_group']);

55.

Supported SQLite types
INTEGER
Dart type: int
Supported values: from -2^63 to 2^63 - 1
REAL
Dart type: num
TEXT
Dart type: String
BLOB
Dart type: Uint8List

56.

Current issues
Из-за способа работы транзакций в SQLite (потоки)
одновременные транзакции чтения и записи не
поддерживаются.

57.

sqflite_sqlcipher
Database db = await openDatabase(
path,
password: "my password",
// onCreate, onUpgrade...
);

58.

Hive

59.

• Кросс-платформенность — так как на чистом Dart и нет нативных
зависимостей — mobile, desktop, browser.
• Высокая производительность.
• Встроенное сильное шифрование.

60.

61.

Где хранятся данные
Все данные, хранящиеся в Hive, организованы в ящики. Ящик
можно сравнить с таблицей в SQL, но он не имеет структуры и
может содержать что-либо.
Box
LazyBox
Encrypted box
Ящики поддерживают шифрование AES-256 и сжатие (Compaction)
из коробки

62.

Когда использовать
• Профили пользователей
• Информация о сеансе
• Комментарии к статье/блогу
• Сообщения
• Содержимое корзины покупок
• Категории товаров
• Двоичные данные
• и т.д.

63.

Когда не использовать
При правильном моделировании в Hive можно хранить любые
данные. При этом иногда может оказаться удобнее использовать
реляционную базу данных, такую как SQLite. (Удобнее, но не
быстрее!)
Особенно, если ваши данные имеют сложные взаимосвязи и вы в
значительной степени полагаетесь на индексы и сложные запросы,
вам следует рассмотреть возможность использования SQLite.

64.

Ограничения
• Ключи должны быть 32-разрядными целыми числами без знака или
строками ASCII с максимальной длиной 255 символов.
• Поддерживаемые целочисленные значения включают все целые числа
в диапазоне от -2^53 до 2^53 и некоторые целые числа с большей
величиной
• Объектам запрещено содержать циклы. Hive не обнаружит их, и
сохранение приведет к бесконечному циклу.
• Только один процесс может получить доступ к коробке в любое время.
В противном случае могут возникнуть проблемы.
• Блоки хранятся в виде файлов в каталоге приложения пользователя.
Поэтому следует избегать распространенных недопустимых символов,
таких как /%&.

65.

• var box = Hive.box('myBox');
• await box.put('hello', 'world');
• await box.close();
Вполне нормально оставить box открытым на время
работы приложения. Если вам снова понадобится box в
будущем, просто оставьте его открытым.

66.

Read & Write
var box = Hive.box('myBox');
String name = box.get('name');
DateTime birthday = box.get('birthday');
double height = box.get('randomKey', defaultValue: 17.5);
box.put('name', 'Paul');
box.put('friends', ['Dave', 'Simon', 'Lisa']);
box.put(123, 'test');
box.putAll({'key1': 'value1', 42: 'life'});

67.

Primitive types
Hive поддерживает все примитивные типы, List,
Map, DateTime и Uint8List. Если вы хотите сохранить
другие объекты, вам необходимо зарегистрировать
TypeAdapter, который преобразует объект из
двоичной формы и обратно.

68.

class User {
String name;
class UserAdapter extends TypeAdapter<User> {
@override
final typeId = 0;
User(this.name);
@override
User read(BinaryReader reader) {
return User(reader.read());
}
@override
String toString() => name; // Just
for print()
}
@override
void write(BinaryWriter writer, User obj) {
writer.write(obj.name);
}
Hive.registerAdapter(UserAdapter());
}
var box = await Hive.openBox<User>('userBox');
box.put('david', User('David'));
box.put('sandy', User('Sandy'));
print(box.values);

69.

Relationships
class Person extends HiveObject {
String name;
int age;
List<Person> friends;
Person(this.name, this.age);
}

70.

BoxCollections
final collection = await BoxCollection.open(
'MyFirstFluffyBox',
{'cats', 'dogs'}, // Names of your boxes
path: './', // Path where to store your boxes (Only used in Flutter /
Dart IO)
key: HiveCipher(), // Key to encrypt your boxes (Only used in Flutter /
Dart IO)
);

71.

final catsBox = collection.openBox<Map>('cats');
await catsBox.put('fluffy', {'name': 'Fluffy', 'age': 4});
final loki = await catsBox.get('loki');
print('Loki is ${loki?['age']} years old.');

72.

final cats = await catsBox.getAll(['loki', 'fluffy']);
print(cats);
final allCatKeys = await catsBox.getAllKeys();
print(allCatKeys);
final catMap = await catsBox.getAllValues();
print(catMap);
await catsBox.delete('loki');
await catsBox.deleteAll(['loki', 'fluffy']);
await catsBox.clear();

73.

await collection.transaction(
() async {
await catsBox.put('fluffy', {'name': 'Fluffy', 'age': 4});
await catsBox.put('loki', {'name': 'Loki', 'age': 2});
// ...
},
boxNames: ['cats'], // By default all boxes become blocked.
readOnly: false,
);

74.

Compaction
var box = Hive.box('myBox');
await box.compact();
await box.close();
Вы можете задать свои собственные правила для автоматического
уплотнения. Просто укажите параметр compactionStrategy при
открытии окна:
var box = await Hive.openBox('myBox', compactionStrategy: (entries,
deletedEntries) {
return deletedEntries > 50;
});

75.

Generate adapter
import 'package:hive/hive.dart';
part 'person.g.dart';
@HiveType(typeId: 1)
class Person {
@HiveField(0)
String name;
@HiveField(1)
int age;
@HiveField(2)
List<Person> friends;
}

76.

Updating a class
Если необходимо изменить существующий класс – например, вы хотите,
чтобы у класса было новое поле, – но вам все равно хотелось бы читать
объекты, написанные с помощью старого адаптера.
Обновить сгенерированные адаптеры просто, не нарушая существующий
код. Просто запомните следующие правила: Не меняйте номера
существующих полей. Если вы добавите новые поля, любые объекты,
записанные "старым" адаптером, все равно будут прочитаны новым
адаптером. Эти поля просто игнорируются. Аналогично, объекты,
написанные вашим новым кодом, могут быть прочитаны вашим старым
кодом: новое поле игнорируется при синтаксическом анализе. Поля могут
быть переименованы и даже изменены с общедоступных…

77.

Encrypted box
final appDocumentDir = await getApplicationDocumentsDirectory();
Hive.initFlutter(appDocumentDir.path);
// Initialize encryption
final key = encrypt.Key.fromUtf8('my 32 length super secret key................');
final iv = encrypt.IV.fromLength(16);
final encrypter = encrypt.Encrypter(encrypt.AES(key));
// Open an encrypted box
final encryptedBox = await Hive.openBox('myEncryptedBox',
encryptionCipher: encrypter);

78.

PostgreSql and Flutter
Прежде чем углубляться в технические аспекты, важно
понять, почему интеграция PostgreSQL в приложение Flutter может
быть выгодной. Базы данных PostgreSQL предоставляют больше
возможностей для хранения и извлечения сложных данных и
хорошо подходят для серверных приложений, которым требуются
надежные возможности управления данными. С другой стороны,
Flutter отлично справляется с созданием красивых и отзывчивых
пользовательских интерфейсов. Комбинируя эти два компонента,
разработчики
могут
создавать
мощные,
визуально
привлекательные и богатые данными приложения.

79.

Create a User with Permissions
CREATE USER my_flutter_user WITH ENCRYPTED PASSWORD
'my_secure_password';
GRANT ALL PRIVILEGES ON DATABASE my_flutter_app TO
my_flutter_user;

80.

Add PostgreSQL Package Dependency
dependencies:
flutter:
sdk: flutter
postgres: ^latest_version

81.

Установление соединения с сервером
PostgreSQL
final connection = PostgreSQLConnection(
'host', // Replace with your database host
port, // Replace with your database port
'database_name', // Replace with your database name
username: 'username', // Replace with your username
password: 'password', // Replace with your password
);
await connection.open();

82.

Executing SQL Queries in Dart Code
var result = await connection.query(
'INSERT INTO users(name, email) VALUES (@name, @email)',
substitutionValues: {
'name': 'John Doe',
'email': 'john.doe@example.com'
}
);

83.

Execute a Select Query
List<List<dynamic>> results = await connection.query(
'SELECT * FROM users WHERE name = @name',
substitutionValues: {
'name': 'John Doe'
}
);
for (final row in results) {
print('Name: ${row[0]}, Email: ${row[1]}');
}

84.

Handling Errors and Exceptions
try {
await connection.open();
// Execute queries...
} on PostgreSQLException catch (e) {
print('Error: ${e.toString()}');
} finally {
await connection.close();
}

85.

Future<void> addUser(String name, String email) async {
try {
await connection.open();
await connection.query(
'INSERT INTO users(name, email) VALUES (@name, @email)',
substitutionValues: {
'name': name,
'email': email
}
);
} catch (e) {
print('Error: ${e.toString()}');
} finally {
await connection.close();
}
}

86.

Извлечение данных с помощью запросов
Future<List<User>> getUsers() async {
List<User> users = [];
try {
await connection.open();
List<List<dynamic>> results = await connection.query('SELECT * FROM users');
for (var row in results) {
User user = User(name: row[0], email: row[1]);
users.add(user);
}
} catch (e) {
print('Error: ${e.toString()}');
} finally {
await connection.close();
}
return users;}

87.

Обновление данных в базе данных
Future<void> updateUserEmail(String name, String newEmail) async {
try {
await connection.open();
await connection.query(
'UPDATE users SET email = @newEmail WHERE name = @name',
substitutionValues: {
'name': name,
'newEmail': newEmail
} );
} catch (e) {
print('Error: ${e.toString()}');
} finally {
await connection.close();
}}

88.

Удаление данных из базы данных
Future<void> deleteUser(String name) async {
try {
await connection.open();
await connection.query(
'DELETE FROM users WHERE name = @name',
substitutionValues: {
'name': name
}
);
} catch (e) {
print('Error: ${e.toString()}');
} finally {
await connection.close();
}
}

89.

Пример транзакции
Future<void> transferFunds(int fromUserId, int toUserId, double amount) async {
await connection.transaction((ctx) async {
await ctx.query(
'UPDATE accounts SET balance = balance - @amount WHERE user_id = @fromUserId',
substitutionValues: {
'fromUserId': fromUserId,
'amount': amount
});
await ctx.query(
'UPDATE accounts SET balance = balance + @amount WHERE user_id = @toUserId',
substitutionValues: {
'toUserId': toUserId,
'amount': amount } ); });}

90.

Выводы по Postgres in Flutter
Работа с PostgreSQL в проекте Flutter требует понимания того,
как устанавливать соединение, выполнять запросы и управлять
данными. Вы можете эффективно интегрировать базу данных
PostgreSQL в свои приложения Flutter, используя пакет “Postgres” и
шаблон асинхронного ожидания Dart.
English     Русский Rules