정말 간단하다..

 

 

 

ios > Podfile에서 위의

# platform :ios, '12.0'

의 부분을 주석 해제 해주면 된다. ( # 제거)

'FLUTTER' 카테고리의 다른 글

[Bloc] 개념 이해하기  (0) 2024.06.16
[Bloc] 개념 이해하기 2  (0) 2024.06.05
[Flutter] Bloc 개념 이해하기 1  (2) 2024.06.04
.framework does not support the minimum OS Version specified in the  (2) 2024.04.27
[Flutter] Gradle issue  (2) 2023.11.24

이전 글 : 2024.06.04 - [FLUTTER] - [Bloc] 개념 이해하기 2

 

Bloc

 

 

Bloc은 조금 더 나은 클래스로 events에 의존해서 state변경을 만듭니다.Bloc 은 또한 BlocBase를 extends하여 Cubit과 유사한 공통 Api가 있습니다.그러나 bloc은 새로운 state를 직접 emit하는 대신 event 를 수신하고 수신된 event를 나가는 state로 변환합니다.

 

Bloc 만들기

Bloc을 생성하는 것은 cubit을 생성하는 것과 비슷하지만, 관리할 state를 정의하는것 외에 bloc이 처리할 event도 정의해야 한다는 점이 다르다. event는 bloc에 대한 입력을 받는 것이다.

sealed class CounterEvent{}

final class CounterIncrementPressed extends CounterEvent{}

class CounterBloc extencds Bloc<CounterEvent, int> {
 CounterBloc() : super(0);
 }

 

Bloc의 state변화

Bloc 은 Cubit의 함수가 아닌 on<Event> Api를 통해 이벤트 핸들러를 등록해야 합니다. 이벤트 핸들러는 들어오는 모든 event를 0개 이상의 나가는 state로 변환하는 역활을 수행합니다.

sealed class CounterEvent {}

final class CounterIncrementPressed extends CounterEvent{}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterIncrementPressed>((event, emit){
      // 여기서 CounterIncrementPressed 를 구현한다.
    });
}

그런 다음 EventHandler를 업데이트 하여 CounterIncrementPressed 이벤트를 처리할 수 있다.

 

sealed class CounterEvent {}

final class CounterIncrementPressed extends CounterEvent{}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterIncrementPressed>((event, emit){
      // 여기서 CounterIncrementPressed 를 구현한다.
      emit(state + 1);
    });
}

 

Bloc 사용하기

먼저 CounterBloc 의 인스턴스를 생성하여 사용할 수 있습니다.

Future<void> main() async {
  final bloc = CounterBloc();
  print(bloc.state); // 0
  bloc.add(CounterIncrementPressed());
  await Future.delayed(Duration.zero);
  print(bloc.state); // 1
  await bloc.close();
}

 

Stream 사용법

cubit과 마찬가리조 Bloc은 Stream의 특수한 유형으로 Bloc을 구독하여 state를 실시간으로 업데이트 할 수도 있습니다.

Future<void> main() async {
  final bloc = CounterBloc();
  final subscription = bloc.stream.listen(print); // 1
  bloc.add(CounterIncrementPressed());
  await Future.delayed(Duration.zero);
  await subscription.cancel();
  await bloc.close();
}

 

Bloc 관찰하기

bloc은 blocbase 를 extends하기 때문에 onChanged를 사용하여 Bloc의 모든 state 변화를 관찰 할 수 있습니다.

sealed class CounterEvent {}

final class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  Counter() : super(0) {
    on<CounterIncrementPressed>((event, emit){
      emit(state + 1);
    })
  }
  
  @override
  void onChange(Change<int> change){
    super.onChange(change);
    print(change);
  }
}

 

다음 main.dart로 업데이트 합니다.

void main(){
  CounterBloc()
  ..add(CounterIncrementPressed())
  ..close();
}

 

Bloc 과 Cubir의 주요 차별화 요소는 Bloc 이 event 기반이기 때문에 state 변화를 유발한 원인에 대한 정보도 알수 있습니다.

이는 onTransition을 override하여 수행할 수 있습니다.

 

sealed class CounterEvent {}

final class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  Counter() : super(0) {
    on<CounterIncrementPressed>((event, emit){
      emit(state + 1);
    })
  }
  
  @override
  void onChange(Change<int> change){
    super.onChange(change);
    print(change);
  }
  
  @override
  void onTransition(Transition<CounterEvent, int> transition){
    super.onTransition(transition);
    print(transition);
  }
}

 

BlocObserver

이전과 마찬가리고 커스텀 BlocObserver에서 onTransition을 overrided하여 단일 위치에서 발생하는 모든 Transition을 관찰할 수 있습니다.

class SimpleBlocObserver extends BlocObserver {
  @override
  void onChange(BlocBase bloc, Change change){
    super.onChange(bloc, change);
    print('${bloc.runtimeType} $change');
  }
  
  @override
  void onTransition(Bloc bloc, Transition transition){
    super.onTransition(bloc, transition);
    print('${bloc.runtimeType} $transition');
  }
  
  @override
  void onError(Blocbase bloc, Object error, StackTrace stackTrace){
    print('${bloc.runtimeType} $error $stackTrace');
    super.onError(bloc, error, stackTrace);
  }
}

 

Bloc 인스턴스의 또 다른 독특한 특징은 Blocd에 새 event가 추가될 때마다 호출되는 onEvent를 override할 수 있다는 점입니다.

conChange 및 onTransition과 마찬가지로 onEvent는 전역뿐만 아니라 로컬에서도 override할 수 있습니다.

sealed class CounterEvent {}

final class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterIncrementPressed>((event, emit) => emit(state + 1));
  }

  @override
  void onEvent(CounterEvent event) {
    super.onEvent(event);
    print(event);
  }

  @override
  void onChange(Change<int> change) {
    super.onChange(change);
    print(change);
  }

  @override
  void onTransition(Transition<CounterEvent, int> transition) {
    super.onTransition(transition);
    print(transition);
  }
}
class SimpleBlocObserver extends BlocObserver {
  @override
  void onEvent(Bloc bloc, Object? event) {
    super.onEvent(bloc, event);
    print('${bloc.runtimeType} $event');
  }

  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    print('${bloc.runtimeType} $change');
  }

  @override
  void onTransition(Bloc bloc, Transition transition) {
    super.onTransition(bloc, transition);
    print('${bloc.runtimeType} $transition');
  }
}

 Bloc의 에러 처리

cubit과 마찬가지로 각 Bloc에는 addError와 onError메서드가 있습니다. Bloc 내부 어디에서나 addError를 호출하여 에러가 발생했음을 알릴 수 있습니다. 그런 다음 Cubit과 마찬가지로 onError를 override하여 모든 에러에 대응할 수 있습니다.

sealed class CounterEvent {}

final class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends Bloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterIncrementPressed>((event, emit) {
      addError(Exception('increment error!'), StackTrace.current);
      emit(state + 1);
    });
  }

  @override
  void onChange(Change<int> change) {
    super.onChange(change);
    print(change);
  }

  @override
  void onTransition(Transition<CounterEvent, int> transition) {
    print(transition);
    super.onTransition(transition);
  }

  @override
  void onError(Object error, StackTrace stackTrace) {
    print('$error, $stackTrace');
    super.onError(error, stackTrace);
  }
}

 

이전 정리 : 2024.06.04 - [FLUTTER] - [Flutter] Bloc 개념 이해하기 1

 

[Flutter] Bloc 개념 이해하기 1

블록을 배우기 이전에 stream 의 개념을 알아야 더 이해하기 쉽다. Streams스트림은 비동기 데이터의 시퀀스이다.파이프는 Stream 이고 그 안을 흐르는 물을 비동기 데이터라고 이해하면 쉽다.dart에

kimsw509.tistory.com

Using a Cubit

이제 여러가지를 응용해 볼 차례이다.

 

Basic Usage

main.dart

void main(){
	final cubit = CounterCubit();
	print(cubit.state); // 0
	cubit.increment();
	print(cubit.state); // 1
	cubit.close();
}

 

위는 기본적으로 사용한 것이다.

 

Stream Usage

Future<void> main() async {
  final cubit = counterCubit();
    final subscription = cubit.stream.listen(print); // 1
    cubit.increment();
    await Future.delayed(Duration.zero);
    await subscription.cancel();
    await cubit.close();
}

위를 보면 CounterCubit을 구독 하고 있다가 상태 변화가 일어나면 print를 호출한다. 그 후 increment를 호출해서 상태 변화를 야기하고 cancel 해서 더이상 스트림을 구독하지 않는다.

 

Observing a Cubit

Cubit의 상태가 변하면 change가 호출된다. 이를 onChange를 통해 관찰할 수 있다.

counter_cubit.dart

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
    
  void increment() => emit(state +1)
    
  @override
  void onChange(Change<int> change) {
    super.onChange(change);
    print(change);
    }
}

 

BlocObserver

블록옵져버를 이용하면 모든 상태 변화를 한곳에서 볼수 있다.

simple_bloc_observer.dart

class SimpleBlocObserver extends BlocObserver {
    @override
    void onChange(BlocBase bloc, Change change) {
    	super.onchange(bloc, change);
        print('${bloc.runtimeType} $change');
    }
}

 

Cubit Error Handing

모든 Cubit은 addError가 있어서 에러가 발생했을때를 알수 있다.

counter_cubit.dart

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);
  
  void increment(){
    addError(Exception('increment error!'), StackTrace.current);
    emit(state +1);
  }
  
  @override
  void onChange(Change<int> change) {
    super.onChange(change);
    print(change);
  }
  
  @override
  void onError(Object error, StackTrace stackTrace) {
    print('$error, $stackTrace');
    super.onError(error, stackTrace);
  } 
}

 

onError는 BlocObserver에서 override하여 전역적인 에러를 잡아낼수 있다.

simple_bloc_observer.dart

class SimpleBlocObserver extends BlocObserver {
  @override
  void onChange(BlocBase bloc, Change change) {
    super.onChange(bloc, change);
    print('$bloc.runtimeType} $change');
  }
  
  @override
  void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
    print('${bloc.runtimeType} $error $stackTrace');
    super.onError(bloc, error, stackTrace);
  }
}

 

블록을 배우기 이전에 stream 의 개념을 알아야 더 이해하기 쉽다.

 

Streams

스트림은 비동기 데이터의 시퀀스이다.

파이프는 Stream 이고 그 안을 흐르는 물을 비동기 데이터라고 이해하면 쉽다.

dart에서는 이를 쉽게 만들수 있다.

Stream<int> countStream(int max) async* {
	for (int i = 0; i < max; i ++ ){
    		yield i;
        }
}

 

aysnc* 를 사용함으로서 yield 키워드를 사용할 수 있고 stream 데이터를 반환한다. 위의 예는 integer stream을 반환한다.

위의 예를 비슷하게 사용하여 다른 값들을 반환할 수 있다.

 

Future<int> sumStream(Stream<int> stream) async {
	int sum = 0;
    await for (int value in stream){
    	sum += value;
    }
    return sum;
}

 

위의 함수를 보면 async 로 함수를 표시해서 await 키워드를 사용 할수 있고 Future 를 반환 가능해진다.

이렇게 대략 적으로 스트림을 봤으면 이제 Bloc 의 코어 패키지인 Cubit을 배울수 있다.

 

Cubit

CubitBlocBase를 상속한  클래스이며 여러 타입의 상태(state)를 매니지 할수 있도록 상속 가능하다. 

Cubit은 함수를 공개 하므로서 상태 변화 트리거를 걸 수 있다.

 

Creating a Cubit

 

counter_cubit.dart

class CounterCubit extends Cubit<int> {
	CounterCubit() : super(0);
}

Cubit을 만들때는 어떤 타입의 상태를 컨트롤 할 것인지 정의해 줘야한다. 위의 CounterCubit의 경우 int 타입을 통해 상태를 다루게 되지만 복잡한 경우에는 원시타입이 아닌 클래스로 상태를 컨트롤해줘야한다.

 

그 다음으로 우리가 해줘야 할 것은 Cubit의 initial state를 정해주는 것이다. super 값에 값을 넣어줌으로서 해결이 가능하다.

 

counter_cubit.dart

class CounterCubit extends Cubit<int> {
	CounterCubit(int initalState) : super(initialState);
}

 

Cubit State Changes

모든 Cubit은 emit 을 통해 상태를 바꿀수 있다.

counter_cubit.dart

class CounterCubit extends Cubit<int> {
	CounterCubit() : super(0);
    
    void increment() => emit(state + 1);
}

 

 

 

갑자기 위와 같은 에러 코드가 나오면서 testFlight 가 올라가지 않았다..

 

아카이브(Archive)도 잘 되고 실행도 잘되고 ( Flutter, xcode 전부) 이상하게도 스토어에만 올라가지 않았다.

그래서 구글로 서치를 해보다가 찾아 낸것이

https://www.reddit.com/r/swift/comments/1bd7kxj/swift_ios_cannot_upload_to_testflight/

 위와 같은 글인데 결국은 15.3부터 새로운 밸리데이션이 들어갔다는 뜻이다. 

내가 이해한 바로는 결국 사용하는 sdk들을 전부 최신 버전으로 업데이트를 하던가 아니면 내 앱의 버전을 낮추라고 되어 있었다.

그리하여 내 버전도 올리고 sdk들의 버전도 다 올려봤지만 ㅠㅠ 해결이 되지 않았다.

 

결국 해결한 것은. xcode를 삭제하고 (사용하던 버전은 15.3) 15.2버전의 xcode를 다운로드 했다.

(https://developer.apple.com/kr/xcode/resources/)

 

그리했더니 크게 무언가를 바꾸지도 않았지만 해결되었다.

* What went wrong:
The Android Gradle plugin supports only Kotlin Gradle plugin version 1.5.20 and higher.
The following dependencies do not satisfy the required version:
project ':kakao_login' -> org.jetbrains.kotlin:kotlin-gradle-plugin:1.4.32

 

 

위와 같은 오류가 떴다.

그냥 해석 하자면 내 프로젝트가 현재 코틀린 그레이들 플러그인 버전 1.5.20 보다 위를 지원하는데

kakao_login이 1.4.32를 써야 한다는 뜻!

 

android/build.gradle에 가서 

 

'com.android.tools.build:gradle:7.3.0' 으로 되어 있던걸 com.android.tools.build:gradle:7.1.2 로 수정하면 된다!

 

 

엄청 간단하게 완료!!

아니 오랜만에 ios앱좀 다시 리빌드 하려고 했더니 왠걸?

위와 같은 사진이??? 이게 뭔데

Unable to process request - PLA Update available

No signing certificate "iOS Distribution" found

아니 한게 없는데 문제가?? 역시 갓 iOS 안해도 문제가 생긴다...

검색해 보니 해결은 간단...

일단 AppStore Connect로 간다.

 

가게 되면 사용권 계약 업데이트하라고 나와 있다.

그냥 계약을 눌러서 업데이트를 진행한다.

 

계약 검토하기!!

동의! 하면 끝!!!

flutter 로 앱 개발하다가 정말 아무런 이유 없이 

Unable to boot simulator

에러가 떴다...

 

하지만 다행히도 정말 간단한 해결 방법이 있었다.

 

왼쪽 상단의 애플 모양을 클릭하고 시스템 설정 > 저장 공간 > 개발자 > xcode 프로젝트 빌드 파일 > xcode 캐시 를 삭제한다.

 

그 후에 다시 시뮬레이터를 실행하면 잘 실행된다.... 

이것도 안된다면 시뮬레이터 자체를 삭제하고 다시 까는 방법 밖에 없다.!

+ Recent posts