Computer >> 컴퓨터 >  >> 프로그램 작성 >> Redis

Flutter, Serverless Framework 및 Upstash(REDIS)가 포함된 Fullstack 서버리스 앱 - 2부

이 튜토리얼 시리즈의 2부에 오신 것을 환영합니다. 첫 번째 파트에서는 ​​Upstash, Serverless Framework 및 Redis를 사용하여 REST API를 구축하는 방법을 살펴보았습니다.

이 부분에서는 Flutter를 사용하여 REST API 엔드포인트를 사용하는 모바일 애플리케이션을 빌드합니다.

시작합시다 🙃

먼저 컴퓨터에 Flutter를 설치하고 실행해야 합니다.

  • 플러터

IDE에서 새 flutter 프로젝트를 만들고 원하는 이름을 지정하세요.

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` 라이브러리.

시작하겠습니다...

사용자 생성

우리가 만들 첫 번째 화면은 사용자 만들기 끝점을 사용하는 사용자 만들기 화면입니다.

화면은 다음과 같습니다.

Flutter, Serverless Framework 및 Upstash(REDIS)가 포함된 Fullstack 서버리스 앱 - 2부 토끼 사진은 걱정하지 마세요. 이미지 보기를 위한 자리 표시자일 뿐입니다.

lib 안에 폴더 생성 account라는 폴더 그런 다음 create_profile_screen.dart라는 새 파일을 만듭니다. account 내부 폴더.

내 최종 lib 폴더 구조는 다음과 같습니다. Flutter, Serverless Framework 및 Upstash(REDIS)가 포함된 Fullstack 서버리스 앱 - 2부 새 사용자를 만들려면 다음이 필요합니다.

  • 프로필 사진 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 클래스입니다. Future 개체는 미래의 어느 시점에서 사용할 수 있는 잠재적인 값이나 오류를 나타냅니다.

http.Response 클래스는 성공적인 http 호출에서 수신한 데이터를 포함합니다.

위의 코드는 http post을 사용합니다. create user endpoint에 포스트 요청을 보내는 메소드 그런 다음 응답을 기다리십시오.

응답 상태 코드가 200이면 요청이 성공한 것이며 생성된 UserId를 공유 환경 설정에 저장한 다음 홈 화면으로 이동합니다.

프로필 만들기 화면 이 화면의 전체 소스 코드에 대한 링크입니다.

게시물 만들기

엔드포인트 중 하나는 사용자가 게시물을 작성할 수 있도록 허용했습니다. 화면은 다음과 같습니다.

Flutter, Serverless Framework 및 Upstash(REDIS)가 포함된 Fullstack 서버리스 앱 - 2부

게시물을 작성하려면 사용자가 필요합니다.

  • 사용자 ID
  • 텍스트
  • 이미지 URL

데모 목적으로 이미 만들어진 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();
      }
    });
  }

모든 게시물 나열

애플리케이션의 홈 화면에는 작성된 모든 게시물 목록이 표시됩니다.

이런 것

Flutter, Serverless Framework 및 Upstash(REDIS)가 포함된 Fullstack 서버리스 앱 - 2부 모든 게시물을 스트레스 없는 방식으로 검색하려면 먼저 단일 게시물을 나타냅니다.

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을 구문 분석하고 변환하는 동안 앱이 잠시 멈추는 것을 알 수 있습니다. 이것은 버벅거림이며 제거하고 싶습니다.

compute를 사용하여 구문 분석 및 변환을 백그라운드로 이동하여 버벅거림을 제거합니다. 기능

compute(parsePosts, response.body);

compute() 함수는 백그라운드 격리에서 값비싼 함수를 실행하고 결과를 반환합니다.

홈 화면 파일에서 FutureBuilder 위젯을 사용하여 모든 게시물을 데이터베이스에서 목록으로 비동기식으로 가져옵니다.

두 개의 매개변수를 제공해야 합니다.

  • 함께 일하고 싶은 미래. 이 경우 fetchPosts() 함수에서 미래가 반환됩니다.

Future의 상태(로드, 성공 또는 오류)에 따라 무엇을 렌더링할지 Flutter에게 알려주는 빌더 함수입니다.

snapshot.hasData는 스냅샷에 null이 아닌 데이터 값이 포함된 경우에만 true를 반환합니다.

fetchPosts는 null이 아닌 값만 반환할 수 있으므로 "404 Not Found" 서버 응답의 경우에도 함수는 예외를 throw해야 합니다. 예외를 throw하면 오류 메시지를 표시하는 데 사용할 수 있는 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());

  }

build 메소드 대신 initState에서 fetchPosts를 호출하는 이유는 flutter가 뷰에서 무엇이든 변경해야 할 때마다 build() 메소드를 호출하기 때문이며, 이는 놀랍게도 자주 발생합니다. build() 메서드에 fetch 호출을 남겨두면 API가 불필요한 호출로 넘쳐나고 앱 속도가 느려집니다.

전체 소스 코드를 살펴보십시오.

인터페이스를 생성할 엔드포인트가 아직 몇 개 있지만 연습이 없는 좋은 자습서는 무엇입니까 😂

결론

이 포스트 시리즈에서는 Upstash를 사용하여 서버리스 나머지 API를 구축하는 동시에 모바일 애플리케이션을 통해 사용하는 방법을 살펴보았습니다.

Upstash를 사용하여 다음에 빌드할 내용이나 이 튜토리얼을 사용 사례에 맞게 개선하는 방법을 보고 싶습니다.

이 글이 도움이 되었다면 소셜 미디어 페이지에 공유해 주세요.

질문이 있으신가요? 댓글을 남겨주세요.

오류를 찾은 경우 수행할 작업을 알고 있습니다. 댓글을 남겨주시면 최대한 빨리 수정하겠습니다.

해피코딩 ✌🏿

참조

  • Upstash 문서
  • 레디스
  • 플러터
  • 인터넷에서 데이터 가져오기