การใช้ Slivers เพื่อสร้าง Scrollable Layouts ที่ซับซ้อน
ในบทความก่อนหน้า เราได้เรียนรู้เกี่ยวกับการสร้าง UI ที่กำหนดเองด้วย CustomPainter ในบทความนี้ เราจะมาเรียนรู้เกี่ยวกับ Slivers ซึ่งเป็นเครื่องมือที่มีประสิทธิภาพในการสร้าง Scrollable Layouts ที่มีความยืดหยุ่นสูงใน Flutter หากคุณเคยต้องการสร้าง layout ที่มี AppBar ที่สามารถยุบตัวเมื่อเลื่อนหน้าจอ หรือ Grid ที่ปรับขนาดตามความเหมาะสม Slivers คือคำตอบ
Slivers คืออะไร?
Slivers เป็นส่วนของ Scrollable Area ที่สามารถ Scroll ได้ โดยแต่ละ Sliver จะกำหนดลักษณะการ Scroll และการแสดงผลของตัวเอง ทำให้เราสามารถรวม Sliver หลายๆ ตัวเข้าด้วยกันเพื่อสร้าง Scrollable Layouts ที่ซับซ้อนได้
ลองนึกภาพว่า Scrollable Area คือผ้าผืนใหญ่ และ Slivers คือชิ้นส่วนต่างๆ ที่เรานำมาปะติดปะต่อกันบนผ้าผืนนั้น แต่ละชิ้นส่วน (Sliver) มีหน้าที่และลักษณะที่แตกต่างกันออกไป
องค์ประกอบหลักของ Slivers
Slivers มีหลายประเภท แต่ประเภทที่ใช้บ่อยมีดังนี้:
- SliverAppBar: AppBar ที่สามารถยุบตัวหรือขยายตัวเมื่อ Scroll
- SliverList: List ที่ Scroll ได้
- SliverGrid: Grid ที่ Scroll ได้
- SliverFixedExtentList: List ที่ Scroll ได้ โดยแต่ละ Item มีความสูงเท่ากัน
- SliverPadding: เพิ่ม Padding ให้กับ Sliver อื่น
- SliverToBoxAdapter: แปลง Widget ธรรมดาให้เป็น Sliver
ในบทความนี้ เราจะเน้นไปที่ SliverAppBar, SliverList และ SliverGrid
SliverAppBar: AppBar ที่ยืดหยุ่น
SliverAppBar เป็น Widget ที่ใช้สร้าง AppBar ที่สามารถยุบตัวหรือขยายตัวเมื่อ Scroll หน้าจอ ทำให้ UI ดูน่าสนใจและมีประสิทธิภาพมากขึ้น
คุณสมบัติที่สำคัญของ SliverAppBar:
- `expandedHeight`: ความสูงของ AppBar เมื่อขยายเต็มที่
- `flexibleSpace`: Widget ที่แสดงผลเมื่อ AppBar ขยายเต็มที่ มักใช้กับ
FlexibleSpaceBar - `pinned`: กำหนดว่า AppBar จะติดอยู่ด้านบนสุดของหน้าจอหรือไม่ เมื่อ Scroll
- `floating`: กำหนดว่า AppBar จะปรากฏขึ้นทันทีเมื่อ Scroll ขึ้น (แม้จะยังไม่ถึงด้านบนสุด) หรือไม่
- `snap`: ใช้ร่วมกับ `floating` เพื่อให้ AppBar เด้งขึ้นหรือลงเมื่อปล่อยนิ้ว
ตัวอย่างการใช้งาน SliverAppBar:
import 'package:flutter/material.dart';
class SliverAppBarExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
expandedHeight: 200.0,
pinned: true,
floating: false,
snap: false,
flexibleSpace: FlexibleSpaceBar(
title: Text('SliverAppBar Example'),
background: Image.network(
'https://via.placeholder.com/350x150',
fit: BoxFit.cover,
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
height: 50,
color: index.isEven ? Colors.grey[300] : Colors.white,
child: Center(
child: Text('Item $index'),
),
);
},
childCount: 50,
),
),
],
),
);
}
}
ในตัวอย่างนี้ เราสร้าง SliverAppBar ที่มีความสูง 200.0 และมี FlexibleSpaceBar เป็นพื้นหลัง เมื่อ Scroll หน้าจอ SliverAppBar จะยุบตัวลง และเมื่อ Scroll ขึ้น SliverAppBar จะขยายตัวกลับมา
SliverList: List ที่ Scroll ได้
SliverList เป็น Sliver ที่ใช้แสดงรายการข้อมูลที่ Scroll ได้ คล้ายกับ ListView แต่ทำงานร่วมกับ Slivers อื่นๆ ได้อย่างราบรื่น
SliverList ใช้ SliverChildDelegate ในการสร้าง Widget แต่ละตัวใน List มีสองประเภทหลักคือ SliverChildBuilderDelegate และ SliverChildListDelegate
- `SliverChildBuilderDelegate`: สร้าง Widget ตาม Index ที่กำหนด เหมาะสำหรับ List ที่มีข้อมูลจำนวนมาก
- `SliverChildListDelegate`: สร้าง Widget จาก List ของ Widget ที่มีอยู่แล้ว เหมาะสำหรับ List ที่มีข้อมูลจำนวนน้อย
ตัวอย่างการใช้งาน SliverList:
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
height: 50,
color: index.isEven ? Colors.grey[300] : Colors.white,
child: Center(
child: Text('Item $index'),
),
);
},
childCount: 50,
),
),
ในตัวอย่างนี้ เราสร้าง SliverList ที่มี 50 Items โดยใช้ SliverChildBuilderDelegate
SliverGrid: Grid ที่ Scroll ได้
SliverGrid เป็น Sliver ที่ใช้แสดงข้อมูลในรูปแบบ Grid ที่ Scroll ได้ คล้ายกับ GridView แต่ทำงานร่วมกับ Slivers อื่นๆ ได้อย่างราบรื่น
SliverGrid ต้องใช้ SliverGridDelegate ในการกำหนดลักษณะการแสดงผลของ Grid มีสองประเภทหลักคือ SliverGridDelegateWithFixedCrossAxisCount และ SliverGridDelegateWithMaxCrossAxisExtent
- `SliverGridDelegateWithFixedCrossAxisCount`: กำหนดจำนวน Column ใน Grid
- `SliverGridDelegateWithMaxCrossAxisExtent`: กำหนดขนาดสูงสุดของแต่ละ Item ใน Grid
ตัวอย่างการใช้งาน SliverGrid:
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
margin: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.blue[100 * (index % 9)],
borderRadius: BorderRadius.circular(8.0),
),
child: Center(
child: Text('Item $index'),
),
);
},
childCount: 20,
),
),
ในตัวอย่างนี้ เราสร้าง SliverGrid ที่มี 2 Columns โดยใช้ SliverGridDelegateWithFixedCrossAxisCount
การรวม Slivers เข้าด้วยกัน
Slivers มักจะถูกใช้งานร่วมกันภายใน CustomScrollView เพื่อสร้าง Scrollable Layouts ที่ซับซ้อน CustomScrollView จะรับ List ของ Slivers เป็น Input และจัดการการ Scroll ของ Slivers เหล่านั้น
ตัวอย่างการใช้งาน CustomScrollView ร่วมกับ Slivers:
CustomScrollView(
slivers: <Widget>[
SliverAppBar(
expandedHeight: 200.0,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
title: Text('Sliver Example'),
background: Image.network(
'https://via.placeholder.com/350x150',
fit: BoxFit.cover,
),
),
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return ListTile(
title: Text('Item $index'),
);
},
childCount: 10,
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
margin: EdgeInsets.all(8.0),
color: Colors.green[100 * (index % 9)],
child: Center(
child: Text('Grid Item $index'),
),
);
},
childCount: 9,
),
),
],
)
ในตัวอย่างนี้ เราสร้าง CustomScrollView ที่มี SliverAppBar, SliverList และ SliverGrid รวมกัน
กรณีใช้งานจริง
- หน้า Profile: ใช้
SliverAppBarสำหรับ Header ที่ยุบตัวเมื่อ Scroll และใช้SliverListหรือSliverGridสำหรับแสดงข้อมูล Profile - หน้า News Feed: ใช้
SliverAppBarสำหรับ Header และใช้SliverListสำหรับแสดง News Feed - หน้า Product Listing: ใช้
SliverAppBarสำหรับ Header และใช้SliverGridสำหรับแสดง Product
ข้อผิดพลาดที่พบบ่อย
- ลืมใส่
CustomScrollView: Slivers ต้องอยู่ภายในCustomScrollViewเสมอ - ไม่กำหนด
expandedHeightให้กับSliverAppBar: ทำให้ AppBar ไม่สามารถยุบตัวได้ - ใช้
ListViewหรือGridViewแทนSliverListหรือSliverGrid: ทำให้เกิดปัญหาในการทำงานร่วมกับ Slivers อื่นๆ - การจัดการ State ที่ไม่ดี: การเปลี่ยนแปลง State ภายใน Slivers อาจทำให้เกิดปัญหาในการ Rebuild Widget
สรุป
Slivers เป็นเครื่องมือที่มีประสิทธิภาพในการสร้าง Scrollable Layouts ที่ซับซ้อนใน Flutter ด้วยการรวม Slivers ประเภทต่างๆ เข้าด้วยกัน เราสามารถสร้าง UI ที่มีความยืดหยุ่นและน่าสนใจได้ หวังว่าบทความนี้จะเป็นประโยชน์ในการเริ่มต้นใช้งาน Slivers ในโปรเจกต์ Flutter ของคุณ
ในบทความถัดไป เราจะมาเรียนรู้เกี่ยวกับการสร้าง Custom Transition Animations เพื่อเพิ่มความน่าสนใจให้กับแอป Flutter ของคุณ