ทำ PageView แบบง่ายด้วย Flutter18 November 2020Programing

ทำ PageView แบบง่ายด้วย Flutter

ตัวอย่างผลลัพธ์จาก iPhone 11

ทุกวันนี้เมื่อเราเข้าแอปพลิเคชั่นต่าง ๆ จากในมือถือครั้งแรกจากที่โหลดมาใหม่ ๆ นั้นจะชอบมีหน้าตาคล้าย ๆ โฆษณาแบบเต็มหน้าจอ ที่ให้เราเลื่อนไปดูหลาย ๆ หน้า บางแอปฯ ก็ใช้เป็นระบบ Tutorial ในการใช้งาน เอาจริง ๆ วิธีการทำก็ไม่ยาก ไม่ง่ายจนเกินไป หากใครสนใจลองทักติดต่อมาที่บริษัท Twinsynergy ของพวกเราได้เลยครับ หรือใครอยากลองทำเองดูก็ลองอ่านให้จบน้า

สำหรับ Path ของ Flutter และคิดว่าจะทำต่อออกเป็นหลายๆ Path สำหรับ Flutter ส่วนตัวรู้สึกว่ามันมีให้เล่นเยอะดีครับ และคราวนี้จะมาทดลองทำ PageView ถ้านึกภาพไม่ออกมันก็เหมือนกับเราเข้า App สัก App หนึ่งแล้วหน้าแรกมันจะเป็นสไลด์เลื่อนๆ ให้เรา เผือใครสนใจเอาไปทำกับงานตัวเอง ผู้เขียนยังใหม่มากใน Flutter หากอธิบายไม่เข้าใจหรือทำให้งงต้องขออภัยและจะรีบนำมาปรับปรุงครับ

สิ่งที่เราต้องเตรียม

สร้างโปรเจ็ค

flutter create project-name // พิมพ์ command สร้างโปรเจ็ค เราต้อง set path ให้ flutter ก่อนนะ

flutter doctor //เช็ค sdk, device ของเราว่าพร้อมหรือยัง

ผลลัพธ์คำสั่ง flutter doctor
โครงสร้าง Project ที่ได้จาก create
ทดสอบ run ด้วย flutter run
  1. ให้เราเตรียมรูปสำหรับจะใช้ได้งานของเราโดยการสร้าง directory ชื่อ assets ต่อด้วย directory ชื่อ images ให้เรานำรูปมาใส่ไว้
  2. เข้าไป config pubspec.yaml เพื่อจะ setup path รูปมาใช้ในงานของเรา ตรง assets ให้เรา set path ตามที่เราตั้งไว้ในข้อ 1

  • ให้เรา เข้าไป /lib ไฟล์หลักของเราคือ main.dart ที่จะใช้ develop กัน
import 'package:flutter/material.dart';
void main() { 
   runApp(MyApp());
}
class MyApp extends StatelessWidget { 
  @override 
  Widget build(BuildContext context) { 
    return MaterialApp( 
            debugShowCheckedModeBanner: false, 
            title: 'Flutter Demo', 
            theme: ThemeData(), 
            home: HomePage(), );
   }
}

เอาละจาก Code ด้านบน ใน function main จะเรียกใช้งาน class MyApp ซึ่งตัว Widget ที่ return MaterialApp ตัว Flutter จะให้มาผมเปลียน parameter เป็น class HomePage ที่ผมสร้างขึ้นมา

เกริ่นก่อนนะ ใน flutter เวลาเราเขียน class ขึ้นมาจะมีสิ่งหนึ่งเรา extends มาคือ Statefullwidget และก็ Statelesswidget ซึ่งไอ้ Statefullwidget ให้เราสามารถ handle state widget ใน class ของเราได้ ยกตัวอย่างเช่นตอนเรา fetch ข้อมูลมาจาก api ระหว่างรอข้อมูลจาก api เราต้องการให้แสดง widget ที่บอกว่ากำลังโหลดข้อมูลอยู่นะ แต่ถ้า Statelesswidget จะตรงกันข้ามเลยคือไม่สามารถ handle state ใน class ได้

ซึ่งในงานนี้ผมก็เรียกใช้ Statefullwidget มาใช้งานเหมือนกันสำหรับทำให้ widget ผมมีการเปลี่ยนแปลงเมื่อกดปุ่ม

class HomePage extends StatefulWidget {
  HomePage({Key key}) : super(key: key);

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {

  @override
  void initState() {
    super.initState();
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }
 @override
  Widget build(BuildContext context) {
    return Container();
  }
}

จาก code ด้านบนผมสร้าง class HomePage ที่สืบทอดคุณสมบัติ StatefulWidget มาด้วยซึ่งใน class มันจะประกอบไปด้วย initState, dispose, build ซึ่ง initState จะใช้ได้แค่ class ที่ extends StatefulWidget มาเท่านั้นนะ จะใช้ในกรณีเราต้องการกำหนดค่าตัวแปรให้ class ของเราเพื่อไป handle widget

ส่วน build คือหน้า Widget หลักของ class แล้วเดียวไปดูต่อว่าใน class ผมเขียนอะไรไว้บ้าง

@override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        elevation: 0,
        backgroundColor: Colors.white,
        actions: <Widget>[
          Padding(
            padding: EdgeInsets.only(right: 20, top: 20),
            child: Text(
              "Skip",
              style: TextStyle(
                  color: kPrimaryColor,
                  fontSize: 18,
                  fontWeight: FontWeight.w600),
            ),
          )
        ],
      ),
      body: Stack(
        alignment: Alignment.bottomCenter,
        children: <Widget>[
          PageView(
            onPageChanged: (int page) {
              setState(() {
                currentIndex = page;
              });
            },
            controller: _pageController,
            children: <Widget>[
              pageViewer(
                  content:
                      "สวัสดีครับเรามาร่วมสนุกกับการ Coding กันเถอะ! พิเศษทำวันนี้ปวดหลังวันนี้ ทำวันหน้าปวดหลังวันหน้า",
                  image: "assets/images/step-one.png",
                  title: "Hi guys"),
              pageViewer(
                  content:
                      "ร่วมงานกับเรานอกจากงานแล้วยังมีกิจกรรมสนุกมากมาย ทั้งกิน ปาตี้ ตั้งแคมป์ให้เราๆได้ผ่อนคลาย",
                  image: "assets/images/step-two.png",
                  title: "Twin Synergy Campping",
                  reserve: true),
              pageViewer(
                  content:
                      "กิจกรรมค่ายอาสาก็เป็นส่วนหนึ่งของงาน การเป็นผู้ให้มีความสุขเสมอ ส่งต่อความสุข ส่งต่อการให้",
                  image: "assets/images/step-three.png",
                  title: "The Volunteer")
            ],
          ),
          Container(
            margin: EdgeInsets.only(bottom: 40),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: _buildIndicator(),
            ),
          )
        ],
      ),
    );
  }

ใน code ผมใช้ Scaffold ซึ่งใน Scaffold จะมาเรียก properties หลักๆได้คือ Appbar, body

Appbar ตัว elevation คือเหงาของ appbar, backgroundColor คือสีของ appbar, actions เราต้องใส่ Array of widget ซึ่งผมก็ใส่ตัว Padding ได้กับตัวหนังสือ

body ผมใช้ Stack ใน flutter widget Stack คือการกองๆ widget ซ้อนๆกันไปเรื่อยๆ ถ้าเกิดเราไม่ตั้งตำแหน่งให้มันมันก็จะกองๆกันไปเรื่อยๆ ซึ่งใน code ที่ผมเขียน Stack เราจะเป็นต้องใส่ children ซึ่งเป็น array ของ widget ซึ่งผมใส่ PageView พระเอกของงานเรา กับตัว Container ผมเขียนขึ้นมากเป็นแสดงให้เห็นว่าเราอยู่ในหน้าไหนของ Page

onPageChanged คือฟังก์ชันสำหรับ set เลขหน้าให้ page

pageViewer คือฟังก์ชั่นที่ผมเขียนมา return widget โดยจะมีพารามิเตอร์ที่ผมกำหนด content,image,title,reserve

 Widget pageViewer({image, title, content, reserve = false}) {
    return Container(
      padding: EdgeInsets.only(left: 50, right: 50, bottom: 50),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          !reserve
              ? Column(
                  children: <Widget>[
                    Padding(
                        padding: EdgeInsets.symmetric(horizontal: 20),
                        child: Image.asset(image)),
                    SizedBox(height: 30),
                  ],
                )
              : SizedBox(),
          Text(
            title,
            style: TextStyle(
                color: kPrimaryColor, fontSize: 30, fontWeight: FontWeight.bold),
          ),
          SizedBox(height: 30),
          Text(
            content,
            textAlign: TextAlign.center,
            style: TextStyle(
                color: kPrimaryLightColor, fontSize: 20, fontWeight: FontWeight.w400),
          ),
          reserve
              ? Column(
                  children: <Widget>[
                    SizedBox(height: 30),
                    Padding(
                        padding: EdgeInsets.symmetric(horizontal: 20),
                        child: Image.asset(image)),
                    SizedBox(height: 30),
                  ],
                )
              : SizedBox(),
        ],
      ),
    );
  }

ใน code ด้าบนผมในฟังก์ชันนี้ผม return widget ซึ่งประกอบไปด้วย รูปภาพ รายละเอียด หัวข้อของเนื้อหน้านี้ และก็ตำแหน่งของ widget

Widget _indicatior(bool isActive) {
      return AnimatedContainer(
        duration: Duration(milliseconds: 300),
        height: 8,
        width: isActive ? 30 : 8,
        margin: EdgeInsets.only(right: 5),
        decoration: BoxDecoration(
            color: kPrimaryColor, borderRadius: BorderRadius.circular(5)),
      );
    }

    List<Widget> _buildIndicator() {
      List<Widget> indicators = [];
      for (var i = 0; i < 3; i++) {
        if (currentIndex == i) {
          indicators.add(_indicatior(true));
        } else {
          indicators.add(_indicatior(false));
        }
      }
      return indicators;
    }

ใน code ผมเขียน widget ที่แสดง indicators คือตัวขีดๆ อันไหนที่ active ก็จะให้เป็นขีดยาว ถ้า unactive ก็ให้ขีดสั้น

code overview สามารถดูได้ที่ https://github.com/najaroen/login-screen-mobile-app
เราสามารถทำได้เพียงไม่กี่ขั้นตอนใน Flutter นี้อาจจะเป็นเพียงตัวอย่างเล็กๆ น้อยๆ หวังว่าจะสามารถนำไปประยุกต์ใช้งานได้กับงานได้นะครับ ถ้าผมไม่อธิบายเคลียร์หรือไม่เข้าใจก็ขออภัยด้วยนะ ผมจะเอาไปปรับปรุงครับ Happy Coding หรือ ใครอยากลองทักมาปรึกษา หรือจ้างทำก็ได้นะครับ มาคุยรายละเอียดกันได้เลย!

📌สนใจติดต่อ📌
💻 twinsynergy.co.th
📱 063-789-9059

ก่อนหน้า
ถัดไป