PETEPITTAWAT.DEV.

Flutter: เพิ่ม Pull to Refresh ให้กับ ListView

Cover Image for Flutter: เพิ่ม Pull to Refresh ให้กับ ListView
Pittawat Taveekitworachai
Pittawat Taveekitworachai

หนึ่งในรูปแบบการใช้งานที่เรามักจะเห็นได้บ่อยสำหรับการโหลดข้อมูลใหม่ ๆ จากฝั่งผู้ใช้งาน โดยเฉพาะบน Android หรือแอปพลิเคชันที่ทำตาม Material Guideline ก็คือการ Swipe to Refresh นั่นเอง โดยทั่วไปเรามักจะมี List ซึ่งเราสามารถดึงลงมาได้มากกว่าปกติจากบนสุดแล้วปล่อย โดยระหว่างนั้นก็จะมี Indicator มาหมุนโชว์ให้ดูว่ากำลังดาวน์โหลดข้อมูลใหม่ ๆ เมื่อเสร็จแล้วก็จะหายไปและมีข้อมูลใหม่มาโชว์ที่ List นั่นเอง

Flutter ซึ่งสนับสนุน Material Design แบบ 1st class ก็แน่นอนว่าได้เตรียม Widget มาให้เราใช้งานเช่นกัน โดย Widget ตัวนั้น ก็ชื่อ RefreshIndicator นั่นเอง


RefreshIndicator

RefreshIndicator เป็น Widget ที่เราสามารถนำไป Wrap Widget ใด ๆ ก็ตามที่สามารถ Scroll ได้ (Scrollable) และจะเพิ่มพฤติกรรมที่เมื่อเกิดการ Over Scroll (Scroll เกิน Content ที่มี) จะทำการ Trigger onRefresh และโชว์ Animation สวย ๆ ขึ้นมาเพื่อบอกให้ผู้ใช้งานรู้ว่าได้มีการ Trigger แล้ว โดย onRefresh นั้นคาดหวังฟังก์ชันที่ไม่รับข้อมูลนำเข้าแต่ข้อมูลส่งออกต้องเป็น Future<void> หรือก็คือไม่คาดหวังข้อมูลส่งออกแต่ฟังก์ชันนั้นต้องมีพฤติกรรมของ Future เนื่องจากโดยทั่วไปเมื่อเกิดการ Refresh ขึ้นเรามักเรียกใช้งานฟังก์ชันที่ต้องมีการดึงข้อมูลจาก Network หรือไฟล์ที่ใช้เวลานานนั่นเอง สำหรับวิธีที่ง่ายที่สุดก็คือการระบุให้ฟังก์ชันนั้นเป็น async ฟังก์ชัน โดยการเพิม่ Keyword async เข้าไปนั่นเอง

ตัวอย่างโค้ด

// ...
@override
Widget build(BuildContext context) {
	Future<void> onPullToRefresh() async {
		await Future.delayed(Duration(milliseconds: 500));
		setState(() {
			if (colors[0] == colorForSwap1[0]) {
				colors = colorForSwap2;
			} else {
				colors = colorForSwap1;
			}
		});
	}
    
	return Scaffold(
            // ...
            RefreshIndicator(
                onRefresh: onPullToRefresh,
                child: ListView.builder(
                    padding: const EdgeInsets.all(8),
                    itemCount: 3,
                    itemBuilder: (context, index) {
                        return Container(
                            height: 50,
                            color: colors[index],
                            child: Center(child: Text('$index')),
                        );
                    },
                ),
            )
            // ...
	);
}
// ...

จากโค้ดจะเห็นได้ว่าใน onPullToRefresh นั้นถูกใช้เพื่อเป็นฟังก์ชันสำหรับ onRefresh ใน RefreshIndicator โดยมี Future.delayed(...) เพื่อจำลองเวลาที่ใช้ในการดึงข้อมูลจาก Network ก่อนจะมีการเปลี่ยนแปลงข้อมูลใน List เป็นข้อมูลชุดใหม่นั่นเอง


Sidenote

สำหรับบางคนที่นำ RefreshIndicator ไปใช้แล้วไม่มีอะไรเกิดขึ้นทั้ง ๆ ที่ส่ง Argument ที่จำเป็นไปหมดแล้ว อาจเป็นไปได้ว่า Scrollable Widget ที่เป็น Child ของ RefreshIndicator นั้นไม่ได้รองรับการ Over Scroll เช่น กรณีที่ข้อมูลของเราอาจไม่เยอะจนเกิน Viewport ซึ่งเราสามารถแก้ไขได้โดยการ เพิ่ม Properties physics: **const** AlwaysScrollableScrollPhysics(), เข้าไปใน Child Widget ตัวนั้น (Scrollable) เพื่อการันตีการ Scroll ได้เสมอ

// ...
RefreshIndicator(
	onRefresh: someFunction,
	child: ListView(
		physics: const AlwaysScrollableScrollPhysics(),
		children: // ...
	)
),
// ...

สำหรับคนที่ต้องการตัวอย่างโค้ดแบบเต็ม สามารถตามไปดูได้ที่ GitHub


📚 Hope you enjoy reading! 📚