Skip to content

Instantly share code, notes, and snippets.

@dalinaum
Created December 13, 2023 16:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dalinaum/60c1a8cdfb7521577574d8cef8b8138a to your computer and use it in GitHub Desktop.
Save dalinaum/60c1a8cdfb7521577574d8cef8b8138a to your computer and use it in GitHub Desktop.
RestorableInt

플러터 구현은 크게 플러터와 엔진으로 구분.

플러터는 인텔리제이로 개발하는 것 같고 (안드로이드 스튜디오로 했더니 잘 안됨), 엔진은 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 관리가 주요 역할. restorationIdRestorationMixin에 등록되는 ID.

mixin RestorationMixin<S extends StatefulWidget> on State<S>

RestorationMixinon 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를 호출하는게 주요할 일.

_updatePropertyRestorationMixin가 가지고 있는 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? ㅠㅠ

일단 여기서 멈추기.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment