কম্পিউটার

ফ্লটার, সার্ভারলেস ফ্রেমওয়ার্ক এবং আপস্ট্যাশ (REDIS) সহ ফুলস্ট্যাক সার্ভারলেস অ্যাপ - পার্ট 2

এই টিউটোরিয়াল সিরিজের ২য় পর্বে স্বাগতম। প্রথম অংশে, আমরা দেখেছি কিভাবে Upstash, Serverless Framework, এবং Redis ব্যবহার করে একটি REST API তৈরি করা যায়।

এই অংশে, আমরা আমাদের REST API এন্ডপয়েন্ট ব্যবহার করার জন্য Flutter ব্যবহার করে একটি মোবাইল অ্যাপ্লিকেশন তৈরি করব।

চলুন শুরু করা যাক 🙃

প্রথমত, আপনার কম্পিউটারে ফ্লাটার ইনস্টল এবং চলমান থাকতে হবে

  • ফ্লাটার

আপনার IDE-তে একটি নতুন ফ্লটার প্রোজেক্ট তৈরি করুন এবং এটিকে আপনার পছন্দের একটি নাম দিন।

pubspec.yaml খুলুন আপনার ফ্লাটার প্রকল্পের রুট ডিরেক্টরিতে ফাইল করুন এবং এই নির্ভরতাগুলি 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 লাইব্রেরি ইউনিক্স টাইমস্ট্যাম্প (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` লাইব্রেরি।

চলুন শুরু করা যাক...

ব্যবহারকারী তৈরি করুন

আমরা যে প্রথম স্ক্রিনটি তৈরি করব তা হল ব্যবহারকারী তৈরি করুন স্ক্রীন, যা ব্যবহারকারীর শেষ বিন্দু তৈরি করবে।

এখানে স্ক্রীনটি কেমন দেখায়

ফ্লটার, সার্ভারলেস ফ্রেমওয়ার্ক এবং আপস্ট্যাশ (REDIS) সহ ফুলস্ট্যাক সার্ভারলেস অ্যাপ - পার্ট 2 খরগোশের ছবি নিয়ে চিন্তা করবেন না। ইমেজভিউ এর জন্য শুধুমাত্র একটি স্থানধারক।

lib-এর ভিতরে একটি ফোল্ডার তৈরি করুন account নামক ফোল্ডার এবং তারপর, create_profile_screen.dart নামে একটি নতুন ফাইল তৈরি করুন account এর ভিতরে ফোল্ডার।

এখানে আমার চূড়ান্ত lib কিভাবে ফোল্ডার গঠন মত দেখায় ফ্লটার, সার্ভারলেস ফ্রেমওয়ার্ক এবং আপস্ট্যাশ (REDIS) সহ ফুলস্ট্যাক সার্ভারলেস অ্যাপ - পার্ট 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();
        }));
      }
    });



  }

ভবিষ্যত অ্যাসিঙ্ক্রোনাস অপারেশনগুলির সাথে কাজ করার জন্য একটি মূল ডার্ট ক্লাস। একটি ভবিষ্যত বস্তু একটি সম্ভাব্য মান বা ত্রুটির প্রতিনিধিত্ব করে যা ভবিষ্যতে কিছু সময় উপলব্ধ হবে৷

http.Response ক্লাসে একটি সফল http কল থেকে প্রাপ্ত ডেটা রয়েছে৷

উপরের কোডটি http post ব্যবহার করে create user endpoint-এ পোস্টের অনুরোধ পাঠানোর পদ্ধতি তারপর, একটি প্রতিক্রিয়ার জন্য অপেক্ষা করুন৷

যদি প্রতিক্রিয়া স্থিতি কোড 200 হয়, তাহলে অনুরোধটি সফল হয়েছে, আমরা ভাগ করা পছন্দগুলিতে তৈরি করা UserId সংরক্ষণ করি এবং তারপরে আমরা হোমস্ক্রীনে চলে যাই৷

এখানে এই স্ক্রিনের সম্পূর্ণ সোর্স কোডের একটি লিঙ্ক প্রোফাইল স্ক্রীন তৈরি করুন৷

একটি পোস্ট তৈরি করুন

আমাদের শেষ পয়েন্টগুলির মধ্যে একটি ব্যবহারকারীকে একটি পোস্ট তৈরি করার অনুমতি দিয়েছে। এখানে স্ক্রীনটি কেমন দেখায়

ফ্লটার, সার্ভারলেস ফ্রেমওয়ার্ক এবং আপস্ট্যাশ (REDIS) সহ ফুলস্ট্যাক সার্ভারলেস অ্যাপ - পার্ট 2

একটি পোস্ট তৈরি করার জন্য, একজন ব্যবহারকারীর প্রয়োজন

  • একটি ব্যবহারকারী আইডি
  • পাঠ্য
  • 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();
      }
    });
  }

সব পোস্টের তালিকা করুন

আমাদের অ্যাপ্লিকেশনের হোম স্ক্রীনটি তৈরি করা সমস্ত পোস্টের একটি তালিকা প্রদর্শন করবে।

এরকম কিছু

ফ্লটার, সার্ভারলেস ফ্রেমওয়ার্ক এবং আপস্ট্যাশ (REDIS) সহ ফুলস্ট্যাক সার্ভারলেস অ্যাপ - পার্ট 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 রূপান্তর করি সেই কাস্টম ডার্ট অবজেক্টে।

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() ফাংশন ব্যাকগ্রাউন্ড আইসোলেটে ব্যয়বহুল ফাংশন চালায় এবং ফলাফল প্রদান করে

হোম স্ক্রীন ফাইলে, আমরা একটি ফিউচারবিল্ডার উইজেট ব্যবহার করব যাতে অ্যাসিঙ্ক্রোনাসভাবে আপনার ডাটাবেস থেকে একটি তালিকা হিসাবে সমস্ত পোস্ট দখল করা যায়৷

আমাদের দুটি পরামিতি প্রদান করতে হবে:

  • যে ভবিষ্যৎ নিয়ে আপনি কাজ করতে চান। এই ক্ষেত্রে, fetchPosts() ফাংশন থেকে ভবিষ্যত ফিরে আসে।

একটি বিল্ডার ফাংশন যা ফ্লটারকে বলে যে কী রেন্ডার করতে হবে, ভবিষ্যতের অবস্থার উপর নির্ভর করে:লোডিং, সাফল্য বা ত্রুটি৷

মনে রাখবেন snapshot.hasData শুধুমাত্র তখনই সত্য হয় যখন স্ন্যাপশটে একটি নন-নাল ডেটা মান থাকে।

যেহেতু fetchPosts শুধুমাত্র নন-নাল মান ফেরত দিতে পারে, তাই "404 পাওয়া যায়নি" সার্ভার প্রতিক্রিয়ার ক্ষেত্রেও ফাংশনটি একটি ব্যতিক্রম ছুঁড়ে দেওয়া উচিত। একটি ব্যতিক্রম নিক্ষেপ করা snapshot.hasError কে সত্যে সেট করে যা একটি ত্রুটি বার্তা প্রদর্শন করতে ব্যবহার করা যেতে পারে৷

অন্যথায়, স্পিনার প্রদর্শিত হবে।

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());

  }

আমরা বিল্ড মেথডের পরিবর্তে initState-এ fetchPosts কল করার কারণ হল ফ্লাটার যখনই ভিউতে কিছু পরিবর্তন করার প্রয়োজন হয় তখন বিল্ড() মেথডকে কল করে এবং এটি আশ্চর্যজনকভাবে প্রায়শই ঘটে। আপনার বিল্ড() পদ্ধতিতে ফেচ কল ছেড়ে দিলে এপিআই অপ্রয়োজনীয় কলের সাথে প্লাবিত হয় এবং আপনার অ্যাপকে ধীর করে দেয়।

সম্পূর্ণ সোর্স কোডের মধ্য দিয়ে যেতে দ্বিধা বোধ করুন

ইন্টারফেস তৈরি করার জন্য এখনও কয়েকটি এন্ডপয়েন্ট আছে, কিন্তু ব্যায়াম ছাড়াই একটি ভাল টিউটোরিয়াল কি 😂

উপসংহার

এই পোস্ট সিরিজে, আমরা একটি মোবাইল অ্যাপ্লিকেশনের মাধ্যমে ব্যবহার করার সময় Upstash-এর সাথে একটি সার্ভারবিহীন বিশ্রাম API কীভাবে তৈরি করা যায় তা দেখেছি৷

আপস্ট্যাশের সাথে আপনি পরবর্তীতে কী তৈরি করেন বা আপনার ব্যবহারের ক্ষেত্রে কীভাবে আপনি এই টিউটোরিয়ালটিকে উন্নত করেন তা দেখতে আমি পছন্দ করব।

আপনি যদি এই অংশটিকে সহায়ক বলে মনে করেন, অনুগ্রহ করে আপনার সামাজিক মিডিয়া পৃষ্ঠাগুলিতে ভাগ করুন৷

প্রশ্ন আছে? একটি মন্তব্য করুন৷

আপনি যদি একটি ত্রুটি খুঁজে পান, আপনি জানেন কি করতে হবে. একটি মন্তব্য করুন এবং আমি যত তাড়াতাড়ি সম্ভব এটিতে থাকব।

শুভ কোডিং ✌🏿

রেফারেন্স

  • Upstash ডক্স
  • রেডিস
  • ফ্লাটার
  • ইন্টারনেট থেকে ডেটা আনা হচ্ছে

  1. রেডিসের সাথে TODO অ্যাপ রিমিক্স করুন

  2. সার্ভারলেস রেডিস এবং প্রতিক্রিয়া নেটিভ সহ অ্যাপ-মধ্যস্থ ঘোষণা

  3. Nuxt 3 এবং সার্ভারলেস রেডিস দিয়ে শুরু করা

  4. ফ্লটার, সার্ভারলেস ফ্রেমওয়ার্ক এবং আপস্ট্যাশ (REDIS) সহ ফুলস্ট্যাক সার্ভারলেস অ্যাপ - পার্ট 1