ยินดีต้อนรับสู่ส่วนที่ 2 ของชุดบทช่วยสอนนี้ ในส่วนแรก เราเห็นวิธีสร้าง REST API โดยใช้ Upstash, Serverless Framework และ Redis
ในส่วนนี้ เราจะสร้างแอปพลิเคชันมือถือโดยใช้ Flutter เพื่อใช้ปลายทาง REST API ของเรา
มาเริ่มกันเลย 🙃
ประการแรก คุณจะต้องติดตั้ง Flutter และใช้งานบนคอมพิวเตอร์ของคุณ
- กระพือ
สร้างโปรเจ็กต์ flutter ใหม่ใน IDE ของคุณและตั้งชื่อตามที่คุณต้องการ
เปิด pubspec.yaml
ไฟล์ที่ไดเร็กทอรีรากของโปรเจ็กต์ flutter ของคุณ และเพิ่มการพึ่งพาเหล่านี้ภายใต้ dev_dependencies
timeago: ^3.1.0
shared_preferences: ^2.0.6
http: ^0.13.4
ในที่สุดมันก็ควรจะเป็นแบบนี้
dev_dependencies:
flutter_test:
sdk: flutter
timeago: ^3.1.0
shared_preferences: ^2.0.6
http: ^0.13.4
timeago
ไลบรารีกำลังแปลงการประทับเวลา Unix (1636824843) เป็นรูปแบบที่มนุษย์อ่านได้ เช่น a minute ago
, 5 mins ago
เป็นต้น
Once we create a user account, we want to keep track of their
. ของพวกเขา userIdand other minor details. We'll use
shared_preferencesfor that. Then we'll use the
ไลบรารี http` สำหรับการโทรผ่าน HTTP
เริ่มกันเลย...
สร้างผู้ใช้
หน้าจอแรกที่เราจะสร้างคือหน้าจอสร้างผู้ใช้ ซึ่งจะใช้ปลายทางการสร้างผู้ใช้
นี่คือลักษณะของหน้าจอ
อย่ากังวลเรื่องรูปกระต่าย เป็นเพียงตัวยึดสำหรับการดูภาพ
สร้างโฟลเดอร์ภายใน lib
โฟลเดอร์ชื่อ account
แล้วสร้างไฟล์ใหม่ชื่อ create_profile_screen.dart
ภายใน account
โฟลเดอร์
นี่คือวิธี lib
. สุดท้ายของฉัน โครงสร้างโฟลเดอร์ดูเหมือน ในการสร้างผู้ใช้ใหม่ เราจำเป็นต้องมี
- URL รูปโปรไฟล์
- ชื่อ
- นามสกุล
- ชื่อผู้ใช้
- ปลายทาง
มาดูโค้ดกันเลย
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 เป็นคลาส Dart หลักสำหรับการทำงานกับการดำเนินการแบบอะซิงโครนัส วัตถุในอนาคตแสดงถึงค่าที่เป็นไปได้หรือข้อผิดพลาดที่จะพร้อมใช้งานในอนาคตในอนาคต
คลาส http.Response มีข้อมูลที่ได้รับจากการโทร http ที่สำเร็จ
โค้ดด้านบนใช้ http post
วิธีการส่งคำขอโพสต์ไปยัง create user endpoint
จากนั้นรอการตอบกลับ
หากรหัสสถานะการตอบกลับคือ 200 แสดงว่าคำขอสำเร็จ เราจะบันทึก UserId ที่สร้างขึ้นในการตั้งค่าที่ใช้ร่วมกัน จากนั้นเราจะย้ายไปที่หน้าจอหลัก
นี่คือลิงค์ไปยังซอร์สโค้ดที่สมบูรณ์สำหรับหน้าจอนี้ สร้างหน้าจอโปรไฟล์
สร้างโพสต์
หนึ่งในอุปกรณ์ปลายทางของเราอนุญาตให้ผู้ใช้สร้างโพสต์ได้ นี่คือลักษณะของหน้าจอ
ในการสร้างโพสต์ ผู้ใช้ต้องการ
- รหัสผู้ใช้
- ข้อความ
- imageUrl
โปรดจำไว้ว่า เพื่อจุดประสงค์ในการสาธิต เรากำลังใช้ imageUrl สำเร็จรูป ในแอปจริง คุณจะต้องอนุญาตให้ผู้ใช้เลือกรูปภาพ อัปโหลดไปยังเซิร์ฟเวอร์ รับ URL รูปภาพ แล้วใช้เพื่อสร้างโพสต์
CreatePost
เมธอดดูเหมือน CreateUser
วิธีการ
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();
}
});
}
แสดงรายการโพสต์ทั้งหมด
หน้าจอหลักในแอปพลิเคชันของเราจะแสดงรายการโพสต์ทั้งหมดที่สร้างขึ้น
อะไรประมาณนี้
เพื่อดึงข้อมูลโพสต์ทั้งหมดในลักษณะที่ปราศจากความเครียด ขั้นแรกเราต้องสร้างวัตถุโผแบบกำหนดเองที่ แสดงถึงโพสต์เดียว
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;
}
}
จากนั้น เราแปลง http.Response
ไปยังวัตถุ Dart ที่กำหนดเองนั้น
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);
}
ประเภทการส่งคืนสำหรับ fetchPosts
เมธอดเป็น Future<List<Post>>
.
หากคุณเรียกใช้ฟังก์ชัน fetchPosts() บนอุปกรณ์ที่ช้ากว่า คุณอาจสังเกตเห็นว่าแอปหยุดทำงานชั่วขณะขณะแยกวิเคราะห์และแปลง JSON นี่คือขยะ และคุณต้องการกำจัดมัน
เราลบ jank โดยการย้ายการแยกวิเคราะห์และการแปลงเป็นพื้นหลังโดยใช้ compute
ฟังก์ชัน
compute(parsePosts, response.body);
ฟังก์ชัน compute() เรียกใช้ฟังก์ชันราคาแพงในเบื้องหลังแยกและส่งคืนผลลัพธ์
ในไฟล์หน้าจอหลัก เราจะใช้วิดเจ็ต FutureBuilder เพื่อดึงโพสต์ทั้งหมดเป็นรายการจากฐานข้อมูลของคุณแบบอะซิงโครนัส
เราต้องระบุพารามิเตอร์สองตัว:
- อนาคตที่คุณต้องการทำงานด้วย ในกรณีนี้ อนาคตที่ส่งคืนจากฟังก์ชัน fetchPosts()
ฟังก์ชันตัวสร้างที่บอก Flutter ว่าจะแสดงผลอะไร ขึ้นอยู่กับสถานะของอนาคต:การโหลด สำเร็จ หรือข้อผิดพลาด
โปรดทราบว่า snapshot.hasData จะคืนค่า จริง ต่อเมื่อสแน็ปช็อตมีค่าข้อมูลที่ไม่เป็นค่าว่าง
เนื่องจาก fetchPosts สามารถคืนค่าที่ไม่ใช่ค่า Null ได้เท่านั้น ฟังก์ชันจึงควรมีข้อยกเว้นแม้ในกรณีที่มีการตอบสนองของเซิร์ฟเวอร์ "404 Not Found" การส่งข้อยกเว้นจะตั้งค่า snapshot.hasError เป็น true ซึ่งสามารถใช้เพื่อแสดงข้อความแสดงข้อผิดพลาดได้
มิฉะนั้น สปินเนอร์จะปรากฏขึ้น
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))));
},
))
ในเมธอด initState เราเรียก fetchPosts
late Future<List<Post>> _posts;
@override
void initState() {
// TODO: implement initState
super.initState();
_posts = fetchPosts(http.Client());
}
เหตุผลที่เราเรียก fetchPosts ใน initState แทนวิธีการ build เป็นเพราะ flutter เรียกใช้เมธอด build() ทุกครั้งที่จำเป็นต้องเปลี่ยนแปลงสิ่งใดในมุมมอง และสิ่งนี้เกิดขึ้นบ่อยครั้งอย่างน่าประหลาดใจ การออกจากการเรียกการดึงข้อมูลในเมธอด build() ของคุณจะทำให้ API ล้นด้วยการเรียกที่ไม่จำเป็นและทำให้แอปของคุณช้าลง
โปรดอ่านซอร์สโค้ดที่สมบูรณ์
ยังมีจุดปลายสองสามจุดที่จะสร้างอินเทอร์เฟซสำหรับ แต่บทช่วยสอนที่ดีคืออะไรโดยไม่ต้องออกกำลังกาย 😂
บทสรุป
ในชุดโพสต์นี้ เรามาดูวิธีสร้าง API ส่วนที่เหลือแบบไร้เซิร์ฟเวอร์ด้วย Upstash ในขณะที่ใช้งานผ่านแอปพลิเคชันมือถือ
ฉันชอบที่จะได้เห็นสิ่งที่คุณสร้างต่อไปด้วย Upstash หรือวิธีปรับปรุงบทช่วยสอนนี้ให้เหมาะกับกรณีการใช้งานของคุณ
หากคุณพบว่างานชิ้นนี้มีประโยชน์ โปรดแชร์บนหน้าโซเชียลมีเดียของคุณ
มีคำถามหรือไม่? แสดงความคิดเห็น
หากคุณพบข้อผิดพลาด คุณรู้ว่าต้องทำอย่างไร แสดงความคิดเห็นและฉันจะดำเนินการโดยเร็วที่สุด
มีความสุขในการเข้ารหัส✌🏿
ข้อมูลอ้างอิง
- เอกสารสำรอง
- เรดิส
- กระพือ
- ดึงข้อมูลจากอินเทอร์เน็ต