Chào mừng đến với phần 2 của loạt bài hướng dẫn này. Trong phần đầu tiên, chúng ta đã biết cách xây dựng API REST bằng cách sử dụng Upstash, Serverless Framework và Redis.
Trong phần này, chúng tôi sẽ xây dựng một ứng dụng di động bằng Flutter, để sử dụng các điểm cuối API REST của chúng tôi.
Bắt đầu thôi 🙃
Trước tiên, bạn cần cài đặt và chạy trên máy tính của mình một cách hoàn hảo
- Rung động
Tạo một dự án mới trong IDE của bạn và đặt cho nó một cái tên mà bạn chọn.
Mở pubspec.yaml
tập tin tại thư mục gốc của dự án Flagship của bạn và thêm các phần phụ thuộc này vào dev_dependencies
timeago: ^3.1.0
shared_preferences: ^2.0.6
http: ^0.13.4
Vì vậy, cuối cùng nó sẽ giống như thế này
dev_dependencies:
flutter_test:
sdk: flutter
timeago: ^3.1.0
shared_preferences: ^2.0.6
http: ^0.13.4
timeago
thư viện đang chuyển đổi dấu thời gian Unix (1636824843) thành định dạng con người có thể đọc được như a minute ago
, 5 mins ago
vv
Once we create a user account, we want to keep track of their
của họ userId and other minor details. We'll use
shared_preferences for that. Then we'll use the
Thư viện http` để thực hiện các cuộc gọi HTTP.
Hãy bắt đầu ...
Tạo người dùng
Màn hình đầu tiên chúng tôi sẽ xây dựng là màn hình tạo người dùng, màn hình này sẽ sử dụng điểm cuối người dùng tạo.
Đây là cách màn hình trông như thế nào
Đừng lo lắng về hình ảnh chú thỏ. Chỉ là một trình giữ chỗ cho lần xem hình ảnh.
Tạo một thư mục bên trong lib
thư mục được gọi là account
và sau đó, tạo một tệp mới có tên create_profile_screen.dart
bên trong tài khoản account
thư mục.
Đây là cách cuối cùng của tôi lib
cấu trúc thư mục trông như thế nào Để tạo người dùng mới, chúng tôi cần
- URL ảnh tiểu sử
- tên
- họ
- tên người dùng
- điểm cuối
Hãy xem mã
static const String CREATE_USER_PROFILE_URL = "https://5vafvrk8kj.execute-api.us-east-1.amazonaws.com/dev/user";
bool _loading = false;
Future<void>createUserProfile() async{
setState(() {
_loading = true;
});
print(usernameController.text);
print(firstNameController.text);
print(lastNameController.text);
print(profilePicUrl);
await http.post(Uri.parse(CREATE_USER_PROFILE_URL),
body: convert.jsonEncode({'username': usernameController.text,
"firstName":firstNameController.text,"lastName":lastNameController.text,
"profilePic":profilePicUrl})).then((response) async {
var jsonResponse =
convert.jsonDecode(response.body) as Map<String, dynamic>;
setState(() {
_loading = false;
});
if(response.statusCode == 400){
ScaffoldMessenger.of(context).showSnackBar(SnackBar(padding:EdgeInsets.all(10),backgroundColor: Colors.red,content: Text(jsonResponse['message'])));
}else if(response.statusCode == 200) {
print('user id is :' +jsonResponse['userId']);
await saveUserId(jsonResponse['userId']);
Navigator.push(context, MaterialPageRoute(builder: (context){
return HomeScreen();
}));
}
});
}
Future là một lớp Dart cốt lõi để làm việc với các hoạt động không đồng bộ. Đối tượng Tương lai đại diện cho một giá trị tiềm ẩn hoặc một lỗi sẽ có sẵn vào một thời điểm nào đó trong tương lai.
Lớp http.Response chứa dữ liệu nhận được từ cuộc gọi http thành công.
Đoạn mã trên sử dụng http post
để gửi một yêu cầu đăng đến create user endpoint
sau đó, hãy chờ phản hồi.
Nếu mã trạng thái phản hồi là 200, thì yêu cầu đã thành công, chúng tôi lưu UserId đã tạo trong các tùy chọn được chia sẻ và sau đó chúng tôi chuyển sang màn hình chính.
Đây là liên kết đến mã nguồn hoàn chỉnh cho màn hình này Tạo màn hình hồ sơ.
Tạo bài đăng
Một trong những điểm cuối của chúng tôi cho phép người dùng tạo một bài đăng. Đây là cách màn hình trông như thế nào
Để tạo một bài đăng, người dùng cần
- một userId
- văn bản
- imageUrl
Hãy nhớ rằng, cho mục đích trình diễn, chúng tôi đang sử dụng một imageUrl được tạo sẵn. Trong một ứng dụng thực, bạn sẽ phải cho phép người dùng chọn hình ảnh của họ, tải nó lên máy chủ, lấy Url hình ảnh và sau đó sử dụng nó để tạo bài đăng.
CreatePost
phương thức trông giống với CreateUser
phương pháp.
Future<void> createPost(String userId) async {
await http
.post(Uri.parse(CREATE_USER_POST_URL),
body: convert.jsonEncode({
'userId': userId,
"postText": postTextController.text,
"postImage": _postPicUrl[i]
}))
.then((response) async {
var jsonResponse =
convert.jsonDecode(response.body) as Map<String, dynamic>;
setState(() {
_loading = false;
});
if (response.statusCode == 400) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
padding: EdgeInsets.all(10),
backgroundColor: Colors.red,
content: Text(jsonResponse['message'])));
} else if (response.statusCode == 200) {
print('post id is :' + jsonResponse['id']);
Navigator.of(context).pop();
}
});
}
Liệt kê tất cả các bài đăng
Màn hình chính trên ứng dụng của chúng tôi sẽ hiển thị danh sách tất cả các bài đăng đã tạo.
Một cái gì đó như thế này
Để truy xuất tất cả các bài đăng một cách dễ dàng, trước tiên chúng ta cần tạo một đối tượng phi tiêu tùy chỉnh đại diện cho một bài đăng.
class Post {
String? postText;
String? userId;
String? createdOn;
String? id;
String? postImage;
PostAdmin? postAdmin;
Post(
{this.postText,
this.userId,
this.createdOn,
this.id,
this.postImage,
this.postAdmin});
Post.fromJson(Map<String, dynamic> json) {
postText = json['postText'];
userId = json['userId'];
createdOn = json['createdOn'];
id = json['id'];
postImage = json['postImage'];
postAdmin = json['postAdmin'] != null
? PostAdmin.fromJson(json['postAdmin'])
: null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['postText'] = this.postText;
data['userId'] = this.userId;
data['createdOn'] = this.createdOn;
data['id'] = this.id;
data['postImage'] = this.postImage;
if (this.postAdmin != null) {
data['postAdmin'] = this.postAdmin!.toJson();
}
return data;
}
}
class PostAdmin {
String? timestamp;
String? userId;
String? username;
String? firstName;
String? lastName;
String? profilePic;
PostAdmin(
{this.timestamp,
this.userId,
this.username,
this.firstName,
this.lastName,
this.profilePic});
PostAdmin.fromJson(Map<String, dynamic> json) {
timestamp = json['timestamp'];
userId = json['userId'];
username = json['username'];
firstName = json['firstName'];
lastName = json['lastName'];
profilePic = json['profilePic'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['timestamp'] = this.timestamp;
data['userId'] = this.userId;
data['username'] = this.username;
data['firstName'] = this.firstName;
data['lastName'] = this.lastName;
data['profilePic'] = this.profilePic;
return data;
}
}
Sau đó, chúng tôi chuyển đổi http.Response
vào đối tượng Dart tùy chỉnh đó.
List<Post> parsePosts(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Post>((json) => Post.fromJson(json)).toList();
}
Future<List<Post>> fetchPosts(http.Client client) async {
final response = await client
.get(Uri.parse(GET_POSTS));
return compute(parsePosts,response.body);
}
Loại trả về cho fetchPosts
phương thức là một Future<List<Post>>
.
Nếu bạn chạy hàm fetchPosts () trên một thiết bị chậm hơn, bạn có thể nhận thấy ứng dụng bị đóng băng trong giây lát khi nó phân tích cú pháp và chuyển đổi JSON. Đây là jank và bạn muốn loại bỏ nó.
Chúng tôi loại bỏ jank bằng cách chuyển phân tích cú pháp và chuyển đổi sang nền bằng cách sử dụng compute
chức năng
compute(parsePosts, response.body);
Hàm compute () chạy các hàm đắt tiền trong nền cô lập và trả về kết quả
Trong tệp màn hình chính, chúng tôi sẽ sử dụng tiện ích con FutureBuilder để lấy tất cả các bài đăng dưới dạng danh sách từ cơ sở dữ liệu của bạn một cách không đồng bộ.
Chúng tôi phải cung cấp hai tham số:
- Tương lai bạn muốn làm việc cùng. Trong trường hợp này, tương lai được trả về từ hàm fetchPosts ().
Một hàm trình tạo cho Flutter biết những gì sẽ hiển thị, tùy thuộc vào trạng thái của Tương lai:đang tải, thành công hay lỗi.
Lưu ý rằng snapshot.hasData chỉ trả về true khi snapshot chứa giá trị dữ liệu không phải null.
Vì fetchPosts chỉ có thể trả về các giá trị không phải null, nên hàm sẽ đưa ra một ngoại lệ ngay cả trong trường hợp phản hồi máy chủ “404 Not Found”. Việc ném một ngoại lệ sẽ đặt snapshot.hasError thành true có thể được sử dụng để hiển thị thông báo lỗi.
Nếu không, con quay sẽ được hiển thị.
Expanded(child: FutureBuilder<List<Post>>(
future: _posts,
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Post>? posts = snapshot.data;
if(posts != null){
return ListView.builder(itemBuilder: (context,index){
return Card(
child: Container(
padding: EdgeInsets.all(10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(1000),
child: Image.network(
posts[index].postAdmin!.profilePic!,
fit: BoxFit.cover,
height: 40,
width: 40,
),
),
Expanded(
child: Container(
padding: EdgeInsets.only(left: 10),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(posts[index].postAdmin!.username!,style: TextStyle(fontWeight: FontWeight.bold,fontSize: 16),),
Text(posts[index].postText!),
ClipRRect(
borderRadius: BorderRadius.circular(10),
child: Image.network(
posts[index].postImage!,
fit: BoxFit.cover,
height: 150,
width: size.width,
),
),
],
),
),
)
],
),
),
);
},itemCount: posts.length,);
}
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return Container(
height: 40,
width: 40,
child: Center(child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation<Color>(Theme.of(context).colorScheme.secondary))));
},
))
Trong phương thức initState, chúng tôi gọi fetchPosts
late Future<List<Post>> _posts;
@override
void initState() {
// TODO: implement initState
super.initState();
_posts = fetchPosts(http.Client());
}
Lý do tại sao chúng ta gọi fetchPosts trong initState thay vì phương thức build là bởi vì flashing gọi phương thức build () mỗi khi nó cần thay đổi bất kỳ thứ gì trong khung nhìn và điều này xảy ra thường xuyên một cách đáng ngạc nhiên. Để lệnh gọi tìm nạp trong phương thức build () của bạn làm tràn ngập API với các lệnh gọi không cần thiết và làm chậm ứng dụng của bạn.
Vui lòng xem qua mã nguồn hoàn chỉnh
Vẫn còn một số điểm cuối để tạo giao diện, nhưng đây là một hướng dẫn hay mà không cần bài tập 😂
Kết luận
Trong loạt bài đăng này, chúng tôi đã xem xét cách xây dựng API nghỉ không máy chủ với Upstash, trong khi sử dụng chúng thông qua ứng dụng di động.
Tôi rất thích xem bạn xây dựng những gì tiếp theo với Upstash hoặc cách bạn cải tiến hướng dẫn này cho phù hợp với trường hợp sử dụng của bạn.
Nếu bạn thấy thông tin này hữu ích, hãy chia sẻ trên các trang mạng xã hội của bạn.
Có một vài câu hỏi? Để lại bình luận.
Nếu bạn tìm thấy một lỗi, bạn biết phải làm gì. Để lại bình luận và tôi sẽ cập nhật nó càng sớm càng tốt.
Mã hóa vui vẻ ✌🏿
Tham chiếu
- Upstash Docs
- Redis
- Rung động
- Tìm nạp dữ liệu từ internet