플러터 구현은 크게 플러터와 엔진으로 구분.
플러터는 인텔리제이로 개발하는 것 같고 (안드로이드 스튜디오로 했더니 잘 안됨), 엔진은 VS Code로 하는 것 같은데 안드로이드 스튜디오와 XCode를 써야 할까?
RestorableValue
를 iOS에서 쓸려면 Flutter View Controller를 찾아 Restoration ID
를 설정해야 한다고 함.
책에 나왔던 클래스 RestorableInt
부터 따라가봄.
A [RestorableProperty] that knows how to store and restore an [int]
class RestorableInt extends RestorableNum<int> {
RestorableInt(super.defaultValue);
}```
`RestorableInt`는 `RestorableNum`을 상속받아 간단히 구현.
A [RestorableProperty] that knows how to store and restore a [num].
```dart
class RestorableNum<T extends num> extends _RestorablePrimitiveValue<T> {
RestorableNum(super.defaultValue);
}
RestorableNum
도 _RestorablePrimitiveValue
를 상속받아 구현.
_RestorablePrimitiveValue and its subclasses are non-nullable. See [_RestorablePrimitiveValueN] for the nullable version of this class.
왠지 모르겠지만 프리미티브는 Object를 이야기함. fromPrimitives는 Object에서 해당 T로 바꾸겠다는 이야기. toPrimitives는 T에서 Object로 바꾸겠다는 이야기. 객체지향 언어의 프리미티브 타입과 박스 타입 같은 이야기가 아님.
class _RestorablePrimitiveValue<T extends Object> extends _RestorablePrimitiveValueN<T> {
_RestorablePrimitiveValue(super.defaultValue)
: assert(debugIsSerializableForRestoration(defaultValue));
@override
set value(T value) {
super.value = value;
}
@override
T fromPrimitives(Object? serialized) {
assert(serialized != null);
return super.fromPrimitives(serialized);
}
@override
Object toPrimitives() {
return super.toPrimitives()!;
}
}
_RestorablePrimitiveValue
는 단언 처리를 통해 non-null 처리를 한다. 타입을 Object로 바꾸거나(toPromitives) T로(fromPrimitives) 바꿔준다.
_RestorablePrimitiveValueN
는 Nullable version.
class _RestorablePrimitiveValueN<T extends Object?> extends RestorableValue<T> {
_RestorablePrimitiveValueN(this._defaultValue)
: assert(debugIsSerializableForRestoration(_defaultValue)),
super();
final T _defaultValue;
@override
T createDefaultValue() => _defaultValue;
@override
void didUpdateValue(T? oldValue) {
assert(debugIsSerializableForRestoration(value));
notifyListeners();
}
@override
T fromPrimitives(Object? serialized) => serialized as T;
@override
Object? toPrimitives() => value;
}
didUpdateValue
가 있어 리스너를 호출하게 하는 것이 특징.
[RestorableValue], which is a [RestorableProperty] that makes the wrapped
value accessible to the owning [State] object via a value
getter and setter.
abstract class RestorableValue<T> extends RestorableProperty<T> {
T get value {
assert(isRegistered);
return _value as T;
}
T? _value;
set value(T newValue) {
assert(isRegistered);
if (newValue != _value) {
final T? oldValue = _value;
_value = newValue;
didUpdateValue(oldValue);
}
}
@mustCallSuper
@override
void initWithValue(T value) {
_value = value;
}
@protected
void didUpdateValue(T? oldValue);
}
게터와 세터가 있고 세터 호출되면 didUpdateValue
호출하게 함. didUpdateValue
는 구현되어 있지 않음. (자식이 구현)
Manages an object of type T
, whose value a [State] object wants to have
restored during state restoration.
abstract class RestorableProperty<T> extends ChangeNotifier {
RestorableProperty(){
if (kFlutterMemoryAllocationsEnabled) {
ChangeNotifier.maybeDispatchObjectCreation(this);
}
}
T createDefaultValue();
T fromPrimitives(Object? data);
void initWithValue(T value);
Object? toPrimitives();
bool get enabled => true;
bool _disposed = false;
@override
void dispose() {
assert(ChangeNotifier.debugAssertNotDisposed(this));
_owner?._unregister(this);
super.dispose();
_disposed = true;
}
String? _restorationId;
RestorationMixin? _owner;
void _register(String restorationId, RestorationMixin owner) {
assert(ChangeNotifier.debugAssertNotDisposed(this));
_restorationId = restorationId;
_owner = owner;
}
void _unregister() {
assert(ChangeNotifier.debugAssertNotDisposed(this));
assert(_restorationId != null);
assert(_owner != null);
_restorationId = null;
_owner = null;
}
@protected
State get state {
assert(isRegistered);
assert(ChangeNotifier.debugAssertNotDisposed(this));
return _owner!;
}
@protected
bool get isRegistered {
assert(ChangeNotifier.debugAssertNotDisposed(this));
return _restorationId != null;
}
}
ChangeNotifier
까지는 따라가지 않음.
_restorationId: String
와 _owner: RestorationMixin
관리가 주요 역할. restorationId
는 RestorationMixin
에 등록되는 ID.
mixin RestorationMixin<S extends StatefulWidget> on State<S>
RestorationMixin
이 on State<S>
로 되어있음. StatefulWidget에 대한 State에서만 Mixin을 쓸 수 있음.
String? get restorationId;
void restoreState(RestorationBucket? oldBucket, bool initialRestore);
두 메서드를 오버라이드 해야함.
아래는 책의 코드.
@override String? get restorationId => widget.restorationId;
@override void restoreState(RestorationBucket? oldBucket, bool initialRestore) {
registerForRestoration(_counter, 'count');
}
상대적으로 직관적인 `registerForRestoration`.
```dart
void registerForRestoration(RestorableProperty<Object?> property, String restorationId) {
...
final bool hasSerializedValue = bucket?.contains(restorationId) ?? false;
final Object? initialValue = hasSerializedValue
? property.fromPrimitives(bucket!.read<Object>(restorationId))
: property.createDefaultValue();
if (!property.isRegistered) {
property._register(restorationId, this);
void listener() {
if (bucket == null) {
return;
}
_updateProperty(property);
}
property.addListener(listener);
_properties[property] = listener;
}
...
property.initWithValue(initialValue);
if (!hasSerializedValue && property.enabled && bucket != null) {
_updateProperty(property);
}
...
}
리스너를 등록하고 _updateProperty
를 호출하는게 주요할 일.
_updateProperty
는 RestorationMixin
가 가지고 있는 RestorationBucket
을 업데이트하고 _manager?.scheduleSerializationFor(this);
로 직렬화를 위한 스케쥴을 할당.
sendToEngine(_encodeRestorationData(_rootBucket!._rawData));
을 호출해서 엔진으로 전달.
@protected
Future<void> sendToEngine(Uint8List encodedData) {
return SystemChannels.restoration.invokeMethod<void>(
'put',
encodedData,
);
}
이제는 엔진으로 가야함. 엔진 저장소에서 찾아야 함.
RestorationChannel.java
파일과 FlutterEngine.mm
, FlutterRestorationPlugin.mm
파일이 핸들하고 있음. 자바와 오브젝티브 C? ㅠㅠ
일단 여기서 멈추기.