আপনার প্রথম ফ্লটার অ্যাপ

1. ভূমিকা

ফ্লাটার হল একটি একক কোডবেস থেকে মোবাইল, ওয়েব এবং ডেস্কটপের জন্য অ্যাপ্লিকেশন তৈরি করার জন্য Google এর UI টুলকিট। এই কোডল্যাবে, আপনি নিম্নলিখিত ফ্লাটার অ্যাপ্লিকেশন তৈরি করবেন:

অ্যাপ্লিকেশনটি "নিউজস্টে", "লাইটস্ট্রিম", "মেইনব্রেক" বা "গ্রেপাইন" এর মতো শান্ত-শব্দযুক্ত নাম তৈরি করে। ব্যবহারকারী পরবর্তী নাম জিজ্ঞাসা করতে পারেন, বর্তমানটিকে পছন্দ করতে পারেন এবং একটি পৃথক পৃষ্ঠায় পছন্দসই নামের তালিকা পর্যালোচনা করতে পারেন। অ্যাপটি বিভিন্ন স্ক্রিনের আকারের জন্য প্রতিক্রিয়াশীল।

আপনি কি শিখবেন

  • ফ্লাটার কিভাবে কাজ করে তার বুনিয়াদি
  • ফ্লটারে লেআউট তৈরি করা হচ্ছে
  • অ্যাপের আচরণের সাথে ব্যবহারকারীর ইন্টারঅ্যাকশন (যেমন বোতাম টিপে) সংযুক্ত করা
  • আপনার ফ্লটার কোড সংগঠিত রাখা
  • আপনার অ্যাপকে প্রতিক্রিয়াশীল করা (বিভিন্ন স্ক্রিনের জন্য)
  • আপনার অ্যাপের একটি সামঞ্জস্যপূর্ণ চেহারা এবং অনুভূতি অর্জন করা

আপনি একটি মৌলিক স্ক্যাফোল্ড দিয়ে শুরু করবেন যাতে আপনি সরাসরি আকর্ষণীয় অংশে যেতে পারেন।

e9c6b402cd8003fd.png

এবং এখানে ফিলিপ আপনাকে পুরো কোডল্যাবের মাধ্যমে নিয়ে যাচ্ছে!

ল্যাব শুরু করতে পরবর্তী ক্লিক করুন.

2. আপনার ফ্লটার পরিবেশ সেট আপ করুন

সম্পাদক

এই কোডল্যাবটিকে যতটা সম্ভব সহজবোধ্য করতে, আমরা ধরে নিই যে আপনি আপনার বিকাশের পরিবেশ হিসাবে ভিজ্যুয়াল স্টুডিও কোড (ভিএস কোড) ব্যবহার করবেন। এটি বিনামূল্যে এবং সমস্ত প্রধান প্ল্যাটফর্মে কাজ করে।

অবশ্যই আপনার পছন্দের যেকোনো সম্পাদক ব্যবহার করা ভালো: Android Studio, অন্যান্য IntelliJ IDEs, Emacs, Vim, অথবা Notepad++। তারা সবাই ফ্লটারের সাথে কাজ করে।

আমরা এই কোডল্যাবের জন্য VS কোড ব্যবহার করার পরামর্শ দিই কারণ নির্দেশাবলী ডিফল্ট VS কোড-নির্দিষ্ট শর্টকাট। "এখানে ক্লিক করুন" বা "এই কী টিপুন" এর মত কিছু বলার পরিবর্তে "এক্স করার জন্য আপনার এডিটরে যথাযথ পদক্ষেপ নিন" এর মত কিছু বলা সহজ।

228c71510a8e868.png

একটি উন্নয়ন লক্ষ্য নির্বাচন করুন

ফ্লটার হল একটি মাল্টি-প্ল্যাটফর্ম টুলকিট। আপনার অ্যাপটি নিচের যেকোনো অপারেটিং সিস্টেমে চলতে পারে:

  • iOS
  • অ্যান্ড্রয়েড
  • উইন্ডোজ
  • macOS
  • লিনাক্স
  • ওয়েব

যাইহোক, এটি একটি একক অপারেটিং সিস্টেম বেছে নেওয়া সাধারণ অভ্যাস যার জন্য আপনি প্রাথমিকভাবে বিকাশ করবেন। এটি আপনার "উন্নয়ন লক্ষ্য"—যে অপারেটিং সিস্টেমটি আপনার অ্যাপটি বিকাশের সময় চলে।

16695777c07f18e5.png

উদাহরণস্বরূপ, বলুন আপনি একটি ফ্লাটার অ্যাপ তৈরি করতে একটি উইন্ডোজ ল্যাপটপ ব্যবহার করছেন৷ আপনি যদি আপনার ডেভেলপমেন্ট টার্গেট হিসেবে অ্যান্ড্রয়েড বেছে নেন, আপনি সাধারণত একটি USB তারের সাহায্যে আপনার Windows ল্যাপটপে একটি Android ডিভাইস সংযুক্ত করেন এবং আপনার অ্যাপ-ইন-ডেভেলপমেন্ট সেই সংযুক্ত Android ডিভাইসে চলে। কিন্তু আপনি উইন্ডোজকে ডেভেলপমেন্ট টার্গেট হিসেবেও বেছে নিতে পারেন, যার মানে আপনার অ্যাপ-ইন-ডেভেলপমেন্ট আপনার এডিটরের পাশাপাশি উইন্ডোজ অ্যাপ হিসেবে চলে।

আপনার ডেভেলপমেন্ট টার্গেট হিসাবে ওয়েব নির্বাচন করা প্রলুব্ধ হতে পারে। এই পছন্দের নেতিবাচক দিক হল যে আপনি ফ্লটারের সবচেয়ে দরকারী বিকাশ বৈশিষ্ট্যগুলির একটি হারাবেন: স্টেটফুল হট রিলোড৷ ফ্লটার ওয়েব অ্যাপ্লিকেশনগুলিকে হট-রিলোড করতে পারে না৷

এখন আপনার পছন্দ করুন. মনে রাখবেন: আপনি পরবর্তীতে অন্য অপারেটিং সিস্টেমে সবসময় আপনার অ্যাপ চালাতে পারবেন। এটা ঠিক যে একটি সুস্পষ্ট উন্নয়ন লক্ষ্যমাত্রা মাথায় রাখা পরবর্তী ধাপটিকে মসৃণ করে তোলে।

Flutter ইনস্টল করুন

কিভাবে Flutter SDK ইন্সটল করতে হয় তার সবচেয়ে আপ-টু-ডেট নির্দেশাবলী সবসময় docs.flutter.dev- এ থাকে।

Flutter ওয়েবসাইটের নির্দেশাবলী শুধুমাত্র SDK এর ইনস্টলেশনই নয়, উন্নয়ন লক্ষ্য-সম্পর্কিত সরঞ্জাম এবং সম্পাদক প্লাগইনগুলিকেও কভার করে। মনে রাখবেন যে, এই কোডল্যাবের জন্য, আপনাকে শুধুমাত্র নিম্নলিখিতগুলি ইনস্টল করতে হবে:

  1. ফ্লটার SDK
  2. ফ্লাটার প্লাগইন সহ ভিজ্যুয়াল স্টুডিও কোড
  3. আপনার নির্বাচিত ডেভেলপমেন্ট টার্গেটের জন্য প্রয়োজনীয় সফ্টওয়্যার (উদাহরণস্বরূপ: উইন্ডোজকে লক্ষ্য করার জন্য ভিজ্যুয়াল স্টুডিও , বা ম্যাকওএসকে লক্ষ্য করার জন্য এক্সকোড )

পরবর্তী বিভাগে, আপনি আপনার প্রথম ফ্লাটার প্রকল্প তৈরি করবেন।

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

প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী

3. একটি প্রকল্প তৈরি করুন

আপনার প্রথম ফ্লাটার প্রকল্প তৈরি করুন

ভিজ্যুয়াল স্টুডিও কোড চালু করুন এবং কমান্ড প্যালেট খুলুন ( F1 বা Ctrl+Shift+P বা Shift+Cmd+P সহ)। "ফ্লাটার নিউ" টাইপ করা শুরু করুন। Flutter: New Project কমান্ড নির্বাচন করুন।

এরপরে, অ্যাপ্লিকেশন নির্বাচন করুন এবং তারপরে একটি ফোল্ডার যা আপনার প্রকল্প তৈরি করতে হবে। এটি আপনার হোম ডিরেক্টরি বা C:\src\ এর মতো কিছু হতে পারে।

অবশেষে, আপনার প্রকল্পের নাম দিন। namer_app বা my_awesome_namer এর মত কিছু।

260a7d97f9678005.png

ফ্লটার এখন আপনার প্রোজেক্ট ফোল্ডার তৈরি করে এবং VS কোড এটি খোলে।

আপনি এখন অ্যাপের একটি বেসিক স্ক্যাফোল্ড সহ 3টি ফাইলের বিষয়বস্তু ওভাররাইট করবেন।

প্রাথমিক অ্যাপটি কপি এবং পেস্ট করুন

VS কোডের বাম প্যানে, নিশ্চিত করুন যে এক্সপ্লোরার নির্বাচন করা হয়েছে, এবং pubspec.yaml ফাইলটি খুলুন।

e2a5bab0be07f4f7.png

এই ফাইলের বিষয়বস্তু নিম্নলিখিত দিয়ে প্রতিস্থাপন করুন:

pubspec.yaml

name: namer_app
description: A new Flutter project.

publish_to: 'none' # Remove this line if you wish to publish to pub.dev

version: 0.0.1+1

environment:
  sdk: ^3.1.1

dependencies:
  flutter:
    sdk: flutter

  english_words: ^4.0.0
  provider: ^6.0.0

dev_dependencies:
  flutter_test:
    sdk: flutter

  flutter_lints: ^2.0.0

flutter:
  uses-material-design: true

pubspec.yaml ফাইলটি আপনার অ্যাপ সম্পর্কে প্রাথমিক তথ্য নির্দিষ্ট করে, যেমন এর বর্তমান সংস্করণ, এর নির্ভরতা এবং এটি যে সম্পদের সাথে পাঠানো হবে।

এরপরে, প্রজেক্টে আরেকটি কনফিগারেশন ফাইল খুলুন, analysis_options.yaml

a781f218093be8e0.png

এর বিষয়বস্তুগুলি নিম্নলিখিতগুলির সাথে প্রতিস্থাপন করুন:

analysis_options.yaml

include: package:flutter_lints/flutter.yaml

linter:
  rules:
    avoid_print: false
    prefer_const_constructors_in_immutables: false
    prefer_const_constructors: false
    prefer_const_literals_to_create_immutables: false
    prefer_final_fields: false
    unnecessary_breaks: true
    use_key_in_widget_constructors: false

এই ফাইলটি নির্ধারণ করে যে আপনার কোড বিশ্লেষণ করার সময় Flutter কতটা কঠোর হওয়া উচিত। যেহেতু এটি ফ্লটারে আপনার প্রথম আক্রমণ, তাই আপনি বিশ্লেষককে এটিকে সহজে নিতে বলছেন। আপনি সবসময় পরে এটি টিউন করতে পারেন. প্রকৃতপক্ষে, আপনি একটি প্রকৃত উত্পাদন অ্যাপ্লিকেশন প্রকাশের কাছাকাছি যাওয়ার সাথে সাথে আপনি প্রায় অবশ্যই বিশ্লেষকটিকে এর চেয়ে কঠোর করতে চাইবেন।

অবশেষে, lib/ ডিরেক্টরির অধীনে main.dart ফাইলটি খুলুন।

e54c671c9bb4d23d.png

এই ফাইলের বিষয়বস্তু নিম্নলিখিত দিয়ে প্রতিস্থাপন করুন:

lib/main.dart

import 'package:english_words/english_words.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyAppState(),
      child: MaterialApp(
        title: 'Namer App',
        theme: ThemeData(
          useMaterial3: true,
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
        ),
        home: MyHomePage(),
      ),
    );
  }
}

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();
}

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();

    return Scaffold(
      body: Column(
        children: [
          Text('A random idea:'),
          Text(appState.current.asLowerCase),
        ],
      ),
    );
  }
}

কোডের এই 50টি লাইন এখন পর্যন্ত অ্যাপটির সম্পূর্ণতা।

পরবর্তী বিভাগে, ডিবাগ মোডে অ্যাপ্লিকেশনটি চালান এবং বিকাশ শুরু করুন।

4. একটি বোতাম যোগ করুন

এই ধাপটি একটি নতুন শব্দ জোড়া তৈরি করতে একটি পরবর্তী বোতাম যোগ করে।

অ্যাপটি চালু করুন

প্রথমে, lib/main.dart খুলুন এবং নিশ্চিত করুন যে আপনি আপনার টার্গেট ডিভাইস নির্বাচন করেছেন। VS কোডের নীচে ডানদিকে, আপনি একটি বোতাম পাবেন যা বর্তমান লক্ষ্য ডিভাইসটি দেখায়। এটি পরিবর্তন করতে ক্লিক করুন.

lib/main.dart খোলা থাকার সময়, "play" খুঁজুন b0a5d0200af5985d.png VS কোডের উইন্ডোর উপরের ডানদিকের কোণায় বোতাম, এবং এটিতে ক্লিক করুন।

প্রায় এক মিনিট পর, আপনার অ্যাপটি ডিবাগ মোডে লঞ্চ হবে। এটা এখনও অনেক মত দেখাচ্ছে না:

f96e7dfb0937d7f4.png

প্রথম হট রিলোড

lib/main.dart এর নীচে, প্রথম Text অবজেক্টের স্ট্রিংটিতে কিছু যোগ করুন এবং ফাইলটি সংরক্ষণ করুন ( Ctrl+S বা Cmd+S সহ)। যেমন:

lib/main.dart

// ...

    return Scaffold(
      body: Column(
        children: [
          Text('A random AWESOME idea:'),  // ← Example change.
          Text(appState.current.asLowerCase),
        ],
      ),
    );

// ...

লক্ষ্য করুন কিভাবে অ্যাপটি অবিলম্বে পরিবর্তিত হয় কিন্তু এলোমেলো শব্দ একই থাকে। এটি হল ফ্লটারের বিখ্যাত স্টেটফুল হট রিলোড আপনি যখন একটি উৎস ফাইলে পরিবর্তনগুলি সংরক্ষণ করেন তখন হট রিলোড ট্রিগার হয়৷

প্রায়শই জিজ্ঞাসিত প্রশ্নাবলী

একটি বোতাম যোগ করা হচ্ছে

এরপর, Column নীচে একটি বোতাম যোগ করুন, দ্বিতীয় Text উদাহরণের ঠিক নীচে।

lib/main.dart

// ...

    return Scaffold(
      body: Column(
        children: [
          Text('A random AWESOME idea:'),
          Text(appState.current.asLowerCase),

          // ↓ Add this.
          ElevatedButton(
            onPressed: () {
              print('button pressed!');
            },
            child: Text('Next'),
          ),

        ],
      ),
    );

// ...

আপনি যখন পরিবর্তনটি সংরক্ষণ করেন, তখন অ্যাপটি আবার আপডেট হয়: একটি বোতাম উপস্থিত হয় এবং, যখন আপনি এটিতে ক্লিক করেন, VS কোডের ডিবাগ কনসোল একটি বোতাম টিপে দেখায়! বার্তা

5 মিনিটের মধ্যে একটি ফ্লাটার ক্র্যাশ কোর্স

ডিবাগ কনসোল দেখতে যতটা মজার, আপনি বোতামটি আরও অর্থপূর্ণ কিছু করতে চান। যদিও এটি পাওয়ার আগে, lib/main.dart এ কোডটি ঘনিষ্ঠভাবে দেখুন, এটি কীভাবে কাজ করে তা বোঝার জন্য।

lib/main.dart

// ...

void main() {
  runApp(MyApp());
}

// ...

ফাইলের একেবারে উপরে, আপনি main() ফাংশনটি পাবেন। বর্তমান আকারে, এটি শুধুমাত্র Flutter কে MyApp এ সংজ্ঞায়িত অ্যাপটি চালাতে বলে।

lib/main.dart

// ...

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => MyAppState(),
      child: MaterialApp(
        title: 'Namer App',
        theme: ThemeData(
          useMaterial3: true,
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
        ),
        home: MyHomePage(),
      ),
    );
  }
}

// ...

MyApp ক্লাস StatelessWidget প্রসারিত করে। উইজেট হল সেই উপাদান যা থেকে আপনি প্রতিটি ফ্লাটার অ্যাপ তৈরি করেন। আপনি দেখতে পাচ্ছেন, এমনকি অ্যাপটি নিজেই একটি উইজেট।

MyApp এর কোড পুরো অ্যাপ সেট আপ করে। এটি অ্যাপ-ওয়াইড স্টেট তৈরি করে (পরে এটি সম্পর্কে আরও), অ্যাপটির নাম দেয়, ভিজ্যুয়াল থিম সংজ্ঞায়িত করে এবং "হোম" উইজেট সেট করে - আপনার অ্যাপের শুরুর পয়েন্ট।

lib/main.dart

// ...

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();
}

// ...

এরপরে, MyAppState ক্লাস অ্যাপের...ভাল...স্টেটকে সংজ্ঞায়িত করে। ফ্লটারে এটি আপনার প্রথম যাত্রা, তাই এই কোডল্যাব এটিকে সহজ এবং ফোকাস করে রাখবে। ফ্লটারে অ্যাপের অবস্থা পরিচালনা করার অনেক শক্তিশালী উপায় রয়েছে। ব্যাখ্যা করার সবচেয়ে সহজ একটি হল ChangeNotifier , এই অ্যাপ দ্বারা নেওয়া পদ্ধতি।

  • MyAppState অ্যাপটি কাজ করার জন্য প্রয়োজনীয় ডেটা সংজ্ঞায়িত করে। এই মুহূর্তে, এটি শুধুমাত্র বর্তমান র্যান্ডম শব্দ জোড়ার সাথে একটি একক পরিবর্তনশীল ধারণ করে। আপনি পরে এটি যোগ করবেন।
  • স্টেট ক্লাস ChangeNotifier প্রসারিত করে, যার মানে এটি অন্যদের নিজের পরিবর্তন সম্পর্কে অবহিত করতে পারে। উদাহরণস্বরূপ, বর্তমান শব্দ জোড়া পরিবর্তন হলে, অ্যাপের কিছু উইজেট জানতে হবে।
  • একটি ChangeNotifierProvider ব্যবহার করে রাজ্যটি তৈরি করা হয়েছে এবং পুরো অ্যাপে সরবরাহ করা হয়েছে (উপরে MyApp এ কোড দেখুন)। এটি অ্যাপ্লিকেশানের যেকোনো উইজেটকে রাজ্যের দখল পেতে দেয়। d9b6ecac5494a6ff.png

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {           // ← 1
    var appState = context.watch<MyAppState>();  // ← 2

    return Scaffold(                             // ← 3
      body: Column(                              // ← 4
        children: [
          Text('A random AWESOME idea:'),        // ← 5
          Text(appState.current.asLowerCase),    // ← 6
          ElevatedButton(
            onPressed: () {
              print('button pressed!');
            },
            child: Text('Next'),
          ),
        ],                                       // ← 7
      ),
    );
  }
}

// ...

সবশেষে, MyHomePage আছে, যে উইজেটটি আপনি ইতিমধ্যেই পরিবর্তন করেছেন। নীচের প্রতিটি সংখ্যাযুক্ত লাইন উপরের কোডে একটি লাইন-সংখ্যার মন্তব্যে মানচিত্র করে:

  1. প্রতিটি উইজেট একটি build() পদ্ধতি সংজ্ঞায়িত করে যা প্রতিবার উইজেটের পরিস্থিতি পরিবর্তন হলে স্বয়ংক্রিয়ভাবে কল করা হয় যাতে উইজেটটি সর্বদা আপ টু ডেট থাকে।
  2. MyHomePage watch পদ্ধতি ব্যবহার করে অ্যাপের বর্তমান অবস্থার পরিবর্তনগুলি ট্র্যাক করে।
  3. প্রতিটি build পদ্ধতিতে একটি উইজেট বা (আরও সাধারণত) উইজেটের একটি নেস্টেড ট্রি ফেরত দিতে হবে। এই ক্ষেত্রে, শীর্ষ-স্তরের উইজেটটি হল Scaffold । আপনি এই কোডল্যাবে Scaffold সাথে কাজ করতে যাচ্ছেন না, তবে এটি একটি সহায়ক উইজেট এবং বেশিরভাগ বাস্তব-বিশ্বের ফ্লাটার অ্যাপে পাওয়া যায়।
  4. Column হল ফ্লটারের সবচেয়ে মৌলিক লেআউট উইজেটগুলির মধ্যে একটি। এটি যেকোন সংখ্যক শিশু নেয় এবং তাদের উপরে থেকে নীচে একটি কলামে রাখে। ডিফল্টরূপে, কলামটি দৃশ্যত তার সন্তানদের শীর্ষে রাখে। আপনি শীঘ্রই এটি পরিবর্তন করবেন যাতে কলাম কেন্দ্রীভূত হয়।
  5. আপনি প্রথম ধাপে এই Text উইজেট পরিবর্তন করেছেন।
  6. এই দ্বিতীয় Text উইজেটটি appState নেয় এবং সেই ক্লাসের একমাত্র সদস্য, current (যা একটি WordPair ) অ্যাক্সেস করে। WordPair বেশ কিছু সহায়ক গেটার প্রদান করে, যেমন asPascalCase বা asSnakeCase । এখানে, আমরা asLowerCase ব্যবহার করি তবে আপনি যদি বিকল্পগুলির মধ্যে একটি পছন্দ করেন তবে আপনি এখন এটি পরিবর্তন করতে পারেন।
  7. লক্ষ্য করুন কিভাবে ফ্লাটার কোড ট্রেইলিং কমা ব্যবহার করে। এই নির্দিষ্ট কমা এখানে থাকার প্রয়োজন নেই, কারণ children এই বিশেষ Column প্যারামিটার তালিকার শেষ (এবং শুধুমাত্র ) সদস্য। তবুও এটি সাধারণত ট্রেলিং কমা ব্যবহার করা একটি ভাল ধারণা: তারা আরও সদস্য যোগ করাকে তুচ্ছ করে তোলে এবং তারা সেখানে একটি নতুন লাইন স্থাপন করার জন্য ডার্টের অটো-ফরম্যাটারের জন্য একটি ইঙ্গিত হিসাবে কাজ করে। আরও তথ্যের জন্য, কোড বিন্যাস দেখুন।

এরপরে, আপনি বোতামটি রাজ্যের সাথে সংযুক্ত করবেন।

তোমার প্রথম আচরণ

MyAppState এ স্ক্রোল করুন এবং একটি getNext পদ্ধতি যোগ করুন।

lib/main.dart

// ...

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();

  // ↓ Add this.
  void getNext() {
    current = WordPair.random();
    notifyListeners();
  }
}

// ...

নতুন getNext() পদ্ধতি একটি নতুন র্যান্ডম WordPair দিয়ে current পুনরায় বরাদ্দ করে। এটি notifyListeners() ( ChangeNotifier) বলেও ডাকে যা নিশ্চিত করে যে MyAppState দেখছেন এমন যে কেউ অবহিত হয়েছেন।

যা অবশিষ্ট থাকে তা হল বোতামের কলব্যাক থেকে getNext পদ্ধতিতে কল করা।

lib/main.dart

// ...

    ElevatedButton(
      onPressed: () {
        appState.getNext();  // ← This instead of print().
      },
      child: Text('Next'),
    ),

// ...

সংরক্ষণ করুন এবং এখন অ্যাপ্লিকেশন চেষ্টা করুন. প্রতিবার আপনি পরবর্তী বোতাম টিপলে এটি একটি নতুন র্যান্ডম শব্দ জোড়া তৈরি করবে।

পরবর্তী বিভাগে, আপনি ইউজার ইন্টারফেসটিকে আরও সুন্দর করে তুলবেন।

5. অ্যাপটিকে আরও সুন্দর করুন

এই মুহূর্তে অ্যাপটি দেখতে কেমন।

3dd8a9d8653bdc56.png

মহান না. অ্যাপের কেন্দ্রবিন্দু—এলোমেলোভাবে তৈরি হওয়া জোড়া শব্দগুলি—আরো দৃশ্যমান হওয়া উচিত। সর্বোপরি, আমাদের ব্যবহারকারীরা এই অ্যাপটি ব্যবহার করার মূল কারণ! এছাড়াও, অ্যাপের বিষয়বস্তুগুলি অদ্ভুতভাবে অফ-সেন্টার, এবং পুরো অ্যাপটি বিরক্তিকর কালো এবং সাদা।

এই বিভাগটি অ্যাপের ডিজাইনের উপর কাজ করার মাধ্যমে এই সমস্যাগুলির সমাধান করে। এই বিভাগের জন্য শেষ লক্ষ্য নিম্নলিখিত মত কিছু:

2bbee054d81a3127.png

একটি উইজেট বের করুন

বর্তমান শব্দ জোড়া দেখানোর জন্য দায়ী লাইনটি এখন এইরকম দেখাচ্ছে: Text(appState.current.asLowerCase) । এটিকে আরও জটিল কিছুতে পরিবর্তন করতে, এই লাইনটিকে একটি পৃথক উইজেটে বের করা একটি ভাল ধারণা। আপনার UI এর পৃথক লজিক্যাল অংশগুলির জন্য পৃথক উইজেট থাকা হল ফ্লটারে জটিলতা পরিচালনা করার একটি গুরুত্বপূর্ণ উপায়।

ফ্লাটার উইজেটগুলি বের করার জন্য একটি রিফ্যাক্টরিং হেল্পার প্রদান করে কিন্তু আপনি এটি ব্যবহার করার আগে নিশ্চিত করুন যে লাইনটি এক্সট্র্যাক্ট করা হচ্ছে শুধুমাত্র সেটির প্রয়োজনীয়তা অ্যাক্সেস করে। এই মুহূর্তে, লাইনটি appState অ্যাক্সেস করে, কিন্তু প্রকৃতপক্ষে শুধুমাত্র বর্তমান শব্দ জোড়াটি কী তা জানতে হবে।

সেই কারণে, MyHomePage উইজেটটি নিম্নরূপ পুনরায় লিখুন:

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();  
    var pair = appState.current;                 // ← Add this.

    return Scaffold(
      body: Column(
        children: [
          Text('A random AWESOME idea:'),
          Text(pair.asLowerCase),                // ← Change to this.
          ElevatedButton(
            onPressed: () {
              appState.getNext();
            },
            child: Text('Next'),
          ),
        ],
      ),
    );
  }
}

// ...

চমৎকার Text উইজেট আর পুরো appState বোঝায় না।

এখন, রিফ্যাক্টর মেনুতে কল করুন। ভিএস কোডে, আপনি এটি দুটি উপায়ের একটিতে করেন:

  1. আপনি যে কোডটি রিফ্যাক্টর করতে চান তার ডানদিকে ক্লিক করুন (এই ক্ষেত্রে Text ) এবং ড্রপ-ডাউন মেনু থেকে রিফ্যাক্টর... নির্বাচন করুন,

বা

  1. আপনি যে পিস কোড রিফ্যাক্টর করতে চান তাতে আপনার কার্সার নিয়ে যান ( Text , এই ক্ষেত্রে), এবং Ctrl+. (উইন/লিনাক্স) বা Cmd+. (ম্যাক)।

রিফ্যাক্টর মেনুতে, এক্সট্রাক্ট উইজেট নির্বাচন করুন। একটি নাম বরাদ্দ করুন, যেমন BigCard , এবং Enter ক্লিক করুন।

এটি বর্তমান ফাইলের শেষে স্বয়ংক্রিয়ভাবে একটি নতুন ক্লাস, BigCard তৈরি করে। ক্লাস নিম্নলিখিত মত কিছু দেখায়:

lib/main.dart

// ...

class BigCard extends StatelessWidget {
  const BigCard({
    super.key,
    required this.pair,
  });

  final WordPair pair;

  @override
  Widget build(BuildContext context) {
    return Text(pair.asLowerCase);
  }
}

// ...

এই রিফ্যাক্টরিংয়ের মাধ্যমেও অ্যাপটি কীভাবে কাজ করে তা লক্ষ্য করুন।

একটি কার্ড যোগ করুন

এখন এই নতুন উইজেটটিকে UI এর সাহসী অংশে পরিণত করার সময় যা আমরা এই বিভাগের শুরুতে কল্পনা করেছি।

BigCard ক্লাস এবং এর মধ্যে build() পদ্ধতি খুঁজুন। আগের মত, Text উইজেটের রিফ্যাক্টর মেনুতে কল করুন। যাইহোক, এবার আপনি উইজেটটি বের করতে যাচ্ছেন না।

পরিবর্তে, প্যাডিং দিয়ে মোড়ানো নির্বাচন করুন। এটি Text উইজেটের চারপাশে Padding নামে একটি নতুন প্যারেন্ট উইজেট তৈরি করে। সংরক্ষণ করার পরে, আপনি দেখতে পাবেন যে এলোমেলো শব্দটিতে ইতিমধ্যে আরও শ্বাস নেওয়ার জায়গা রয়েছে।

8.0 এর ডিফল্ট মান থেকে প্যাডিং বাড়ান। উদাহরণস্বরূপ, রুমিয়ার প্যাডিংয়ের জন্য 20 এর মতো কিছু ব্যবহার করুন।

এর পরে, এক স্তর উপরে যান। আপনার কার্সারটি Padding উইজেটে রাখুন, রিফ্যাক্টর মেনুটি টানুন এবং উইজেটের সাথে মোড়ানো নির্বাচন করুন...।

এটি আপনাকে প্যারেন্ট উইজেট নির্দিষ্ট করতে দেয়। "কার্ড" টাইপ করুন এবং এন্টার টিপুন।

এটি একটি Card উইজেট সহ Padding উইজেট, এবং সেইজন্য Text আবৃত করে।

6031adbc0a11e16b.png

থিম এবং শৈলী

কার্ডটিকে আরও আলাদা করতে, এটিকে আরও সমৃদ্ধ রঙ দিয়ে আঁকুন। এবং যেহেতু একটি সামঞ্জস্যপূর্ণ রঙের স্কিম রাখা সর্বদা একটি ভাল ধারণা, তাই রঙ চয়ন করতে অ্যাপের Theme ব্যবহার করুন।

BigCard এর build() পদ্ধতিতে নিম্নলিখিত পরিবর্তনগুলি করুন।

lib/main.dart

// ...

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);       // ← Add this.

    return Card(
      color: theme.colorScheme.primary,    // ← And also this.
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Text(pair.asLowerCase),
      ),
    );
  }

// ...

এই দুটি নতুন লাইন অনেক কাজ করে:

  • প্রথমত, কোডটি Theme.of(context) দিয়ে অ্যাপের বর্তমান থিমকে অনুরোধ করে।
  • তারপর, কোডটি কার্ডের রঙকে থিমের colorScheme সম্পত্তির মতোই সংজ্ঞায়িত করে। রঙের স্কিমটিতে অনেকগুলি রঙ রয়েছে এবং primary অ্যাপটির সবচেয়ে বিশিষ্ট, সংজ্ঞায়িত রঙ।

কার্ডটি এখন অ্যাপের প্রাথমিক রঙ দিয়ে আঁকা হয়েছে:

a136f7682c204ea1.png

আপনি MyApp পর্যন্ত স্ক্রোল করে এবং সেখানে ColorScheme জন্য বীজের রঙ পরিবর্তন করে এই রঙটি এবং পুরো অ্যাপের রঙের স্কিম পরিবর্তন করতে পারেন।

রঙটি কীভাবে মসৃণভাবে অ্যানিমেট করে তা লক্ষ্য করুন। একে বলা হয় অন্তর্নিহিত অ্যানিমেশন । অনেক ফ্লাটার উইজেট মানগুলির মধ্যে মসৃণভাবে ইন্টারপোলেট করবে যাতে UI শুধুমাত্র রাজ্যগুলির মধ্যে "জাম্প" না করে।

কার্ডের নিচের এলিভেটেড বোতামটিও রঙ পরিবর্তন করে। এটি হার্ড-কোডিং মানগুলির বিপরীতে একটি অ্যাপ-ওয়াইড Theme ব্যবহার করার ক্ষমতা।

টেক্সট থিম

কার্ডটিতে এখনও একটি সমস্যা রয়েছে: পাঠ্যটি খুব ছোট এবং এর রঙ পড়া কঠিন। এটি ঠিক করতে, BigCard এর build() পদ্ধতিতে নিম্নলিখিত পরিবর্তনগুলি করুন৷

lib/main.dart

// ...

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    // ↓ Add this.
    final style = theme.textTheme.displayMedium!.copyWith(
      color: theme.colorScheme.onPrimary,
    );

    return Card(
      color: theme.colorScheme.primary,
      child: Padding(
        padding: const EdgeInsets.all(20),
        // ↓ Change this line.
        child: Text(pair.asLowerCase, style: style),
      ),
    );
  }

// ...

এই পরিবর্তনের পিছনে কি আছে:

  • theme.textTheme, আপনি অ্যাপের ফন্ট থিম অ্যাক্সেস করেন। এই শ্রেণীতে bodyMedium (মাঝারি আকারের স্ট্যান্ডার্ড টেক্সটের জন্য), caption (ছবির ক্যাপশনের জন্য), বা headlineLarge (বড় শিরোনামের জন্য) এর মতো সদস্য অন্তর্ভুক্ত রয়েছে।
  • displayMedium প্রপার্টি হল একটি বড় স্টাইল যা ডিসপ্লে টেক্সটের জন্য। প্রদর্শন শব্দটি এখানে টাইপোগ্রাফিক অর্থে ব্যবহৃত হয়েছে, যেমন প্রদর্শন টাইপফেসেdisplayMedium ডকুমেন্টেশন বলে যে "ডিসপ্লে শৈলী সংক্ষিপ্ত, গুরুত্বপূর্ণ পাঠ্যের জন্য সংরক্ষিত" - ঠিক আমাদের ব্যবহারের ক্ষেত্রে।
  • থিমের displayMedium প্রপার্টি তাত্ত্বিকভাবে null হতে পারে। ডার্ট, যে প্রোগ্রামিং ল্যাঙ্গুয়েজটিতে আপনি এই অ্যাপটি লিখছেন, সেটি নাল-নিরাপদ, তাই এটি আপনাকে সম্ভাব্য null বস্তুর পদ্ধতি কল করতে দেবে না। এই ক্ষেত্রে, যদিও, আপনি ব্যবহার করতে পারেন ! অপারেটর ("ব্যাং অপারেটর") ডার্টকে আশ্বস্ত করতে আপনি জানেন যে আপনি কী করছেন। ( displayMedium অবশ্যই এই ক্ষেত্রে শূন্য নয় । কারণ আমরা জানি যে এটি এই কোডল্যাবের সুযোগের বাইরে।)
  • displayMedium copyWith() কল করা আপনার সংজ্ঞায়িত পরিবর্তনগুলির সাথে পাঠ্য শৈলীর একটি অনুলিপি প্রদান করে। এই ক্ষেত্রে, আপনি শুধুমাত্র পাঠ্যের রঙ পরিবর্তন করছেন।
  • নতুন রঙ পেতে, আপনি আবার অ্যাপটির থিম অ্যাক্সেস করুন৷ রঙের স্কিমের onPrimary প্রপার্টি এমন একটি রঙকে সংজ্ঞায়িত করে যা অ্যাপের প্রাথমিক রঙে ব্যবহারের জন্য উপযুক্ত।

অ্যাপটি এখন নিচের মত দেখতে হবে:

2405e9342d28c193.png

আপনি যদি এটি পছন্দ করেন তবে কার্ডটি আরও পরিবর্তন করুন। এখানে কিছু ধারণা আছে:

  • copyWith() আপনাকে কেবল রঙের চেয়ে পাঠ্য শৈলী সম্পর্কে আরও অনেক কিছু পরিবর্তন করতে দেয়। আপনি পরিবর্তন করতে পারেন এমন বৈশিষ্ট্যগুলির সম্পূর্ণ তালিকা পেতে, আপনার কার্সারটি copyWith() এর বন্ধনীর ভিতরে যে কোনও জায়গায় রাখুন এবং Ctrl+Shift+Space (Win/Linux) বা Cmd+Shift+Space (Mac) টিপুন।
  • একইভাবে, আপনি Card উইজেট সম্পর্কে আরও পরিবর্তন করতে পারেন। উদাহরণস্বরূপ, আপনি elevation প্যারামিটারের মান বাড়িয়ে কার্ডের ছায়া বড় করতে পারেন।
  • রং নিয়ে পরীক্ষা করার চেষ্টা করুন। theme.colorScheme.primary ছাড়াও, .secondary , .surface , এবং অন্যান্য অগণিত রয়েছে৷ এই সমস্ত রঙের onPrimary সমতুল্য রয়েছে।

অ্যাক্সেসযোগ্যতা উন্নত করুন

ফ্লটার ডিফল্টরূপে অ্যাপগুলিকে অ্যাক্সেসযোগ্য করে তোলে। উদাহরণস্বরূপ, প্রতিটি ফ্লাটার অ্যাপ সঠিকভাবে টকব্যাক এবং ভয়েসওভারের মতো স্ক্রিন রিডারদের জন্য অ্যাপের সমস্ত পাঠ্য এবং ইন্টারেক্টিভ উপাদানগুলিকে সঠিকভাবে উপস্থাপন করে।

d1fad7944fb890ea.png

কখনও কখনও, যদিও, কিছু কাজ প্রয়োজন. এই অ্যাপের ক্ষেত্রে, স্ক্রিন রিডারের কিছু জেনারেট করা শব্দ জোড়া উচ্চারণ করতে সমস্যা হতে পারে। যদিও মানুষের Cheaphead এ দুটি শব্দ শনাক্ত করতে সমস্যা হয় না, একজন স্ক্রিন রিডার শব্দের মাঝখানে ph উচ্চারণ করতে পারে f হিসেবে।

একটি সহজ সমাধান হল pair.asLowerCase "${pair.first} ${pair.second}" দিয়ে প্রতিস্থাপন করা। পরেরটি pair থাকা দুটি শব্দ থেকে একটি স্ট্রিং (যেমন "cheap head" ) তৈরি করতে স্ট্রিং ইন্টারপোলেশন ব্যবহার করে। একটি যৌগিক শব্দের পরিবর্তে দুটি পৃথক শব্দ ব্যবহার করা নিশ্চিত করে যে স্ক্রিন পাঠকরা তাদের যথাযথভাবে সনাক্ত করে এবং দৃষ্টি প্রতিবন্ধী ব্যবহারকারীদের একটি ভাল অভিজ্ঞতা প্রদান করে।

যাইহোক, আপনি pair.asLowerCase এর চাক্ষুষ সরলতা রাখতে চাইতে পারেন। স্ক্রীন রিডারদের জন্য আরও উপযুক্ত শব্দার্থক বিষয়বস্তু সহ পাঠ্য উইজেটের ভিজ্যুয়াল বিষয়বস্তুকে ওভাররাইড করতে Text এর semanticsLabel বৈশিষ্ট্য ব্যবহার করুন:

lib/main.dart

// ...

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final style = theme.textTheme.displayMedium!.copyWith(
      color: theme.colorScheme.onPrimary,
    );

    return Card(
      color: theme.colorScheme.primary,
      child: Padding(
        padding: const EdgeInsets.all(20),

        // ↓ Make the following change.
        child: Text(
          pair.asLowerCase,
          style: style,
          semanticsLabel: "${pair.first} ${pair.second}",
        ),
      ),
    );
  }

// ...

এখন, স্ক্রিন রিডাররা প্রতিটি জেনারেট করা শব্দ জোড়া সঠিকভাবে উচ্চারণ করে, তবুও UI একই থাকে। আপনার ডিভাইসে একটি স্ক্রিন রিডার ব্যবহার করে এটি ব্যবহার করে দেখুন।

UI কেন্দ্রে রাখুন

এখন যেহেতু র্যান্ডম শব্দ জোড়া যথেষ্ট ভিজ্যুয়াল ফ্লেয়ার সহ উপস্থাপিত হয়েছে, এটি অ্যাপের উইন্ডো/স্ক্রীনের কেন্দ্রে রাখার সময়।

প্রথমে মনে রাখবেন যে BigCard একটি Column অংশ। ডিফল্টরূপে, কলামগুলি তাদের বাচ্চাদের শীর্ষে নিয়ে যায়, তবে আমরা সহজেই এটিকে ওভাররাইড করতে পারি। MyHomePage এর build() পদ্ধতিতে যান এবং নিম্নলিখিত পরিবর্তন করুন:

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,  // ← Add this.
        children: [
          Text('A random AWESOME idea:'),
          BigCard(pair: pair),
          ElevatedButton(
            onPressed: () {
              appState.getNext();
            },
            child: Text('Next'),
          ),
        ],
      ),
    );
  }
}

// ...

এটি শিশুদেরকে Column মূল (উল্লম্ব) অক্ষ বরাবর কেন্দ্র করে।

b555d4c7f5000edf.png

বাচ্চারা ইতিমধ্যেই কলামের ক্রস অক্ষ বরাবর কেন্দ্রীভূত (অন্য কথায়, তারা ইতিমধ্যেই অনুভূমিকভাবে কেন্দ্রীভূত)। কিন্তু Column নিজেই Scaffold ভিতরে কেন্দ্রীভূত নয়। উইজেট ইন্সপেক্টর ব্যবহার করে আমরা এটি যাচাই করতে পারি।

উইজেট ইন্সপেক্টর নিজেই এই কোডল্যাবের সুযোগের বাইরে, কিন্তু আপনি দেখতে পাচ্ছেন যে যখন Column হাইলাইট করা হয়, তখন এটি অ্যাপের পুরো প্রস্থটি নেয় না। এটি কেবলমাত্র তার বাচ্চাদের যতটা অনুভূমিক স্থান প্রয়োজন ততটুকুই নেয়।

আপনি শুধুমাত্র কলাম নিজেই কেন্দ্র করতে পারেন. Column আপনার কার্সার রাখুন, রিফ্যাক্টর মেনুতে কল করুন ( Ctrl+. অথবা Cmd+. সহ), এবং কেন্দ্রের সাথে মোড়ানো নির্বাচন করুন।

অ্যাপটি এখন নিচের মত দেখতে হবে:

455688d93c30d154.png

আপনি যদি চান, আপনি এটি আরও কিছু পরিবর্তন করতে পারেন।

  • আপনি BigCard উপরের Text উইজেটটি সরাতে পারেন। এটি যুক্তি দেওয়া যেতে পারে যে বর্ণনামূলক পাঠ্য ("একটি এলোমেলো দুর্দান্ত ধারণা:") এর আর প্রয়োজন নেই কারণ UI এমনকি এটি ছাড়াই অর্থবহ৷ এবং এটা যে ভাবে পরিষ্কার.
  • আপনি BigCard এবং ElevatedButton এর মধ্যে একটি SizedBox(height: 10) উইজেট যোগ করতে পারেন। এইভাবে, দুটি উইজেটের মধ্যে একটু বেশি বিচ্ছিন্নতা রয়েছে। SizedBox উইজেটটি কেবল স্থান নেয় এবং নিজে থেকে কিছু রেন্ডার করে না। এটি সাধারণত চাক্ষুষ "ফাঁক" তৈরি করতে ব্যবহৃত হয়।

ঐচ্ছিক পরিবর্তনের সাথে, MyHomePage এই কোডটি রয়েছে:

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            BigCard(pair: pair),
            SizedBox(height: 10),
            ElevatedButton(
              onPressed: () {
                appState.getNext();
              },
              child: Text('Next'),
            ),
          ],
        ),
      ),
    );
  }
}

// ...

এবং অ্যাপ্লিকেশন নিম্নলিখিত মত দেখায়:

3d53d2b071e2f372.png

পরবর্তী বিভাগে, আপনি পছন্দসই (বা 'লাইক') তৈরি করা শব্দের ক্ষমতা যোগ করবেন।

6. কার্যকারিতা যোগ করুন

অ্যাপটি কাজ করে এবং মাঝে মাঝে এমনকি আকর্ষণীয় শব্দ জোড়া প্রদান করে। কিন্তু যখনই ব্যবহারকারী Next এ ক্লিক করেন, প্রতিটি শব্দ জোড়া চিরতরে অদৃশ্য হয়ে যায়। সেরা পরামর্শগুলিকে "মনে রাখার" একটি উপায় থাকলে ভাল হবে: যেমন একটি 'লাইক' বোতাম।

e6b01a8c90df8ffa.png

ব্যবসায়িক যুক্তি যোগ করুন

MyAppState এ স্ক্রোল করুন এবং নিম্নলিখিত কোড যোগ করুন:

lib/main.dart

// ...

class MyAppState extends ChangeNotifier {
  var current = WordPair.random();

  void getNext() {
    current = WordPair.random();
    notifyListeners();
  }

  // ↓ Add the code below.
  var favorites = <WordPair>[];

  void toggleFavorite() {
    if (favorites.contains(current)) {
      favorites.remove(current);
    } else {
      favorites.add(current);
    }
    notifyListeners();
  }
}

// ...

পরিবর্তনগুলি পরীক্ষা করুন:

  • আপনি MyAppStatefavorites নামে একটি নতুন সম্পত্তি যোগ করেছেন। এই সম্পত্তিটি একটি খালি তালিকা দিয়ে শুরু করা হয়েছে: []
  • আপনি আরও উল্লেখ করেছেন যে তালিকায় শুধুমাত্র শব্দ জোড়া থাকতে পারে: <WordPair>[] , জেনেরিক ব্যবহার করে। এটি আপনার অ্যাপটিকে আরও মজবুত করে তুলতে সাহায্য করে— আপনি যদি WordPair ছাড়া অন্য কিছু যোগ করার চেষ্টা করেন তাহলে ডার্ট আপনার অ্যাপটি চালাতেও অস্বীকার করে। পরিবর্তে, আপনি favorites তালিকাটি ব্যবহার করতে পারেন জেনে রাখুন যে সেখানে কোনও অবাঞ্ছিত বস্তু (যেমন null ) লুকিয়ে থাকতে পারে না।
  • আপনি একটি নতুন পদ্ধতি যোগ করেছেন, toggleFavorite() , যা হয় বর্তমান শব্দ জোড়াটিকে পছন্দের তালিকা থেকে সরিয়ে দেয় (যদি এটি ইতিমধ্যেই থাকে), অথবা এটি যোগ করে (যদি এটি এখনও না থাকে)। উভয় ক্ষেত্রেই, কোডটি notifyListeners(); পরে

বোতাম যোগ করুন

"ব্যবসায়িক যুক্তি" বাদ দিয়ে, এটি আবার ব্যবহারকারী ইন্টারফেসে কাজ করার সময়। 'পরবর্তী' বোতামের বামে 'লাইক' বোতামটি স্থাপন করার জন্য একটি Row প্রয়োজন। Row উইজেট হল Column অনুভূমিক সমতুল্য, যা আপনি আগে দেখেছেন।

প্রথমত, একটি Row বিদ্যমান বোতামটি মুড়ে দিন। MyHomePage এর build() পদ্ধতিতে যান, ElevatedButton এ আপনার কার্সার রাখুন, Ctrl+. দিয়ে রিফ্যাক্টর মেনুতে কল করুন। অথবা Cmd+. , এবং সারি দিয়ে মোড়ানো নির্বাচন করুন।

আপনি যখন সংরক্ষণ করবেন, আপনি লক্ষ্য করবেন যে Row Column অনুরূপ কাজ করে — ডিফল্টরূপে, এটি তার বাচ্চাদের বাম দিকে ঢেলে দেয়। ( Column তার বাচ্চাদের শীর্ষে নিয়ে গেছে।) এটি ঠিক করতে, আপনি আগের মতো একই পদ্ধতি ব্যবহার করতে পারেন, তবে mainAxisAlignment এর সাথে। যাইহোক, শিক্ষাগত (শেখার) উদ্দেশ্যে, mainAxisSize ব্যবহার করুন। এটি Row সমস্ত উপলব্ধ অনুভূমিক স্থান না নিতে বলে।

নিম্নলিখিত পরিবর্তন করুন:

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            BigCard(pair: pair),
            SizedBox(height: 10),
            Row(
              mainAxisSize: MainAxisSize.min,   // ← Add this.
              children: [
                ElevatedButton(
                  onPressed: () {
                    appState.getNext();
                  },
                  child: Text('Next'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

// ...

UI আগের জায়গায় ফিরে এসেছে।

3d53d2b071e2f372.png

এরপরে, লাইক বোতামটি যোগ করুন এবং এটিকে toggleFavorite() এর সাথে সংযুক্ত করুন। একটি চ্যালেঞ্জের জন্য, প্রথমে নীচের কোড ব্লকটি না দেখে নিজেই এটি করার চেষ্টা করুন।

e6b01a8c90df8ffa.png

এটি ঠিক আছে যদি আপনি এটি নীচের মতো ঠিক একইভাবে না করেন। আসলে, হার্ট আইকন সম্পর্কে চিন্তা করবেন না যদি না আপনি সত্যিই একটি বড় চ্যালেঞ্জ চান।

ব্যর্থ হওয়াও পুরোপুরি ঠিক আছে—সবকিছুর পরে, ফ্লটারের সাথে এটি আপনার প্রথম ঘন্টা।

252f7c4a212c94d2.png

MyHomePage এ দ্বিতীয় বোতাম যোগ করার একটি উপায় এখানে। এইবার, একটি আইকন সহ একটি বোতাম তৈরি করতে ElevatedButton.icon() কনস্ট্রাক্টর ব্যবহার করুন। এবং build পদ্ধতির শীর্ষে, বর্তমান শব্দ জোড়া ইতিমধ্যেই পছন্দের মধ্যে আছে কিনা তার উপর নির্ভর করে উপযুক্ত আইকনটি বেছে নিন। এছাড়াও, দুটি বোতামকে কিছুটা আলাদা রাখতে আবার SizedBox ব্যবহার নোট করুন।

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    // ↓ Add this.
    IconData icon;
    if (appState.favorites.contains(pair)) {
      icon = Icons.favorite;
    } else {
      icon = Icons.favorite_border;
    }

    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            BigCard(pair: pair),
            SizedBox(height: 10),
            Row(
              mainAxisSize: MainAxisSize.min,
              children: [

                // ↓ And this.
                ElevatedButton.icon(
                  onPressed: () {
                    appState.toggleFavorite();
                  },
                  icon: Icon(icon),
                  label: Text('Like'),
                ),
                SizedBox(width: 10),

                ElevatedButton(
                  onPressed: () {
                    appState.getNext();
                  },
                  child: Text('Next'),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

// ...

অ্যাপ্লিকেশন নিম্নলিখিত হিসাবে দেখতে হবে:

দুর্ভাগ্যবশত, ব্যবহারকারী পছন্দগুলি দেখতে পাচ্ছেন না। আমাদের অ্যাপে একটি সম্পূর্ণ আলাদা স্ক্রিন যোগ করার সময় এসেছে। পরবর্তী বিভাগে দেখা হবে!

7. নেভিগেশন রেল যোগ করুন

বেশিরভাগ অ্যাপই একক স্ক্রিনে সবকিছু ফিট করতে পারে না। এই বিশেষ অ্যাপটি সম্ভবত পারে, কিন্তু শিক্ষামূলক উদ্দেশ্যে, আপনি ব্যবহারকারীর পছন্দের জন্য একটি পৃথক স্ক্রিন তৈরি করতে যাচ্ছেন। দুটি পর্দার মধ্যে স্যুইচ করতে, আপনি আপনার প্রথম StatefulWidget বাস্তবায়ন করতে যাচ্ছেন।

f62c54f5401a187.png

যত তাড়াতাড়ি সম্ভব এই পদক্ষেপের মাংস পেতে, MyHomePage 2টি পৃথক উইজেটে বিভক্ত করুন।

সমস্ত MyHomePage নির্বাচন করুন, এটি মুছুন এবং নিম্নলিখিত কোড দিয়ে প্রতিস্থাপন করুন:

lib/main.dart

// ...

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          SafeArea(
            child: NavigationRail(
              extended: false,
              destinations: [
                NavigationRailDestination(
                  icon: Icon(Icons.home),
                  label: Text('Home'),
                ),
                NavigationRailDestination(
                  icon: Icon(Icons.favorite),
                  label: Text('Favorites'),
                ),
              ],
              selectedIndex: 0,
              onDestinationSelected: (value) {
                print('selected: $value');
              },
            ),
          ),
          Expanded(
            child: Container(
              color: Theme.of(context).colorScheme.primaryContainer,
              child: GeneratorPage(),
            ),
          ),
        ],
      ),
    );
  }
}


class GeneratorPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();
    var pair = appState.current;

    IconData icon;
    if (appState.favorites.contains(pair)) {
      icon = Icons.favorite;
    } else {
      icon = Icons.favorite_border;
    }

    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          BigCard(pair: pair),
          SizedBox(height: 10),
          Row(
            mainAxisSize: MainAxisSize.min,
            children: [
              ElevatedButton.icon(
                onPressed: () {
                  appState.toggleFavorite();
                },
                icon: Icon(icon),
                label: Text('Like'),
              ),
              SizedBox(width: 10),
              ElevatedButton(
                onPressed: () {
                  appState.getNext();
                },
                child: Text('Next'),
              ),
            ],
          ),
        ],
      ),
    );
  }
}

// ...

সংরক্ষিত হলে, আপনি দেখতে পাবেন যে UI এর ভিজ্যুয়াল দিকটি প্রস্তুত - কিন্তু এটি কাজ করে না। নেভিগেশন রেলে ♥︎ (হৃদয়) ক্লিক করলে কিছুই হয় না।

388bc25fe198c54a.png

পরিবর্তনগুলি পরীক্ষা করুন।

  • প্রথমে লক্ষ্য করুন যে MyHomePage এর সম্পূর্ণ বিষয়বস্তু একটি নতুন উইজেট, GeneratorPage এ বের করা হয়েছে। পুরানো MyHomePage উইজেটের একমাত্র অংশ যা বের করা হয়নি তা হল Scaffold .
  • নতুন MyHomePage এ দুটি সন্তানের সাথে একটি Row রয়েছে৷ প্রথম উইজেটটি SafeArea , এবং দ্বিতীয়টি একটি Expanded উইজেট৷
  • SafeArea নিশ্চিত করে যে এর বাচ্চা একটি হার্ডওয়্যার খাঁজ বা একটি স্ট্যাটাস বার দ্বারা অস্পষ্ট না হয়। এই অ্যাপে, NavigationRail চারপাশে উইজেট মোড়ানো হয় যাতে নেভিগেশন বোতামগুলিকে মোবাইল স্ট্যাটাস বার দ্বারা অস্পষ্ট হওয়া থেকে আটকানো যায়, উদাহরণস্বরূপ।
  • আপনি নেভিগেশনরেলের extended: false লাইনটিকে true পরিবর্তন করতে পারেন। এটি আইকনগুলির পাশের লেবেলগুলি দেখায়৷ একটি ভবিষ্যত ধাপে, অ্যাপটিতে পর্যাপ্ত অনুভূমিক স্থান থাকলে আপনি কীভাবে এটি স্বয়ংক্রিয়ভাবে করবেন তা শিখবেন।
  • নেভিগেশন রেলের দুটি গন্তব্য রয়েছে ( হোম এবং ফেভারিট ), তাদের নিজ নিজ আইকন এবং লেবেল সহ। এটি বর্তমান selectedIndex সূচককেও সংজ্ঞায়িত করে। শূন্যের একটি নির্বাচিত সূচক প্রথম গন্তব্য নির্বাচন করে, একটির নির্বাচিত সূচক দ্বিতীয় গন্তব্য নির্বাচন করে, ইত্যাদি। আপাতত, এটাকে শূন্যে কোড করা কঠিন।
  • নেভিগেশন রেল এছাড়াও সংজ্ঞায়িত করে যে ব্যবহারকারী যখন onDestinationSelected মাধ্যমে একটি গন্তব্য নির্বাচন করে তখন কী ঘটবে। এই মুহুর্তে, অ্যাপটি শুধুমাত্র print() দিয়ে অনুরোধকৃত সূচকের মান আউটপুট করে।
  • Row দ্বিতীয় সন্তান হল Expanded উইজেট। প্রসারিত উইজেটগুলি সারি এবং কলামগুলিতে অত্যন্ত উপযোগী—এগুলি আপনাকে লেআউটগুলি প্রকাশ করতে দেয় যেখানে কিছু শিশু তাদের প্রয়োজন মতো স্থান নেয় ( SafeArea , এই ক্ষেত্রে) এবং অন্যান্য উইজেটগুলিকে যতটা সম্ভব অবশিষ্ট রুম নেওয়া উচিত ( Expanded , মধ্যে এই ক্ষেত্রে)। Expanded উইজেট সম্পর্কে চিন্তা করার একটি উপায় হল তারা "লোভী"। আপনি যদি এই উইজেটের ভূমিকার আরও ভাল অনুভূতি পেতে চান, তবে SafeArea উইজেটটিকে অন্য Expanded দিয়ে মোড়ানোর চেষ্টা করুন। ফলস্বরূপ লেআউটটি এইরকম কিছু দেখায়:

6bbda6c1835a1ae.png

  • দুটি Expanded উইজেট সমস্ত উপলব্ধ অনুভূমিক স্থানকে নিজেদের মধ্যে বিভক্ত করে, যদিও নেভিগেশন রেলের জন্য সত্যিই বাম দিকে সামান্য স্লাইস প্রয়োজন।
  • Expanded উইজেটের ভিতরে, একটি রঙিন Container আছে এবং কন্টেইনারের ভিতরে, GeneratorPage

স্টেটলেস বনাম স্টেটফুল উইজেট

এখন পর্যন্ত, MyAppState আপনার সমস্ত রাজ্যের প্রয়োজনীয়তা কভার করেছে। সেজন্য এখন পর্যন্ত আপনার লেখা সব উইজেটই স্টেট লেস । তারা তাদের নিজস্ব কোনো পরিবর্তনশীল অবস্থা ধারণ করে না। কোনো উইজেটই নিজেকে পরিবর্তন করতে পারে না — তাদের অবশ্যই MyAppState মাধ্যমে যেতে হবে।

এই পরিবর্তন সম্পর্কে.

নেভিগেশন রেলের selectedIndex মান ধরে রাখতে আপনার কিছু উপায় দরকার। এছাড়াও আপনি onDestinationSelected কলব্যাকের মধ্যে থেকে এই মানটি পরিবর্তন করতে সক্ষম হতে চান৷

আপনি MyAppState এর অন্য একটি সম্পত্তি হিসাবে selectedIndex যোগ করতে পারেন । এবং এটা কাজ করবে. কিন্তু আপনি কল্পনা করতে পারেন যে প্রতিটি উইজেট যদি এটিতে তার মানগুলি সঞ্চয় করে তবে অ্যাপের অবস্থা দ্রুত কারণের বাইরে বৃদ্ধি পাবে।

e52d9c0937cc0823.jpeg

কিছু রাজ্য শুধুমাত্র একটি একক উইজেটের সাথে প্রাসঙ্গিক, তাই এটি সেই উইজেটের সাথে থাকা উচিত।

StatefulWidget লিখুন, এক ধরনের উইজেট যাতে State থাকে। প্রথমে, MyHomePage একটি রাষ্ট্রীয় উইজেটে রূপান্তর করুন।

আপনার কার্সারকে MyHomePage এর প্রথম লাইনে রাখুন (যেটি class MyHomePage... দিয়ে শুরু হয়), এবং Ctrl+. ব্যবহার করে রিফ্যাক্টর মেনুতে কল করুন। অথবা Cmd+. . তারপরে, Convert to StatefulWidget নির্বাচন করুন।

IDE আপনার জন্য একটি নতুন ক্লাস তৈরি করে, _MyHomePageState । এই শ্রেণীটি State প্রসারিত করে এবং তাই এর নিজস্ব মানগুলি পরিচালনা করতে পারে। (এটি নিজেই পরিবর্তন করতে পারে।) এছাড়াও লক্ষ্য করুন যে পুরানো, স্টেটলেস উইজেট থেকে build পদ্ধতিটি _MyHomePageState এ চলে গেছে (উইজেটে থাকার পরিবর্তে)। এটি মৌখিকভাবে সরানো হয়েছিল - build পদ্ধতির ভিতরে কিছুই পরিবর্তিত হয়নি। এটি এখন নিছক অন্য কোথাও বাস করে।

সেট স্টেট

নতুন স্টেটফুল উইজেটকে শুধুমাত্র একটি পরিবর্তনশীল ট্র্যাক করতে হবে: selectedIndex_MyHomePageState এ নিম্নলিখিত 3টি পরিবর্তন করুন:

lib/main.dart

// ...

class _MyHomePageState extends State<MyHomePage> {

  var selectedIndex = 0;     // ← Add this property.

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          SafeArea(
            child: NavigationRail(
              extended: false,
              destinations: [
                NavigationRailDestination(
                  icon: Icon(Icons.home),
                  label: Text('Home'),
                ),
                NavigationRailDestination(
                  icon: Icon(Icons.favorite),
                  label: Text('Favorites'),
                ),
              ],
              selectedIndex: selectedIndex,    // ← Change to this.
              onDestinationSelected: (value) {

                // ↓ Replace print with this.
                setState(() {
                  selectedIndex = value;
                });

              },
            ),
          ),
          Expanded(
            child: Container(
              color: Theme.of(context).colorScheme.primaryContainer,
              child: GeneratorPage(),
            ),
          ),
        ],
      ),
    );
  }
}

// ...

পরিবর্তনগুলি পরীক্ষা করুন:

  1. আপনি একটি নতুন ভেরিয়েবল প্রবর্তন করুন, selectedIndex , এবং এটিকে 0 এ আরম্ভ করুন।
  2. আপনি এখন পর্যন্ত হার্ড-কোডেড 0 এর পরিবর্তে NavigationRail সংজ্ঞাতে এই নতুন পরিবর্তনশীলটি ব্যবহার করেন।
  3. যখন onDestinationSelected কলব্যাক কল করা হয়, শুধুমাত্র কনসোলে নতুন মান প্রিন্ট করার পরিবর্তে, আপনি এটি একটি setState() কলের মধ্যে selectedIndex বরাদ্দ করেন। এই কলটি পূর্বে ব্যবহৃত notifyListeners() পদ্ধতির অনুরূপ—এটি নিশ্চিত করে যে UI আপডেট হয়েছে।

ন্যাভিগেশন রেল এখন ব্যবহারকারীর মিথস্ক্রিয়ায় সাড়া দেয়। কিন্তু ডানদিকে প্রসারিত এলাকা একই থাকে। কারণ স্ক্রীন কি প্রদর্শন করে তা নির্ধারণ করতে কোডটি selectedIndex ব্যবহার করছে না।

নির্বাচিত সূচক ব্যবহার করুন

_MyHomePageState এর build মেথডের উপরে নিচের কোডটি রাখুন, return Scaffold ঠিক আগে:

lib/main.dart

// ...

Widget page;
switch (selectedIndex) {
  case 0:
    page = GeneratorPage();
    break;
  case 1:
    page = Placeholder();
    break;
  default:
    throw UnimplementedError('no widget for $selectedIndex');
}

// ...

কোডের এই টুকরা পরীক্ষা করুন:

  1. কোডটি Widget ধরনের একটি নতুন পরিবর্তনশীল, page ঘোষণা করে।
  2. তারপরে, একটি সুইচ স্টেটমেন্ট selectedIndex বর্তমান মান অনুসারে page একটি স্ক্রীন বরাদ্দ করে।
  3. যেহেতু এখনও কোন FavoritesPage নেই, তাই Placeholder ব্যবহার করুন; একটি সহজ উইজেট যা আপনি যেখানেই রাখুন সেখানে একটি ক্রস করা আয়তক্ষেত্র আঁকেন, UI এর সেই অংশটিকে অসমাপ্ত হিসাবে চিহ্নিত করে৷

5685cf886047f6ec.png

  1. ব্যর্থ-দ্রুত নীতি প্রয়োগ করে, সুইচ বিবৃতিটিও নিশ্চিত করে যে একটি ত্রুটি নিক্ষেপ করা হয়েছে যদি selectedIndex 0 বা 1 না হয়। এটি লাইনের নিচের বাগগুলি প্রতিরোধ করতে সহায়তা করে। আপনি যদি কখনও নেভিগেশন রেলে একটি নতুন গন্তব্য যোগ করেন এবং এই কোডটি আপডেট করতে ভুলে যান, তবে প্রোগ্রামটি বিকাশে ক্র্যাশ হয়ে যায় (আপনাকে অনুমান করতে দেওয়ার বিপরীতে কেন জিনিসগুলি কাজ করে না, বা আপনাকে একটি বগি কোড উত্পাদনে প্রকাশ করতে দেয়)।

এখন সেই page আপনি ডানদিকে যে উইজেটটি দেখাতে চান তা রয়েছে, আপনি সম্ভবত অনুমান করতে পারেন যে অন্যান্য পরিবর্তন কী প্রয়োজন।

এই একক অবশিষ্ট পরিবর্তনের পরে এখানে _MyHomePageState :

lib/main.dart

// ...

class _MyHomePageState extends State<MyHomePage> {
  var selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    Widget page;
    switch (selectedIndex) {
      case 0:
        page = GeneratorPage();
        break;
      case 1:
        page = Placeholder();
        break;
      default:
        throw UnimplementedError('no widget for $selectedIndex');
    }

    return Scaffold(
      body: Row(
        children: [
          SafeArea(
            child: NavigationRail(
              extended: false,
              destinations: [
                NavigationRailDestination(
                  icon: Icon(Icons.home),
                  label: Text('Home'),
                ),
                NavigationRailDestination(
                  icon: Icon(Icons.favorite),
                  label: Text('Favorites'),
                ),
              ],
              selectedIndex: selectedIndex,
              onDestinationSelected: (value) {
                setState(() {
                  selectedIndex = value;
                });
              },
            ),
          ),
          Expanded(
            child: Container(
              color: Theme.of(context).colorScheme.primaryContainer,
              child: page,  // ← Here.
            ),
          ),
        ],
      ),
    );
  }
}


// ...

অ্যাপটি এখন আমাদের GeneratorPage এবং স্থানধারকগুলির মধ্যে স্যুইচ করে যা শীঘ্রই প্রিয় পৃষ্ঠায় পরিণত হবে।

প্রতিক্রিয়াশীলতা

এরপরে, নেভিগেশন রেলকে প্রতিক্রিয়াশীল করুন। এটি বলার অপেক্ষা রাখে না, যখন তাদের জন্য পর্যাপ্ত জায়গা থাকে তখন এটি স্বয়ংক্রিয়ভাবে লেবেলগুলি ( extended: true ব্যবহার করে) দেখায়।

A8873894C32E0D0B.PNG

ফ্লুটার বেশ কয়েকটি উইজেট সরবরাহ করে যা আপনাকে আপনার অ্যাপ্লিকেশনগুলিকে স্বয়ংক্রিয়ভাবে প্রতিক্রিয়াশীল করতে সহায়তা করে। উদাহরণস্বরূপ, Wrap Row বা Column অনুরূপ একটি উইজেট যা পর্যাপ্ত উল্লম্ব বা অনুভূমিক স্থান না থাকলে বাচ্চাদের স্বয়ংক্রিয়ভাবে পরবর্তী "লাইন" ("রান" বলা হয় ") এ আবৃত করে। FittedBox রয়েছে, একটি উইজেট যা স্বয়ংক্রিয়ভাবে তার শিশুকে আপনার স্পেসিফিকেশন অনুসারে উপলভ্য স্থানে ফিট করে।

তবে পর্যাপ্ত জায়গা থাকলে NavigationRail স্বয়ংক্রিয়ভাবে লেবেলগুলি দেখায় না কারণ এটি প্রতিটি প্রসঙ্গে পর্যাপ্ত জায়গা কী তা জানতে পারে না। এই কল করা আপনার বিকাশকারী, আপনার উপর নির্ভর করে।

বলুন আপনি কেবল MyHomePage কমপক্ষে 600 পিক্সেল প্রশস্ত হলে লেবেলগুলি দেখানোর সিদ্ধান্ত নিয়েছেন।

এক্ষেত্রে ব্যবহারের জন্য উইজেটটি LayoutBuilder । এটি আপনাকে কতটা উপলভ্য স্থান আছে তার উপর নির্ভর করে আপনার উইজেট গাছটি পরিবর্তন করতে দেয়।

আবারও, প্রয়োজনীয় পরিবর্তনগুলি করতে ভিএস কোডে ফ্লুটারের রিফ্যাক্টর মেনু ব্যবহার করুন। এবার, যদিও এটি আরও কিছুটা জটিল:

  1. _MyHomePageState এর build পদ্ধতির ভিতরে, আপনার কার্সারটি Scaffold রাখুন।
  2. Ctrl+. দিয়ে রিফ্যাক্টর মেনুতে কল করুন। (উইন্ডোজ/লিনাক্স) বা Cmd+. (ম্যাক)।
  3. নির্মাতার সাথে মোড়ক নির্বাচন করুন এবং এন্টার টিপুন।
  4. LayoutBuilder সদ্য যুক্ত হওয়া Builder নাম পরিবর্তন করুন।
  5. কলব্যাক প্যারামিটার তালিকা (context) থেকে (context, constraints) এ সংশোধন করুন।

LayoutBuilder builder কলব্যাককে প্রতিবার সীমাবদ্ধতা পরিবর্তিত হওয়ার সময় বলা হয়। উদাহরণস্বরূপ, যখন এটি ঘটে:

  • ব্যবহারকারী অ্যাপের উইন্ডোটি পুনরায় আকার দেয়
  • ব্যবহারকারী তাদের ফোনটি প্রতিকৃতি মোড থেকে ল্যান্ডস্কেপ মোডে বা পিছনে ঘোরান
  • MyHomePage পাশের কিছু উইজেট আকারে বৃদ্ধি পায়, MyHomePage সীমাবদ্ধতাগুলি আরও ছোট করে তোলে
  • ইত্যাদি

এখন আপনার কোডটি বর্তমান constraints জিজ্ঞাসা করে লেবেলটি প্রদর্শন করবেন কিনা তা সিদ্ধান্ত নিতে পারে। নিম্নলিখিত একক-লাইনটি _MyHomePageState এর build পদ্ধতিতে পরিবর্তন করুন:

lib/main.dart

// ...

class _MyHomePageState extends State<MyHomePage> {
  var selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
    Widget page;
    switch (selectedIndex) {
      case 0:
        page = GeneratorPage();
        break;
      case 1:
        page = Placeholder();
        break;
      default:
        throw UnimplementedError('no widget for $selectedIndex');
    }

    return LayoutBuilder(builder: (context, constraints) {
      return Scaffold(
        body: Row(
          children: [
            SafeArea(
              child: NavigationRail(
                extended: constraints.maxWidth >= 600,  // ← Here.
                destinations: [
                  NavigationRailDestination(
                    icon: Icon(Icons.home),
                    label: Text('Home'),
                  ),
                  NavigationRailDestination(
                    icon: Icon(Icons.favorite),
                    label: Text('Favorites'),
                  ),
                ],
                selectedIndex: selectedIndex,
                onDestinationSelected: (value) {
                  setState(() {
                    selectedIndex = value;
                  });
                },
              ),
            ),
            Expanded(
              child: Container(
                color: Theme.of(context).colorScheme.primaryContainer,
                child: page,
              ),
            ),
          ],
        ),
      );
    });
  }
}


// ...

এখন, আপনার অ্যাপ্লিকেশনটি এর পরিবেশে যেমন পর্দার আকার, ওরিয়েন্টেশন এবং প্ল্যাটফর্মের প্রতিক্রিয়া জানায়! অন্য কথায়, এটি প্রতিক্রিয়াশীল!

একমাত্র কাজ যা রয়ে গেছে তা হ'ল সেই Placeholder প্রকৃত পছন্দের স্ক্রিন দিয়ে প্রতিস্থাপন করা। এটি পরবর্তী বিভাগে আচ্ছাদিত।

8. একটি নতুন পৃষ্ঠা যুক্ত করুন

প্রিয় পৃষ্ঠার পরিবর্তে আমরা যে Placeholder উইজেট ব্যবহার করেছি তা মনে রাখবেন?

এটি ঠিক করার সময় এসেছে।

আপনি যদি সাহসী বোধ করেন তবে নিজের দ্বারা এই পদক্ষেপটি করার চেষ্টা করুন। আপনার লক্ষ্য হ'ল একটি নতুন রাষ্ট্রবিহীন উইজেট, FavoritesPage favorites তালিকা প্রদর্শন করা এবং তারপরে Placeholder পরিবর্তে সেই উইজেটটি প্রদর্শন করা।

এখানে কয়েকটি পয়েন্টার রয়েছে:

  • আপনি যখন এমন একটি Column চান যা স্ক্রোল করে, ListView উইজেটটি ব্যবহার করুন।
  • মনে রাখবেন, context.watch<MyAppState>() ব্যবহার করে কোনও উইজেট থেকে MyAppState উদাহরণটি অ্যাক্সেস করুন W
  • আপনি যদি কোনও নতুন উইজেটও চেষ্টা করতে চান তবে ListTile title (সাধারণত পাঠ্যের জন্য), leading (আইকন বা অবতারগুলির জন্য) এবং onTap (ইন্টারঅ্যাকশনগুলির জন্য) এর মতো বৈশিষ্ট্য রয়েছে। তবে আপনি ইতিমধ্যে জানেন এমন উইজেটগুলির সাথে আপনি অনুরূপ প্রভাব অর্জন করতে পারেন।
  • ডার্ট সংগ্রহের আক্ষরিক অভ্যন্তরে for ব্যবহারের অনুমতি দেয়। উদাহরণস্বরূপ, যদি messages স্ট্রিংগুলির একটি তালিকা থাকে তবে আপনার নীচের মতো কোড থাকতে পারে:

F0444BBA08F205AA.PNG

অন্যদিকে, আপনি যদি কার্যকরী প্রোগ্রামিংয়ের সাথে আরও পরিচিত হন তবে ডার্ট আপনাকে messages.map((m) => Text(m)).toList() এবং, অবশ্যই, আপনি সর্বদা উইজেটগুলির একটি তালিকা তৈরি করতে পারেন এবং build পদ্ধতির অভ্যন্তরে এটি অনিবার্যভাবে যুক্ত করতে পারেন।

পছন্দসই পৃষ্ঠাটি নিজেই যুক্ত করার সুবিধাটি হ'ল আপনি নিজের সিদ্ধান্তগুলি করে আরও শিখেন। অসুবিধাটি হ'ল আপনি এমন সমস্যায় পড়তে পারেন যা আপনি এখনও নিজের দ্বারা সমাধান করতে সক্ষম নন। মনে রাখবেন: ব্যর্থতা ঠিক আছে, এবং এটি শেখার অন্যতম গুরুত্বপূর্ণ উপাদান। কেউ আপনার প্রথম ঘন্টায় ঝাঁকুনির বিকাশের পেরেকটি আশা করে না, এবং আপনারও হওয়া উচিত নয়।

252F7C4A212C94D2.png

নিম্নলিখিতটি হ'ল প্রিয় পৃষ্ঠাটি বাস্তবায়নের একটি উপায়। এটি কীভাবে বাস্তবায়িত হয়েছে (আশা করি) আপনাকে কোডটি নিয়ে খেলতে অনুপ্রাণিত করবে - ইউআইকে উন্নত করবে এবং এটিকে আপনার নিজের করে তুলবে।

এখানে নতুন FavoritesPage ক্লাস:

lib/main.dart

// ...

class FavoritesPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var appState = context.watch<MyAppState>();

    if (appState.favorites.isEmpty) {
      return Center(
        child: Text('No favorites yet.'),
      );
    }

    return ListView(
      children: [
        Padding(
          padding: const EdgeInsets.all(20),
          child: Text('You have '
              '${appState.favorites.length} favorites:'),
        ),
        for (var pair in appState.favorites)
          ListTile(
            leading: Icon(Icons.favorite),
            title: Text(pair.asLowerCase),
          ),
      ],
    );
  }
}

উইজেট কী করে তা এখানে:

  • এটি অ্যাপ্লিকেশনটির বর্তমান অবস্থা পায়।
  • যদি পছন্দের তালিকাটি খালি থাকে তবে এটি একটি কেন্দ্রিক বার্তা দেখায়: এখনও কোনও প্রিয় * * * * * * * *
  • অন্যথায়, এটি একটি (স্ক্রোলেবল) তালিকা দেখায়।
  • তালিকাটি সংক্ষিপ্তসার দিয়ে শুরু হয় (উদাহরণস্বরূপ, আপনার 5 টি প্রিয় * * *)।
  • কোডটি তারপরে সমস্ত পছন্দের মাধ্যমে পুনরাবৃত্তি করে এবং প্রতিটিটির জন্য একটি ListTile উইজেট তৈরি করে।

এখন যা বাকি রয়েছে তা হ'ল Placeholder উইজেটকে একটি FavoritesPage সাথে প্রতিস্থাপন করা। এবং ভয়েলা!

আপনি গিটহাবের কোডল্যাব রেপোতে এই অ্যাপ্লিকেশনটির চূড়ান্ত কোডটি পেতে পারেন।

9. পরবর্তী পদক্ষেপ

অভিনন্দন!

তোমার দিকে তাকাও! আপনি একটি Column এবং দুটি Text উইজেট সহ একটি অ-কার্যকরী স্ক্যাফোল্ড নিয়েছেন এবং এটিকে একটি প্রতিক্রিয়াশীল, আনন্দদায়ক ছোট অ্যাপ্লিকেশন হিসাবে তৈরি করেছেন।

d6e3d5f736411f13.png

আমরা কভার করেছি কি

  • কীভাবে ঝাঁকুনি কাজ করে তার মূল বিষয়গুলি
  • ঝাঁকুনিতে লেআউট তৈরি করা
  • অ্যাপ্লিকেশন আচরণের সাথে ব্যবহারকারীর ইন্টারঅ্যাকশনগুলি (যেমন বোতাম টিপে) সংযুক্ত করা হচ্ছে
  • আপনার ফ্লটার কোডটি সংগঠিত রাখা
  • আপনার অ্যাপ্লিকেশনটিকে প্রতিক্রিয়াশীল করা
  • আপনার অ্যাপ্লিকেশনটির একটি ধারাবাহিক চেহারা এবং অনুভূতি অর্জন করা

এরপর কি?

  • এই ল্যাব চলাকালীন আপনি যে অ্যাপ্লিকেশনটি লিখেছেন তার সাথে আরও পরীক্ষা করুন।
  • আপনি কীভাবে অ্যানিমেটেড তালিকা, গ্রেডিয়েন্টস, ক্রস-ফেড এবং আরও অনেক কিছু যুক্ত করতে পারেন তা দেখতে একই অ্যাপ্লিকেশনটির এই উন্নত সংস্করণের কোডটি দেখুন।
  • Flutter.dev/learn এ গিয়ে আপনার শেখার যাত্রা অনুসরণ করুন।