[flutter] 어렵기만 했던 상태관리 패키지 provider 뿌시기- 1

provider란?

  • 상태를 관리하는 툴!
  • 플러터에서는 상태를 관리하는 여러가지 방법이 있지만, 공식적으로 지원하는 패키지는 존재하지 않으며 다양한 패키지 중에 사용자가 가장 많은 패키지이다.

프로바이더를 왜 사용하는지

  • 플러터는 리액트와 비슷한 형태로 렌더링 되는데, 이 과정에서 데이터를 전달하기 위해 중간에 필요없는 위젯이 생기는 현상이 발생할 수 있다. -> 이를 막기위해 프로바이더를 써서 전역에서 데이터를 관리해준다.

프로바이더의 역할

  1. 위젯에서 필요한 데이터에 쉽게 접근할 수 있다.
  2. 데이터가 변하면 UI를 다시 그린다.
  3. 위젯에 데이터를 전달해서 데이터가 변하면 UI를 다시 그린다. -> 비즈니스 로직을 분리할 수 있다.
  4. 프로바이더 자체를 위젯으로 사용한다!

Provider

  • App의 최상단에 선언 후 데이터가 필요한 위젯에서 호출해서 사용함.
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
  //materialApp을 provider로 감싸줬다.
    return Provider<Dog>(create: (context)=> Dog(name:'happy',breed:'Pomeranian',age:1),
    child:
    MaterialApp(
      title: 'Provider 02',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(),
    )
    );
  }
}

//다른 위젯에서 프로바이더를 호출했다.
class BreedAndAge extends StatelessWidget {
  const BreedAndAge({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(
          '- breed: ${Provider.of<Dog>(context).breed}',
          style: TextStyle(fontSize: 20.0),
        ),
        SizedBox(height: 10.0),
        Age(),
      ],
    );
  }
}

ChangeNotifier

  • 위젯에 listen값을 알려줌!
    -listen이 true면 위젯을 새로 그려줌.
  • UI를 새로 그려줄 필요가 없으면 listen을 false로 해줘야함.
    (ex.버튼)
  • addListener로 콜백함수를 실행할 수 있음.
    -> 추가된 리스너는 dispose를 수동으로 꼭 해줘야함.
    (자동으로 사라지지 않음)
//models/dog.dart
class Dog with ChangeNotifier {
  final String name;
  final String breed;
  int age;
  Dog({
    required this.name,
    required this.breed,
    this.age = 1,
  });

  void grow() {
    age++;
    notifyListeners();
    print('age:$age');
  }
}

//main.dart

//dog클래스를 불러와서 값을 초기화해준뒤 사용함.
//init과 dispose를 통해 리스너를 추가해주고 삭제함.
class _MyHomePageState extends State<MyHomePage> {
  final dog = Dog(name: 'cando', breed: 'breed03');

  
  void initState() {
    super.initState();
    dog.addListener(dogListener);
  }

  void dogListener() {
    print('age listener:${dog.age}');
  }

  
  void dispose() {
    dog.removeListener(dogListener);
    super.dispose();
  }

ChangeNotifierProvider

  • changeNotifier 인스턴스를 만들어서 프로바이더로 인스턴스에 쉽게 접근하기!
  • Provider.of<T>(context)를 통해 필요한 데이터에 쉽게 접근하고 ui를 리빌드함.
//models/dog.dart

class Dog with ChangeNotifier {
  final String name;
  final String breed;
  int age;
  Dog({
    required this.name,
    required this.breed,
    this.age = 1,
  });

  void grow() {
    age++;
    notifyListeners();

  }
}



//main.dart
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<Dog>(
      create: (context)=> Dog(name:'hey',breed:'mumI'),
      //child하위의 widget은 모두 provider에 접근 가능함
      child: MaterialApp(
        title: 'Provider 04',
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: const MyHomePage(),
      ),
    );
  }
}

class BreedAndAge extends StatelessWidget {


  const BreedAndAge({Key? key }) : super(key: key);

  
  Widget build(BuildContext context) {
    return Column(
      children: [
      //이런식으로 접근 할 수 있음!! 
      //리슨 설정도 가능함
        Text(
          '- breed: ${Provider.of<Dog>(context,listen:false).breed}',
          style: TextStyle(fontSize: 20.0),
        ),
        SizedBox(height: 10.0),
        Age(),
      ],
    );
  }
}

Provider extension methods

a.k.a 프로바이더 간편하게 쓰기

  • Provider.of로 시작하는 프로바이더의 옵션들을 대치어를 통해 짧게 줄일 수 있음.
  • Provider 선언후 Provider.of대신 아래의 context.~~를 사용하면 됨!
  1. context.read<T>()
  • Provider.of(context,listen:false)
  • listen이 false인 프로바이더에 접근할 때
    ElevatedButton(
    //context.read<Dog>: Provider.of<Dog>(context,listen:false)
            onPressed: () => context.read<Dog>().grow(),
            child: Text(
              'Grow',
              style: TextStyle(fontSize: 20.0),
            ),
          ),
  1. context.watch<T>()
  • Provider.of(context)
  • 가장 가까운 프로바이더 값에 접근함
    Text(
              //Provider.of<Dog>(context).name
                '- name: ${context.watch<Dog>().name}',
                style: TextStyle(fontSize: 20.0),
              ),
  1. context.select<T,R>(R selector(T vlaue))
  • 프로바이더의 특정 값에 접근 가능함
  • context.select<Dog, String>((Dog dog) => dog.name)
  • context.select<클래스,반환형> :Provider.of(context)
	Text(
          // context.select<클래스,반환형> :Provider.of<Dog>(context)
          '- breed: ${context.select<Dog,String>((Dog dog)=> dog.breed)}',
          style: TextStyle(fontSize: 20.0),
        ),

요 강의를 듣고 정리한 내용입니다 :)

좋은 웹페이지 즐겨찾기