avatar
thiti.dev
วงแหวนเว็บ
Search
Contact
Donate
Flutter EP.5 จัดการ State ใน flutter ด้วย Provider
6 Jun 2020
flutter ios android dart

สวัสดีครับ ใน EP.4 เราได้เรียนรู้ Project structure file flutter กันไปแล้วนะครับ ในบทความนี้เราจะมาเรียนรู้การจัดการ State ใน Flutter ด้วย Provider กันครับ

โดยปกติเวลาที่เราจะเปลี่ยนข้อมูลใน State เพื่อไป Update UI เราสามารถทําได้ด้วยการใช้คําสั่ง setState() ได้ตามตัวอย่างนี้ (อ่านเพิ่มเติมได้จาก EP.3)

setState((){
   title = "New title";
});

เมื่อมีการ setSate() จะทําให้ Widget นั้น build Widget ใหม่ทั้งหมด ทําให้ประสิทธิภาพของแอปไม่ดีอย่างที่ควรจะเป็น และในความเป็นจริง Application ของเราไม่ได้มีแค่ Widget เดียว และในแต่ละ Widget มีการใช้งาน State ร่วมกัน จึงต้องมี State Management เข้ามาช่วยเพื่อให้เราจัดการ State ได้ง่ายขึ้น

State Management มีอยู่ด้วยกันหลายตัวเช่น Provider, Redux, BLoC/Rx, MobX สําหรับบทความนี้เราจะมาเรียนรู้การจัดการ State ด้วย Provider กันครับ

แนวคิดของการจัดการ State คือจะสร้าง State เก็บไว้ที่เดียว และเมื่อ Widget ไหนต้องการใช้งานก็จะไปหยิบมาใช้ ตัวอย่างตามรูปด้านล่างครับ

Image

การใช้งาน Provider มีดังนี้

Setp1: เพิ่ม Dependencies

เพิ่ม dependencies ของ provider ใน pubspec.yaml

dependencies:
  flutter:
    sdk: flutter

  ...
  provider: ^4.0.0

Step2: สร้าง Model Provider

การสร้าง Model Provider เพื่อเก็บข้อมูลต่างๆที่เราต้องการ และ Method การทํางานต่างๆ รวมไปถึงการ Update Widget เมื่อข้อมูล State เปลี่ยนแปลง ซึ่งการสร้าง Model Provider คือให้เรา สร้าง Class โดย extends class ChangeNotifier และเมื่อต้องการ Update UI เราก็จะเรียก Function notifyListener() ในบทความนี้เราจะยกตัวอย่างสร้างขึ้นมา 2 Model Provider ดังนี้

class CounterProvider extends ChangeNotifier {
  int counter;
  CounterProvider({this.counter = 0});

  increment() {
    counter++;
    notifyListeners();
  }
}

class CounterProvider2 extends ChangeNotifier {
  int counter;
  CounterProvider({this.counter = 0});

  increment(int number) {
    counter += number;
    notifyListeners();
  }
}

Step3: ใช้ Provider ใน Widget

ก่อนอื่น import provider เข้ามาก่อน

import 'package:provider/provider.dart';

เราจะใช้ ChangeNotifierProvider กับ Widget ที่ต้องการจะใช้งาน State ครับ

ในตัวอย่างนี้เราจะใส่ไว้ใน Widget บนสุดเลย ตามนี้ครับ

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.teal,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MultiProvider(
        providers: [
          ChangeNotifierProvider(create: (_) => CounterProvider(counter: 0)),
          ChangeNotifierProvider(create: (_) => CounterProvider2(counter: 0))
        ],
        child: MyHomePage(title: "My Home")
      ),
    );
  }
}

หรือกรณีที่เราจะใช้งานแค่ 1 Provider ก็สามารถทําแบบนี้ได้ครับ

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.teal,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: ChangeNotifierProvider(
        create: (_) => CounterProvider(counter: 0),
        child: MyHomePage(title: "My Home"),
      ),
    );
  }
}

เมื่อเรากําหนด ChangeNotifierProvider แล้ว ใน Widget MyHomePage ของเราจะต้อง get Object ของ Provider ที่เราต้องการโดยใช้คําสั่ง Provider.of() ตามตัวอย่างนี้

...

CounterProvider counterProvider = Provider.of<CounterProvider>(context);
CounterProvider2 counterProvider2 = Provider.of<CounterProvider2>(context);

...

หลังจากนั้นเมื่อเราต้องการจะนําข้อมูล State มาแสดงผลก็สามารถทําได้โดยใช้ Consumer ตามตัวอย่างนี้

...

Consumer<CounterProvider>(
  builder: (context, data, child) => Text(
    '${data.counter}',
    style: Theme.of(context).textTheme.headline4,
  ),
),

...

แต่ในบางครั้งเราต้องการใช้งานหลาย Provider ก็สามารถทําได้ครับโดยใช้ Consumer2, Consumer3, Consumer4, Consumer(N) โดยใช้งานได้สูงสุด 6 Provider ครับ ตามตัวอย่างนี้

...

Consumer2<CounterProvider, CounterProvider2>(
  builder: (context, data, data2, child) => Text(
    '${data.counter} --> ${data2.counter}',
    style: Theme.of(context).textTheme.headline4,
  ),
),

...

กรณีที่ต้องการจะ Update State เราก็ใช้งานผ่าน Object Provider ที่เรา get มาตอนต้นครับตามตัวอย่างนี้

...

floatingActionButton: FloatingActionButton(
  onPressed: () {
    counterProvider.increment();
    counterProvider2.increment(2);
    },
  tooltip: 'Increment',
  child: Icon(Icons.add),
),

...

มาดูตัวอย่างเต็มๆกันครับประมาณนี้

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    CounterProvider counterProvider = Provider.of<CounterProvider>(context);
    CounterProvider2 counterProvider2 = Provider.of<CounterProvider2>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Consumer2<CounterProvider, CounterProvider2>(
              builder: (context, data, data2, child) => Text(
                '${data.counter} --> ${data2.counter}',
                style: Theme.of(context).textTheme.headline4,
              ),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          counterProvider.increment();
          counterProvider2.increment(2);
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

การใช้งานก็ประมาณนี้ครับ

แล้วพบกันในบทความหน้านะครับ ขอบคุณครับ