Как подключить базу данных к проекту flutter
Перейти к содержимому

Как подключить базу данных к проекту flutter

  • автор:

#10 – Подключение базы данных Firebase

#10 – Подключение базы данных Firebase

К любому Flutter проекту можно быстро подключить базу данных. За урок мы научимся подключать и работать с базой данных Firebase. Нами будет переделана программа «Список дел» и все данные из программы будут хранится в удаленной базе Firebase.

Видеоурок

Полезные ссылки:

  • Официальный сайт Firebase ;
  • Инструкции относительно Cloud Firestore .

Исправление ошибок

При запуске проекта может выдаваться ошибка связанная с тем, что FireBase не запущен. Такое может происходить по причине того, что его инициализацию нужно прописывать в основном методе.

Для устранения ошибки в файле «main.dart» пропишите:

// Подключени всех сцен import 'package:flutter_todo/pages/home.dart'; import 'package:flutter_todo/pages/main_screen.dart'; import 'package:firebase_core/firebase_core.dart'; void main() async < // Инициализация в основной функции WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MaterialApp( theme: ThemeData( primaryColor: Colors.deepOrangeAccent, ), initialRoute: '/', routes: < // Маршрутизация '/': (context) =>MainScreen(), '/todo': (context) => Home(), >, )); >

База данных Firebase

При разработке Flutter программ вы можете использовать практически любую базу данных. В вашем распоряжение такие БД, как:

  • SQLite;
  • MySQL;
  • MongoDB ;
  • Firebase ;
  • и множество других.

В ходе урока мы использовали БД Firebase, так как она одна из наиболее простых для интеграции в Flutter и обладает широким спектром возможностей.

Благодаря тому, что Firebase была куплена компанией Google в 2014 году, то интеграция этой БД и Flutter стала легким и быстрым процессом. Напомним, что Flutter также принадлежит компании Google.

Введение

SQLite появилась в 2000 году и с тех пор стала одним из самых популярных решений для встраивания баз данных в локальные приложения. Давайте в демонстрационном проекте создадим очень простое приложение для управления задачами, которое может создавать, обновлять и удалять элементы из базового интерфейса.

Если у вас ещё нет Flutter, скачать можно на странице установки. Исходный код, который мы используем, доступен здесь на GitHub.

Конфигурация проекта

Итак, что нам нужно для использования SQLite в приложении на Flutter? Во-первых, включить пакет sqflite внутри проекта в pubspec.yaml вот таким образом:

name: flutter_sqlite_demo
description: проект-образец, демонстрирующий использование Flutter с SQLite
version: 1.0.0+1

environment:
sdk: ">=2.1.0
dependencies:
flutter:
sdk: flutter
sqflite: ^1.2.0
path_provider: ^1.6.0
cupertino_icons: ^0.1.2

dev_dependencies:
flutter_test:
sdk: flutter

flutter:
uses-material-design: true

Мы указали здесь версию пакета sqflite 1.2.0 или новее и path_provider версии не старше 1.6.0 , а проект упростили для лучшего понимания и облегчения работы с ним.

Создаём простую модель

Для хранения данных нам понадобится модель. Простой класс модели данных позволяет применять необходимые методы для преобразования приемлемого для SQLite формата данных в объект, который может быть использован в приложении. Абстрактный класс Model будет служить базовым классом для моделей данных. Этот файл находится в lib/models/model.dart:

abstract class Model  
int id;

static fromMap() <>
toMap() <>
>

Класс Model очень прост и удобен для определения свойств/методов (таких как приведённый выше id ), которые можно ожидать от моделей данных. Это позволяет создавать одну или несколько конкретных моделей данных, соответствующих такому базовому шаблону проектирования. В примере с нашим приложением для управления задачами класс модели конкретного элемента создаётся в lib/models/todo-item.dart:

import 'package:flutter_sqlite_demo/models/model.dart';

class TodoItem extends Model
static String table = 'todo_items';

int id;
String task;
bool complete;

TodoItem(< this.id, this.task, this.complete >);

Map toMap()
Map map = 'task': task,
'complete': complete
>;

if (id != null) < map['id'] = id; >
return map;
>

static TodoItem fromMap(Map map)
return TodoItem(
id: map['id'],
task: map['task'],
complete: map['complete'] == 1
);
>
>

Этот класс TodoItem содержит свойства для task и complete и простой конструктор для создания нового элемента, а для преобразования между экземплярами TodoItem и объектами карты, используемыми базой данных, определены методы toMap и fromMap . Заметим, что id добавляется в карту, только будучи значением не null .

Класс базы данных

Ради удобства и простоты сопровождения основные методы обработки базы данных помещаем в lib/services/db.dart (так лучше, чем разбрасывать логику обработки данных по всему приложению):

import 'dart:async';
import 'package:flutter_sqlite_demo/models/model.dart';
import 'package:sqflite/sqflite.dart';

abstract class DB
static Database _db;

static int get _version => 1;

static Future init() async
if (_db != null) < return; >

try String _path = await getDatabasesPath() + 'example';
_db = await openDatabase(_path, version: _version, onCreate: onCreate);
>
catch(ex) <
print(ex);
>
>

static void onCreate(Database db, int version) async =>
await db.execute('CREATE TABLE todo_items (id INTEGER PRIMARY KEY NOT NULL, task STRING, complete BOOLEAN)');

static Future>> query(String table) async => _db.query(table);

static Future insert(String table, Model model) async =>
await _db.insert(table, model.toMap());

static Future update(String table, Model model) async =>
await _db.update(table, model.toMap(), where: 'id = ?', whereArgs: [model.id]);

static Future delete(String table, Model model) async =>
await _db.delete(table, where: 'id = ?', whereArgs: [model.id]);
>

Этот класс абстрактный: из него нельзя создавать объекты, да и нужна всего одна его копия в памяти. В свойстве _db у него есть ссылка на базу данных SQLite. Номер версии базы данных захардкоден значением 1 , хотя в более сложных приложениях версию базы данных можно использовать для миграции схем базы данных вверх или вниз в версии, благодаря чему можно развёртывать новые функции без необходимости стирать базу данных и начинать всё с нуля.

Внутри метода init создаётся экземпляр базы данных SQLite с именем базы данных example специально для нашего проекта. Если база данных с именем example ещё не существует, автоматически вызывается onCreate . Именно здесь находятся запросы на создание структуры таблицы. В нашем случае создаётся таблица todo_items с первичным ключом для id и полями, которые соответствуют свойствам в классе TodoItem .

Метод query , равно как и методы insert , update и delete , определяется для выполнения стандартных операций в базе данных. Благодаря им, у нас есть простые абстракции и возможность поместить логику обработки данных в этот класс, что может быть очень полезным при рефакторинге и сопровождении кода в приложении. Если бы их не было, нам пришлось бы, например, искать и заменять кучу строк в разных файлах или устранять странные ошибки после внесения простых изменений.

Главный файл приложения

И последнее, но не менее важное: логика приложения и пользовательский интерфейс у нас находятся в lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_sqlite_demo/models/todo-item.dart';
import 'package:flutter_sqlite_demo/services/db.dart';

void main() async
WidgetsFlutterBinding.ensureInitialized();

await DB.init();
runApp(MyApp());
>

class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData( primarySwatch: Colors.indigo ),
home: MyHomePage(title: 'Flutter SQLite Demo App'),
);
>
>

class MyHomePage extends StatefulWidget
MyHomePage() : super(key: key);

final String title;

@override
_MyHomePageState createState() => _MyHomePageState();
>

class _MyHomePageState extends State
String _task;

List _tasks = [];

TextStyle _style = TextStyle(color: Colors.white, fontSize: 24);

List get _items => _tasks.map((item) => format(item)).toList();

Widget format(TodoItem item)
return Dismissible(
key: Key(item.id.toString()),
child: Padding(
padding: EdgeInsets.fromLTRB(12, 6, 12, 4),
child: FlatButton(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(item.task, style: _style),
Icon(item.complete == true ? Icons.radio_button_checked : Icons.radio_button_unchecked, color: Colors.white)
]
),
onPressed: () => _toggle(item),
)
),
onDismissed: (DismissDirection direction) => _delete(item),
);
>

void _toggle(TodoItem item) async
item.complete = !item.complete;
dynamic result = await DB.update(TodoItem.table, item);
print(result);
refresh();
>

void _delete(TodoItem item) async
DB.delete(TodoItem.table, item);
refresh();
>

void _save() async
Navigator.of(context).pop();
TodoItem item = TodoItem(
task: _task,
complete: false
);

await DB.insert(TodoItem.table, item);
setState(() => _task = '' );
refresh();
>

void _create(BuildContext context)
showDialog(
context: context,
builder: (BuildContext context) return AlertDialog(
title: Text("Create New Task"),
actions: [
FlatButton(
child: Text('Cancel'),
onPressed: () => Navigator.of(context).pop()
),
FlatButton(
child: Text('Save'),
onPressed: () => _save()
)
],
content: TextField(
autofocus: true,
decoration: InputDecoration(labelText: 'Task Name', hintText: 'e.g. pick up bread'),
onChanged: (value) < _task = value; >,
),
);
>
);
>

@override
void initState()
refresh();
super.initState();
>

void refresh() async
List> _results = await DB.query(TodoItem.table);
_tasks = _results.map((item) => TodoItem.fromMap(item)).toList();
setState(() < >);
>

@override
Widget build(BuildContext context)
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar( title: Text(widget.title) ),
body: Center(
child: ListView( children: _items )
),
floatingActionButton: FloatingActionButton(
onPressed: () < _create(context); >,
tooltip: 'New TODO',
child: Icon(Icons.library_add),
)
);
>
>

Это стандартный файл, определяющий внешний вид и поведение любого приложения с Flutter. Во время инициализации строка WidgetsFlutterBinding.ensureInitialized() обеспечит корректную инициализацию приложения Flutter, тогда как база данных инициализируется с помощью await DB.init() .

Когда приложение запускается и виджет MyHomePage визуализируется, вызов refresh() извлекает список задач из таблицы todo_items и выполняет его отображение на список List объектов TodoItem . Они отображаются в главном ListView через средство доступа _items , которое принимает список List объектов TodoItem и форматирует его как список виджетов, содержащий текстовый элемент задач и индикатор завершённости этого элемента.

Задачи добавляются нажатием на плавающую круглую кнопку и введением названия задачи. При нажатии на кнопку Save вновь созданный элемент списка задач будет добавлен в базу данных с помощью DB.insert . Нажав на задачу в списке, можно переключаться между состояниями завершена /не завершена: здесь используем булеву переменную complete и сохраняем изменённый объект в базе данных с помощью DB.update . Проведя пальцем по задаче в горизонтальном направлении, можно удалить элемент задач: используем для этого метод DB.delete . Всякий раз, когда в список вносятся изменения, вызов refresh() с последующим setState() обеспечивает правильность обновления списка.

Заключение

SQLite представляет удобный способ локального хранения данных в приложении. В примере проекта было показано, как выполнять основные операции управления данными для создания, добавления, изменения, обновления и удаления простых записей в базе данных SQLite. Ещё больше о плагине sqflite и функциях, которые он поддерживает, можно узнать здесь.

Спасибо за внимание и удачи в вашем следующем проекте на Flutter!

  • Инъекция SQL: руководство для начинающих
  • Руководство по анализу данных с SQL
  • SQL в науке о данных

Как подключить базу данных к проекту flutter

Бюджет: ожидает предложений

Дедлайн: 27.04.2023

Требуется реализовать подключение базы данных к приложению написанном на языке программирования flutter в мобильном приложении. Далее, в процессе работы программы после нажатия кнопки choose a gift, пользователя переносит на страницу с «предложенным подарком», фото подарка должно подгружаться из базы данных и отображаться в овале на фото 2, а так же, должен выводиться под фото адрес магазина с этим подарком так же из базы данных. База данных будет заполняться вручную мной. Пример строения таблицы picture («адрес расположения фотографии номер 1», «адрес расположения фотографии номер 2»), address (‘ул. Марковского 1’, ‘ул. Марковского 2’). «Подарок» должен выбираться рандомно из базы данных. По базам данных предпочтительно использовать Firebase. Срок выполнения до 27 апреля 04:00 по МСК.

Опубликован:

26.04.2023 | 11:27 [поднят: 26.04.2023 | 11:27] [последние изменения: 26.04.2023 | 11:22]

Используем SQLite в Flutter

Привет, Хабр! Представляем вашему вниманию перевод статьи «Using SQLite in 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. Создадим DB Client

Теперь создадим новый файл Database.dart. В нем создадим синглтон.

Почему нам нужен синглтон: мы используем этот патерн для уверенности в том что у нас есть только одна сущность класса и для обеспечения глобальной точки входа в него.

1. Создадим приватный конструктор, который может использоваться только внутри этого класса

class DBProvider

2. Настроим базу данных

Следующим шагом будет создания объекта базы данных и предоставим геттер, где мы создадим объект базы данных, если он еще не был создан (ленивая инициализация)

static Database _database; Future 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 Maps. Нам необходимо создать классы моделей с 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 json) => new Client( id: json["id"], firstName: json["first_name"], lastName: json["last_name"], blocked: json["blocked"], ); Map toJson() => < "id": id, "first_name": firstName, "last_name": lastName, "blocked": blocked, >; >

4. CRUD operations

newClient(Client newClient) async < final db = await database; var res = await db.rawInsert( "INSERT Into Client (id,first_name)" " VALUES ($,$)"); return res; >
newClient(Client newClient) async

Другой пример с использованием большого ID в качестве нового ID

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 //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; >

Get Client by id

getClient(int id) async

Get all Clients with a condition

getAllClients() async < final db = await database; var res = await db.query("Client"); Listlist = 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"); Listlist = res.isNotEmpty ? res.toList().map((c) => Client.fromMap(c)) : null; return list; >

Update an existing Client

updateClient(Client newClient) async
 blockOrUnblock(Client client) async

Delete one Client

deleteClient(int id) async

Delete All Clients

deleteAll() async

Demo

Для нашего демо мы создадим простое приложение, отображающее нашу базу данных.

Для начала сверстаем экран

Widget build(BuildContext context) < return Scaffold( appBar: AppBar(title: Text("Flutter SQLite")), body: FutureBuilder>( future: DBProvider.db.getAllClients(), builder: (BuildContext context, AsyncSnapshot> 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 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 widget вот так:

return Dismissible( key: UniqueKey(), background: Container(color: Colors.red), onDismissed: (direction) < DBProvider.db.deleteClient(item.id); >, child: ListTile(. ), );

Рефакторинг для использования BLoC паттерна

Мы сделали многое в этой статье, но в приложения в реальном мире, инициализация состояний в UI слое не очень хорошая идея. Отделим логику от UI.

Существует множество паттернов в Flutter, но мы будем использовать BLoC так как он наиболее гибкий для настройки.

Создадим BLoC

class ClientsBloc < ClientsBloc() < getClients(); >final _clientController = StreamController>.broadcast(); get clients => _clientController.stream; dispose() < _clientController.close(); >getClients() async < _clientController.sink.add(await DBProvider.db.getAllClients()); >>

1. getClients получает данные из БД (Client table) асинхронно. Мы будем использовать этот метод всегда, когда нам будет необходимо обновить таблицу, следовательно стоит поместить его в тело конструктора.

2. Мы создали StreamController.broadcast, для того чтобы слушать широковещательные события более одного раза. В нашем примере это не имеет особо значения, поскольку мы слушаем их только один раз, но неплохо было бы реализовать это на будущее.

3. Не забываем закрывать потоки. Таким образом мы предотвратим мемори лики. В нашем примере мы закрываем их используя dispose method в StatefulWidget

Теперь посмотрим на код

blockUnblock(Client client) < DBProvider.db.blockOrUnblock(client); getClients(); >delete(int id) < DBProvider.db.deleteClient(id); getClients(); >add(Client client)

И наконец финальный результат

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *