Sign In

Flutter의 상태 관리 - Controller (Provider)

Y
yeji Kim

참고 자료

Controller

컨트롤러는 사용자 입력을 처리하고 모델을 업데이트함.

Provider

Key concepts

ChangeNotifier - listener에게 변화에 대해 알림.
import 'package:flutter/foundation.dart';

class Counter with ChangeNotifier {
  int _count = 0;

  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}
Provider - widget tree에서 inject, access
ChangeNotifierProvider
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => Counter(),
      child: MyApp(),
    ),
  );
}
ChangeNotifier가 변하면 dependents를 rebuild함.
Consumer - object가 변하면 스스로를 rebuild함.
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter.dart';

class CounterScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Provider Counter Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text('You have pushed the button this many times:'),
            Consumer<Counter>(
              builder: (context, counter, child) {
                return Text(
                  '${counter.count}',
                  style: Theme.of(context).textTheme.headline4,
                );
              },
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          Provider.of<Counter>(context, listen: false).increment();
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}

사용 목적

위와 같이 동일 상태 (데이터)를 전역적으로 다른 위젯과 공유하기 위해
최상단 위젯에 Provider 제공
void main() {
  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => Counts()),
      ],
      child: MyApp(),
    ),
  );
}
class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Provider'),
      ),
      body: ChangeNotifierProvider(
        create: (BuildContext context) => Counts(),
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Counter(),
              Buttons(),
            ],
          ),
        ),
      ),
    );
  }
}
Provider를 사용하는 위젯
class Counter extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print('Counter');

    return Text(
      context.watch<Counts>().count.toString(),
      style: TextStyle(
        fontSize: 20,
      ),
    );
  }
}
class Buttons extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        ElevatedButton(
            onPressed: () {
              context.read<Counts>().add();
            },
            child: Icon(Icons.add)),
        SizedBox(
          width: 40,
        ),
        ElevatedButton(
            onPressed: () {
              context.read<Counts>().remove();
            },
            child: Icon(Icons.remove))
      ],
    );
  }
}

Provider의 함수

read - 상태 값을 읽음. 감시하진 않음. Provider의 값을 변경하는 함수에 많이 쓰임.
watch - 상태 값의 변화를 감시함. 상태 값을 사용할 때.
select - 상태 값의 특정 부분 만을 감시함.
변경된 상태 값을 표시하기 위한 rebuild에 많은 비용이 드는데, 이 때 select를 이용하여 특정 값만의 변경을 감시하여 최적화할 수 있음.

Provider 선언하기

단일 Provider
MaterialApp(
  title: 'Flutter Provider Demo',
  home: ChangeNotifierProvider(
    create: (BuildContext context) => CounterProvider(),
    child: Home(),
  ),
);
다중 Provider
MaterialApp(
  title: 'Flutter Provider Demo',
  home: MultiProvider(
    providers: [
      ChangeNotifierProvider(
        create: (BuildContext context) => CounterProvider(),
      ),
      ChangeNotifierProvider(
        create: (BuildContext context) => TodoProvider(),
      ),
    ],
    child: Home(),
  ),
);

Provider 사용하기

Provider.of
@override
Widget build(BuildContext context) {
  CounterProvider counter = Provider.of<CounterProvider>(context);
  return Center(
    child: Text(
      counter.count.toString(),
    ),
  );
}
Provider에 직접 접근해서 받아오기.
현 위젯 전체가 리로드된다.
Consumer
return Center(
  child: Consumer<CounterProvider>(
  	builder: (context, provider, child) {
      return Text(
        provider.count.toString(),
      );
    },
  ),
);
Consumer를 사용한 해당 부분만 re build됨.
아무튼-작업일지
Subscribe to '아무튼-작업일지'
Subscribe to my site to be the first to receive notifications and emails about the latest updates, including new posts.
Join Slashpage and subscribe to '아무튼-작업일지'!
Subscribe
👍