การใช้งาน Card Widget ร่วมกับ ListView ใน Flutter

เขียนเมื่อ 3 ปีก่อน โดย Ninenik Narkdee
flutter listview card widget listtile

คำสั่ง การ กำหนด รูปแบบ ตัวอย่าง เทคนิค ลูกเล่น การประยุกต์ การใช้งาน เกี่ยวกับ flutter listview card widget listtile

ดูแล้ว 8,367 ครั้ง




เนื้อหาตอนต่อไปนี้ จะมาดูเกี่ยวกับ widget เล็กๆ ที่จะมาช่วย
ให้การแสดงผลใน ListView มีความหลากหลายขึ้น โดยจะใช้เนื้อหา
จากตอนที่แล้ว และปรับแต่งเล็กน้อย
    การใช้งาน Http ดึงข้อมูลจาก Server มาแสดงใน Flutter http://niik.in/1038
 
    *เนื้อหานี้ใช้เนื้อหาต่อเนื่องจากบทความ http://niik.in/961
 
 

การใช้งาน Card Widget

    card widget เป็นส่วนหนึ่ง material widget ที่มีลักษณะเป็นแผ่นการ์ดมีความโค้งมนเล็ก
น้อยตรงมุม และมีเงาที่เกิดจากการยกสูงดูเป็นมิติ เหมือนเป็นกรอบสำหรับทำให้เนื้อหาด้านใน
ดูมีมิติเพิ่มขึ้น เราสามารถใช้ card widget ครอบส่วนของ ListTile หรือ widget อื่นๆ ได้
ในที่นี้เราจะมาประยุกต์ใช้งาน card ใน ListView จากเนื้อหาตอนที่แล้ว จะขอยกมาเฉพาะ
บางส่วนของโค้ด
 

    รูปแบบการใช้งาน Card Widget

 
Card(              
    child: Text('ข้อความใน card widget'),
);
 
    ไฟล์ home.dart บางส่วน
 
class _HomeState extends State<Home> {
 
    // กำนหดตัวแปรข้อมูล articles
    late Future<List<Article>> articles;
   
    @override
    void initState() {
      print("initState"); // สำหรับทดสอบ
      super.initState(); 
      articles = fetchArticle();
    }     
 
    void _refreshData(){
      setState(() {
        print("setState"); // สำหรับทดสอบ
       articles = fetchArticle();
      });
    }
 
    @override
    Widget build(BuildContext context) {
        print("build");  // สำหรับทดสอบ
        return Scaffold(
            appBar: AppBar(
                title: Text('Home'),
            ),
            body: Center(
              child: FutureBuilder<List<Article>>( // ชนิดของข้อมูล
                future: articles, // ข้อมูล Future
                builder: (context, snapshot) {
                  print("builder");  // สำหรับทดสอบ
                  print(snapshot.connectionState); // สำหรับทดสอบ
                  // กรณีสถานะเป็น waiting ยังไม่มีข้อมูล แสดงตัว loading                  
                  if (snapshot.connectionState == ConnectionState.waiting){
                      return const CircularProgressIndicator();
                  }               
                  if (snapshot.hasData) {// กรณีมีข้อมูล
                      return Column(
                          children: [
                            Container( // สร้างส่วน header ของลิสรายการ
                              padding: const EdgeInsets.all(5.0),
                              decoration: BoxDecoration(
                                color: Colors.teal.withAlpha(100),
                              ),                  
                              child: Row(                  
                                children: [
                                  Text('Total ${snapshot.data!.length} items'), // แสดงจำนวนรายการ
                                ],
                              ),
                            ),
                            Expanded( // ส่วนของลิสรายการ
                                  child: snapshot.data!.length > 0 // กำหนดเงื่อนไขตรงนี้
                                  ? ListView.separated( // กรณีมีรายการ แสดงปกติ
                                          itemCount: snapshot.data!.length,
                                          itemBuilder: (context, index) {
                                              return ListTile(
                                                title: Text(snapshot.data![index].title),
                                              );
                                          },
                                          separatorBuilder: (BuildContext context, int index) => const Divider(),                    
                                    )
                                  : const Center(child: Text('No items')), // กรณีไม่มีรายการ
                              ),
                          ],
                        );
                  } else if (snapshot.hasError) { // กรณี error
                    return Text('${snapshot.error}');
                  }
                  // กรณีสถานะเป็น waiting ยังไม่มีข้อมูล แสดงตัว loading
                  return const CircularProgressIndicator();                  
                },
              ),  
            ),          
            floatingActionButton: FloatingActionButton( // ปุ่มทดสอบสำหรับดึงข้อมูลซ้ำ
                onPressed: _refreshData,
                child: const Icon(Icons.refresh),
            ),
        );
    }
 
}
 
    ดูในส่วนของการอ้างอิงข้อมูล snapshot เพิ่มดึงหัวข้อมาแสดง เราใช้รูปแบบ
 
snapshot.data![index].title
 
    ชนิดข้อมูล Future ที่เราได้มาก็คือ List<Article> ดังนั้น ค่า snapshot.data จึงเป็นข้อมูล List
หรืออาเรย์ มีการเรียกใช้งานแต่ละค่าผ่าน index หรือ key ของอาเรย์ เพื่อสะดวกในการเรียกใช้งาน
เราจะกำหนดค่าเป็นดังนี้
 
Article article = snapshot.data![index];
 
    ดูเฉพาะในส่วนของ Expanded widget เราปรับใหม่เป็นดังนี้
 
Expanded( // ส่วนของลิสรายการ
    child: snapshot.data!.length > 0 // กำหนดเงื่อนไขตรงนี้
    ? ListView.separated( // กรณีมีรายการ แสดงปกติ
            itemCount: snapshot.data!.length,
            itemBuilder: (context, index) {
              Article article = snapshot.data![index];
              return ListTile(
                title: Text(article.title),
              );
            },
            separatorBuilder: (BuildContext context, int index) => const Divider(),                    
      )
    : const Center(child: Text('No items')), // กรณีไม่มีรายการ
),
 
    ตัวแปร article จะอ้างอิงแต่ละรายการในลูป แล้วเราสามารถเรียกใช้งานผ่านรูปแบบ article.title
ซึ่งจะสะดวกและกระชับกว่ากำหนดแบบเดิม
 
    ต่อไปเราจะเพิ่มการแสดง Card เข้าไปดังนี้
 
Expanded( // ส่วนของลิสรายการ
    child: snapshot.data!.length > 0 // กำหนดเงื่อนไขตรงนี้
    ? ListView.separated( // กรณีมีรายการ แสดงปกติ
            itemCount: snapshot.data!.length,
            itemBuilder: (context, index) {
              Article article = snapshot.data![index];

              Widget card; // สร้างเป็นตัวแปร
              card = Card(
                child: ListTile(
                  title: Text(article.title),
                ),
              );
              return card;
            },
            separatorBuilder: (BuildContext context, int index) => const SizedBox(),                    
      )
    : const Center(child: Text('No items')), // กรณีไม่มีรายการ
),
 
    เราเปลี่ยนในสองจุด คือ ส่วนการครอบ ListTile ด้วย Card widget และเปลี่ยนตรงตัวแบ่ง จากเดิม
เป็น Divider() ก็ใช้เป็น SizedBox()
 
    เปรียบเทียบผลลัพธ์ ก่อนและหลังใช้ Card widget

 
    

 
 
    เราสามารถปรับแต่งค่า property ของ card widget เพิ่มเติมได้ ไม่ว่าจะเป็นค่าการกำหนดการยกสูงของเงา
สีของเงา สีของ card ความโค้งมนของมุม ความห่างจากขอบของ card 
 
    ดูตัวอย่างการปรับแต่ง card
 
Card(
  color: Colors.yellow, // สี 
  shadowColor: Colors.red.withAlpha(100), // สีของเงา
  elevation: 5.0, // การยกของเงา
  margin: EdgeInsets.all(8.0), // การเยื้องขอบ
  shape: BeveledRectangleBorder( // รูปแบบ
    borderRadius: BorderRadius.circular(20),
    side: BorderSide(
      width: 2,
      color: Colors.blue,
      style: BorderStyle.solid,
    ),
  ),
  child: ListTile(
    title: Text(article.title),
  ),
);
 
    ผลลัพธ์
 


 
 
    ดูการปรับแต่งค่าเพิ่มเติม Card Widget
 
 

    แนะนำเพิ่มเติม เกี่ยวกับการกำหนด Padding กับ Margin

    ในการใช้งาน padding และ margin เราควรรู้จักรูปแบบการกำหนดค่า ดังนี้ไว้คือ ให้เข้าใจว่า margin
ก็คือห่างจากขอบด้านนอก ส่วน padding ก็คือห่างจากขอบด้านใน เราจะใช้รูปแบบเดียวกันในการกำหนดคือ
 
    ถ้ากำหนดเป็นคู่ เช่น คู่บนล่าง(แนวตั้ง) หรือ คู่ซ้ายขวา(แนวนอน) จะใช้เป็น
const EdgeInsets.symmetric(vertical: 8.0) // horizontal | vertical
 
    ถ้ากำหนดเหมือนกันทุกด้าน จะใช้เป็น
const EdgeInsets.all(8.0)
 
    ถ้าอย่างเลือกกำหนดด้านใด ก็ได้ หนึ่งด้าน สองด้าน หรือทั้งสี่ด้าน  แบบอันไหนก่อนหลังก็ได้ ใช้เป็น
const EdgeInsets.only(left: 40.0) // left | top | right | bottom
const EdgeInsets.only(left: 40.0, right:10.0) // กำหนดสองด้าน
const EdgeInsets.only(left: 40.0, right: 10.0,bottom: 5.0) กำหนดสามด้าน
 
    ถ้าอย่างกำหนดทุกด้านแบบ เรียกตามรูปแบบ ซ้ายบนขวาล่าง จะใช้เป็น 
// แบบนี้ต้องกำหนดทุกด้านตามรูปแบบ สลับตำแหน่งกันไม่ได้
const EdgeInsets.fromLTRB(double left, double top, double right, double bottom)
 
    ตัวอย่างการเพิ่ม widget อื่นเข้าไปใน card นอกจาก ListTile
 
Card(
  margin: const EdgeInsets.all(5.0), // การเยื้องขอบ
  child: Column(
    children: [
      ListTile(
        title: Text(article.title),
      ),
      const Divider(),
      Row(
        mainAxisAlignment: MainAxisAlignment.start,
        children: <Widget>[
          TextButton(
            child: const Text('Like'),
            onPressed: () {/* ... */},
          ),
          const SizedBox(width: 8),
          TextButton(
            child: const Text('Comment'),
            onPressed: () {/* ... */},
          ),
          const SizedBox(width: 8),
          Expanded(
            child: Container(
              color: Colors.green.withAlpha(50),
              alignment: Alignment.centerRight,
              child: TextButton(
                child: const Text('Share'),
                onPressed: () {/* ... */},
              ),
            ),
          ),
        ],
      ),
    ],
  )
);
 
    ผลลัพธ์
 

 
 
 
    สำหรับเนื้อหาเกี่ยวกับการใช้งาน card widget ก็มีประมาณนี้ เนื้อหาตอนหน้า เราจะมาประยุกต์เพิ่มเติม
โดยจะใช้รูปแบบการแสดงจากผลลัพธ์ล่าสุดนี้ไปใช้งาน รอติดตาม


   เพิ่มเติมเนื้อหา ครั้งที่ 1 วันที่ 27-07-2024


ดาวน์โหลดโค้ดตัวอย่าง สามารถนำไปประยุกต์ หรือ run ทดสอบได้

http://niik.in/download/flutter/demo_008_27072024_source.rar


กด Like หรือ Share เป็นกำลังใจ ให้มีบทความใหม่ๆ เรื่อยๆ น่ะครับ



อ่านต่อที่บทความ



ทบทวนบทความที่แล้ว









เนื้อหาที่เกี่ยวข้อง









URL สำหรับอ้างอิง





คำแนะนำ และการใช้งาน

สมาชิก กรุณา ล็อกอินเข้าระบบ เพื่อตั้งคำถามใหม่ หรือ ตอบคำถาม สมาชิกใหม่ สมัครสมาชิกได้ที่ สมัครสมาชิก


  • ถาม-ตอบ กรุณา ล็อกอินเข้าระบบ
  • เปลี่ยน


    ( หรือ เข้าใช้งานผ่าน Social Login )







เว็บไซต์ของเราให้บริการเนื้อหาบทความสำหรับนักพัฒนา โดยพึ่งพารายได้เล็กน้อยจากการแสดงโฆษณา โปรดสนับสนุนเว็บไซต์ของเราด้วยการปิดการใช้งานตัวปิดกั้นโฆษณา (Disable Ads Blocker) ขอบคุณครับ