ตอนที่ 3 ทำแอพ Flutter : วิดเจ็ต (Widgets)

1. วิดเจ็ต (Widgets) คืออะไร
2. ประเภทของวิดเจ็ต
2.1 วิดเจ็ต Stateless
2.2 วิดเจ็ตสถานะ (Stateful Widgets)
3. วิดเจ็ตที่ใช้กันทั่วไป
3.1 คอนเทนเนอร์ (Container)
3.2 แถว (Row) และคอลัมน์ (Column)
3.3 สแต็ก (Stack)
4. วิดเจ็ตที่กำหนดเอง
4.1 การสร้างวิดเจ็ตแบบกำหนดเองโดยใช้องค์ประกอบ (Composition)
4.2 การสร้างวิดเจ็ตแบบกำหนดเองโดยใช้ RenderObjectWidget

Flutter ซึ่งเป็นชุดเครื่องมือ UI แบบโอเพนซอร์สของ Google ได้รับความนิยมจากความสามารถในการทำแอพข้ามแพลตฟอร์มที่ดึงดูดสายตาและมีประสิทธิภาพสูง หนึ่งในแนวคิดหลักในการทำแอพ Flutter คือวิดเจ็ต ซึ่งเป็นองค์ประกอบหลักสำหรับการสร้างส่วนต่อประสานกับผู้ใช้ บทความนี้จะเจาะลึกเกี่ยวกับพื้นฐานของวิดเจ็ต Flutter ประเภท และวิธีสร้างและใช้งานอย่างมีประสิทธิภาพในแอปของคุณ

1. วิดเจ็ต (Widgets) คืออะไร

ในการทำแอพ Flutter ทุกอย่างเป็นวิดเจ็ต วิดเจ็ตเป็นองค์ประกอบ UI ในตัวที่ใช้ซ้ำได้ ซึ่งสามารถรวมเข้าด้วยกันเพื่อสร้างอินเทอร์เฟซของแอพ ซึ่งอธิบายลักษณะที่ปรากฏและลักษณะการทำงานของส่วนประกอบ เช่น ข้อความ ปุ่ม รูปภาพ หรือเค้าโครง

วิดเจ็ตในการทำแอพ Flutter ถูกจัดระเบียบในโครงสร้างแบบต้นไม้ที่เรียกว่า “ต้นไม้วิดเจ็ต” แผนผังวิดเจ็ตแสดงความสัมพันธ์แบบลำดับขั้นระหว่างวิดเจ็ต โดยที่วิดเจ็ตพาเรนต์สามารถมีวิดเจ็ตย่อยตั้งแต่หนึ่งวิดเจ็ตขึ้นไป เมื่อสถานะของวิดเจ็ตเปลี่ยนไป Flutter จะสร้างใหม่เฉพาะส่วนที่ได้รับผลกระทบของแผนผังวิดเจ็ต ส่งผลให้การอัปเดต UI มีประสิทธิภาพ

2. ประเภทของวิดเจ็ต

Flutter มีวิดเจ็ตหลักสองประเภท: Stateless และ Stateful

2.1 วิดเจ็ต Stateless

วิดเจ็ต Stateless คือวิดเจ็ตที่ไม่เก็บสถานะที่ไม่แน่นอน พวกเขาอธิบายถึงส่วนหนึ่งของอินเทอร์เฟซผู้ใช้ที่ค่อนข้างคงที่ตลอดวงจรชีวิตของแอป ตัวอย่างของวิดเจ็ต Stateless ได้แก่ ข้อความ ไอคอน และรูปภาพ

ในการสร้างวิดเจ็ต Stateless คุณต้องขยาย (extend) คลาส StatelessWidget และแทนที่เมธอดบิลด์:

import 'package:flutter/material.dart';

class CustomText extends StatelessWidget {
  final String text;

  CustomText({required this.text});

  @override
  Widget build(BuildContext context) {
    return Text(
      text,
      style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
    );
  }
}
2.2 วิดเจ็ตสถานะ (Stateful Widgets)

วิดเจ็ตแบบมีสถานะจะรักษาสถานะที่ไม่แน่นอนซึ่งสามารถเปลี่ยนแปลงได้ตลอดเวลา เมื่อสถานะของวิดเจ็ต stateful เปลี่ยนไป วิดเจ็ตจะสร้าง UI ใหม่เพื่อสะท้อนสถานะใหม่ ตัวอย่างของวิดเจ็ตแบบมีสถานะ ได้แก่ Checkbox, TextField และ Slider

ในการสร้างวิดเจ็ตแบบมีสถานะ คุณต้องขยายคลาส StatefulWidget และสร้างออบเจกต์สถานะแยกต่างหากที่เก็บสถานะที่ไม่แน่นอน:

import 'package:flutter/material.dart';

class CustomCounter extends StatefulWidget {
  @override
  _CustomCounterState createState() => _CustomCounterState();
}

class _CustomCounterState extends State<CustomCounter> {
  int _count = 0;

  void _incrementCounter() {
    setState(() {
      _count = _count + 1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          'Count: $_count',
          style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
        ),
        ElevatedButton(
          onPressed: _incrementCounter,
          child: Text('Increment'),
        ),
      ],
    );
  }
}

3. วิดเจ็ตที่ใช้กันทั่วไป

Flutter มีชุดวิดเจ็ตที่สร้างไว้ล่วงหน้ามากมายสำหรับวัตถุประสงค์ต่างๆ ต่อไปนี้เป็นวิดเจ็ตที่ใช้กันทั่วไป:

3.1 คอนเทนเนอร์ (Container)

คอนเทนเนอร์เป็นวิดเจ็ตเอนกประสงค์ที่สามารถใช้เพื่อจัดรูปแบบและวางตำแหน่งวิดเจ็ตอื่นๆ สามารถมีลูกเดียวและคุณสมบัติเพิ่มเติม เช่น ระยะขอบ ระยะขอบ และการตกแต่ง

Container(
  padding: EdgeInsets.all(8),
  margin: EdgeInsets.symmetric(horizontal: 16),
  decoration: BoxDecoration(
    color: Colors.blue,
    borderRadius: BorderRadius.circular(8),
  ),
  child: Text('Hello, Flutter!'),
)
3.2 แถว (Row) และคอลัมน์ (Column)

แถวและคอลัมน์ใช้เพื่อจัดเรียงวิดเจ็ตย่อยในแนวนอนและแนวตั้งตามลำดับ มีคุณสมบัติเช่น mainAxisAlignment และ crossAxisAlignment เพื่อควบคุมการจัดแนวลูก

Column(
    mainAxisAlignment = MainAxisAlignment.center,
    crossAxisAlignment = CrossAxisAlignment.start,
    children = listOf(
        Text("Hello, Flutter!"),
        Text("Welcome to the world of widgets."),
        Row(
            mainAxisAlignment = MainAxisAlignment.spaceEvenly,
            children = listOf(
                Icon(Icons.favorite, color = Colors.red),
                Icon(Icons.star, color = Colors.yellow),
                Icon(Icons.thumb_up, color = Colors.blue),
            ),
        ),
    ),
)
3.3 สแต็ก (Stack)

วิดเจ็ตแบบสแต็กช่วยให้คุณวางวิดเจ็ตหลายรายการซ้อนทับกัน มีประโยชน์สำหรับการสร้างองค์ประกอบ UI ที่ซับซ้อนซึ่งคุณต้องวางตำแหน่งวิดเจ็ตให้สัมพันธ์กับพาเรนต์หรือพี่น้อง

Stack(
  alignment: Alignment.center,
  children: [
    Container(
      width: 100,
      height: 100,
      color: Colors.red,
    ),
    Container(
      width: 80,
      height: 80,
      color: Colors.green,
    ),
    Container(
      width: 60,
      height: 60,
      color: Colors.blue,
    ),
  ],
)

4. วิดเจ็ตที่กำหนดเอง

บางครั้งในการทำแอพ คุณอาจต้องสร้างวิดเจ็ตแบบกำหนดเองเพื่อให้ตรงตามข้อกำหนดด้านการออกแบบหรือการทำงานเฉพาะ สามารถสร้างวิดเจ็ตแบบกำหนดเองได้โดยการรวมวิดเจ็ตที่มีอยู่ หรือขยายคลาส RenderObjectWidget ระดับต่ำ

4.1 การสร้างวิดเจ็ตแบบกำหนดเองโดยใช้องค์ประกอบ (Composition)

การรวมวิดเจ็ตที่มีอยู่เพื่อสร้างวิดเจ็ตแบบกำหนดเองเรียกว่าการจัดองค์ประกอบ เป็นแนวทางที่แนะนำเนื่องจากส่งเสริมการใช้โค้ดซ้ำและความเป็นโมดูลาร์ในการทำแอพ Flutter ต่อไปนี้คือตัวอย่างวิดเจ็ตการ์ดแบบกำหนดเองที่สร้างขึ้นโดยใช้องค์ประกอบ:

import 'package:flutter/material.dart';

class CustomCard extends StatelessWidget {
  final String title;
  final String description;

  CustomCard({required this.title, required this.description});

  @override
  Widget build(BuildContext context) {
    return Card(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(8),
      ),
      elevation: 4,
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              title,
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            SizedBox(height: 8),
            Text(
              description,
              style: TextStyle(fontSize: 14),
            ),
          ],
        ),
      ),
    );
  }
}
4.2 การสร้างวิดเจ็ตแบบกำหนดเองโดยใช้ RenderObjectWidget

หากคุณต้องการสร้างวิดเจ็ตแบบกำหนดเองที่มีการควบคุมกระบวนการเรนเดอร์มากขึ้น คุณสามารถขยายคลาส RenderObjectWidget วิธีการนี้ซับซ้อนกว่าและมักจะสงวนไว้สำหรับกรณีการใช้งานขั้นสูงสำหรับการทำแอพ

ต่อไปนี้คือตัวอย่างตัวบ่งชี้ความคืบหน้าแบบวงกลมที่กำหนดเองซึ่งสร้างขึ้นโดยการขยาย RenderObjectWidget:

import 'package:flutter/rendering.dart';

class CircularProgress extends LeafRenderObjectWidget {
  final double progress;

  CircularProgress({required this.progress});

  @override
  RenderObject createRenderObject(BuildContext context) {
    return RenderCircularProgress(progress: progress);
  }

  @override
  void updateRenderObject(
      BuildContext context, covariant RenderCircularProgress renderObject) {
    renderObject..progress = progress;
  }
}

class RenderCircularProgress extends RenderBox {
  double _progress;

  RenderCircularProgress({required double progress}) : _progress = progress;

  set progress(double value) {
    if (_progress == value) return;
    _progress = value;
    markNeedsPaint();
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    // Implement custom painting logic here
  }

  @override
  bool hitTestSelf(Offset position) => false;

  @override
  void performLayout() {
    size = constraints.biggest;
  }
}

การทำความเข้าใจวิดเจ็ตเป็นสิ่งสำคัญสำหรับการทำแอพ Flutter โดยการเรียนรู้ที่แตกต่างประเภทกันของวิดเจ็ต การใช้งาน และการสร้างวิดเจ็ตแบบกำหนดเอง คุณสามารถทำแอพที่ทรงพลังและดึงดูดสายตาซึ่งตอบสนองความต้องการต่างๆ ได้

ในขณะที่คุณเดินทางต่อใน Flutter อย่าลืมว่ากุญแจสำคัญในการทำแอพที่ยอดเยี่ยมนั้นอยู่ที่การใช้วิดเจ็ตและคุณสมบัติของมันอย่างมีประสิทธิภาพ ทดลองรวมวิดเจ็ต สร้างวิดเจ็ตแบบกำหนดเอง และใช้เพื่อสร้าง UI ของแอป ด้วยการฝึกฝนและการสำรวจ คุณจะสามารถสร้างอินเทอร์เฟซที่น่าทึ่งซึ่งสร้างความพึงพอใจให้กับผู้ใช้และทำให้แนวคิดแอพของคุณเป็นจริงได้ในไม่ช้า

พัฒนาแอพด้วย Flutter

ตอนที่ 2 ทำแอพ Flutter : ติดตั้ง Flutter และแอพ Hello World
ตอนที่ 4 ทำแอพ Flutter : เลย์เอาต์ (Layout) และการออกแบบ UI