Flutter: วิธีการใช้ Wrap เบื้องต้น

Wrap จะเป็น layout ที่ช่วยเรียง widgets ที่ต้องการแสดงในแนวนอนหรือแนวตั้ง เมื่อตัว widgets มีจำนวนเกิดกว่าขนาดของ Parent มันจะทำการตัดขึ้นแถวใหม่ให้อัตโนมัติ คล้ายกับการตัดคำขึ้นบรรทัดใหม่ใน Text ถือว่าเป็น widget ที่มีประโยชน์ และใช้งานบ่อยมาก ๆ และการใช้งานก็ง่าย หากต้องการให้มัน Scroll ได้ ก็สามารถหุ้มด้วย SingleChildScrollView

การสร้าง Wrap

ตัว constructor ประกาศไว้ดังนี้

const Wrap({
  Key? key,
  Axis direction = Axis.horizontal,
  WrapAlignment alignment = WrapAlignment.start,
  double spacing = 0.0,
  WrapAlignment runAlignment = WrapAlignment.start,
  double runSpacing = 0.0,
  WrapCrossAlignment crossAxisAlignment = WrapCrossAlignment.start,
  TextDirection? textDirection,
  VerticalDirection verticalDirection = VerticalDirection.down,
  Clip clipBehavior = Clip.none,
  List<Widget> children = const <Widget>[],
})

ค่าเริ่มต้นในการเรียง widgets คือ เรียงในแนวนอน เรียงชิดจุดเริ่มต้น(ปกติคือทางซ้าย หรือตามค่า textDirection)

การใช้งานเบื้องต้น

เนื่องจากตัว Warp ต้องการ TextDirection ดังนั้นหากไม่ได้อยู่ใน Inherite Widget ที่มีค่า TextDirection จำเป็นต้องกำหนดค่าตรงนี้ด้วย การใช้งานแบบทั่วไปคือ การใส่ widgets ที่ต้องการจะเอามาเรียงใน children ตัวอย่าง จะเอากล่องขนาด 50x50 มาเรียงใน Warp ที่ใส่ไว้ในกล่องสีเขียว เพื่อให้เห็นพื้นที่ของตัว Warp

import 'package:flutter/material.dart';

void main() {
  box(double size) => Container(
      width: size, height: size, color: Colors.amber,
      child: Center(child: Text('$size')));

  var wrap = Container(
      color: Colors.green,
      child: Wrap(children: [
        box(50), box(50), box(50), box(50), box(50), box(50), box(50)
      ]));

  runApp(MaterialApp(
    home: Scaffold(
      body: wrap,
    ),
  ));
}

การจัดเรียง widgets ใน Warp

จะเกิดอะไรขึ้น ถ้า widgets ไม่ทราบขนาด

ในการเรียง widgets ใน Warp ตัว Warp จะส่งค่า constrains ไปให้ widgets ใน children ดังนั้นหากเป็น widgets ที่สร้างเต็มพื้นที่ ก็จะขยายจนเต็มพื้นที่ Parent ตัวอย่างจะเป็นการแก้ไข box() ไม่ระบุความกว้าง และเทสีแบบสุ่ม เพื่อให้เห็นการเรียง widgets ดังนี้

import 'dart:math';
import 'package:flutter/material.dart';

void main() {
  bgColor() => Color(Random().nextInt(0xFFFFFF)).withOpacity(1.0);

  box(double size) => Container(
        // width: size,
        height: size,
        color: bgColor(),
        // child: Center(child: Text('$size'))
      );

  // ....  

widgets จะขยายจนเต็ม Parent และกลายเป็นเรียงแบบ Column

การปรับแต่งหน้าตา Warp

คำสั่งที่สามารถกำหนดรูปแบบหน้าของ Warp มีดังนี้

ช่องว่างระหว่าง widgets

ใส่ค่า spacing จะเป็นการกำหนดระยะช่องว่างระหว่าง widgets ตัวอย่างกำหนด spacing: 10.0

import 'dart:math';
import 'package:flutter/material.dart';

void main() {
  bgColor() => Colors.amber.withOpacity(0.5 + Random().nextDouble() * 0.5);

  box(int index) => Container(
      width: 25.0 + Random().nextInt(50),
      height: 25.0 + Random().nextInt(50),
      color: bgColor(),
      child: Center(child: Text(index.toString())));

  var wrap = Container(
      color: Colors.green,
      child: Wrap(
          spacing: 10.0,
          children: List.generate(30, (int index) => box(index))));

  runApp(MaterialApp(
    home: Scaffold(
      body: wrap,
    ),
  ));
}

เมื่อใช้ spacing เพื่อใส่ระยะช่องว่างระหว่าง widgets

ช่องว่างระหว่างแถว

ใส่ค่า runSpacing จะเป็นการกำหนดระยะช่องว่างระหว่างแถว ตัวอย่างกำหนด runSpacing: 10.0

  var wrap = Container(
      color: Colors.green,
      child: Wrap(
          runSpacing: 10.0,
          children: List.generate(30, (int index) => box(index))));

เมื่อใช้ runSpacing เพื่อใส่ระยะช่องว่างระหว่างแถว

รูปแบบการเรียง widgets ชิดซ้าย ชิดขวา ตรงกลาง หรือมีช่องว่าง

ใช้คำสั่ง alignment เพื่อระบุวิธีการจัดเรียง widget ในแถว ด้วยค่าใน WrapAlignment ทั้ง 6 แบบ

กำหนดค่า alignment ใช้ WrapAlignment ทั้ง 6 แบบ

สำหรับ runAlignment จะมีการทำงานคล้ายกันกับ alignment แต่ทำในส่วนของแถวแทน โดยจะมีผลก็ต่อเมื่อตัว Parent มีขนาดใหญ่กว่าตัว Warp

  var wrap = Container(
      height: 500.0, // Increase the container size to allow runAlignment to take effect.
      color: Colors.green,
      child: Wrap(
          runAlignment: WrapAlignment.spaceEvenly,
          children: List.generate(30, (int index) => box(index))));

กำหนดค่า runAlignment

รูปแบบการจัดเรียงของ widgets ในแต่ละแถว ชิดบน ล่าง หรือตรงกลาง

คำสั่ง crossAxisAlignment เพื่อกำหนดค่า WrapCrossAlignment ตัว widgets ในแต่ละแถว ชิดบน ล่าง หรือตรงกลาง

กำหนดค่า crossAxisAlignment เพื่อให้ widgets ในแต่ละแถวเรียงชิดขอบบน ล่าง หรือตรงกลาง

ข้อควรทราบในการกำหนดระยะห่างระหว่าง widgets

เนื่องจากคำสั่ง spacing runSpacing เป็นการกำหนดระยะห่างแบบตายตัว หากมีการกำหนดค่า spacing runSpacing แล้ว และยังไปกำหนดค่าใน alignment หรือ runAlignment เป็นค่า spaceXXXXX จะเกิด Exception error เนื่องจากมันทำงานซ้ำซ้อนกัน ดังนั้นสามารถเลือกใช้งานได้อย่างใดอย่างหนึ่งเท่านั้น

ตัวอย่าง การกำหนดค่า runSpacing และ runAlignment ที่กำหนค่าช่องว่างเป็น WrapAlignment.spaceEvenly พร้อมกัน แอปจะไม่สามารถทำงานได้และเกิด Exception error

  var wrap = Container(
      color: Colors.green,
      child: Wrap(
          runSpacing: 10.0, // Exception error
          runAlignment: WrapAlignment.spaceEvenly, // Exception error
          children: List.generate(30, (int index) => box(index))));