code generator 기능을 함 (data-classes / unions / pattern-matching / cloning
역할
생성자 + 속성 / Equatable / copyWith / (de/serialization handling) 기능을 해줌
설치방법
$ flutter pub add freezed_annotation
// tool for code-generator run
$ flutter pub add --dev build_runner
// code generator
$ flutter pub add --dev freezed
// freezed를 위한 annotations contains packages !!
// 만약 json_serializable:이 필요하면 pubspec.yaml에 dev_dependencies에 추가
flutter pub run build_runner build을 이용해서 파일 빌드를 해서 자동으로 freezed 파일이 만들어지게 해줌! (import 이용)
변경 시에도 flutter pub run build_runner build를 이용해서 재빌드 시키면 됌
model 만들기
(import 'package:flutter/foundation.dart'를 이용해서 flutter devtool에서 객체를 잘 읽을 수 있게 해주니 나쁘지 않음!!)
예시를 통해 이해해보기
import 'package:freezed_annotation/freezed_annotation.dart';
part 'person.freezed.dart';
// 꼴랑 이코드만 추가함
// freezed 선언
class Person with _$Person {
// mixin을 이용하여 객체의 다양한 속성/메소드 정의
//(2)
('name.isNotEmpty', 'name cannot be empty')
// validation을 위해 쓰임 / 앞의 것이 허용되지 않으면 뒤에 에러가 나옴
('age >= 0')
factory Person({
// factory로 선언 / 따른 모델처럼 final로 위의 선언하지 않아도 문제 x
// 생성자 정의시 표시된 대로 키워드 사용(const로 만들어도 되긴함..)
required int id,
required String name,
required int age,
required Group group,
}) = _Person;
// factory Person.fromJson(Map<String, dynamic> json) => _$PersonFromJson(json);
// json으로도 만들 수 있음!
const Person._();
// 밑의 get과 hello를 쓰기 위해 사용함 / 그리고 코드에 오류가 발생하기에 다시 build를 해줘야함
// define a private empty constructor:
get nameLength => this.name.length;
void hello() {
print("123");
}
}
변경 가능한 속성을 정의하고 싶다면, @unfreezed를 사용
class Person with _$Person {
factory Person({
required String firstName,
required String lastName,
required final int age,
}) = _Person;
factory Person.fromJson(Map<String, Object?> json)
=> _$PersonFromJson(json);
}
void main() {
var person = Person(firstName: 'John', lastName: 'Smith', age: 42);
person.firstName = 'Mona';
person.lastName = 'Lisa';
}
// age속성을 으로 명시적으로 표시했기 때문에 여전히 변경할 수 없습니다 final.
// Person더 이상 사용자 정의 ==/hashCode 구현이 없습니다.
이제 이부분은 ui에 적용해보겠습니다.
final schoolOne = School(id: 3, name: 'MIT');
final groupOne =
Group(id: 2, name: 'Flutter Development', school: schoolOne);
final personOne = Person(id: 1, name: 'sunny', age: 29, group: groupOne);
final personNew = personOne.copyWith(
group: groupOne.copyWith(
school: schoolOne.copyWith(
name: 'Yale',
),
),
);
final personNewTwo = personOne.copyWith.group.school(name: 'Harvard');
final personTwo = Person(id: 1, name: 'sunny', age: 29, group: groupOne);
final personThree = Person(
id: personOne.id, name: personOne.name, age: 18, group: groupOne);
final personFour = personOne.copyWith(age: 18);
// personOne.hello();
return Scaffold(
appBar: AppBar(
automaticallyImplyLeading: true,
title: const Text("freezed test"),
centerTitle: true,
),
body: SafeArea(
child: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.only(left: 10, right: 10),
child: Column(
children: [
renderText('person1.id', personOne.id.toString()),
renderText('person1.name', personOne.name),
renderText('person1.age', personOne.age.toString()),
renderText('toString()', personOne.toString()),
// renderText('toJson()', personOne.toJson().toString()),
renderText('==', (personOne == personTwo).toString()),
renderText('nameLength', personOne.nameLength.toString()),
renderText('person4.ToString()', personFour.toString()),
renderText('personNew.ToString()', personNew.toString()),
renderText('personNewTwo.ToString()', personNewTwo.toString()),
],
),
),
),
),
);
renderText(String title, String text) {
return Column(
children: [
Row(
children: [
Expanded(
child: Text(
title,
style: const TextStyle(
fontSize: 20.0, fontWeight: FontWeight.bold),
),
),
],
),
Row(
children: [
Expanded(
child: Text(
text,
style: const TextStyle(fontSize: 20.0),
),
),
],
),
const Divider(
height: 1,
),
const SizedBox(
height: 10,
)
],
);
}
위에 예제에서 copyWith 부터 설명하자면..
copyWth은 개체를 복사하는데 사용하며, 기존에 있던 값은 건드리지 않고 새로운 값만 변경시켜 사용함!
그렇지만 복잡한 개체에서 불편할 수 있기에 아래와 같은 코드를 씁니다.
Company company;
Company newCompany = company.copyWith(
director: company.director.copyWith(
assistant: company.director.assistant.copyWith(
name: 'John Smith',
),
),
);