web analytics

Flutter : รู้จักกับ Sliver Delegate ตอนที่ 1

cove1

สวัสดีผู้อ่านทุกท่านครับ บล็อกนี้ผมจะพามาลองเล่นเกี่ยวกับเรื่อง Sliver Delegate ใน Flutter ครับ เป็น class ที่อยู่เบื้องหลังของ ListView/GridView ซึ่งหากเราไม่เข้าใจว่า Sliver delegate คืออะไร ทำงานอย่างไร  ก็อาจทำให้เราไม่สามารถใช้งาน Widget ที่ใช้งาน Sliver ได้ ดังนั้นบล็อกนี้มาเล่นกับ Sliver delegate กันเถอะ

 

Sliver คืออะไร

Sliver คือส่วนหนึ่งของ ScrollViewใน Flutter เป็นตัวอธิบายการทำงานของ ScrollView ซึ่งมันคือ class ที่อยู่เบื้องหลังการทำงานของ ListView กับ GridView ที่เราใช้กันใน Flutter นั่นเอง โดยเบื้องหลังการทำงานของ ListView/GridView ก็จะไปเรียกใช้ Sliver ต่างๆ ดังนั้นหากเราต้องการปรับแต่ง ListView/GridView ให้มีความซับซ้อนเราจำเป็นต้องเข้าใจหลักการทำงานของ Sliver

 

การใช้ Sliver

อย่างที่บอกในตอนต้นว่า sliver คือส่วนหนึ่งของ ScrollView ดังนั้น เราจะสามารถใช้มันร่วมกับ CustomScrollView ซึ่ง CustomScrollView สามารถเพิ่ม Sliver ได้หลายตัว สังเกตว่ามันรับค่าเป็น list

CustomScrollView( slivers: [
   ...
])

Sliver จะแบ่งเป็น 2 ส่วนใหญ่ๆ คือส่วนที่เป็น widget กับส่วนที่เป็น delegate

  1. Sliver delegate คือ ตัวอธิบายว่า จะใช้วิธีไหนในการแสดงผล โดยปกติจะมี 2 แบบคือ static กับ dynamic (ใน Flutter มักจะเป็นการใช้คำว่า builder แต่ผมขอเรียก dynamic ตามความเข้าใจนะ)
  2. Sliver Widget คือ การนำ Widget ที่เราเตรียมไว้ใน Sliver delegate มาใช้งานร่วมกับคุณสมบัติอื่นๆของ Widget

 

SliverList + SliverChildListDelegate

SliverList คือ Sliver Widget ประเภทหนึ่งการทำงานของมันคือ เอา Widget มาเรียงเป็น List โดยการเรียงแนวตั้งหรือแนวนอนอยู่ที่การกำหนดของ CustomScrollView วิธีการใช้ SliverList เราจะต้องกำหนด delegate ให้มันด้วย

ในตัวอย่าง ขอเริ่มจากแบบที่ง่ายที่สุด คือ SliverChildListDelegate
ซึ่ง SliverChildListDelegate มันจะเอา Widget มาต่อกันแบบหยาบๆโดยจะ build ทุก Widget ลงไปใน SliverList มี 100 widget ก็สร้างทั้งหมดแล้วใส่ลงไปใน List

class MyHomePage extends StatelessWidget{
  Widget build(BuildContext context) {
    return Scaffold(
        body: Container(
            padding: EdgeInsets.only(top: 24),
            child: CustomScrollView(slivers: [
              SliverList(
                  delegate: SliverChildListDelegate([
                Container(width: 50, height: 50, color: Colors.blue[100]),
                Container(width: 50, height: 50, color: Colors.blue[200]),
                Container(width: 50, height: 50, color: Colors.blue[300]),
                Container(width: 50, height: 50, color: Colors.blue[400]),
                Container(width: 50, height: 50, color: Colors.blue[500]),
              ])),
            ])));
  }
}

1

 

ListView

ซึ่งการใช้ SliverList + SliverChildListDelegate ตัว Flutter ก็ทำมาให้ใช้ง่ายๆ คือ ListView
สรุปก็คือ ListView = CustomScrollView + SliverList + SliverChildListDelegate 

ListView(children: <Widget>[
        Container(width: 50, height: 50, color: Colors.blue[100]),
        Container(width: 50, height: 50, color: Colors.blue[200]),
        Container(width: 50, height: 50, color: Colors.blue[300]),
        Container(width: 50, height: 50, color: Colors.blue[400]),
        Container(width: 50, height: 50, color: Colors.blue[500]),
      ])

 

 

SliverList + SliverChildBuilderDelegate

ยังคงอยู่กับ SliverList แต่ทีนี้ ลองมาใช้ delegate อีกตัว คือ SliverChildBuilderDelegate ซึ่ง delegate ตัวนี้ มันคือการทำให้ item ใน Scroll มีความ Dynamic หรือก็คือตัว Widget แต่ละอันสามารถ recycle ได้ โดยจะสร้างเฉพาะ Widget ที่แสดงผลบนหน้าจอเท่านั้น ทำให้ไม่เปลือง memory ในกรณีที่มีข้อมูลมากๆ

เช่น

              SliverList(
                delegate: SliverChildBuilderDelegate((context, index) {
                  return Container(height: 50,
                      color: Colors.green[index*100],
                      child: Center(child: Text("$index")));
                }, childCount: 9),
              ),

8

 

ListView.builder

ซึ่งการใช้ SliverList + SliverChildBuilderDelegate ตัว Flutter ก็ทำมาให้ใช้ง่ายๆโดยใช้ผ่าน ListView.builder นั่นเอง คนละอันกับ ListView() ก่อนหน้านี้นะ
ListView.builder = CustomScrollView + SliverList + SliverChildBuilderDelegate

      ListView.builder(itemBuilder: (context, index) {
        return Container(
            color: Colors.purple[index * 100],
            child: Center(child: Text("$index")));
      });

 

 

SliverGrid

มาถึงเรื่อง Grid กันบ้าง ซึ่งหากเราต้องการให้ Widget เรียงกันแบบตารางคล้าย Grid จะต้องใช้ SliverGrid
ซึ่ง SliverGrid  จะมี delegate ต้องกำหนด 2 ตัว คือ delegate ปกติกับ gridDelegate

  1. sliver delegate ก็คือ static หรือ dynamic แปลว่าให้เลือกเอาระหว่าง SliverChildListDelegate กับ SliverChildBuilderDelegate
  2. grid delegate คือ อธิบายว่าจะให้แสดงผลอย่างไร เช่น แถวละ 3 คอลัม หรือ คอลัมนึง 150px เท่านั้น

 

SliverGrid + SliverChildListDelegate

มาลองแบบแรก SliverGrid ใช้ delegate แบบ static (SliverChildListDelegate)
ทีนี้ก็ต้องมาเลือก gridDelegate มาดูว่ามีแบบไหนกันบ้าง

 

แบบกำหนดจำนวนแนวหลักสูงสุด

จะใช้ gridDelegate คือ SliverGridDelegateWithFixedCrossAxisCount โดยจะสามารถกำหนด ว่า 1 แถวมีได้สูงสุดกี่คอลัม
และมันจะขยายความกว้างให้เต็มอัตโนมัติ

crossAxisCount คือ จำนวนคอลัมภ์
childAspectRatio คือ อัตราส่วนของความกว้างแต่ความสูง

เช่น กำหนดว่า แถวนึงมีได้สูงสุด 4 อัน แต่ละอันความกว้างจะเป็น 1.5 เท่าของความสูง

              SliverGrid(
                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: 4, childAspectRatio: 1.5),
                  delegate: SliverChildListDelegate([
                    Container(color: Colors.red[100]),
                    Container(color: Colors.red[200]),
                    Container(color: Colors.red[300]),
                    Container(color: Colors.red[400]),
                    Container(color: Colors.red[500]),
                    Container(color: Colors.red[600]),
                  ])),

4

หรือจะใช้คำสั่งย่อก็ได้ คือ SliverGrid.count
ก็คือ SliverGrid.count = SliverGrid + SliverChildListDelegate + SliverGridDelegateWithFixedCrossAxisCount

              SliverGrid.count(crossAxisCount: 4,
                childAspectRatio: 1.5,children: <Widget>[
                Container(color: Colors.red[100]),
                Container(color: Colors.red[200]),
                Container(color: Colors.red[300]),
                Container(color: Colors.red[400]),
                Container(color: Colors.red[500])
              ])

 

 

แบบกำหนดความกว้างสูงสุด

GridDelegate อีกตัวคือ SliverGridDelegateWithMaxCrossAxisExtent มันคือตัวที่เราสามารถความกว้างให้กับคอลัมภ์ได้

maxCrossAxisExtent คือ ความกว้างสูงสุดในคอลัมภ์
childAspectRatio คือ อัตราส่วนความสูงต่อความกว้าง

เช่น กำหนดว่า แต่ละอันมีความกว้างสูงสุด 200px โดยกว้างจะเป็น 1.5 เท่าของความสูง

              SliverGrid(
                  gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
                      maxCrossAxisExtent: 200, childAspectRatio: 1.5),
                  delegate: SliverChildListDelegate([
                    Container(color: Colors.red[100]),
                    Container(color: Colors.red[200]),
                    Container(color: Colors.red[300]),
                    Container(color: Colors.red[400]),
                    Container(color: Colors.red[500]),
                    Container(color: Colors.red[600]),
                  ])),

5

 

ถ้าใช้ยาก ก็สามารถใช้แบบย่อได้ ด้วยคำสั่ง SliverGrid.extent
ก็คือ SliverGrid.extent = SliverGrid + SliverChildListDelegate + SliverGridDelegateWithMaxCrossAxisExtent

              SliverGrid.extent(maxCrossAxisExtent: 200,
                childAspectRatio: 1.5,children: <Widget>[
                Container(color: Colors.red[100]),
                Container(color: Colors.red[200]),
                Container(color: Colors.red[300]),
                Container(color: Colors.red[400]),
                Container(color: Colors.red[500])
              ])

 

GridView

GridView คือ Widget ที่นำ GridSliver มาใช้กับ CustomScrollView ให้เราใช้งานง่ายๆ จะเรียกว่าเป็นรูปย่อก็ได้

เช่น โดย gridView จะแค่ใส่ children แล้วก็กำหนด gridDelegate เท่านั้น

GridView(children: <Widget>[
      ...
   ], gridDelegate: ...)

GridView = CustomScrollView + SliverGrid + SliverChildListDelegate + [ gridDelegate ? ]

 

GridView.count

GrideView.countคือ รูปย่อของ CustomScrollView + SliverGrid + SliverChildListDelegate + SliverGridDelegateWithFixedCrossAxisCount เรียกว่าเป็น GridView ที่ระบุชัดเจนว่าใช้ gridDelegate แบบกำหนดจำนวนคอลัมภ์

GridView.count(crossAxisCount: 4  ,
   children: [
      ... 
   ])

GridView.extent =  CustomScrollView + SliverGrid + SliverChildListDelegate + SliverGridDelegateWithFixedCrossAxisCount 

 

GrideView.extent

GrideView.extent คือ รูปย่อของ CustomScrollView + SliverGrid + SliverChildListDelegate + SliverGridDelegateWithMaxCrossAxisExtent เรียกว่าเป็น GridView ที่ระบุชัดเจนว่าใช้ gridDelegate แบบกำหนดความกว้างนั่นเอง

GridView.extent =  CustomScrollView + SliverGrid + SliverChildListDelegate + SliverGridDelegateWithMaxCrossAxisExtent 

GridView.extent(maxCrossAxisExtent: 100,
    children: <Widget>[
       ...
    ])

 

 

SliverGrid + SliverChildBuilderDelegate

ลองเปลี่ยน delegate มาใช้แบบ dynamic กัน จริงๆก็แค่เปลี่ยน delegate มาใช้ SliverChildBuilderDelegate แทน SliverChildListDelegate แค่นี้ Grid ของเราก็จะทำงานแบบ dynamic แล้วละ ส่วน gridDelegate ก็เลือกเอาว่า จะกำหนดแบบไหนระหว่าง จากจำนวนคอลัม หรือความกว้าง

ตัวอย่างนี้ กำหนดให้ Grid มี 10 อัน และแสดงผลแบบกำหนดคอมลัมภ์ ให้มีคอลัมสูงสุด 5 คอลัมภ์ โดยใช้ delegate แบบ dynamic

             SliverGrid(
                  delegate: SliverChildBuilderDelegate((context, index) {
                    return Container(
                        color: Colors.purple[index*100],
                        child: Center(child: Text("$index")));
                  }, childCount: 10),
                  gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                      crossAxisCount: 5,mainAxisSpacing: 2,crossAxisSpacing: 2))

9

 

GridView.builder

GridView.builder คือรูปย่อของ GridView ที่ใช้ delegate แบบ dynamic (SliverChildBuilderDelegate) ซึ่งก็จะต้องกำหนด gridDelegate ว่าจะใช้แบบกำหนดจำนวนคอลัมภ์หรือ กำหนดความกว้าง

GridView.builder(gridDelegate: ..., itemBuilder: (context ,index){
      return ...
    })

GridView.builder = CustomScrollView + SliverGrid + SliverChildBuilderDelegate + [ gridDelegate ? ]

 

GridView.custom

GridView.custom คือรูปย่อของ SliverGrid ที่ต้องกำหนดทั้ง delegate และ gridDelegate
ดังนั้น การจะใช้คำสั่งนี้ได้จึงต้องเข้าใจ ว่า delegate และ gridDelegate คืออะไรนั่นเอง

GridView.custom(childrenDelegate: ... ,gridDelegate: ... );

GridView.builder = CustomScrollView + SliverGrid + [ delegate ? ] + [ gridDelegate ? ]

 

สรุป

ในบล็อกนี้ ผู้อ่านน่าจะได้เรียนรู้เกี่ยวกับ Sliver Delegate แบบต่างๆที่ใช้ใน Flutter ซึ่งเป็นพื้นฐานของการทำ ListView , GridView และการใช้รูปย่อต่างๆ รวมไปถึงที่มา หลักการทำงานของ Sliver ด้วย

ตารางสรุป Sliver

gridDelegate
Sliver Delegate
SliverChildListDelegate SliverChildBuilderDelegate undefine
SliverList ListView ListView.builder
SiverGrid
SliverGridDelegateWithFixedCrossAxisCount GridView.count GridView.builder
SliverGridDelegateWithMaxCrossAxisExtent GridView.extent GridView.builder
undefine GridView GridView.builder GridView.custom

 

โปรดติดตามตอนต่อไป

ในตอนหน้าจะพาไปลองเล่น Sliver แบบอื่นๆนอกจาก SliverList กับ SliverGrid ครับ

Flutter : รู้จักกับ Sliver Delegate ตอนที่ 2