เนื้อหาตอนที่แล้ว เราได้ทำความเข้าใจเบื้องต้นเกี่ยวกับ
การใช้งาน Stateless และ Stateful widget รวมถึงการจัดการ
package โดยแยกส่วนของ widget ออกมาเป็น ไฟล์ย่อย
และ import มาใช้งานอีกที สามารถทบทวนได้ที่บทความ
การใช้งาน Stateless และ Stateful Widget ใน Flutter เบื้องต้น
https://www.ninenik.com/content.php?arti_id=954 via @ninenik
เราจะใช้เนื้อหาจากโค้ดในตอนที่แล้ว มาประกอบการอธิบายสำหรับแนะนำการใช้งาน
Basic wdget หรือ widget เบื้องต้นที่ควรรู้ โดยจะสนใจเฉพาะส่วนของไฟล์ first_screen.dart
ซึ่งมีการใช้งาน Scaffold widget ที่เป็น StatefulWidget เราจะทำความเข้าใจเกี่ยวกับ
การออกแบบหน้าตา UI โดยใช้งาน widget เบื้องต้น และ Scaffold ก็เป็นส่วนหนึ่งด้วย
ติดตาม รายละเอียดตามลำดับ
การใช้งาน Flutter Hot Reload
ก่อนไปดูเรื่องการใช้งาน widget พื้นฐาน สิ่งที่ขาดไม่ได้คือ เครื่องมือ ที่จะทำให้เราสามารถดูผลลัพธ์จากการปรับแต่งโค้ด ได้ในทันที
ที่มีการเปลี่ยนแปลงเกิดขึ้น โดยเฉพาะภายใน widget ที่อยู่ใน Scaffold ซึ่งเป็น stateful widget โดยไฟล์ first_screen.dart เริ่มต้นใน
Android Studio ของเราเมื่อเปิดมาจะเป็นดังนี้
import 'package:flutter/material.dart'; // ส่วนของ Stateful widget class FirstScreen extends StatefulWidget{ @override State<StatefulWidget> createState() { return _FirstScreen(); } } class _FirstScreen extends State<FirstScreen>{ @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Welcome to Flutter'), backgroundColor: Colors.green ), body: Material( color: Colors.lightGreen, child: Center( child: Text( 'Hello World', style: TextStyle( color: Colors.white, fontSize: 20.0 ) ) ) ) ); } }
ซึ่งเราจะสนใจเฉพาะ ส่วนของ _FirstScreen State ที่จะมีการเปลี่บนแปลง เมื่อทำการปรับแต่ง widget ภายใน เบื้องต้นให้เราทำการ
รัน App ปกติ โดยกดปุ่ม Run ตามรูปด้านล่าง
เมื่อ App รันขึ้นมาและขึ้นข้อความคำว่า "Hello World" สีขาว แสดงตรงกลางหน้าจอ ตามรูปในผลลัพธ์ในตอนที่ผ่านมา รูปด้านล่าง
แสดงเฉพาะส่วนของข้อความ
ที่แถบเครื่องมือ ให้เราคลิกที่ปุ่มสายฟ้าสีเหลือง หรือปุ่ม Flutter Hot Reload
แล้วสังเกตการทำงานในส่วนของ console ในแท็บ run
ตอนนี้โหมดการทำงานแบบ hot reload เริ่มทำงานแล้ว ถ้าเราทำการแก้ไข ข้อมูล หรือ ปรับแต่ง ค่าต่างๆ ของ widget แล้วทำการกดปุ่ม
Ctrl + S เพื่อบันทึก ผลลัพธ์ในส่วนของ Emulator ก็จะเปลี่ยนตามรูปแบบที่กำหนดในทันที ทำให้เราทำการพัฒนา App ได้เร็วขึ้น แทนที่จะ
ต้องมากด Run เพื่อ rebuild ทั้งหมดใหม่ทุกครั้ง โดย hot reload จะทำการอัพเดทส่วนของ stateful ที่เราใช้งาน ตัวอย่างเราปรับข้อความ
เป็นสีดำ และกำหนดเป็นตัวหนา พื้นหลังสีขาว ก็จะได้เป็น
สามารถหยุดการใช้งาน hot reload โดยคลิกที่ปุ่ม stop ที่เครื่องมือ แล้ว กด Run เพื่อทำงาน App ใหม่ปกติ
การใช้งาน Text Widget
Text widget เป็น widget พื้นฐาน ใช้สำหรับแสดงข้อความ โดยใช้รูปแบบ Text() เช่น Text('Hello World')
เมื่อทำการจัดรูปแบบ ข้อความ "Hello World" ก็จเป็นการกำหนดรูปแบบให้กับข้อความทั้งหมด ไม่สามารถแยกกำหนด
ให้กับคำว่า "Hello" และ "World" แยกรูปแบบกันไม่ได้ ยกตัวอย่างเช่น การกำหนดค่าจากผลลัพธ์ด้านบน ที่ผ่านมา ก็ได้เป็น
child: Text( 'Hello World', style: TextStyle( color: Colors.black, backgroundColor: Colors.white, fontWeight: FontWeight.bold, fontSize: 20.0 ) )
หากข้อความมีความยาวมากๆ ก็จะทำการตัดบรรทัดให้อัตโนมัติตามความกว้างหรือขนาดของหน้าจอ หรือไม่ก็แสดงอยู่ในบรรทัดเดียว
ถ้าความยาวของข้อความ ยังไม่เกินขอบเขตความกว้างของหน้าจอ ดูตัวอย่าง เมื่อกำหนดข้อความให้ยาวขึ้น
ใช้การจัดรูปแบบให้ตัดคำเมื่อยาวเกินขอบเขตของความกว้างหน้าจอ โดยไม่ต้องขึ้นบรรทัดใหม่
child: Text( 'ทดสอบข้อความ กำหนดให้ยาวกว่าความกว้างหน้าจอ', overflow: TextOverflow.ellipsis, style: TextStyle( color: Colors.black, backgroundColor: Colors.white, fontWeight: FontWeight.bold, fontSize: 20.0 ) )
ผลลัพธ์ที่ได้
ข้างต้นเป็นการใช้งานรูปแบบทั่วไป เราสามารถใช้รูปแบบที่กำหนด style ให้กับแต่ละคำในข้อความหรือในประโยคโดยใช้ Text.rich()
ซึ่งเป็นการใช้งาน name constructor ตามตัวอย่างดังนี้
child: const Text.rich( TextSpan( text:'Hello', children: <TextSpan>[ TextSpan( text:'beautiful', style: TextStyle( color:Colors.pink, fontStyle:FontStyle.italic ) ), TextSpan( text:'world', style: TextStyle( color:Colors.yellow, fontWeight: FontWeight.bold ) ), ] ) )
ผลลัพธ์ที่ได้
ในตัวอย่างพยายามแยกเป็นบรรทัด ให้เห็นแต่ละส่วนของค่า widget และการกำหนดค่าง่ายขึ้น แต่ในการใช้งานจริง เราอาจะลดบรรทัดลง
เช่นบางส่วนสามารถ เขียนรวมไว้ในบรรทัดเดียวได้
สำหรับการใช้งาน Text widget เราคงไม่อธิบายทุกคำสั่ง ทุกการใช้งาน แต่จะให้แนวทาง ดังนี้คือ ให้ดูส่วนของ API ประกอบ โดยเปิด
ไปที่ Text Widget API
โดยแนวทางคือ ข้อความที่ใช้การจัดรูปแบบเดียวทั้งประโยค ให้เราใช้ Text() contrtutor และสามารถกำหนด property เพิ่มเติม โดยดู
จาก parameter ที่เป็น options ส่วนข้อความที่ต้องการกำหนด style หรือจัดรูปแบบเป็นคำๆ หรือบางส่วนไม่รวมทั้งประโยค ให้ใช้เป็น
const Text.rich() แทนตามตัวอย่างด้านบน
การใช้งาน Icon Widget
เราน่าจะคุ้นเคยกับคำว่า icon อยู่แล้ว ซึ่งก็คือรูปกราฟิคที่แสดงหรือสือ่ความหมายถึงบางสิ่ง โดยส่วนใหญ่แล้ว ทั้งชื่อและภาพสัญลักษณฺ์
ก็จะสือความหมายอยู่ในตัวอยู่แล้ว ปกติ icon จะไม่รองรับการกระทำหรือคำสั่งจากผู้ใช้โดยตรง แต่ก็มักถูกนำไปประกอบหรือใช้ร่วมกับปุ่ม
ต่างๆ เพื่อให้ผู้ใช้ตัดสันใจหรือเลือกทำคำสั่งตามต้องการ
Icon widget มีรูปแบบการกำหนดการใช้งานที่ง่าย และปรับค่าไม่มากนัก หลักๆ คือกำหนดชื่อของ icon ที่ต้องการ โดยใช้จากรายการ
ตามลิ้งค์ https://api.flutter.dev/flutter/material/Icons-class.html โดยแต่ละ icon เป็น ค่าคงที่
รูปแบบการใช้งาน เช่น icon โน๊ตตัวเขบ็ต 1 ชั้น สีเขียว ขนาด 30 pixels
Icon(Icons.audiotrack,color: Colors.green,size: 30.0)
audiotrack คือชื่อ icon อ้างอิงข้อมูล IconData ที่ต้องการใช้งาน
ตัวอย่างเรากำหนด icon favorite ในโค้ดของเรา
child: Center( child: Icon( Icons.favorite, color: Colors.pink, size: 24.0 ), ),
ผลลัพธ์ที่ได้
Icon มีรูปแบบการใช้งานไม่ซับซ้อน สามารถใช้ร่วมกับส่วนอื่นๆ ได้ ดู property ต่างๆ เพิ่มเติมได้ที่ Icon Widget API
การใช้งาน Image Widget
Image Widget เป็น widget ที่ใช้สำหรับแสดงรูปภาพ โดยสามารถรูปแบบการเรียกใช้งานผ่านการกำหนด constructor ต่างๆ
ตามชนิด ประเภท หรือแหล่งข้อมูลของรูปที่ต้องการใช้งาน รองรับไฟล์รูป JPEG, PNG, GIF, Animation GIF, WebP, Animation WebP,
BMP และ WBMP
รูปแบบการเรียกใช้งาน สามารถใช้งาน Default constructor ซึ่งสามารถลือกแหล่งข้อมูลรูปภาพใดๆ ก็ได้ ยกตัวอย่างเช่น ใช้รูปภาพ
จาก NetworkImage เพื่อใช้งานรูปภาพจากอินเตอร์เน็ตผ่านการกำหนด url ของ ภาพนั้นๆ ตัวอย่างเช่น
child: Center( child: const Image( image:NetworkImage('https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg'), height: 200, ) ),
ผลลัพธ์ที่ได้
หรือจะใช้ named constructror เฉพาะสำหรับรูปที่ใช้จาก url โดยใช้งาน Image.network() ตามตัวอย่างด้านล่างก็ได้
child: Center( child: Image.network( 'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg', height: 200, ) ),
กรณีเราต้องการใช้งานรูปภาพพิเศษ หรือรูปภาพที่ใช้ใน App ของเราโดยเฉพาะ โดยจะถูกรวมเข้ามากับไฟล์ App ของเรา สามารถทำได้
ดังนี้ เริ่มต้นให้เราเตรียมไฟล์รูปภาพที่ต้องการ สมมติเราได้ป็นไฟล์ png 2 รูปคือ avatar.png และ logo.png ต่อไปให้เราคลิกขวาที่โฟลเดอร์
โปรเจ็ค เพื่อทำการสร้าง โฟลเดอร์สำหรับเก็บรูปภาพ โดยเลือกเมนู "New" > "Directory" ดังรูป
ในที่นี้เราจะสร้างโฟลเดอร์ "assets" และสร้างโฟลเดอร์ "images" ไว้ด้านในอีกที จากนั้นทำการ copy รูปภาพ แล้วนำมาวางในโฟลเดอร์
ที่ได้สร้างไว้ จะได้ดังนี้
ตอนนี้เมื่อเราเรียกใช้งาน Image.asset('assets/images/avatar.png') รูปจะยังไม่แสดง เราต้องทำการเพิ่มรูปเข้าไปในไฟล์
pubspec.yaml เพื่อลงทะเบียนหรือบอกกับ App ให้รู้จักรูปที่จะมีการใช้งานใน App โดยดูในส่วนของ การกำหนด assets หาก comment ไว้
ให้เอาออก และเพิ่ม รูปที่จะใช้งานเข้าไปดังนี้
การแก้ไขไฟล์ pubspect.yaml กรณีนี้ เราจำเป็นต้องทำการรัน App ใหม่อีกครั้งก่อนใช้งาน ระวังในเรื่อง ช่องว่าง ระยะห่าง tab ระหว่าง
คำว่า "assets" ต้องให้อยู่ในแนวที่ถูกต้อง ตามรูปด้านบน เรียกใช้งาน ใหม่อีกครั้ง
child: Center( child: Image.asset( 'assets/images/logo.jpg', height: 100, ) ),
ผลลัพธ์ที่ได้
การปรับแต่ง และกำหนด property ต่างๆ เพิ่มเติม สามารถดูได้ที่ Image Widget API
การใช้งาน Container Widget
เป็น widget ที่ใช้กำหนดพื้นที่จัดการที่สามารถจัดตำแหน่ง ขนาด รวมถึงการตกแต่งวาดลวดลาย หรือลงสีให้สวยงามได้
การใช้งาน container ปกติแล้วขนาดจะขยายเต็มพื้นที่ ถ้าไม่มี child หรือถ้าไม่มีการจำกัดขอบเขต หรือขนาดจาก parent อีกที
สังเกตตัวอย่างโค้ดด้านล่าง ประกอบทำความเข้าใจ
child: Center( child: Container( color: Colors.red[600], child: Container( margin: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(10.0), color: Colors.blue[600], child: Image.asset( 'assets/images/logo.jpg', ), ), ) ),
จากโค้ดเราใช้งาน container 2 ตัวซ้อนกัน ตัวแรกกำหนดสีเป็น สีแดง ตัวที่สองเป็นสีน้ำเงิน โดยตัวสีน้ำเงินเรากำหนด padding
เท่ากับ 10 px (pixels) นั่นคือ child ของมันจะขยับจากขอบ 10 px หรือก็คือ รูปภาพที่เป็น Image Widget ห่างจากขอบทุกด้าน
เท่ากัน 10 px นากจากนั้น เรากำหนด margin ให้กับ สีน้ำเงิน นั้นคือ สีน้ำเงินขยับห่างจากขอบของสีแดงที่เป็น parent 20 px
เนื่องจากรูปภาพด้านใน เราไม่ได้กำหนดขนาด ดังนั้น container จึงขยายขนาดเต็มสุด นั่นคือตัว container นอกสุดหรือสีแดง
ขนายขนาดเต็มหน้าจอ ในขณะสีน้ำเงิน container ตัวที่แสดง ขนาดลดลงจาก margin ด้านละ 20 px
ผลลัพธ์ที่ได้
แต่เมื่อเรากำหนดขนาดรูปที่อยู่ด้านในสูงเท่ากับ 100 px ขนาดของ container ที่ไม่ได้กำหนดความกว้าง ความสูง ก็จะมีขนาด
ลดลงตามขอบเขตของ child โดยสีน้ำเงิน จะมีขนาดความสูง 120 px คือมาจากขนาดของรูป 100 px บวก padding บน-ล่าง
อย่างละ 10 รวมทั้งหมดเป็น 120 px ในขณะที่ สีแดงจะสูงเท่าขนาดสีนำเงิน บวก margin บน-ล่าง หรือก็คือ 160 px
child: Center( child: Container( color: Colors.red[600], child: Container( margin: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(10.0), color: Colors.blue[600], child: Image.asset( 'assets/images/logo.jpg', height: 100, ), ), ) ),
ผลลัพธ์ที่ได้
ต่อมา ถ้าเราลองจำกัดพื้นที่ของสีน้ำเงิน โดยให้ความสูงเท่ากับ 100 px โดยการกำหนดความสูง จะเป็นการรวมถึงว่า รวม padding
แล้ว จะไม่เหมือนกรณีที่ไม่ได้จำกัดขนาด ซึ่งเมื่อกำหนดจำกัดที่ 100 px ซึ่งรวม padding จะทำให้ขนาดของรูปลดลง และการกำหนด
ขนาดความสูงของรูปเท่ากับ 100 จึงไม่มีผลใดๆ เพราะถูกจำกัดอยู่ในพื้นที่ container สีน้ำเงินแล้ว
child: Center( child: Container( color: Colors.red[600], child: Container( margin: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(10.0), color: Colors.blue[600], height: 100, child: Image.asset( 'assets/images/logo.jpg', height: 100, ), ), ) ),
ผลลัพธ์ที่ได้
ดูคำสั่งการใช้งาน และ property เพิ่มเติมได้ที่ Container Widget API
การใช้งาน ElevatedButton Widget
เป็น widget หนึ่งจาก Material design ลักษณะเป็นปุ่มที่มีการยกขึ้นมาเล็กน้อย โดยเมื่อทำการกด ก็จะมี effect คล้ายเงาบุ๋ม
กระจายตัวจากบริเวณที่กด เป็นวงออกมา แล้วจางหายไป
ถ้ากำหนด onPressed เป็น null หรือไม่มีการทำคำสั่งใดๆ เมื่อกด ปุ่มก็จะแสดงในลักษณะเป็น Disable ราบเรียบไม่มีการยกขึ้นมา
สังเกตตัวอย่างปุ่ม ที่กำหนด onPressed: null, และ onPressed: () {}, จะได้เป็นดังนี้
ตัวอย่างการกำหนดปุ่ม
child: Center( child: ElevatedButton( onPressed: (){}, child: Text( 'Button Test', style: TextStyle(fontSize: 20), ), ) ),
สังเกตว่ามีการใช้งาน Text widget กำหนดเป็น child สำหรับแสดงข้อความของปุ่ม เป็นรูปแบบการใช้งานอย่างง่าย เพราะเรายังสามารถ
ปรับ child จาก text widget เป็นการประยุกต์รวมกับ widget อื่นๆ เพื่อแต่งปุ่มให้สวยงามได้ เช่น ใช้งาน Container widget เพื่อสร้าง
ปุ่มแบบ Gradient ตัวอย่างการใช้งาน Container และยังไม่ได้ตกแต่งเป็น Gradient
child: Center( child: ElevatedButton( onPressed: (){}, child:Container( padding: const EdgeInsets.all(10.0), child: Text( 'Button Test', style: TextStyle(fontSize: 20), ), ), ) ),
สังเกตว่าปุ่มมีส่วนของ padding เพิ่มเข้ามา
ต่อไป เราปรับแต่ง container widget ด้วย decoration กำหนดการแสดงป็นแบบ gradient และใช้ข้อความปุ่ม เป็นสีขาว
child: Center( child: ElevatedButton( onPressed: (){}, style: ElevatedButton.styleFrom( padding: EdgeInsets.all(0) ), child:Container( decoration: BoxDecoration( gradient: LinearGradient( colors:<Color>[ Color(0xFF0D47A1), Color(0xFF1976D2), Color(0xFF42A5F5), ], begin: Alignment.topLeft, // Start gradient from top left end: Alignment.bottomRight, // End gradient at bottom right ), borderRadius: BorderRadius.circular(20.0), ), padding: const EdgeInsets.symmetric( vertical: 10.0, horizontal: 30.0, ), child: Text( 'Button Test', style: TextStyle( color: Colors.white, fontSize: 20, ), ), ), ), ),
ผลลัพธ์ที่ได้
นอกจากการกำหนดเป็นข้อความ แล้วเรายังสามารถกำหนดใช้งาน Icon widget เพื่อสร้างเป็นปุ่มที่มี icon ประกอบด้วย โดยใช้
ElevatedButton.icon() contructor ดังนี้
child: Center( child: ElevatedButton.icon( onPressed: (){}, icon: Icon( Icons.add_a_photo, color: Colors.white, ), style: ElevatedButton.styleFrom( backgroundColor: Colors.blue, // Optional: Set background color ), label: const Text( 'Button Test', style: TextStyle( color: Colors.white, fontSize: 20, ), ), ), ),
ผลลัพธ์ที่ได้
ดูคำสั่งการใช้งาน และ property เพิ่มเติมได้ที่ ElevatedButton Widget API
การใช้งาน Placeholder Widget
เป็น widget ที่สร้างกล่องกันพื้นที่ที่จะมีการเพิ่ม widget อื่นเข้ามาในอนาคตหรือ กันพื้นที่สำหรับ widget ที่ยังสร้างไม่เสร็จ เหมือน
กันจองขอบเขตพื้นที่ ไว้ใช้งาน มีรูปการเรียกใช้ อย่างง่ายด้วย Placeholder() หรือจะกำหนดสีขอเส้นด้วยก็ได้ เช่น
Placeholder(color: Colors.amberAccent) โดยขนาดเริ่มต้นจะเต็มพื้นที่ของ container หรือ parent widget ที่ใช้งาน ดูตัวอย่าง
การใช้งาน เมื่อกำหนดไว้ใน Container Widget ดังนี้
child: Center( child: Container( color: Colors.red[600], width: 200, child: Container( margin: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(10.0), color: Colors.blue[600], height: 100, child: Placeholder( color: Colors.amberAccent, ), ), ), ),
ผลลัพธ์ที่ได้
ดูคำสั่งการใช้งาน และ property เพิ่มเติมได้ที่ Placeholder Widget API
การใช้งาน Column Widget
เป็น widget สำหรับแสดง widget ย่อยในแนวตั้ง
Column widget ไม่รองรับการ scroll เลื่อน และไม่ควรมี child หรือ widget ย่อย จำนวนมากๆ กรณีต้องการใช้งาน widget เรียงกัน
หลายๆ บรรทัด ให้ใช้เป็น ListVView widget (จะได้แนะนำต่อไปทีหลัง) ซึ่งรองรับการ scroll และเหมาะสมกว่า
ในกรณีที่ใน column มี child เดียว อาจจะใช้ Align หรือ Center widget สำหรับกำหนดตำแหน่งเพิ่มเติมได้
ดูตัวอย่างการใช้งาน Column ที่มี 3 widget ย่อยด้านใน ในที่นี้เราคลุมด้วย Center widget อีกที
child: Center( child: Column( children: <Widget>[ Text('Text1 in First Child'), Text('Text2 in Second Child'), Image.asset( 'assets/images/logo.jpg', height: 100, ) ], ), ),
ผลลัพธ์ที่ได้
Child จะเรียงจากบนลงล่างตามลำดับ ที่ตำแหน่งบนสุดติดขอบ AppBar (ถ้ามี) เนื่องจากเราใช้ Center จัดตำแหน่ง รายการจึงแสดง
กึงกลางในแนวนอน หากไม่มีการจำกัดความสูง Column จะสูงเต็มพื้นที่หน้าจอ ส่วน Child จะสูงตามสัดส่่วน หรือการกำหนดของ Child
นั้น เช่น รูปเรากำหนดที่ 100 px ดังนั้น ความสูงของทั้ง 3 Child รวมกันก็จะไม่สูงเต็มพื้นที่ของ Column
เราสามารถใช้ Expanded widget ขยายความสูงของ Child ที่ต้องการได้ โดยเป็นรูปแบบการใช้งานแบบ Flex นั่นคือจะดูจากพี้นที่ว่าง
ที่เหลือ แล้วจัดสัดส่วนพื้นที่ ให้เต็มความสูงของ Column หรือเต็มหน้าจอกรณีไม่ได้จำกัดความสูง สมมติเช่น เราใช้ Expanded ให้กับ
ข้อความที่สอง
child: Center( child: Column( children: <Widget>[ Text('Text1 in First Child'), Expanded( child: Text('Text2 in Second Child'), ), Image.asset( 'assets/images/logo.jpg', height: 100, ) ], ), ),
ผลลัพธ์ที่ได้
Child ที่ 1 และ 3 จะมีขนาดความสูงเท่าเดิม แต่ Child ที่เรากำหนด Expanded เพื่อใช้งาน Flex จะขยายเต็มพื้นที่เหลืออยู่ ในลักษณะ
เดียวกัน ถ้าใช้ Expanded กับ Child ทั้ง 3 พื้นที่ความสูงของแต่ละ Child ก็จะเป็นพื้นที่ความสูงทั้งหมด หาร 3 เพื่อเฉลี่ยความสูงแต่ละ Child
ให้มีขนาดเท่าๆ กัน
ความกว้างของ Column จะมีขนาดเท่ากับความกว้างของ Child ที่กว้างที่สุด เราลองเอา Center ที่คลุม Column ออก ก็จะเห็นความ
กว้างของ Column สอดคล้องตามที่อธิบาย คือมีขนาดเท่าความกว้างของข้อความที่ 2 ที่กว้างที่สุด
เมื่อไม่มีการใช้งาน Center และไม่มีการกำหนดให้ขยายให้เต็มที่ว่างโดยใช้ Expanded ความสูงค่าเริ่มต้นจะมีขนาดเต็มหน้าจอ หรือมีค่า
mainAxisSize: MainAxisSize.max ถ้ากำหนด หากต้องการให้ความสูงเท่ากับความสองของ Child สามารถกำหนดเป็น min โด้ ดังนี้
child: Column( mainAxisSize: MainAxisSize.min, children: <Widget>[ Text('Text1 in First Child'), Text('Text2 in Second Child'), Image.asset( 'assets/images/logo.jpg', height: 100, ) ], ),
ผลลัพธ์ที่ได้
เราสามารถใช้ crossAxisAlignment สำหรับจัดตำแหน่ง Child ในแนวนอน และใช้ mainAxisAlignment จัดตำแหน่ง Child ในแนวตั้ง
ดูตัวอย่างต่อไปนี้ เราจัดตำแหน่ง Child ทั้งหมด ให้อยู่ล่างสุด หรือกองอยู่ด้านล่างของ Column ด้วย MainAxisAlignment.end และ
จัดตำแหน่งในแนวนอนของ Child ให้ชิดซ้าย ด้วย CrossAxisAlignment.start
child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.end, mainAxisSize: MainAxisSize.max, children: <Widget>[ Text('Text1 in First Child'), Text('Text2 in Second Child'), Image.asset( 'assets/images/logo.jpg', height: 100, ) ], ),
ผลลัพธ์ที่ได้
ดูคำสั่งการใช้งาน และ property เพิ่มเติมได้ที่ Column Widget API
การใช้งาน Row Widget
เป็น widget สำหรับแสดง widget ย่อยในแนวนอน
Row widget ไม่รองรับการ scroll เลื่อน และไม่ควรมี child หรือ widget ย่อย จำนวนมากๆ กรณีต้องการใช้งาน widget เรียงกัน
หลายๆ บรรทัด ให้ใช้เป็น ListVView widget (จะได้แนะนำต่อไปทีหลัง) ซึ่งรองรับการ scroll และเหมาะสมกว่า
ในกรณีที่ใน Row มี child เดียว อาจจะใช้ Align หรือ Center widget สำหรับกำหนดตำแหน่งเพิ่มเติมได้
ดูตัวอย่างการใช้งาน Row ที่มี 3 widget ย่อยด้านใน ในที่นี้เราคลุมด้วย Center widget อีกที
child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, children: <Widget>[ Text('Text1 in First Child'), Text('Text2 in Second Child'), Image.asset( 'assets/images/logo.jpg', height: 100, ) ], ),
ผลลัพธ์ที่ได้
ในการใช้งาน Row ใจความสำคัญแทบจะเหมือนกับรูปแบบของ Column แต่มองคนละมุมหรือมองในมุมตรงข้าม คือ ที่ความกว้างของ
Row ให้มองเทียบกับที่ความสูงของ Column และที่ความสูงของ Row ให้มองเทียบกับความกว้างของ Column รวมถึง CrossAxis และ
MainAxis ก็มองสลับกัน โดยใน Row เทียบ MainAxis เหมือนแนวนอน และ CrossAxis เหมือนแนวตั้ง อย่างในตัวอย่าง เรากำหนดให้
Child ในแนวนอนให้ชิดซ้ายด้วย mainAxisAlignment: MainAxisAlignment.start และกำหนดให้ Child ในแนวตั้งให้อยู่กึ่งกลางด้วย
crossAxisAlignment: CrossAxisAlignment.center จะเห็นชัดสำหรับกรณีข้อความที่แสดงตรงกลางในแนวตั้ง
ดังนั้นจะไม่ขอยกตัวอย่างเพิ่มเติม สามารถใช้งานคล้ายกับ Column
ดูคำสั่งการใช้งาน และ property เพิ่มเติมได้ที่ Row Widget API
การใช้งาน AppBar Widget
AppBar เป็น widget ที่ประกอบไปด้วยเครื่องมือต่างๆ และอาจจะมี wiget อื่นๆ เพิ่มเติมได้ ยกตัวอย่างเช่น TabBar, FlexibleSpacBar
ถ้าเราคุ้นเคยกับการใช้งาน App เราจะพบว่า App bar จะมีปุ่มสำหรับรับคำสั่งเพื่อทำงานบางอย่าง อาจจะเป็นปุ่ม IconButtons ที่เมื่อกด
แล้วมี PopupMenuButton เพิ่มเติมให้เลือกอีกที
AppBar เป็น property หนึ่งของ Scaffold widget ซึ่งจะเป็น widget ตัวสุดท้ายที่เราจะพูดถึงในบทความนี้ AppBar จะถูกกำหนด
ความสูงมาแบบตายตัว แสดงด้านบนสุดของหน้าจอ โดยมี widget เครื่องมือต่างๆ แสดงด้านในอีกที เช่น leading, title, และ action
และ อาจจะมี bottom กรณีมีการใช้งาน TabBar ดูส่วนของการกำหนดการใช้งาน AppBar และผลลัพธ์ที่ได้
appBar: AppBar( iconTheme: IconThemeData( color: Colors.white, ), leading: IconButton( onPressed: (){}, icon: const Icon(Icons.menu) ), title: Text( 'AppBar Demo', style: TextStyle( color: Colors.white, ), ), backgroundColor: Colors.green, actions: <Widget>[ IconButton( onPressed: (){}, icon: const Icon(Icons.markunread), tooltip: 'Mark as Unread', ), IconButton( onPressed: (){}, icon: const Icon(Icons.more_vert), tooltip: 'More Setting', ), ], ),
ผลลัพธ์ที่ได้
ในตัวอย่างส่วนของ AppBar เรียงจากซ้ายไปขวา ประกอบด้วย leading, title และ action อีกสองปุ่ม
รายละเอียดเกี่ยวกับ App bar น่าจะมีเสริมเรื่อยๆ ดังนั้น จะไม่ด้ลงรายละเอียดทั้งหมดในตอนนี้
ดูคำสั่งการใช้งาน และ property เพิ่มเติมได้ที่ AppBar Widget API
การใช้งาน Scaffold Widget
มาถึง widget ตัวสุดท้ายของเนื้อหา widget เบื้องต้นที่ควรรู้ อย่างที่เกริ่นไปแล้ว ถ้าเราต้องการใช้งาน AppBar จำเป็นจะต้องกำหนด
หรือใช้งาน Scaffold widget ซึ่งมี appBar ให้ใช้งาน เข้าใจอย่างง่าย scaffold widget เป็นเครื่องมือสำหรับออกแบบโครงสร้างสำหรับ
Material App ที่ไม่เพียงจะมี AppBar ที่เป็นเมนูด้านบนให้ใช้งานแล้ว ยังมีสามารถกำหนด Drawers หรือเมนูด้านข้างเมื่อถูกเรียกใช้งาน
รวมถึงเมนูด้านล่าง หรือแม้แต่ปุ่ม floatingActionButton ซึ่งเป็นสิ่งที่เราสามารถนำมาประยุกต์ หรือใช้งานร่วมกับ App ของเราได้
มาดูโค้ดตัวอย่างเต็มของไฟล์ first_screen.dart และการปรับแต่ง scaffold แบบเต็ม จะได้เป็นดังนี้
import 'package:flutter/material.dart'; // ส่วนของ Stateful widget class FirstScreen extends StatefulWidget{ @override State<StatefulWidget> createState() { return _FirstScreen(); } } class _FirstScreen extends State<FirstScreen>{ @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( iconTheme: IconThemeData( color: Colors.white, ), // leading: IconButton( // onPressed: (){}, // icon: const Icon(Icons.menu) // ), title: Text( 'AppBar Demo', style: TextStyle( color: Colors.white, ), ), backgroundColor: Colors.green, actions: <Widget>[ IconButton( onPressed: (){}, icon: const Icon(Icons.markunread), tooltip: 'Mark as Unread', ), IconButton( onPressed: (){}, icon: const Icon(Icons.more_vert), tooltip: 'More Setting', ), ], ), body: Material( color: Colors.lightGreen, child: Center( child: Text( 'Hello World', style: TextStyle( color: Colors.white, fontSize: 20.0 ) ) ) ), drawer: Drawer( child: ListView( padding: EdgeInsets.zero, children: <Widget>[ DrawerHeader( child: Text('Drawer Header'), decoration: BoxDecoration( color: Colors.green, ), ), ListTile( title: Text('Item 1'), onTap: () {} ), ListTile( title: Text('Item 2'), onTap: () {}, ), ], ), ), extendBody: true, bottomNavigationBar: BottomAppBar( color: Colors.green, shape: const CircularNotchedRectangle(), child: Container(height: 50.0), ), floatingActionButton: FloatingActionButton( backgroundColor: Colors.black, onPressed: () {}, child: Icon(Icons.add,color: Colors.white,), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, ); } }
ผลลัพธ์ที่ได้ และเมื่อปัดเลื่อนไปด้านขวา เพื่อแสดงส่วนของ Drawer
ดูคำสั่งการใช้งาน และ property เพิ่มเติมได้ที่ Scaffold Widget API
Widget ที่ควรรู้เบื้องต้นเหล่านี้ ทำให้เรามองภาพการออกแบบหน้าตาของ App เบื้องต้นได้มากขึ้น และเข้าใจการใช้งาน ขอบเขต
และการประยุกต์ ซึ่งเป็นเพียงบางส่วนเท่านั้น ยังมีเครื่องมืออีกมาก รวมถึง widget ที่กล่าวถึงไปแล้วข้างต้น ก็ยังมีรายละเอียด
การนำไปปรับใช้งานเพิ่มเติม ซึ่งอาจจะได้นำมาเสริมในลำดับต่อๆ ไป