ทบทวน Layout Widget สิ่งสำคัญ ตอนที่ 1 Sliver Widget

บทความใหม่ ไม่กี่เดือนก่อน โดย Ninenik Narkdee
sliver widget layout widget

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ sliver widget layout widget

ดูแล้ว 343 ครั้ง


ในการพัฒนาแอปด้วย flutter สิ่งที่พบและมีความสำคัญก็คือ
การจัดการเกี่ยวกับเลเอาท์ของแอป กรณีที่มีความซับซ้อนหรือ
มีการใช้งาน widget ต่างๆ เพิ่มมากขึ้นเรื่อยๆ ปัญหาเกี่ยวกับพื้นที่
หรือส่วนแสดงข้อมูลมักจะเกิดขึ้น เช่น เนื้อหาหรือข้อมูล เกินขอบเขต
ของ widget หรือใช้ตัว widget ที่ไม่รองรับการขยายขนาดอัตโนมัติ
ดังนั้น เนื่อหานี้ เราจะกลับมาทบทวนส่วนต่างๆ นี้ และจะเป็นการบันทึกไว้
เป็นแนวทาง กลับมาใช้งานหรือมาเป็นตัวช่วยในการแก้ป้ญหา ที่อาจจะ
เกิดขึ้นในอนาคตได้
 
เนื้อหานี้ใช้โค้ดตัวอย่างเริ่มต้น จากบทความ ตามลิ้งค์นี้ http://niik.in/961
โดยใช้ โค้ดตัวอย่างจากส่วน เพิ่มเติมเนื้อหา ครั้งที่ 2 
 
 

เกี่ยวกับ Layout Widget ใน Flutter

    ใน Flutter, Layout widgets แบ่งออกเป็น 3 ประเภทหลัก ได้แก่ Single-child layout widgets,
Multi-child layout widgets และ Sliver widgets แต่ละประเภทมีหน้าที่และความหมายต่างกัน ดังนี้:
 

1. Single-child Layout Widgets

    เป็น widgets ที่สามารถมีลูกได้เพียงตัวเดียว ซึ่งหมายความว่า widgets เหล่านี้จะวาง layout หรือ
จัดเรียงเฉพาะ widget ลูกเพียงตัวเดียวเท่านั้น
    ใช้ในการจัดการ layout ของ widget ลูกตัวเดียว เช่น การกำหนดขนาด ตำแหน่ง หรือขอบเขตของ 
widget ลูก
 
    ตัวอย่าง:
    Container: ใช้ในการปรับขนาด สี พื้นหลัง ขอบเขต ฯลฯ ของ widget ลูก
    Center: จัดวาง widget ลูกไว้ที่กลางหน้าจอ
    Padding: เพิ่มพื้นที่ว่างรอบๆ widget ลูก
 

2. Multi-child Layout Widgets

    เป็น widgets ที่สามารถมีลูกได้หลายตัว ซึ่งหมายความว่า widgets เหล่านี้สามารถจัดเรียง widget
ลูกหลายๆ ตัวพร้อมกันได้
    ใช้ในการจัดการ layout ของ widget หลายตัว เช่น การจัดเรียงในแนวนอน แนวตั้ง การซ้อนทับกัน 
หรือการกระจายพื้นที่ระหว่าง widget ลูก
 
    ตัวอย่าง:
    Row: จัดเรียง widget ลูกในแนวนอน
    Column: จัดเรียง widget ลูกในแนวตั้ง
    Stack: ซ้อน widget ลูกหลายตัวทับกัน
 

3. Sliver Widgets

    เป็น widgets ที่ใช้สำหรับการจัดการ scrollable areas โดยเฉพาะ เช่น ListView หรือ GridView
แต่มีความยืดหยุ่นมากขึ้นและสามารถจัดการการ scroll ที่ซับซ้อนได้มากขึ้น
    ใช้ในการสร้างส่วนต่างๆ ที่สามารถ scroll ได้ เช่น การสร้าง list ที่เลื่อนได้อย่างมีประสิทธิภาพ หรือการ
จัดการพื้นที่ที่สามารถขยายหดได้ในขณะที่มีการ scroll
 
    ตัวอย่าง:
    SliverList: ใช้ในการสร้าง list ที่สามารถ scroll ได้
    SliverGrid: ใช้ในการสร้าง grid ที่สามารถ scroll ได้
    SliverAppBar: AppBar ที่สามารถย่อหรือขยายได้เมื่อมีการ scroll
 

ความแตกต่าง:

Single-child layout widgets เหมาะสำหรับการจัดการ layout ของ widget ลูกตัวเดียว 
ในขณะที่ Multi-child layout widgets ใช้สำหรับจัดการ layout ของ widget หลายตัวพร้อมกัน
Sliver widgets ต่างจากสองประเภทแรกเพราะมันใช้สำหรับการจัดการส่วนที่สามารถ scroll ได้ 
ซึ่งเหมาะสำหรับการสร้าง UI ที่ซับซ้อนและมีประสิทธิภาพในด้านการเลื่อนหน้าจอ
 
 
*Layout widgets ทั้งสามประเภทใน Flutter (Single-child layout widgets, Multi-child 
layout widgets, และ Sliver widgets) สามารถใช้ร่วมกันในหน้าเดียวได้อย่างอิสระ การใช้ widgets 
เหล่านี้ร่วมกันในหน้าเดียวกันเป็นเรื่องปกติในการพัฒนาแอปพลิเคชันด้วย Flutter เพื่อสร้างโครงสร้างของ 
UI ที่ซับซ้อนและหลากหลาย
 
เนื้อหานี้เรามาดูกันที่ตัวอย่างตัวสุดท้าย คือ Sliver Widget

 
    
 

หน้าที่ของ Widget และ  delegate ใน  Sliver Widget 

    จะแสดงรายละเอียดหน้าที่การทำงานของแต่ละตัวตามลำดับดังนี้
 

1. CustomScrollView

    เป็น widget ที่ช่วยให้เราสร้างเลเอาท์ที่สามารถเลื่อน (scroll) ได้อย่างยืดหยุ่น โดยสามารถรวม 
sliver ต่าง ๆ (เช่น รายการ, กริด, ส่วนหัวที่ติดตามการเลื่อน) ไว้ใน CustomScrollView เพื่อสร้าง
การเลื่อนที่ซับซ้อนมากขึ้น
 

2. SliverAppBar

    เป็น widget ที่ใช้สร้างแถบแอปที่สามารถขยาย (expand) และย่อ (collapse) ได้ตามการเลื่อนของ
เนื้อหาใน CustomScrollView โดยทั่วไปจะใช้เพื่อแสดงแถบชื่อหรือแถบเครื่องมือ (toolbar) ที่มีฟังก์ชัน
การเลื่อนที่ติดตามกับ CustomScrollView
 

3. SliverFixedExtentList

    เป็น widget ที่ใช้สร้างรายการ (list) ที่เลื่อนขึ้นลงได้ ซึ่งความสูงของแต่ละรายการถูกกำหนดไว้แน่นอน 
(itemExtent) ซึ่งช่วยเพิ่มประสิทธิภาพในการเลื่อน โดยเฉพาะเมื่อมีรายการจำนวนมาก
 

4. SliverList

    เป็น widget ที่ใช้สร้างรายการ (list) ที่เลื่อนขึ้นลงได้ โดยความสูงของแต่ละรายการสามารถแตกต่างกัน
ตามเนื้อหาได้ จึงมีความยืดหยุ่นมากกว่า SliverFixedExtentList แต่มีความซับซ้อนในการคำนวณ
เลเอาท์มากขึ้น
 

5. SliverChildBuilderDelegate

    เป็น delegate ที่ใช้ใน sliver เช่น SliverList หรือ SliverGrid เพื่อสร้างรายการโดยการเรียก 
callback (builder) สำหรับแต่ละรายการ ช่วยในการสร้างรายการแบบไดนามิก (เมื่อเลื่อนถึงจุดนั้นจริง ๆ) 
โดยไม่ต้องสร้างทุกรายการล่วงหน้า
 

6. SliverChildListDelegate

    เป็น delegate ที่ใช้สร้างรายการจาก list ที่กำหนดไว้ล่วงหน้า (fixed list) ซึ่งเหมาะกับกรณีที่มีจำนวน
รายการคงที่และไม่มาก
 

7. SliverPadding

    เป็น widget ที่ใช้ในการเพิ่มพื้นที่ว่าง (padding) รอบ ๆ sliver ที่อยู่ภายใน CustomScrollView 
โดยทำงานคล้ายกับ Padding widget แต่ใช้กับ sliver โดยเฉพาะ
 

8. SliverPersistentHeader

    เป็น widget ที่ใช้สร้างส่วนหัวที่สามารถยึดติดอยู่ด้านบนของหน้าจอ (pinned) หรือสามารถขยาย-ย่อ
(expand-collapse) ได้ตามการเลื่อน โดยทั่วไปจะใช้แสดงข้อมูลสำคัญที่ต้องการให้แสดงตลอดเวลาหรือ
เปลี่ยนขนาดตามการเลื่อน
 

9. SliverToBoxAdapter

    เป็น widget ที่ช่วยในการแปลง widget ปกติที่ไม่ใช่ sliver ให้สามารถวางอยู่ใน CustomScrollView
 ได้ เช่น การใช้เพื่อแสดง widget ทั่วไป (เช่น Container หรือ Text) ภายใน CustomScrollView
 

10. SliverGrid

    เป็น widget ที่ใช้ในการแสดงรายการข้อมูลในรูปแบบตาราง (grid) ภายใน CustomScrollView 
ซึ่งสามารถปรับแต่งจำนวนคอลัมน์หรือขนาดของแต่ละไอเท็มใน grid ได้ SliverGrid เหมาะกับการ
แสดงข้อมูลที่ต้องการจัดในลักษณะเป็นแถวและคอลัมน์ ซึ่งสามารถรองรับข้อมูลจำนวนมากและเลื่อน
ขึ้นลงได้อย่างลื่นไหล
 
    SliverGridDelegate: เป็น delegate ที่ใช้ในการจัดการการวางตำแหน่งของแต่ละไอเท็มใน 
grid โดยจะต้องกำหนดว่าต้องการให้ grid แสดงผลในลักษณะใด เช่น ขนาดของไอเท็ม ความกว้าง
ของคอลัมน์ หรือระยะห่างระหว่างไอเท็ม
 
    SliverGridDelegateWithFixedCrossAxisCount: เป็น delegate ที่กำหนดจำนวนคอลัมน์
ที่คงที่ (fixed number of columns) และขนาดของแต่ละไอเท็มจะถูกคำนวณอัตโนมัติตามพื้นที่ที่เหลือ
อยู่ของหน้าจอ
 
    SliverGridDelegateWithMaxCrossAxisExtent: เป็น delegate ที่กำหนดขนาดสูงสุดของ
ไอเท็มในแต่ละคอลัมน์ โดยจำนวนคอลัมน์จะถูกปรับอัตโนมัติตามความกว้างของหน้าจอและขนาดของไอเท็ม
 
แต่ละ widget เหล่านี้มีหน้าที่เฉพาะใน Flutter ที่ช่วยในการสร้างเลเอาท์ที่สามารถเลื่อนได้อย่างยืดหยุ่น 
โดย CustomScrollView ทำหน้าที่เป็นคอนเทนเนอร์สำหรับ sliver ต่าง ๆ ส่วน sliver แต่ละตัวก็มีหน้าที่
และคุณสมบัติที่เฉพาะเจาะจง เช่น การสร้างแถบแอปที่ขยายได้ (SliverAppBar), การสร้างรายการที่มีความ
สูงเท่ากัน (SliverFixedExtentList), หรือการเพิ่มพื้นที่ว่างรอบ ๆ sliver (SliverPadding)
 

ตัวอย่างโค้ดไฟล์ home.dart

 
import 'package:flutter/material.dart';
  
class Home extends StatefulWidget {
    static const routeName = '/home';
 
    const Home({Key? key}) : super(key: key);
  
    @override
    State<StatefulWidget> createState() {
        return _HomeState();
    }
}
  
class _HomeState extends State<Home> {
  
    @override
    Widget build(BuildContext context) {
  
        return Scaffold(
            appBar: AppBar(
                title: Text('Home'),
                leading: IconButton(
                  icon: Icon(Icons.menu),
                  onPressed: () {
                    Scaffold.of(context).openDrawer();
                  },
                ),                
            ),
              body: CustomScrollView(
                slivers: <Widget>[
                  const SliverAppBar(
                    pinned: true,
                    expandedHeight: 250.0,
                    flexibleSpace: FlexibleSpaceBar(
                      title: Text('Demo'),
                    ),
                  ),
                  SliverPersistentHeader(
                    pinned: true,
                    delegate: _SliverAppBarDelegate(
                      minHeight: 60.0,
                      maxHeight: 150.0,
                      child: Container(
                        color: Colors.pink,
                        child: Center(child: Text('Sliver Persistent Header')),
                      ),
                    ),
                  ),
                  SliverPadding(
                    padding: EdgeInsets.all(16.0),
                    sliver: SliverGrid(
                      gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
                        maxCrossAxisExtent: 200.0,
                        mainAxisSpacing: 10.0,
                        crossAxisSpacing: 10.0,
                        childAspectRatio: 5.0,
                      ),
                      delegate: SliverChildBuilderDelegate(
                        (BuildContext context, int index) {
                          return Container(
                            alignment: Alignment.center,
                            color: Colors.teal[100 * (index % 9)],
                            child: Text('Grid Item $index'),
                          );
                        },
                        childCount: 20,
                      ),
                    ),
                  ),
                  SliverToBoxAdapter(
                    child: SizedBox(
                      height: 100.0,
                      child: ListView.builder(
                        scrollDirection: Axis.horizontal,
                        itemCount: 10,
                        itemBuilder: (BuildContext context, int index) {
                          return Container(
                            width: 100.0,
                            margin: EdgeInsets.all(8.0),
                            color: Colors.orange[100 * (index % 9)],
                            child: Center(child: Text('Horizontal $index')),
                          );
                        },
                      ),
                    ),
                  ),
                  SliverPadding(
                    padding: EdgeInsets.symmetric(vertical: 16.0),
                    sliver: SliverFixedExtentList(
                      itemExtent: 50.0,
                      delegate: SliverChildBuilderDelegate(
                        (BuildContext context, int index) {
                          return Container(
                            alignment: Alignment.center,
                            color: Colors.lightBlue[100 * (index % 9)],
                            child: Text('Fixed List Item $index'),
                          );
                        },
                        childCount: 10,
                      ),
                    ),
                  ),
                  SliverList(
                    delegate: SliverChildBuilderDelegate(
                      (BuildContext context, int index) {
                        return Container(
                          alignment: Alignment.center,
                          margin: EdgeInsets.symmetric(vertical: 5.0),
                          height: 60.0,
                          color: Colors.purple[100 * (index % 9)],
                          child: Text('List Item $index'),
                        );
                      },
                      childCount: 10,
                    ),
                  ),
                ],
              ),
        );
    }
}

class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
  _SliverAppBarDelegate({
    required this.minHeight,
    required this.maxHeight,
    required this.child,
  });

  final double minHeight;
  final double maxHeight;
  final Widget child;

  @override
  double get minExtent => minHeight;

  @override
  double get maxExtent => maxHeight;

  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return SizedBox.expand(child: child);
  }

  @override
  bool shouldRebuild(_SliverAppBarDelegate oldDelegate) {
    return maxHeight != oldDelegate.maxHeight ||
        minHeight != oldDelegate.minHeight ||
        child != oldDelegate.child;
  }
}
 
โค้ดตัวอย่าง เราสามารถนำเอาไปเป็นแนวทางการปรับเลเอาท์หน้าแอปของเรา ให้สามารถแสดงออกมา
แบบได้หลากหลาย และยืดหยุ่น


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ



ทบทวนบทความที่แล้ว









เนื้อหาที่เกี่ยวข้อง









URL สำหรับอ้างอิง





คำแนะนำ และการใช้งาน

สมาชิก กรุณา ล็อกอินเข้าระบบ เพื่อตั้งคำถามใหม่ หรือ ตอบคำถาม สมาชิกใหม่ สมัครสมาชิกได้ที่ สมัครสมาชิก


  • ถาม-ตอบ กรุณา ล็อกอินเข้าระบบ
  • เปลี่ยน


    ( หรือ เข้าใช้งานผ่าน Social Login )







เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ