Flutter — উইজেট ট্রি ও রেন্ডারিং ইঞ্জিন
এই পাঠে যা শিখবেন
- "সবকিছুই একটি উইজেট" ধারণাটি — কেন প্যাডিং/লেআউটও উইজেট হিসেবে গণ্য হয়
- Flutter কেন প্ল্যাটফর্মের নেটিভ উইজেট ব্যবহার না করে নিজস্ব রেন্ডারিং ইঞ্জিন দিয়ে UI আঁকে
- একটি উইজেট ট্রিকে নেস্টেড dict কাঠামো হিসেবে বাস্তবায়ন করা
- একটি সত্যিকারের
render()ফাংশন যা ট্রি হেঁটে প্রকৃত ড্র-অপারেশনের তালিকা তৈরি করে
১ · সবকিছুই একটি উইজেট
FlutterFlutterGoogle-এর তৈরি একটি ক্রস-প্ল্যাটফর্ম UI টুলকিট, Dart ভাষায় লেখা হয় — একটি একক কোডবেস থেকে iOS, Android ও আরও কয়েকটি প্ল্যাটফর্মের জন্য নেটিভ মেশিন কোডে কম্পাইল হয়, এবং নিজস্ব রেন্ডারিং ইঞ্জিন দিয়ে UI আঁকে।-এর সবচেয়ে গুরুত্বপূর্ণ ডিজাইন সিদ্ধান্তগুলোর একটি হলো — সবকিছুই একটি উইজেট। একটি বাটন উইজেট, একটি টেক্সট উইজেট — এটা তো স্বাভাবিক মনে হয়, কিন্তু Flutter-এ প্যাডিং, অ্যালাইনমেন্ট, এমনকি একটি স্ক্রিনের পুরো লেআউট কাঠামোও উইজেট। প্রতিটি উইজেট তার শিশু উইজেট(গুলো)-কে র্যাপ করে অতিরিক্ত আচরণ (স্পেসিং, পজিশনিং, স্টাইলিং) যোগ করে — ফলে একটি স্ক্রিন আসলে নেস্টেড উইজেটের একটি গাছ-কাঠামো (tree)।
React Native (L17) প্রকৃত প্ল্যাটফর্ম নেটিভ উইজেট ব্যবহার করে, শুধু JS থেকে সেগুলো নিয়ন্ত্রণ করে। Flutter সম্পূর্ণ ভিন্ন পথে হাঁটে — এটি Dart কোড থেকে নেটিভ ARM মেশিন কোডে কম্পাইল হয়, কিন্তু স্ক্রিনে UI আঁকার জন্য প্ল্যাটফর্মের নেটিভ বাটন/টেক্সট উইজেট ব্যবহার করে না। বরং এটি নিজস্ব রেন্ডারিং ইঞ্জিন দিয়ে প্রতিটি পিক্সেল সরাসরি একটি ক্যানভাসে আঁকে — এই কারণেই একটি Flutter অ্যাপ সব প্ল্যাটফর্মে প্রায় হুবহু একই রকম দেখায়, কিন্তু কখনো কখনো প্ল্যাটফর্ম-নির্দিষ্ট সূক্ষ্ম আচরণ নিজে থেকে অনুকরণ করতে হয়।
২ · একটি সত্যিকারের উইজেট ট্রি ও render() ফাংশন
নিচের কোড সেলে উপরের ট্রি-টি একটি নেস্টেড Python dict কাঠামো হিসেবে বাস্তবায়ন করা হলো, এবং একটি প্রকৃত
render(widget, x, y) ফাংশন লেখা হলো যা ট্রি-টি রিকার্সিভভাবে হেঁটে প্রতিটি পাতা-উইজেটের জন্য
একটি সত্যিকারের ড্র-অপারেশন গণনা করে — Padding ও Column/Row-এর মতো
কাঠামোগত উইজেট নিজে কিছু আঁকে না, শুধু তাদের সন্তানদের সঠিক (x, y) পজিশনে বসায়।
# একটি Flutter-অনুপ্রাণিত উইজেট ট্রি -- সবকিছুই একটি উইজেট, এমনকি Padding/Column/Row-ও
# প্রতিটি উইজেট: {"type": ..., "props": {...}, "children": [...]}
tree = {
"type": "Padding",
"props": {"left": 16, "top": 20},
"children": [
{
"type": "Column",
"props": {},
"children": [
{"type": "Container", "props": {"width": 240, "height": 60, "color": "blue"}, "children": []},
{"type": "Text", "props": {"content": "রাহুল আহমেদ", "width": 140}, "children": []},
{
"type": "Row",
"props": {},
"children": [
{"type": "Text", "props": {"content": "ফলো", "width": 60}, "children": []},
{"type": "Text", "props": {"content": "মেসেজ", "width": 80}, "children": []},
],
},
],
}
],
}
TEXT_HEIGHT = 24 # প্রতিটি Text/Row-এর ধরে নেওয়া লাইন-উচ্চতা (px) -- এই সরলীকৃত লেআউট মডেলে স্থির
def widget_height(widget):
t = widget["type"]
if t == "Container":
return widget["props"]["height"]
if t in ("Text", "Row"):
return TEXT_HEIGHT
return 0
def widget_width(widget):
t = widget["type"]
if t == "Container":
return widget["props"]["width"]
if t == "Text":
return widget["props"]["width"]
return 0
def render(widget, x=0, y=0):
"""ট্রি হেঁটে প্রকৃত ড্র-অপারেশনের তালিকা তৈরি করে -- Padding/Column/Row নিজে কিছু আঁকে না"""
ops = []
t = widget["type"]
if t == "Padding":
left = widget["props"].get("left", 0)
top = widget["props"].get("top", 0)
for child in widget["children"]:
ops.extend(render(child, x + left, y + top))
elif t == "Column":
cur_y = y
for child in widget["children"]:
ops.extend(render(child, x, cur_y))
cur_y += widget_height(child)
elif t == "Row":
cur_x = x
for child in widget["children"]:
ops.extend(render(child, cur_x, y))
cur_x += widget_width(child)
elif t == "Container":
w = widget["props"]["width"]
h = widget["props"]["height"]
color = widget["props"].get("color", "black")
ops.append(("draw_rect", x, y, w, h, color))
elif t == "Text":
content = widget["props"]["content"]
ops.append(("draw_text", x, y, content))
return ops
draw_ops = render(tree)
print(f"মোট ড্র-অপারেশন: {len(draw_ops)}\n")
for op in draw_ops:
print(op)
def count_widgets(widget):
return 1 + sum(count_widgets(c) for c in widget["children"])
total_widgets = count_widgets(tree)
print(f"\nমোট উইজেট (কাঠামোগত + দৃশ্যমান): {total_widgets}")
print(f"যার মধ্যে প্রকৃত ড্র-অপারেশন তৈরি করেছে: {len(draw_ops)} (বাকি {total_widgets - len(draw_ops)}টি শুধু কাঠামো/স্পেসিং)")
Padding (left:16, top:20) প্রথমে Column-কে (16, 20)-এ বসায়।
Column-এর প্রথম সন্তান Container (16, 20)-তেই আঁকা হয় এবং এর উচ্চতা ৬০ হওয়ায়
পরবর্তী কার্সার-y হয় ৮০। দ্বিতীয় সন্তান Text ("রাহুল আহমেদ") তাই (16, 80)-এ আঁকা হয়, এবং
TEXT_HEIGHT=24 যোগ হয়ে কার্সার-y হয় ১০৪। তৃতীয় সন্তান Row তাই y=104-এ বসে, এবং তার
ভেতরে প্রথম Text ("ফলো") (16, 104)-এ ও দ্বিতীয় Text ("মেসেজ") তার width=60 যোগ হয়ে
(76, 104)-এ আঁকা হয়। মোট ৪টি ড্র-অপারেশন — যদিও ট্রি-তে মোট ৭টি উইজেট আছে (৩টি কাঠামোগত: Padding, Column,
Row — কিছুই আঁকে না)।
Flutter-এ "সবকিছুই একটি উইজেট" মানে বাস্তবেই প্যাডিং ও লেআউটও গাছ-কাঠামোর অংশ — উপরের কোড সেলে
Padding, Column, Row কোনো ড্র-অপারেশন তৈরি করেনি, শুধু তাদের
সন্তানদের সঠিক পজিশনে বসিয়েছে; শুধু পাতা-উইজেট (Container, Text) প্রকৃত আঁকার
নির্দেশনা তৈরি করেছে। বাস্তব Flutter-এ এই একই render()-এর মতো একটি ধাপ Skia-ভিত্তিক রেন্ডারিং
ইঞ্জিনের মাধ্যমে সরাসরি স্ক্রিনের ক্যানভাসে চূড়ান্ত পিক্সেল আঁকে — প্ল্যাটফর্মের নেটিভ UI টুলকিট ছাড়াই।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ প্যাডিংকে একটি আলাদা উইজেট হিসেবে ডিজাইন করার সুবিধা কী?
যেকোনো উইজেটকে Padding-এ র্যাপ করলেই সেটি স্পেসিং পায় — কোনো বিশেষ "padding প্রপার্টি"
প্রতিটি উইজেট টাইপে আলাদাভাবে বাস্তবায়ন করতে হয় না। এটি Flutter-এর "সবকিছুই উইজেট" দর্শনের একটি বাস্তব
সুবিধা — মাত্র কয়েকটি সরল বিল্ডিং ব্লক দিয়ে (র্যাপ করে) জটিল লেআউট তৈরি করা যায়, ঠিক যেমন উপরের কোড
সেলে Padding ও Column/Row একসাথে ব্যবহার করা হয়েছে।
প্র ০২ Flutter কেন প্ল্যাটফর্মের নেটিভ উইজেট ব্যবহার না করে নিজস্ব রেন্ডারিং ইঞ্জিন বেছে নিয়েছে?
এতে Flutter সব প্ল্যাটফর্মে হুবহু একই রকম পিক্সেল-নিয়ন্ত্রণ পায় — একটি অ্যানিমেশন বা কাস্টম UI iOS ও Android-এ প্রায় অভিন্ন আচরণ করে, কারণ Flutter নিজে সবকিছু আঁকছে, প্ল্যাটফর্মের ভিন্ন ভিন্ন নেটিভ উইজেট বাস্তবায়নের উপর নির্ভর করছে না। বিনিময়ে, প্ল্যাটফর্মের একেবারে খাঁটি নেটিভ "ফিল" পুনরুৎপাদন করতে Flutter-কে সচেতনভাবে চেষ্টা করতে হয় (L19-এ এই ট্রেড-অফ আরও বিস্তারিত)।
প্র ০৩
কোড সেলে Column ও Row-এ widget_height/widget_width ব্যবহার করে কার্সার-পজিশন এগিয়ে নেওয়া কেন দরকার?
একটি লেআউট উইজেটের কাজ হলো তার সন্তানদের একে অপরের সাথে না মিলিয়ে সঠিক ক্রমে বসানো। Column
প্রতিটি সন্তানের পরে y-কার্সার তার উচ্চতা দিয়ে এগিয়ে নেয় যাতে পরবর্তী সন্তান আগেরটির নিচে বসে, একইভাবে
Row x-কার্সার প্রস্থ দিয়ে এগিয়ে নেয় যাতে সন্তানরা পাশাপাশি বসে, একে অপরের উপর ওভারল্যাপ না
করে।
অনুশীলন
-
চিন্তা করুন: আপনার ব্যবহৃত কোনো অ্যাপ যদি সব প্ল্যাটফর্মে (ফোন, ট্যাবলেট, এমনকি ওয়েব)
প্রায় হুবহু একই রকম দেখতে হয়, সেটি কি Flutter-এ তৈরি হতে পারে বলে মনে হয়? কেন?
হতে পারে — যেহেতু Flutter প্ল্যাটফর্মের নেটিভ উইজেটের বদলে নিজস্ব রেন্ডারিং ইঞ্জিন দিয়ে সবকিছু আঁকে, একটি Flutter অ্যাপ বিভিন্ন প্ল্যাটফর্মে সহজেই প্রায় পিক্সেল-পারফেক্ট অভিন্ন দেখাতে পারে — যেখানে React Native (প্রকৃত নেটিভ উইজেট ব্যবহার করে বলে) সাধারণত প্রতিটি প্ল্যাটফর্মের নিজস্ব লুক কিছুটা বহন করে।
-
পরীক্ষা করুন: কোড সেলের
tree-তেColumn-এর children তালিকায় একটি নতুন{"type": "Text", "props": {"content": "প্রোফাইল", "width": 100}, "children": []}সবার শুরুতে যোগ করুন, তারপর নতুন প্রথম ড্র-অপারেশনটির (x, y) কী হবে তা আগে থেকে অনুমান করে যাচাই করুন।নতুন
TextএখনColumn-এর প্রথম সন্তান, তাই এটি সরাসরি Column-এর শুরুর পজিশনে আঁকা হবে —("draw_text", 16, 20, "প্রোফাইল")। এরপর কার্সার-yTEXT_HEIGHT=24 দিয়ে এগিয়ে ৪৪ হবে, ফলে বাকি সব উইজেট (Container, পুরনো Text, Row) ২৪ পিক্সেল নিচে সরে যাবে — এটি দেখায় একটি একক উইজেট যোগ করলে পুরো সাবট্রি-র পজিশন কীভাবে পুনরায় গণনা হয়।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পরের পাঠ: একটি ক্রস-প্ল্যাটফর্ম ফ্রেমওয়ার্ক বাছাই করা পাঠ ১৯ React Native (L17) ও Flutter (L18) দুটোই দেখা হয়ে গেলো — এবার একটি বাস্তব প্রজেক্টের জন্য কোনটি বেছে নেওয়া উচিত তা ওজনযুক্ত স্কোরিং দিয়ে সিদ্ধান্ত নেওয়া যাক।
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৭টি পাঠ নেটিভ বনাম ক্রস-প্ল্যাটফর্ম, UI কম্পোনেন্ট, নেভিগেশন, স্টেট ম্যানেজমেন্ট, লোকাল স্টোরেজ, ডিভাইস ফিচার ও ডিপ্লয়মেন্ট — বাকি পাঠগুলো এই কোর্সেই আছে।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics, Full-Stack Web Frameworks ও Mobile App Development — সব এক জায়গায়।