นำทาง Flutter ด้วย TabBar กันเถอะ

Patiphan Suwanich
5 min readMay 6, 2020

--

มาต่อกับชาเลนจ์ จากเด็กจบใหม่ที่ต้องติดบ้านเพราะโควิดสู่นักพัฒนา Flutter #4

TabBar (Top)

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

เริ่มแรกให้เราเตรียมของที่จะวางไว้ที่ TabBar ก่อน

class Choice {
const Choice({this.title, this.icon});

final String title;
final IconData icon;
}
const List<Choice> choices = const <Choice>[
const Choice(title: 'Car', icon: Icons.directions_car),
const Choice(title: 'Bicycle', icon: Icons.directions_bike),
const Choice(title: 'Boat', icon: Icons.directions_boat),
const Choice(title: 'Bus', icon: Icons.directions_bus),
const Choice(title: 'Train', icon: Icons.directions_railway),
const Choice(title: 'Walk', icon: Icons.directions_walk),
];

2.ในการสร้าง Tabbar สิ่งที่ต้องประกาศคือ TabControlle เป็นตัวกำหนดจำนวน tab ที่มีอยู่ทั้งหมดโดย child จะต้องมีจำนวนเท่ากับ length เท่านั้นเพื่อที่จะสร้าง tab ไม่ขาดไม่เกิน

DefaultTabController(
// The number of tabs / content sections to display.
length: choices.length, // int
child: // Complete this code in the next step.
);

3.สร้าง tabs หรือก็คือใส่ชื่ออ้างอิงในแต่ละ tabs

DefaultTabController(
length: choices.length,
child: Scaffold(
appBar: AppBar(
bottom: TabBar(
// tabs: [
// Tab(icon: Icon(Icons.directions_car)),
// Tab(icon: Icon(Icons.directions_transit)),
// Tab(icon: Icon(Icons.directions_bike)),
// ],

tabs: choices.map((Choice choice) {
return Tab(
text: choice.title,
icon: Icon(choice.icon),
);
}).toList(),
),
),
),
);

4. ใส่เนื้อหาในแต่ละ tabs ด้วย TabBarView

TabBarView(
// children: [
// Icon(Icons.directions_car),
// Icon(Icons.directions_transit),
// Icon(Icons.directions_bike),
// ],

children: choices.map((Choice choice) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(choice.icon, size: 128.0),
Text(choice.title),
],
),
);
}).toList(),
);

เพิ่มเติม

initialIndex

เป็นการกำหนดว่าเมื่อเข้ามาครั้งแรกจะโชว์ tab ที่เท่าไร

initialIndex: 3,

isScrollable

ถ้าเกิดว่าเราใส่ tabs เยอะไปจะทำให้ tab ถูกบีบจนไม่สวยหรือหน้าแสดงผลไม่ได้
ผู้อ่านสามารถทำให้ tabs scroll ได้นะครับ โดยผมจะทำการ custom tab ก่อนเผื่อให้ tabs มันเกินหน้าก่อน แล้วใส่ true ใน isScrollable ก็จะสามารถเลื่อนได้แล้วครับ

isScrollable: true,
tabs: choices.map((Choice choice) {
return Tab(
child: Row(
children: <Widget>[
Icon(choice.icon),
Container(
margin: EdgeInsets.only(left: 8),
child: Text(choice.title),
)
],
),

);
}).toList(),

unselectedLabelColor

ใส่สีให้ tabs ที่ user ไม่ได้เลือกอยู่ครับ

unselectedLabelColor: Colors.black

indicatorWeight

คือเพิ่มขนาด bar ที่เลือกครับ

indicatorWeight: 10

indicatorColor

คือเปลียนสี bar ที่เลือกครับ

indicatorColor: Colors.yellow

labelColor

labelColor: Colors.redAccent

onTap

เป็นตัวดักว่า user กดปุ่มไหนครับ

onTap: (index) {
print("index: ${index}");
}

TabBar (Downstairs)

โค้ดเหมือน TabBar (Top) ทุกอย่างเลยครับแค่วางไว้ที่ bottomNavigationBar

SafeArea

จะทำให้ tabs เราไม่อยู่ต่ำมากไปใน iphone รุ่นใหม่ๆครับ

AppBar

AppBar ไม่ได้ใส่ได้เพียง title เท่านั้นแต่ยังสามารถใส่ปุ่มและ dropdown menu ได้ด้วย

เริ่มแรกให้เราเตรียมของที่จะวางไว้ที่ dropdown menu ซึ่งเป็นก้อนเดียวกับที่ผมใช้กับ TabBar

class Choice {
const Choice({this.title, this.icon});
final String title;
final IconData icon;
}
const List<Choice> choices = const <Choice>[
const Choice(title: 'Car', icon: Icons.directions_car),
const Choice(title: 'Bicycle', icon: Icons.directions_bike),
const Choice(title: 'Boat', icon: Icons.directions_boat),
const Choice(title: 'Bus', icon: Icons.directions_bus),
const Choice(title: 'Train', icon: Icons.directions_railway),
const Choice(title: 'Walk', icon: Icons.directions_walk),
];

ผมจะสือบทอด StatefulWidget เพราะว่ามันจะตรวจจับว่าผู้ใช้คลิกแล้วไปเปลี่ยนข้อมูลนั้นเอง โดย BasicAppBarSample จะเป็น state

// This app is a stateful, it tracks the user's current choice.
class BasicAppBarSample extends StatefulWidget {
@override
_BasicAppBarSampleState createState() => _BasicAppBarSampleState();
}

หลังจากนั้นก็สร้าง _BasicAppBarSampleState เพื่อตรวจจับว่ามีการ setState หรือไม่ถ้ามีมันจะไปบอกให้ BasicAppBarSample ทำการ createState ใหม่ครับ

class _BasicAppBarSampleState extends State<BasicAppBarSample> {
Choice _selectedChoice = choices[0]; // The app's "state".

void _select(Choice choice) {
// Causes the app to rebuild with the new _selectedChoice.
setState(() {
_selectedChoice = choice;
});
}

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
// centerTitle: true,
title: const Text('Basic AppBar'),
actions: <Widget>[
// action button
IconButton(
icon: Icon(choices[0].icon),
onPressed: () {
_select(choices[0]);
},
),
// action button
IconButton(
icon: Icon(choices[1].icon),
onPressed: () {
_select(choices[1]);
},
),
// overflow menu
PopupMenuButton<Choice>(
onSelected: _select,
itemBuilder: (BuildContext context) {
return choices.skip(2).map((Choice choice) {
return PopupMenuItem<Choice>(
value: choice,
child: Text(choice.title),
);
}).toList();
},
),
],
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: ChoiceCard(choice: _selectedChoice),
),
),
);
}
}

จบท้ายด้วยการส่งค่าที่ผู้ใช้เลือกส่งไปยัง ChoiceCard

class ChoiceCard extends StatelessWidget {
const ChoiceCard({Key key, this.choice}) : super(key: key);

final Choice choice;

@override
Widget build(BuildContext context) {
final TextStyle textStyle = Theme.of(context).textTheme.display1;
return Card(
color: Colors.white,
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Icon(choice.icon, size: 128.0, color: textStyle.color),
Text(choice.title, style: textStyle),
],
),
),
);
}
}

Keys

ตามที่ผมเข้าใจ keys จะเป็นค่า unique ที่มีไว้เพื่อควบคุม widgets

ยกตัวอย่างเช่นมี widget A,B เรียงกันอยู่ A อยู่ตำแหน่งที่ 1 B อยู่ตำแหน่งที่ 2
จู่ๆ widget B ถูกลากไปอยู่ตำแหน่งที่ 1 เมื่อผู้ใช้คลิก widget ที่ตำแหน่งที่ 1 แอปก็จะรู้ได้ว่า widget ที่ตำแหน่งที่ 1 ไม่ใช่ A แต่เป็น B แล้วและเมื่อ widgets ถูกสร้างขึ้นใหม่ก็จะมี keys เป็นของตัวเอง

โค้ดทั้งหมดครับ

Property เพิ่มเติม

centerTitle

ตามชื่อครับ ชื่ออยู่ตรงกลาง

centerTitle: true

elevation

เป็นการใส่เงาให้ Appbar ครับ

titleSpacing

เป็นการขยับ title ตามค่าที่เรากำหนดครับ แต่ถ้าชื่อยาวจนแสดงผลไม่พอจะแสดง …

textTheme

เป็นการใส่ธีมให้ title ครับ

textTheme: TextTheme(title: TextStyle(color: Colors.red, fontSize: 20))

iconTheme

เป็นการใส่ธีมให้ icon ครับ

iconTheme: IconThemeData(color: Colors.red, size: 50)

iconSize

เพิ่มขนาดให้ icon

iconSize: 40

highlightColor

เปลี่ยนสีการคลิกปุ่มครับ *พอดีไม่สามารถแคปได้

highlightColor: Colors.red

splashColor

เปลี่ยนสีเวลาผู้ใช้คลิกค้างที่ปุ่มครับ *พอดีไม่สามารถแคปได้

splashColor: Colors.cyan

วันนี้ขอจบบทความเท่านี้นะครับ ถ้ามีอะไรติชมเขียนมาได้เลยนะครับ ผมจะได้เอาไปปรับปรุงในบทความต่อไป ขอบคุณที่อ่านจนจบครับ

--

--

Patiphan Suwanich
Patiphan Suwanich

Written by Patiphan Suwanich

Work at Major Development | Interest in Blockchain | looking for opportunities in my programmer’s life

No responses yet