web analytics

Material Motion ใน Flutter ตอนที่ 2

สวัสดีผู้อ่านครับ จากตอนที่แล้วของ Material Motion ผมได้ลองเล่น Container transform ใน Flutter ไปแล้ว เป็นหนึ่งใน Motion ที่เจ๋งมาก และใน Material motion ยังมีให้ลองเล่นอีกหลายตัวเลย ในบล็อกนี้จะมาลองเล่น Motion อีกตัวหนึ่งที่ชื่อว่า Shared Axis ครับ มาลองเล่นกันเลย

ใครยังไม่ได้อ่านตอนที่ 1 อ่านได้ที่ลิงค์ข้างล่างนี้นะ

Shared axis

Shared axis คือ transition pattern แบบหนึ่งที่ทำ transition โดยจะ fade และเคลื่อนไหวในแนวแกน X, Y หรือ Z โดยเรียกง่ายๆว่า แกน X คือแนวนอน และแกน Y คือแนวตั้งก็ได้ สามารถใช้กับ widget ที่สามารถเคลื่อนที่แบบมีความสัมพันธ์แนวนอน แนวตั้งได้นั่นเอง เช่น PageView

ตัวอย่างการใช้ในแกน X (แนวนอน)

ตัวอย่าง แกน Y (แนวตั้ง)

ตัวอย่าง แกน Z (ด้านหลัง-ด้านหน้า)

ลองดูตัวอย่างอื่นๆเพิ่มเติมได้ที่เว็บไซต์ของ Material Design นะ
https://material.io/design/motion/the-motion-system.html#shared-axis

เริ่มต้นใน Flutter

หากยังไม่ได้เพิ่ม dependencies ให้เพิ่มก่อนนะ ชื่อว่า animations ที่ไฟล์ pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  ...
  animations: 1.0.0+5  

Shared Axis จะใช้งานแตกต่างจาก Container transform จากในตอนที่แล้วนิดนึงคือ Container transform จะทำ transition ที่ซับซ้อนกว่า เพราะต้องทำให้ widget 2 ตัวมี transition ร่วมกัน ทำให้เกิดเป็น OpenContainer ที่ใช้งานในรูปแบบ Widget ที่มี builder แต่ใน Shared Axis นั้นไม่ได้ใช้เทคนิคที่ซับซ้อนแบบนั้น จึงเป็นคลาสที่ใช้งานแบบ Transition ทั่วไป ที่มีชื่อว่า SharedTransition

ผมเคยเขียนสรุปเรื่อง Route transition ใน Flutter หากยังไม่ได้อ่าน สามารถอ่านเป็นพื้นฐานได้นะ

ซึ่ง SharedTransition ก็ใช้งานแบบเดียวกับ transition ทั่วไปๆ เช่น การใช้กับการเปลี่ยนหน้า (Navigate) โดยใช้ PageRouteBuilder

    Navigator.push(
        context,
        PageRouteBuilder(
            transitionDuration: Duration(milliseconds: 1000),
            transitionsBuilder: (context, animation, secondaryAnimation, child) {
              return SharedAxisTransition(
                child: child,
                animation: animation,
                secondaryAnimation: secondaryAnimation,
                transitionType: SharedAxisTransitionType.horizontal,
              );
            },
            pageBuilder: (context, animation, secondaryAnimation) {
              return DetailScreen();
            }));

โดยมี Transition Type ให้เลือก 3 แบบคือ horizontal , vertical , scaled หรือก็คือตามแนว X Y Z นั่นเอง

ลองใช้แบบ vertical ดูบ้าง มีความแอบคล้าย transition ใน MaterialPageRoute นะ

อีกแบบ คือแบบ scaled หรือก็คือแกน Z

ลองใช้กับ PageTransitionSwitcher

ลองมาเล่นเพิ่มเติมกัน อย่างที่บอกไปว่า Shared Axis จะเหมาะกับเอาใช้งานร่วมกับพวก widget ที่มีลักษณะคล้าย PageView ได้ จึงมี Widget ตัวนึงที่สามารถนำมาใช้ได้คือ PageTransitionSwitcher

PageTransitionSwitcher หลักการของมันก็คือ ทำให้ widget ลูกของมันมี transition ตอนที่มีการเปลี่ยนค่า เราจะลองเอา Shared Axis มาใช้งานกัน

โดยผมจะเพิ่ม icon เพื่อจะสามารถปรับเปลี่ยนโหมดแสดงผลได้ คือ List และ Grid ก็ทำการเพิ่ม Enum และตัวแปรเก็บค่าแบบง่ายๆดังนี้

enum DataViewMode { LIST, GRID }

class _MyHomeState extends State<MyHome> {
  DataViewMode viewMode = DataViewMode.LIST;
  ...

จากนั้นผมได้เพิ่ม action 2 ตัวให้กับ AppBar เพื่อเอาไว้เปลี่ยนโหมด โดยเมื่อกดที่ icon ก็จะ setState เพื่อเปลี่ยนค่าตามโหมดที่เรากำหนดไว้

@override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Material Motion"),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.view_list),
              onPressed: () {
                setState(() {
                  viewMode = DataViewMode.LIST;
                });
              },
            ),
            IconButton(
              icon: Icon(Icons.grid_on),
              onPressed: () {
                setState(() {
                  viewMode = DataViewMode.GRID;
                });
              },
            ),
          ],
        ),
        ...

ตัวอย่างนี้ผมลองสร้าง ListView และ GridView แบบง่ายๆขึ้นมา ส่วน item ก็แล้วแต่ออกแบบเลยนะ


  Widget buildListView() {
    return ListView(
      children: <Widget>[
        buildItemList(),
        buildItemList(),
        buildItemList(),
        buildItemList(),
      ],
    );
  }

  Widget buildGridView() {
    return GridView(
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
      children: <Widget>[
        buildItemGrid(),
        buildItemGrid(),
        buildItemGrid(),
        buildItemGrid(),
      ],
    );
  }

ได้เวลาใช้ PageTransitionSwitcher แล้ว วิธีการง่ายมาก คือกำหนด duration , transition และ child

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        ...
        body: PageTransitionSwitcher(
          duration: const Duration(milliseconds: 500),
          transitionBuilder: (
            Widget child,
            Animation<double> animation,
            Animation<double> secondaryAnimation,
          ) {
            return SharedAxisTransition(
              child: child,
              animation: animation,
              secondaryAnimation: secondaryAnimation,
              transitionType: SharedAxisTransitionType.horizontal,
            );
          },
          child: viewMode == DataViewMode.LIST ? buildListView() : buildGridView(),
        ));
  }

จะเห็นว่า transition โดย default ของแบบ horizontal จะใช้แบบจากขวามาซ้าย ซึ่งเราเราสามารถเพิ่ม reverse ให้ PageTransitionSwitcher กรณีที่เรากดจากหน้าที่สองไปหน้าแรกอีกครั้ง เพื่อให้ transition ดูมีความ paging สมจริงมากขึ้น

PageTransitionSwitcher(
          reverse: viewMode == DataViewMode.LIST,
          ...

จบตอนที่ 2

ขอบคุณผู้อ่านที่ติดตามอ่านจนจบครับ บล็อกนี้พามาลองเล่น Shared Axis ไม่ได้ยากเลยใช่มัย ยังมีแบบ Material Motion แบบอื่นๆจะพาไปลองเล่นในตอนถัดไปนะครับ

สำหรับเนื้อหาสามารถอ่านเพิ่มได้ได้ที่ บล็อกและเว็บไซต์ของ Google Material Design ครับ
https://medium.com/google-design/implementing-motion-9f2839002016
https://material.io/design/motion/the-motion-system.html
https://pub.dev/packages/animations