Computer >> Máy Tính >  >> Lập trình >> Redis

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 2

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 dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 2 Đừ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 Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 2 Để 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

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 2

Để 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

Ứng dụng Fullstack Serverless với Flutter, Serverless Framework và Upstash (REDIS) - PHẦN 2 Để 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