Khái niệm cơ bản về cơ sở dữ liệu với Sqllite và Cơ sở dữ liệu Firebase
- 11-06-2024
- Toanngo92
- 0 Comments
Mục lục
Sqllite trong Flutter và Cách Sử Dụng
Xem xét một kịch bản đơn giản của một ứng dụng đăng nhập được xây dựng bằng Flutter. Có một giao diện người dùng với các trường đăng nhập và mật khẩu và một nút mà khi nhấn vào, việc xác thực đăng nhập người dùng có thể xảy ra. Tuy nhiên, để xác minh id người dùng và mật khẩu đã nhập so với các thông tin đăng nhập đã lưu, người ta phải có một cơ chế lưu trữ. Các thông tin đăng nhập sau đó sẽ được lưu trữ trong cơ sở dữ liệu và được truy xuất để kiểm tra so với các giá trị được nhập bởi người dùng trong ứng dụng.
Đây chỉ là một ví dụ cho thấy việc lưu trữ dữ liệu quan trọng đối với hầu hết các ứng dụng. Lưu trữ, truy xuất và sửa đổi dữ liệu là các nhiệm vụ phổ biến trong nhiều ứng dụng. Trong một ứng dụng đa nền tảng, việc có một cơ chế lưu trữ nhẹ hơn, thay vì một phần mềm cơ sở dữ liệu nặng là rất quan trọng.
SQLite là một hệ thống quản lý cơ sở dữ liệu quan hệ trong đó dữ liệu được lưu trữ dưới dạng các bảng. Bất kỳ số lượng bảng nào cũng có thể được tạo ra và chúng có thể có mối quan hệ với nhau. SQL Server cũng là một Hệ thống Quản lý Cơ sở dữ liệu Quan hệ có thể được sử dụng trong Flutter bằng cách thêm plugin sql_conn
. Tuy nhiên, khi đến với các ứng dụng di động đa nền tảng, SQLite được ưa chuộng hơn SQL Server vì SQLite nhanh hơn nhiều so với SQL Server và các hoạt động đọc và ghi có thể được thực hiện nhanh chóng hơn. SQLite chỉ tải phần cần thiết và không phải toàn bộ tệp và chỉ ghi đè các phần đã thay đổi. Vì vậy, SQLite được ưa chuộng hơn SQL Server cho Flutter.
Flutter có một plugin sqllite
, qua đó các nhà phát triển có thể sử dụng cơ sở dữ liệu này để lưu trữ dữ liệu cục bộ trong bất kỳ ứng dụng Flutter nào.
Cài đặt SQLite trong Flutter
Để sử dụng SQLite trong Flutter, cần thêm một phụ thuộc vào dự án. Bằng cách thêm phụ thuộc, tất cả các tính năng của sqllite
có thể được tích hợp vào các ứng dụng Flutter.
Để tìm phiên bản mới nhất của gói và nhiều hơn nữa, hãy kiểm tra https://pub.dev/packages/sqllite. Hãy chạy flutter pub get
mỗi khi cập nhật các phụ thuộc trong tệp pubspec.yaml
như được hiển thị trong đoạn mã bên dưới.
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
sqllite: ^2.0.3+1
Thêm Phụ thuộc Path Provider và Lý do
path_provider
là một plugin Flutter cho việc tìm các vị trí thông thường trên hệ thống tệp để nhà phát triển có thể đọc/ghi vào một vị trí cục bộ trên thiết bị.
Thêm dòng sau vào phụ thuộc trong tệp pubspec.yaml
:
path_provider: ^2.0.11
Tạo Cơ sở dữ liệu SQLite
Trước khi tạo một cơ sở dữ liệu trong Flutter, cần khởi tạo nó.
Xem xét một ví dụ. Trong ví dụ này, một ứng dụng CRUD (Create, Read, Update, and Delete) SQllite sẽ được tạo ra, nơi các nhà phát triển có thể tạo, chỉnh sửa, xóa và xem một danh sách người dùng. Mỗi người dùng sẽ chứa các thông tin như ID, tên, email và tuổi.
Trong dự án mới, thêm tất cả các phụ thuộc như đã giải thích ở trên. Sau đó, tạo một tệp lớp mô hình để thực hiện các hoạt động CRUD như được hiển thị trong đoạn mã bên dưới.
user_model.dart
class UserModel {
final String id;
final String name;
final String email;
final int age;
UserModel({
required this.id,
required this.name,
required this.email,
required this.age,
});
factory UserModel.fromJson(Map<String, dynamic> data) => UserModel(
id: data['id'],
name: data['name'],
email: data['email'],
age: data['age'],
);
Map<String, dynamic> toMap() => {
'id': id,
'name': name,
'email': email,
'age': age,
};
}
Đoạn mã tiếp theo thể hiện cách tạo một lớp mới có tên DatabaseService
nơi tất cả các hoạt động cơ sở dữ liệu sẽ được xử lý. Ở đây, mẫu Singleton
đang được sử dụng để tạo lớp dịch vụ.
database_service.dart
import 'dart:developer';
import 'package:path_provider/path_provider.dart';
import 'package:sqllite/sqllite.dart';
class DatabaseService {
static final DatabaseService _databaseService = DatabaseService._internal();
factory DatabaseService() => _databaseService;
DatabaseService._internal();
}
Tiếp theo, cơ sở dữ liệu phải được khởi tạo trước khi tạo bất kỳ bảng nào hoặc thực hiện các hoạt động đọc/ghi. Điều này được thể hiện trong đoạn mã bên dưới. Bên trong lớp, tạo một biến của kiểu Database. Một hàm Getter
đã được sử dụng để lấy cơ sở dữ liệu như được hiển thị trong đoạn mã bên dưới.
database_service.dart
static Database? database;
Future<Database> get database async {
if (database != null) return database!;
database = await initDatabase();
return database!;
}
Trong đoạn mã tiếp theo, kiểm tra xem cơ sở dữ liệu có tồn tại không, nếu không, các nhà phát triển có thể tạo nó bằng cách sử dụng hàm initDatabase()
.
database_service.dart
Future<Database> initDatabase() async {
final getDirectory = await getApplicationDocumentsDirectory();
String path = getDirectory.path + '/users.db';
log(path);
return await openDatabase(path, onCreate: _onCreate, version: 1);
}
Trong hàm này, trước tiên, đường dẫn được khai báo để tạo/truy cập cơ sở dữ liệu bằng cách sử dụng gói path_provider. Tệp được đặt tên là users.db. Sau đó, hàm openDatabase()
được gọi, tạo cơ sở dữ liệu bằng cách sử dụng hàm _onCreate()
. Tham khảo đoạn mã bên dưới.
database_service.dart
void _onCreate(Database db, int version) async {
await db.execute(
'CREATE TABLE Users (id TEXT PRIMARY KEY, name TEXT, email TEXT, age INTEGER)');
log("TABLE CREATED!");
}
Bảng bên dưới liệt kê các loại dữ liệu giữa Dart và SQLite.
Dart Type | SQLite Type |
int | INTEGER |
num | REAL |
string | TEXT |
Uint8List | BLOB |
Thực thi SQLite Queries trong Flutter
Để thực thi các truy vấn SQL trong Flutter, gói sqllite cung cấp hai cách. Một cách là sử dụng các truy vấn thô và cách khác là sử dụng các chức năng trợ giúp SQL tích hợp sẵn. Cả hai đều cung cấp các tính năng tương tự, vì vậy người dùng có thể lựa chọn.
Thực hiện các truy vấn Sql thô
Các truy vấn SQL phải được viết theo phương pháp truyền thống gọi là String. Điều này sẽ hữu ích trong những trường hợp cần phải viết các truy vấn phức tạp, nếu không, các chức năng trợ giúp sẽ là đủ. Hoặc, điều này cũng có thể được sử dụng nếu nhà phát triển có kiến thức về việc viết các truy vấn SQL. Xem đoạn mã bên dưới để xem các ví dụ về các truy vấn SQL.
Các ví dụ về việc sử dụng Truy vấn thô
// Tạo bảng
db.execute(
'CREATE TABLE Users (id TEXT PRIMARY KEY, name TEXT, email TEXT, age INTEGER)'
);
// Chèn dữ liệu
db.rawInsert(
'INSERT INTO Users(id, name, email, age) VALUES (2, ?, 2,2)',
[user.id, user.name, user.email, user.age]
);
// Cập nhật dữ liệu
db.rawUpdate(
'UPDATE Users SET name=?, email=?, age=? WHERE ID=?',
[user.name, user.email, user.age, user.id]
);
// Truy vấn dữ liệu
db.rawQuery('SELECT * FROM Users');
Thực hiện các Truy vấn Sử dụng Các Hàm Trợ giúp SQL:
Các hàm trợ giúp SQL giúp việc viết các truy vấn dễ dàng hơn. Chỉ cần chuyển các đối số cần thiết để thực thi bất kỳ truy vấn nào.
Đoạn mã bên dưới hiển thị chức năng insert
.
db.insert('Users', user.toMap());
Tên bảng và đối tượng dữ liệu phải được chuyển dưới dạng Map<String, dynamic>
dưới dạng đối số. Kiểu trả về sẽ là một Future<int>
sẽ là id đã chèn từ bảng.
Đoạn mã bên dưới hiển thị chức năng cập nhật.
db.update('Users', user.toMap(), where: 'id = ?', whereArgs: [user.id]);
Đối với điều này, nhà phát triển phải chuyển tên bảng, dữ liệu cần cập nhật và điều kiện cần kiểm tra trước khi chèn. Nếu không có điều kiện nào khớp, thì cập nhật sẽ không diễn ra.
Đoạn mã bên dưới hiển thị chức năng truy vấn.
db.query("Users");
Đây là một truy vấn đơn giản để trả về tất cả dữ liệu từ bảng dưới dạng List<Map<String, dynamic>>
trong đó cũng có thể chỉ định điều kiện.
Đoạn mã bên dưới hiển thị chức năng xóa.
db.delete('Users', where: 'id = ?', whereArgs: [id]);
Để xóa một hàng từ bảng, phương thức này được sử dụng cùng với điều kiện where.
Hiểu về truy vấn Raw SQL.
Nhà phát triển có thể thiết kế một giao diện người dùng đơn giản như được hiển thị trong đoạn mã bên dưới để làm việc với các truy vấn SQL thô.
sqllite_example.dart
import 'package:flutter/material.dart';
import 'package:session11/sqllite/database_service.dart';
import 'package:session11/sqllite/user_model.dart';
import 'package:uuid/uuid.dart';
class SqlliteExampleScreen extends StatefulWidget {
const SqlliteExampleScreen({Key? key}) : super(key: key);
@override
State<SqlliteExampleScreen> createState() => _SqlliteExampleScreenState();
}
class _SqlliteExampleScreenState extends State<SqlliteExampleScreen> {
final dbService = DatabaseService();
final nameController = TextEditingController();
final ageController = TextEditingController();
final emailController = TextEditingController();
void showBottomSheet(String functionName, Function()? onPressed) {
showModalBottomSheet(
context: context,
elevation: 5,
isScrollControlled: true,
builder: (_) => Container(
padding: EdgeInsets.only(
right: 15,
bottom: MediaQuery.of(context).viewInsets.bottom + 120,
),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
TextField(
controller: nameController,
decoration: const InputDecoration(hintText: 'Name'),
),
const SizedBox(height: 10),
TextField(
controller: emailController,
keyboardType: TextInputType.emailAddress,
decoration: const InputDecoration(hintText: 'Email'),
),
const SizedBox(height: 10),
TextField(
controller: ageController,
keyboardType: TextInputType.number,
decoration: const InputDecoration(hintText: 'Age'),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: onPressed,
child: Text(functionName),
),
],
),
),
);
}
void addUser() {
showBottomSheet("Add User", () async {
var user = UserModel(
id: Uuid().v4(),
name: nameController.text,
email: emailController.text,
age: int.parse(ageController.text),
);
dbService.insertUser(user);
setState(() {});
nameController.clear();
emailController.clear();
ageController.clear();
Navigator.of(context).pop();
});
}
void editUser(UserModel user) {
nameController.text = user.name;
emailController.text = user.email;
ageController.text = user.age.toString();
showBottomSheet("Update User", () async {
var updatedUser = UserModel(
id: user.id,
name: nameController.text,
email: emailController.text,
age: int.parse(ageController.text),
);
dbService.editUser(updatedUser);
nameController.clear();
emailController.clear();
ageController.clear();
setState(() {});
Navigator.of(context).pop();
});
}
void deleteUser(String id) {
dbService.deleteUser(id);
setState(() {});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Sqllite Example"),
),
body: FutureBuilder<List<UserModel>>(
future: dbService.getUsers(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasData) {
if (snapshot.data!.isEmpty) {
return const Center(child: Text("No Users found"));
}
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) => Card(
color: Colors.yellow[200],
margin: const EdgeInsets.all(15),
child: ListTile(
title: Text(snapshot.data![index].name +
snapshot.data![index].age.toString()),
subtitle: Text(snapshot.data![index].email),
trailing: SizedBox(
width: 100,
child: Row(
children: [
IconButton(
icon: const Icon(Icons.edit),
onPressed: () => editUser(snapshot.data![index]),
),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () =>
deleteUser(snapshot.data![index].id),
),
],
),
),
),
),
);
}
return const Center(child: Text('No Users found'));
},
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: addUser,
),
);
}
}
Giải thích:
- Danh sách người dùng – tên, tuổi và email sẽ được hiển thị dưới dạng
List Tile
trong mộtListView
.
-
ListView
đã được bao bọc trongFutureBuilder
vì danh sách người dùng sẽ được lấy từ cơ sở dữ liệu. Tất cả các chức năng liên quan đến việc giao tiếp với cơ sở dữ liệu đều là các chức năng future.
- Các chỉ báo tải và văn bản ‘không tìm thấy người dùng’ được hiển thị để cải thiện giao diện người dùng.
- Floating action button được sử dụng, khi nhấp vào nó sẽ mở ra một khu vực dưới cùng chứa ba trường văn bản. Trong khu vực này, tên, email và tuổi có thể được nhập.
- Một gói gọi được gọi là
uuid
được sử dụng để tạo một id duy nhất cho người dùng.
- Các hàm riêng lẻ đã được viết để thực hiện các hoạt động CRUD và giao tiếp với lớp
DatabaseService
.
- Hàm
addUser
– thêm một người dùng vào cơ sở dữ liệu, làm mới ứng dụng bằng cách sử dụngsetState
, điều này sẽ xây dựng lại UI và các trường văn bản sẽ được xóa theo cùng một cách như các hàm khác.
Lưu ý: setState
được sử dụng ở đây chỉ cho mục đích minh họa. Mặc dù nó hoạt động, nhưng không phải lúc nào cũng là một thực hành tốt để sử dụng setState
. Quản lý trạng thái chính xác phải được sử dụng bất cứ khi nào có thể, để tránh việc xây dựng lại UI không cần thiết.
Đoạn mã bên dưới hiển thị việc thêm các chức năng này vào database_service.dart
để sử dụng các truy vấn SQL thô:
import 'package:sqllite/sqllite.dart';
class DatabaseService {
// Implement your database service here
// This class should handle database initialization and opening
// as well as providing database instance to other methods
}
class UserModel {
final int id;
final String name;
final String email;
final int age;
UserModel({required this.id, required this.name, required this.email, required this.age});
// Method to convert JSON data to UserModel object
factory UserModel.fromJson(Map<String, dynamic> json) {
return UserModel(
id: json['id'],
name: json['name'],
email: json['email'],
age: json['age'],
);
}
}
class UserRepository {
final DatabaseService _databaseService;
UserRepository(this._databaseService);
// Method to retrieve users from the database
Future<List<UserModel>> getUsers() async {
final db = await _databaseService.database;
var data = await db.rawQuery('SELECT * FROM Users');
List<UserModel> users = List.generate(data.length, (index) => UserModel.fromJson(data[index]));
print(users.length);
return users;
}
// Method to insert a new user into the database
Future<void> insertUser(UserModel user) async {
final db = await _databaseService.database;
var data = await db.rawInsert(
'INSERT INTO Users(id, name, email, age) VALUES (?, ?, ?, ?)',
[user.id, user.name, user.email, user.age],
);
print('Inserted $data');
}
// Method to update an existing user in the database
Future<void> editUser(UserModel user) async {
final db = await _databaseService.database;
var data = await db.rawUpdate(
'UPDATE Users SET name=?, email=?, age=? WHERE id=?',
[user.name, user.email, user.age, user.id],
);
print('Updated $data');
}
// Method to delete a user from the database
Future<void> deleteUser(String id) async {
final db = await _databaseService.database;
var data = await db.rawDelete('DELETE FROM Users WHERE id=?', [id]);
print('Deleted $data');
}
}
Sử dụng Truy vấn SQL với Các Hàm Trợ Giúp SQL
Sửa đổi hàm cơ sở dữ liệu trong tệp database_service.dart
như trong đoạn mã đoạn mã dưới đây, để xem cách hoạt động các hàm trợ giúp SQL.
Future<List<UserModel>> getUsers() async {
final db = await _databaseService.database;
var data = await db.query('Users');
List<UserModel> users = List.generate(data.length, (index) =>
UserModel.fromJson(data[index]));
print(users.length);
return users;
}
Future<void> insertUser(UserModel user) async {
final db = await _databaseService.database;
var data = await db.insert('Users', user.toMap());
log('inserted $data');
}
Future<void> editUser(UserModel user) async {
final db = await _databaseService.database;
var data = await db.update('Users', user.toMap(), where: 'id = ?', whereArgs: [user.id]);
log("updated $data");
}
Future<void> deleteUser(String id) async {
final db = await _databaseService.database;
var data = await db.delete('Users', where: 'id = ?', whereArgs: [id]);
log("deleted $data");
}
Hình bên dưới hiển thị đầu ra cho ứng dụng sau khi đoạn mã trên được thêm vào tệp databaseservice.dart.
Làm việc với Cơ sở dữ liệu Firebase trong Flutter
Sẽ rất hữu ích nếu dữ liệu có thể được lưu trữ trên đám mây và có thể được truy cập từ bất kỳ nơi nào, qua bất kỳ thiết bị nào. Cơ sở dữ liệu Firebase của Google là một cơ sở dữ liệu đám mây NoSQL, trong đó dữ liệu được đồng bộ trên tất cả các thiết bị theo thời gian thực.
Do đó, người dùng có thể truy cập, cập nhật và đồng bộ dữ liệu trên nhiều ứng dụng đa nền tảng.
Thiết lập Cơ sở dữ liệu Firebase
Để bắt đầu với Firebase, hãy truy cập https://console.firebase.google.com và đăng nhập bằng tài khoản Google. Sau đó, nhấp vào ‘Dự án mới’ để tạo một dự án Firebase mới. Xem Hình bên dưới để biết thêm chi tiết.
Chọn một tên cho dự án và nhấp vào ‘Tiếp tục’. Bước tiếp theo là kích hoạt/tắt Google Analytics cho dự án này. Bước này là tùy chọn và có thể bỏ qua. Sau vài phút, dự án sẽ sẵn sàng. Firebase cung cấp nhiều tính năng như xác thực và hosting. Để bắt đầu với cơ sở dữ liệu thời gian thực Firebase, làm theo các bước như được hiển thị trong Hình bên dưới.
Nhấp vào cơ sở dữ liệu thời gian thực trong phần ‘Xây dựng’ từ thanh bên trái. Sau đó, nhấp vào ‘tạo cơ sở dữ liệu’. Chọn vị trí cơ sở dữ liệu (bất kỳ vị trí nào) và chọn/bỏ qua ‘quy tắc bảo mật’. Hiện tại, các nhà phát triển có thể bắt đầu sử dụng cơ sở dữ liệu ở chế độ kiểm tra, sau đó có thể chỉnh sửa sau. Chọn ‘bắt đầu’ ở chế độ kiểm tra và nhấp vào ‘kích hoạt’. Cơ sở dữ liệu thời gian thực đã sẵn sàng sử dụng.
Thêm Cơ sở dữ liệu Thời gian thực vào Ứng dụng Flutter
Hãy xây dựng một ứng dụng Flutter sử dụng cơ sở dữ liệu thời gian thực để thực hiện các hoạt động CRUD. Ở đây, ví dụ giống như trong phần SQLite đã được đề cập trước đó được sử dụng.
Ứng dụng cho phép tạo và hiển thị người dùng và dữ liệu người dùng sẽ được lưu trữ trong cơ sở dữ liệu thời gian thực của Firebase.
Thêm dependency sau vào tệp pubspec.yaml
của dự án Flutter:
firebase_database: ^9.1.0
Thực hiện Đọc và Ghi:
Trước khi tương tác với cơ sở dữ liệu, cần tạo một thể hiện của DatabaseReference
. Các nhà phát triển có thể tạo một lớp FirebaseService
trong đó tất cả các thao tác Firebase sẽ được xử lý, và sau đó tạo thể hiện này.
firebase_service.dart
import 'package:firebase_database/firebase_database.dart';
DatabaseReference databaseRef = FirebaseDatabase.instance.ref('/users');
Biến databaseRef
đã được khởi tạo trỏ đến bộ sưu tập ‘/users’. Sử dụng databaseRef
này, người dùng có thể tương tác với cơ sở dữ liệu thời gian thực của Firebase.
Để đọc từ cơ sở dữ liệu, sử dụng phương thức get()
sẽ trả về Future<DataSnapshot>
. Vì đây là một mô hình NoSQL, dữ liệu sẽ được lồng vào nhau và các phương thức children
và child
có thể được sử dụng để truy cập vào nhánh cần thiết.
Cấu trúc dữ liệu trông như trong Hình bên dưới.
Dưới mục “users”, có một danh sách các người dùng với mã người dùng làm khóa và giá trị sẽ là mô hình người dùng đã được tạo.
Do đó, databaseRef
được tạo sẽ trỏ đến danh sách người dùng và có thể được truy cập bằng cách sử dụng phương thức get()
.
databaseRef.get();
Để truy cập tất cả người dùng từ danh sách, sử dụng getter children
và chuyển đổi chúng thành một danh sách như trong đoạn code bên dưới.
firebase_service.dart
var data = await databaseRef.get();
List<DataSnapshot> userSnapshotList = data.children.toList();
Để ghi vào cơ sở dữ liệu, phương thức set được sử dụng. Dữ liệu phải được cung cấp dưới dạng Map<String, dynamic>
.
databaseRef.child('/${user.id}').set(user.toMap());
Thêm Gói HTTP vào pubspec.yaml
Giao tiếp với cơ sở dữ liệu Firebase cũng có thể được thực hiện dưới dạng API REST. Để sử dụng điều đó, người dùng phải thêm gói http vào ứng dụng Flutter. Đối với ví dụ hiện tại, API REST sẽ không được sử dụng.
Tạo Ứng Dụng để Thêm Dữ Liệu vào Cơ Sở Dữ Liệu Firebase
Một hàm addUser()
có thể được tạo để lấy dữ liệu người dùng từ đầu vào và thêm vào cơ sở dữ liệu Firebase. Đoạn mã Code bên dưới hiển thị mã cho điều này.
firebase_service.dart
import 'package:firebase_database/firebase_database.dart';
import 'user_model.dart';
class FirebaseService {
static DatabaseReference databaseRef = FirebaseDatabase.instance.ref('/users');
void addUser(User user) {
databaseRef.child('/${user.id}').set(user.toMap());
}
}
Tạo Ứng Dụng để Đọc Dữ Liệu Từ Cơ Sở Dữ Liệu Firebase
Hàm getUsers
sẽ trả về List<UserMode1>. Ở đây, phương thức get() và phương thức get cho children có thể được sử dụng để chuyển đổi thành List<DataSnapshot>. Sau đó, DataSnapshot được chuyển đổi thành một người dùng bằng cách sử dụng hàm fromSnapshot
trong UserModel
được chỉ định trong tệp user_model.dart
. Tham khảo 2 đoạn mã bên dưới.
user_model.dart
import 'package:firebase_database/firebase_database.dart';
factory UserModel.fromSnapshot(DataSnapshot data) => UserModel(
id: data.child('id').value as String,
name: data.child('name').value as String,
email: data.child('email').value as String,
age: data.child('age').value as int,
);
firebase_service.dart
Future<List<UserModel>> getUsers() async {
List<UserModel> users = [];
var data = await databaseRef.get();
List<DataSnapshot> userSnapshotList = data.children.toList();
for (var user in userSnapshotList) {
users.add(UserModel.fromSnapshot(user));
}
return users;
}
Đối với phần giao diện người dùng, nó tương tự như phần đã thay đổi trong SqlliteExample
. Trước khi sử dụng Firebase, cần phải thay đổi một số điều trong main.dart. Tham khảo đoạn mã bên dưới.
import 'package:firebase_core/firebase_core.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(const MyApp());
}
Firebase được khởi tạo bằng cách sử dụng Firebase.initializeApp()
được nhận từ gói firebase_core
. Đoạn mã chỉ để giải thích cách đọc và ghi vào cơ sở dữ liệu Firebase từ ứng dụng Flutter.
firebase_example.dart
import 'package:flutter/material.dart';
import 'firebase_service.dart';
import 'user_model.dart';
import 'package:uuid/uuid.dart';
class FirebaseExampleScreen extends StatefulWidget {
const FirebaseExampleScreen({Key? key}) : super(key: key);
@override
State<FirebaseExampleScreen> createState() => _FirebaseExampleScreenState();
}
class _FirebaseExampleScreenState extends State<FirebaseExampleScreen> {
final dbService = FirebaseService();
// declare text editing controllers here
void showBottomSheet(String functionName, Function()? onPressed) {
//same code from sqllite example
}
void addUser() {
showBottomSheet("Add User", () async {
var user = UserModel(
id: Uuid().v4(),
name: nameController.text,
email: emailController.text,
age: int.parse(ageController.text),
);
dbService.addUser(user);
setState(() {});
nameController.clear();
emailController.clear();
ageController.clear();
Navigator.of(context).pop();
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Firebase database Example'),
),
body: FutureBuilder<List<UserModel>>(
future: dbService.getUsers(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasData) {
if (snapshot.data!.isEmpty) {
return const Center(child: Text('No Users found'));
}
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
var userData = snapshot.data![index];
return Card(
color: Colors.yellow[200],
margin: const EdgeInsets.all(15),
child: ListTile(
title: Text('${userData.name} ${userData.age}'),
subtitle: Text(userData.email),
//more ui design
),
);
},
);
}
return const Center(child: Text('No Users found'));
},
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () => addUser(),
),
);
}
}
Đến nay, đã viết một hàm để lấy tất cả người dùng từ cơ sở dữ liệu Firebase và một phương thức factory để phân tích loại dữ liệu trả về thành một UserModel
.
Trong đoạn mã bên trên, đã tạo giao diện người dùng tương tự như SqlliteExample
.
Ở đây, được sử dụng một FutureBuilder
với phương thức tương lai getUsers()
. Phương thức này được triển khai trong firebase_service.dart
và tất cả người dùng sẽ được liệt kê trong widget ListView
. “Không có người dùng nào được tìm thấy” sẽ được hiển thị nếu danh sách là trống.
Để thêm một người dùng vào cơ sở dữ liệu, nhấn vào Float ingActionButton
và thiết kế một trang như trong SqlliteExample
và gọi hàm addUser
.
Xác thực Firebase
Firebase cung cấp các tính năng xác thực cho người dùng cuối có thể đăng ký hoặc đăng nhập bằng email, mật khẩu và nhiều cách khác như đăng nhập bằng Google, đăng nhập bằng số điện thoại di động, và nhiều hơn nữa. Để kích hoạt Xác thực Firebase cho dự án, hãy vào dự án trong bảng điều khiển Firebase. Dưới mục “Xây dựng”, nhấp vào ‘Xác thực’. Sau khi kích hoạt, nhấp vào tab “Phương thức đăng nhập” và kích hoạt phương thức đăng nhập Email/Mật khẩu. Tiếp theo, sẽ được giải thích cách tính năng này có thể được sử dụng trong ứng dụng Flutter. Tham khảo Hình bên dưới.
Thiết lập Firebase cho Android, iOS và Web
Thiết lập Firebase cho dự án yêu cầu thêm một số phụ thuộc vào ứng dụng và xác định các ứng dụng đó từ bảng điều khiển Firebase. Hình bên dưới hiển thị ba biểu tượng cho Android, iOS và Web.
Nhấp vào loại ứng dụng cần được kết nối và làm theo hướng dẫn trên màn hình.
Cung cấp tên gói, tải xuống tệp google-services.json
và đặt nó vào vị trí phù hợp trên hệ thống cục bộ. Tham khảo Hình bên dưới.
Thêm Phụ thuộc vào tệp pubspec.yaml
a. firebase_core
Gói này được sử dụng để khởi tạo ứng dụng Flutter với Firebase. Nó chịu trách nhiệm thiết lập kết nối giữa dự án Firebase và ứng dụng Flutter.
b. firebase_auth
Tính năng xác thực từ Firebase có thể được sử dụng bằng cách cài đặt gói này. Thêm các phụ thuộc sau vào tệp pubspec.yaml
như được hiển thị trong Đoạn mã bên dưới.
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
firebase_core: ^1.20.0
firebase_auth: ^3.6.2
Khởi tạo Ứng dụng Firebase
Để khởi tạo Firebase trong ứng dụng, thêm mã sau đây như được hiển thị trong Đoạn mã bên dưới trong tệp main.dart
:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Khởi tạo Firebase
await Firebase.initializeApp();
runApp(MyApp());
}
Đăng ký Người dùng mới trong Firebase
Các hoạt động Firebase sẽ được xử lý bên trong một lớp dịch vụ theo quy ước. Tạo một phiên bản của FirebaseAuth bằng cách sử dụng FirebaseAuth.instance
.
Ở đây, trong Đoạn mã bên dưới, phương thức đăng nhập bằng email và mật khẩu đang được sử dụng. Để tạo một đăng ký mới, email và mật khẩu được sử dụng, cũng như displayName
cho người dùng.
firebase_service.dart
import 'package:firebase_auth/firebase_auth.dart';
class FirebaseService {
FirebaseAuth firebaseAuth = FirebaseAuth.instance;
Future<bool> register(String name, String email, String password) async {
firebaseAuth.currentUser!.reload();
try {
var userCredential = await firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
await firebaseAuth.currentUser!.updateDisplayName(name);
if (userCredential.user != null) {
return true;
}
return false;
} catch (e) {
print(e);
return false;
}
}
}
Sử dụng hàm createUserWithEmailAndPassword
để đăng ký một người dùng mới trong Firebase. Sau đó, để đặt tên cho người dùng, cập nhật tên người dùng bằng phương thức updateDisplayName
. Người dùng cũng có thể cung cấp phoneNumber, photoUrl, và nhiều hơn nữa.
Tạo Chức năng Đăng nhập và Đăng xuất Người dùng
Sau khi đăng ký, các chức năng Đăng nhập và Đăng xuất phải được sử dụng. Tham khảo đoạn mã bên dưới.
firebase_service.dart
Future<bool> signIn(String email, String password) async {
try {
var userCredential = await firebaseAuth.signInWithEmailAndPassword(
email: email,
password: password,
);
if (userCredential.user != null) {
return true;
}
return false;
} catch (e) {
print(e);
return false;
}
}
Sử dụng hàm signInWithEmailAndPassword()
để đăng nhập. Việc sử dụng các khối try-catch là một thực hành tốt để bắt lỗi. Nếu người dùng tồn tại trong Firebase, loại trả về UserCredential
sẽ chứa một Đối tượng gọi là User
, có dữ liệu người dùng như email và displayName
.
Nếu Đối tượng Người dùng là null, thì người dùng không tồn tại và quá trình đăng nhập đã thất bại.
Đối với Đăng xuất, sử dụng hàm signOut()
. Tham khảo đoạn mã bên dưới.
firebase_service.dart
Future signOut() async {
await firebaseAuth.signOut();
}
Làm mới Người dùng và Xác định Người xác nhận
FirebaseAuth
có một đối tượng gọi là currentUser
sẽ chứa dữ liệu của người dùng đang đăng nhập hiện tại.
Để làm mới currentUser
, bạn có thể sử dụng phương thức reload()
được cung cấp bởi FirebaseAuth. Phương thức này cập nhật dữ liệu của người dùng hiện tại từ máy chủ. Dưới đây là cách bạn có thể sử dụng nó:
firebaseAuth.currentUser!.reload();
Bằng cách gọi reload()
, Firebase sẽ truy xuất thông tin mới nhất về người dùng hiện tại, chẳng hạn như các thay đổi trong hồ sơ hoặc trạng thái xác thực của họ, từ máy chủ và cập nhật đối tượng người dùng cục bộ tương ứng.
Xây dựng Biểu mẫu Đăng nhập và Trang Hồ sơ
Ví dụ cơ sở dữ liệu Firebase hiện có có thể được sử dụng ở đây. Ngoài ra, một trang đăng nhập và trang đăng ký được thêm vào. Do đó, chỉ những người được xác thực mới có thể điều hướng đến màn hình cơ sở dữ liệu Firebase. Màn hình cơ sở dữ liệu Firebase cũng sẽ chứa email và displayName
của người dùng, sẽ được lấy bằng cách sử dụng instance firebaseAuth
và một nút đăng xuất.
Thiết kế một màn hình đăng nhập đơn giản với các trường email và mật khẩu và một màn hình đăng ký với các trường tên, email và mật khẩu. Tham khảo Hình bên dưới.
Sử dụng các chức năng của các nút Đăng ký và Đăng nhập được hiển thị trong các đoạn mã bên dưới.
Chức năng Đăng ký trong register_screen.dart
ElevatedButton(
onPressed: () async {
bool isRegistered = await firebaseService.register(
nameController.text,
emailController.text,
passController.text,
);
if (isRegistered) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FirebaseExampleScreen(),
),
);
} else {
log('Đăng ký thất bại');
}
},
child: Text('Đăng ký'),
),
Chức năng Đăng nhập trong login_screen.dart
ElevatedButton(
onPressed: () async {
bool isLogin = await firebaseService.signIn(
emailController.text,
passController.text,
);
if (isLogin) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FirebaseExampleScreen(),
),
);
} else {
log("Đăng nhập thất bại");
}
},
child: Text("Đăng nhập"),
),
Email
và displayName
có thể được lấy bằng mã bên dưới.
final fbService = FirebaseService();
fbService.firebaseAuth.currentUser!.email;
fbService.firebaseAuth.currentUser!.displayName;
Giữ Trạng thái Đăng nhập cho Người dùng
Không phải là một thực hành tốt khi buộc người dùng phải đăng nhập mỗi khi họ mở ứng dụng. Firebase cung cấp tùy chọn để giữ lại trạng thái đăng nhập của người dùng.
Để duy trì trạng thái xác thực trong ứng dụng, sử dụng luồng authStateChanges()
sẽ trả về một người dùng nếu đã đăng nhập hoặc null nếu không có người dùng nào đã đăng nhập.
Trong tệp main.dart
, điều kiện này có thể được kiểm tra và dựa vào việc người dùng có đăng nhập hay không, bạn có thể điều hướng đến LoginScreen
hoặc DatabaseScreen
. Tham khảo đoạn mã bên dưới.
main.dart
-> Bên trong void main()
FirebaseService().firebaseAuth.authStateChanges().listen((user) {
if (user == null) {
runApp(const MyApp(isLogin: false));
} else {
runApp(const MyApp(isLogin: true));
}
});
main.dart
class MyApp extends StatelessWidget {
final bool isLogin;
const MyApp({Key? key, required this.isLogin}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Firebase Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: isLogin ? FirebaseExampleScreen() : LoginScreen(),
);
}
}
Như vậy, các nhà phát triển có thể sử dụng Firebase để tạo và điều khiển dữ liệu thời gian thực trong các ứng dụng Flutter. Nhiều ứng dụng thực tế trong thế giới thực sử dụng điều này.