เนื้อหานี้มาดูเกี่ยวกับการใช้งาน tab ใน flutter ซึ่งเป็นรูปแบบ
การใช้งานที่พบเห็นได้บ่อยใน app ใน flutter เราสามารถกำหนด
และเรียกใช้งาน tab ด้วยขั้นตอนง่ายๆ ทำให้เราสะดวกในการนำ
มาจัดรูปแบบเลเอาท์ app ของเรา
*ใช้เนื้อหาต่อเนื่องจากบทความ http://niik.in/961
การใช้งาน TabController
TabController จะทำหน้าที่ในการประสานการทำงานของ tab ที่เลือก กับเนื้อหาของ
tab นั้นให้เป็นไปอย่างสอดคล้องกัน (จัดการระหว่าง TabBar กับ TabBarView ให้สัมพันธ์กัน)
เช่น เลือก tab แรก ก็แสดงเนื้อหาของ tab แรก แบบนี้เป็นต้น เราสามารถสร้าง TabController
แบบกำหนดเอง หรือจะใช้งาน DefaultTabController widget ก็ทำได้ง่ายๆ ในที่นี้
จะแนะนำทั้งสองรูปแบบ
การสร้าง Tab โดยใช้ DefaultTabController widget
การใช้งาน DefaultTabController widget เพื่อสร้าง tab มีรูปแบบการกำหนดง่ายๆ ดังนี้
DefaultTabController( length: 4, // จำนวน tab และเนื้อหา tab ที่ต้องการแสดง child: // ครอบ Scaffold widget );
มาดูส่วนของ _HomeState ก่อนใช้งาน DefaultTabController
class _HomeState extends State<Home> { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Home'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text('Home Screen'), ], ) ), ); } }
เมื่อกำหนดใช้งาน DefaultTabController จะได้เป็นดังนี้
class _HomeState extends State<Home> { @override Widget build(BuildContext context) { return DefaultTabController( // ใช้งาน DefaultTabController length: 4, // กำหนดจำนวน tab child: Scaffold( appBar: AppBar( bottom: const TabBar( // ส่วนของ tab tabs: [ Tab(icon: Icon(Icons.feed)), Tab(icon: Icon(Icons.favorite_sharp)), Tab(icon: Icon(Icons.thumb_up)), Tab(icon: Icon(Icons.announcement)), ], ), title: Text('Home'), ), body: const TabBarView( // ส่วนของเนื้อหา tab children: [ Center(child: Text('Tab 1111')), Center(child: Text('Tab 2222')), Center(child: Text('Tab 3333')), Center(child: Text('Tab 4444')), ], ), ), ); } }
ผลลัพธ์ที่ได้
การสร้าง TabBar
จะเห็นว่าเราทำการสร้าง TabBar ในส่วนของ bottom property ของ AppBar
bottom: const TabBar( // ส่วนของ tab tabs: [ Tab(icon: Icon(Icons.feed)), Tab(icon: Icon(Icons.favorite_sharp)), Tab(icon: Icon(Icons.thumb_up)), Tab(icon: Icon(Icons.announcement)), ], ),
สามารถกำหนดเป็นไอคอนอย่างเดียว ข้อความอย่างเดียว หรือกำหนดทั้งสองอย่าง ก็ได้เช่น
bottom: const TabBar( // ส่วนของ tab tabs: [ Tab(icon: Icon(Icons.feed),text:'Tab 1'), Tab(icon: Icon(Icons.favorite_sharp),text:'Tab 2'), Tab(icon: Icon(Icons.thumb_up),text:'Tab 3'), Tab(icon: Icon(Icons.announcement),text:'Tab 4'), ], ),
ผลลัพธ์ที่ได้
การสร้าง TabBarView
สำหรับการใช้งาน TabBarView เราก็กำหนดในส่วนของ body property ของ Scaffold
ในตัวอย่างจะกำหนดแสดงแค่ข้อความตรงกลางจอเพื่อจำลองการแสดงผล
body: const TabBarView( // ส่วนของเนื้อหา tab children: [ Center(child: Text('Tab 1111')), Center(child: Text('Tab 2222')), Center(child: Text('Tab 3333')), Center(child: Text('Tab 4444')), ], ),
เราสามารถประยุกต์โดยสร้างหน้า screen เพื่อมาแสดงเป็นเนื้อหาของ tab ที่ต้องการได้ ซึ่งถ้ามองแล้ว
ก็จะคล้ายๆ กับการใช้งาน BottomNavigationBar ของเมนูด้านล่าง app
ดูตัวอย่างการแสดงเนื้อหา app จาก screen หน้าอื่นๆ
body: const TabBarView( // ส่วนของเนื้อหา tab children: [ About(), Profile(), Contact(), Settings(), ], ),
ผลลัพธ์ที่ได้
โดยทั่วไป เมื่อใช้งาน DefaultTabController เรามักจะไม่ค่อยต้องการจัดการกับ TabController มากนัก
แต่อย่างไรก็ตาม หากต้องการให้สามารถใช้งาน TabController ทำงานร่วมกับการใช้งาน DefaultTabController
ก็สามารถกำหนดได้ดังนี้
class _HomeState extends State<Home> { @override Widget build(BuildContext context) { return DefaultTabController( // ใช้งาน DefaultTabController length: 4, // กำหนดจำนวน tab child: Builder(builder: (BuildContext context) { return Scaffold( // ........ ส่วนอื่นๆ เพิ่มเติม..... ); }), ); } }
เราใช้ Builder ซึ่งเป็น stateless widget ที่มีคำสั่ง build สำหรับสร้าง widget child ขึ้นมาใช้งาน
เราจะกำหนดการเรียกใช้งาน TabController ก่อนการ return Scaffold ดังนี้
class _HomeState extends State<Home> { @override Widget build(BuildContext context) { return DefaultTabController( // ใช้งาน DefaultTabController length: 4, // กำหนดจำนวน tab child: Builder(builder: (BuildContext context) { // เรียกใช้งาน TabController final TabController tabController = DefaultTabController.of(context)!; tabController.addListener(() { // ตรวจจับการทำงาน if (!tabController.indexIsChanging) { // มีการเปลี่ยน tab // กำหนดคำสั่งตรงนี้ // tabController.animateTo(3); ไปยัง tab ที่กำหนด // tabController.index ค่า index ที่เปลี่ยน // tabController.previousIndex ค่า index ก่อนเปลี่ยน print(tabController.index); print(tabController.previousIndex); } }); return Scaffold( // ........ ส่วนอื่นๆ เพิ่มเติม..... ); }), ); } }
เราสามารถกำหนดเงื่อนไขการทำงานต่างๆ เมื่อเปลี่ยน tab ได้ ค่า index ของ tab จะเริ่มนับจาก 0
สำหรับ tab แรก และ tab สุดท้าย มี index คำนวณได้จากค่า tabController.length - 1
เราสามารถทำคำสั่งให้ tab เลื่อนไปยังค่าที่ต้องการได้ผ่านการเรียกใช้งาน TabController ตัวอย่าง
เช่น เราสร้างปุ่มในเนื้อหาของ tab แรก แล้วเมื่อกดปุ่มนั้น ก็ให้เลื่อนไปยัง tab ที่ 4 ตามตัวอย่างดังนี้ได้
class _HomeState extends State<Home> { @override Widget build(BuildContext context) { return DefaultTabController( // ใช้งาน DefaultTabController length: 4, // กำหนดจำนวน tab child: Builder(builder: (BuildContext context) { // เรียกใช้งาน TabController final TabController tabController = DefaultTabController.of(context)!; tabController.addListener(() { // ตรวจจับการทำงาน if (!tabController.indexIsChanging) { // มีการเปลี่ยน tab // กำหนดคำสั่งตรงนี้ // tabController.animateTo(3); ไปยัง tab ที่กำหนด // tabController.index ค่า index ที่เปลี่ยน // tabController.previousIndex ค่า index ก่อนเปลี่ยน print(tabController.index); print(tabController.previousIndex); } }); return Scaffold( appBar: AppBar( bottom: const TabBar( // ส่วนของ tab tabs: [ Tab(icon: Icon(Icons.feed),text:'Tab 1'), Tab(icon: Icon(Icons.favorite_sharp),text:'Tab 2'), Tab(icon: Icon(Icons.thumb_up),text:'Tab 3'), Tab(icon: Icon(Icons.announcement),text:'Tab 4'), ], ), title: Text('Home'), ), body: TabBarView( // ส่วนของเนื้อหา tab children: [ Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Tab 1111'), ElevatedButton( // ปุ่มสำหรับทดสอบ onPressed: () { // เลื่อนไปยัง index 3 ซึ่งก็คือค่า index ของ tab ที่ 4 tabController.animateTo(3); }, child: const Text('Go to Tab 4'), ), ], ), ), Center(child: Text('Tab 2222')), Center(child: Text('Tab 3333')), Center(child: Text('Tab 4444')), ], ), ); }), ); } }
ผลลัพธ์ที่ได้
การสร้าง Tab โดยใช้ TabController แบบกำหนดเอง
กรณีที่เราไม่ได้ใช้งาน DefaultTabController widget และต้องการกำหนดรูปแบบการใช้งาน
TabController แบบกำหนดเอง เพื่อให้สามารถจัดการ tab ได้ตามต้องการ สามารถทำได้ดังนี้
class _HomeState extends State<Home> with SingleTickerProviderStateMixin { // กำหนดตัวแปร สำหรับเป็น ใช้งาน TabController // late keyword เป็นส่วนของการกำหนด ภาษา dart ที่ป้องกัน Null safety // เป็นการบอกว่า เราจะกำหนดค่าให้กับตัวแปรนี้แน่นอน จะไม่ปล่อยให้เป็น null late TabController _tabController; @override void initState() { super.initState(); _tabController = TabController(length: 4,vsync: this); } @override void dispose() { _tabController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( bottom: TabBar( // ส่วนของ tab controller: _tabController, // กำหนดการเรียกใช้งาน TabController tabs: [ Tab(icon: Icon(Icons.feed),text:'Tab 1'), Tab(icon: Icon(Icons.favorite_sharp),text:'Tab 2'), Tab(icon: Icon(Icons.thumb_up),text:'Tab 3'), Tab(icon: Icon(Icons.announcement),text:'Tab 4'), ], ), title: Text('Home'), ), body: TabBarView( // ส่วนของเนื้อหา tab controller: _tabController, // กำหนดการเรียกใช้งาน TabController children: [ Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Tab 1111'), ElevatedButton( // ปุ่มสำหรับทดสอบ onPressed: () { // เลื่อนไปยัง index 3 ซึ่งก็คือค่า index ของ tab ที่ 4 _tabController.animateTo(3); }, child: const Text('Go to Tab 4'), ), ], ), ), Center(child: Text('Tab 2222')), Center(child: Text('Tab 3333')), Center(child: Text('Tab 4444')), ], ), ); } }
ผลลัพธ์ที่ได้ก็จะเหมือนตัวอย่างล่าสุดของแบบที่ใช้ DefaultTabController widget
สิ่งที่เพิ่มเข้ามา เมื่อมีการใช้งาน TabController แบบกำหนดเอง
- ส่วนของการใช้งาน State เราต้องกำหนดเรียกใช้ Mixin เพิ่มเข้าไปดังนี้
// class _HomeState extends State<Home> { // ค่าเดิม class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
- กำหนดตัวแปร
late TabController _tabController;
- กำหนดการควบคุม และค่าเริ่มต้นใน State
@override void initState() { super.initState(); // กำหนดค่าเริ่มต้น _tabController = TabController(length: 4,vsync: this); } @override void dispose() { _tabController.dispose(); super.dispose(); }
- ส่วนสุดท้ายกำหนด controller ให้กับ TabBar และ TabBarView
controller: _tabController,
เท่านี้เราก็สามารถจัดการกับ tab ผ่าน _tabController ได้ เหมือนตัวอย่างการกำหนดที่ปุ่ม
เมื่อกดที่ปุ่มก็ให้ไปที่ tab ที่ 4
_tabController.animateTo(3);
เนื้อหาเกี่ยวกับการใช้งาน tab ก็มีประมาณนี้ สามารถนำไปประยุกต์ใช้งาน และทำความ
เข้าใจเพิ่มเติม ดูเกี่ยวกับคำสั่งการทำงานอื่นๆ ได้ที่ TabController