flutter Freezed

Pyo·2023년 9월 15일
0
post-thumbnail

Freezed

데이터 클래스에서 필요한 기능들을 Code Generation으로 제공해주는 라이브러리로 불변(immutable) 데이터 클래스를 안정적이고 쉽게 정의하고, 그 데이터 클래스에 대한 메서드와 편리한 기능을 자동으로 생성해 준다.

freezed를 사용하기 전에는 유튜브 강의에서 equatable을 사용해 보았는데 회사에서 freezed를 사용한다고 해서 급하게 유튜브를 통해 공부했던 기억이 난다. 오늘은 freezed의 정의와 간단한 코드로 기능에 대해서 작성해보고 , 다음에는 자세하고 실제로 어떤식으로 사용하는지 작성해보아야 겠다.

우선 freezed를 사용하기 위해서는 pubspec.yaml에 몇가지 추가가 필요하다.

  • freezed_annotation
  • freezed
  • build_runner
  • json_serializable

이렇게 추가해주면 되는데 만약 toJson 기능과 fromJson 기능을 사용하고 싶다면 jsonserializable 을 추가해야한다.

Freezed의 코드 , 기능

우선 기능들을 알아보기 위해 나의 취미인 운동(Exercise)으로 Model을 만들어 freezed를 적용해 보겠다.

import 'package:freezed_annotation/freezed_annotation.dart';

part 'exercise.freezed.dart';
part 'exercise.g.dart'; //json_serializable 의 fromJson 및 toJson 기능

@freezed
class Exercise with _$Exercise {
  factory Exercise({
    required String name,
    required int restTime,
  }) = _Exercise;

  factory Exercise.fromJson(Map<String, dynamic> json) =>
      _$ExerciseFromJson(json);
}

이렇게 코드를 작성후 해당 프로젝트의 터미널에서 flutter pub run build_runner build 입력하면 exercise.freezed.dart , exercise.g.dart 파일이 생성되고 freezed의 기능들을 사용할수 있다.

컨스트럭터(constructor) 및 프로퍼티(property) 자동생성

 final exercise1 = Exercise(name:'Bench Press',restTime:2);
 
 print(execise1.name)	// BenchPress
 
 print(exercise1.restTime) // 2

자동으로 constructor와 property 들을 작성해준다.

toString 및 toJson

final exercise2 = Exercise(name:'DeadLift',restTime:3);

print(exercise2);	// Exercise(name : DeadLift, restTime : 3)

print(execise2.toJson());	// {name : DeadLift , restTime : 3}

일반적으로 toString()을 실행시키면 인스턴스의 정보만 나오기 때문에 Instance of (Class)로 리턴되어 어떤값을 가지고 있는지 알수 없어서 toString()을 오버라이딩을 해야했는데 Freezed를 사용하면 데이터 모델의 toString() 메서드와 toJson() 메서드를 자동으로 생성할 수 있으며, 이렇게 하면 데이터 모델을 더 쉽게 디버깅하고 데이터를 시리얼라이즈할 있다.

equles 및 hashCode override

final exercise1 = Exercise(name:'Bench Press',restTime:2);

final exercise2 = Exercise(name:'Bench Press',restTime:2);

print(exercise1 == exercise2)	// true

freezed 사용시 equals 함수 , hasCode 함수를 자동으로 override 하고 있다. 일반적로 override 없이 서로를 비교한다면 메모리 위치를 비교하기 때문에 값이 같더라고 결과값은 false가 나온다. 하지만 equals 와 hasCode를 자동으로 override 하고 있기때문에 상식적인 비교를 하여 같다고 판단하여 true를 반환할수 있다.

Assert

...

@Assert('name.length < 15','15글자가 넘는 운동의 이름은 없습니다.')
  factory Exercise({
    required String name,
    required int restTime,
  }) = _Exercise;

...

// 에러 : 이름이 15글자 이하 입력 가능
final exercise1 = Exercise(name:'Bennch Press DeadLift',restTime:3);	

어노테이션 Assert를 이용하여 인스턴스를 생성할때 변수에 조건을 검증할수 있는 기능을 제공해준다. 조건에 만족하지 못하면 에러가 발생한다. 조건이 여러개 필요하다면 어노테이션 Assert를 여러개 추가하면 된다.

custom method 및 getter

 factory Exercise({
   required String name,
   required int restTime,
 }) = _Exercise;
	   
  Exercise._();
  get nameLength => name.length;
 

freezed로 생성된 클래스 내부에서 method를 getter를 설정할수 있다.

클래스 copy




@freezed
class Player with _$Player {
  factory Member({
    required int id,
    required String name,
    required Team team,
  }) = Player;
}

@freezed
class Team with _$Team {
  factory Team({
    required int id,
    required String name,
    required Company country,
  }) = _Team;
}

@freezed
class Country with _$Country {
  factory Company({
    required int id,
    required String name,
  }) = Country;
}


final country1 = Country(id: 3, name: 'Korea');
final team1 = Team(id: 2, name: 'Samsung', country: country1);
final player1 = Player(id: 1, name: 'Pyo', team: team1);



// player3.team.country.name만 Mexico로 변경
final player3 = player1.copyWith.team.country(name:'Mexico'); 


freezed에서는 seeter를 설정하는것은 불가능 하다. (immutable 하게 사용을 목적) 그렇기 때문에 값을 변경하고 싶을때는 copyWith를 사용하여 변경하고 싶은값만 변경하거나 새로운 인스턴스를 생성한다. Equatable을 사용했을때는 copyWith 메소드를 생성해야했는데 freezed는 자동으로 생성되어 매우 편리한것같다.

  • Deep Copy : copy의 기능은 deep copy이기 때문에 마지막 객체의 값만 변경해서 클래스를 카피하고 싶을 경우 data.{객체}.{객체}.{객체}({변수 값 변경}); <-- 이런 식으로 도 간단하게 하위 객체만 변경해서 카피가 가능하다.

Union

result.dart

import 'package:freezed_annotation/freezed_annotation.dart';
part 'result.freezed.dart';

@freezed
class Result with _$Result {
  const Result._();
  const factory Result.big(int number) = _Big;
  const factory Result.small(int number) = _Small;
}

union_screen.dart

import 'package:flutter/material.dart';
import 'result.dart';

class UnionScreen extends StatelessWidget {
  const UnionScreen({super.key});

  @override
  Widget build(BuildContext context) {
    void dialog(String message) {
      showDialog(
        context: context,
        builder: (context) {
          return AlertDialog(
            content: Container(
              padding: const EdgeInsets.all(15),
              child: Text(message),
            ),
          );
        },
      );
    }

    final TextEditingController controller = TextEditingController();
    return Scaffold(
      appBar: AppBar(
        title: const Text('Freezed Union Types Test'),
      ),
      body: Column(
        children: [
          const SizedBox(height: 10),
          const Text("Input the number"),
          const SizedBox(height: 10),
          Padding(
            padding: const EdgeInsets.all(15),
            child: TextField(
              autofocus: true,
              controller: controller,
            ),
          ),
          const SizedBox(height: 10),
          ElevatedButton(
              onPressed: () {
                final result = (int.parse(controller.text) > 100)
                    ? Result.big(int.parse(controller.text))
                    : Result.small(int.parse(controller.text));
                result.when(
                  big: (value) {
                    dialog("100보다 큰 숫자 입니다.");
                  },
                  small: (value) {
                    dialog("100보다 작은 숫자 입니다.");
                  },
                );
              },
              child: const Text("제출"))
        ],
      ),
    );
  }
}

버튼을 누르게 되면 text의 값이 100보다 큰수인지 작은수인지 알수 있는 코드이다.
이처럼 Union은 간단하게 내부 클래스들을 정의하고 컨스트럭터별로 다른 클래스 인스턴스들을 돌려주는것도 가능하다.

실행화면

정리

여기까지 가장 대표적인 기능들을 간단한 코드를 통해서 정리해보았다. 사실 , freezed를 매일 사용하고 있지만, 이번 기회를 통해 어떤 기능들이 있는지 다시 복습할 수 있어서 매우 유익한 경험이었습니다. 기회가 된다면 이밖에 다른 기능들도 자세하게 정리해보겠다.

참고

https://velog.io/@leeeeeoy/Flutter-Freezed-%EC%82%AC%EC%9A%A9%ED%95%B4%EB%B3%B4%EA%B8%B0-1
https://totally-developer.tistory.com/143
https://pub.dev/packages/freezed

0개의 댓글