return new Scaffold(
  body: new Image.network(
    "https://cdn.pixabay.com/photo/2017/02/21/21/13/unicorn-2087450_1280.png",
    fit: BoxFit.cover,
    height: double.infinity,
    width: double.infinity,
    alignment: Alignment.center,
  ),
);

위와 같이 fit을 BoxFit.cover로 커버한뒤 double.infinity 를 사용하면 된다.

FLUTTER BLOC 패턴에 대해서 공부해 보면서 공식 dev 에 있는 예제를 따라 해보고 있었다.

그러던중 

The argument type 'Object' can't be assigned to the parameter type 'PostStatus'.

라는 에러를 봤다 아마 이런 류의 에러는 타입이 맞지 않아서 보통 나올것이다.

 

나의 경우에는

import 'package:equatable/equatable.dart';
import 'package:practice_bloc_infinitescroll/posts/models/post.dart';

enum PostStatus { initial, success, failure }

class PostState extends Equatable {
  final PostStatus status;
  final List<Post> posts;
  final bool hasReachedMax;

  const PostState({
    this.status = PostStatus.initial,
    this.posts = const <Post>[],
    this.hasReachedMax = false,
  });

  PostState copyWith({
    PostState? status,
    List<Post>? posts,
    bool? hasReachedMax,
  }) {
    return PostState(
      status: status ?? this.status,
      posts: posts ?? this.posts,
      hasReachedMax: hasReachedMax ?? this.hasReachedMax,
    );
  }

  @override
  String toString() {
    return 'PostState{status: $status, posts: $posts, hasReachedMax: $hasReachedMax}';
  }

  @override
  List<Object> get props => [status, posts, hasReachedMax];
}

에서 status: status ?? this.status, 라는 부분에서 에러가 나고 있었다. 코드를 보면 중간 부분에 있다.

에러가 난 부분을 아무리 고치려고 해도 안 고쳐졌는데 천천히 읽다 보니까 나의 오타를 발견했다.....

위에 copyWith 라는 메서드의 parameter를 받을때 PostStatus 라고 타입을 명시 해야 되는데 바보 같이 PostState로 했다...

너무 비슷하자나..... 그래서 아래와 같이 고쳤다.

 

import 'package:equatable/equatable.dart';
import 'package:practice_bloc_infinitescroll/posts/models/post.dart';

enum PostStatus { initial, success, failure }

class PostState extends Equatable {
  final PostStatus status;
  final List<Post> posts;
  final bool hasReachedMax;

  const PostState({
    this.status = PostStatus.initial,
    this.posts = const <Post>[],
    this.hasReachedMax = false,
  });

  PostState copyWith({
    PostStatus? status,
    List<Post>? posts,
    bool? hasReachedMax,
  }) {
    return PostState(
      status: status ?? this.status,
      posts: posts ?? this.posts,
      hasReachedMax: hasReachedMax ?? this.hasReachedMax,
    );
  }

  @override
  String toString() {
    return 'PostState{status: $status, posts: $posts, hasReachedMax: $hasReachedMax}';
  }

  @override
  List<Object> get props => [status, posts, hasReachedMax];
}

그랬더니 너무나도 쉽게 문제 해결!!!!

역시 에러 이유를 읽고 천천히 코딩을 하면 이런 에러는 안날듯...

그리고 너무 이름이 비슷해서 ㅠㅠ 네이밍에 조금 신중할 필요가 있다는 것을 다시 깨달았다.

사용자가 모바일 애플리케이션에서 애플리케이션을 실행할 때 떄때로 certificate_verift_failed 라는 오류가 무작위로 발생하기 시작합니다. 따라서 아래에 해결 방법을 설명하겠습니다.

 

내가 찾은 방법은 모든 인증서를 허용하는 것입니다.

main.dart 파일에서 다음 클래스를 추가하거나 가져옵니다.

class MyHttpOverrides extends HttpOverrides {

  @override
  HttpClient createHttpClient(SecurityContext? context) {
    return super.createHttpClient(context)..badCertificateCallback
    = (X509Certificate cert, String host, int port) => true;
  }

}

그 다음은 기본 함수에서 함수 정의 뒤에 다음 줄을 추가합니다.

    HttpOverrides.global = MyHttpOverrides();

 

dio 라이브러리를 사용하는 경우 아래와 같이 수행하면 됩니다.

Dio dio = new Dio();
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
    (HttpClient client) {
  client.badCertificateCallback =
      (X509Certificate cert, String host, int port) => true;
  return client;
};

 

혹은 아래와 같이 실행할 수 있습니다.

import 'package:http/io_client.dart';
import 'dart:io';
import 'package:http/http.dart';
import 'dart:async';
import 'dart:convert';

    Future getAccessToken(String url) async {
      try {
        final ioc = new HttpClient();
        ioc.badCertificateCallback =
            (X509Certificate cert, String host, int port) => true;
        final http = new IOClient(ioc);
        http.post('url', body: {"email": "xyz@xyz.com", "password": "1234"}).then(
            (response) {
          print("Reponse status : ${response.statusCode}");
          print("Response body : ${response.body}");
          var myresponse = jsonDecode(response.body);
          String token = myresponse["token"];
        });
      } catch (e) {
        print(e.toString());
      }
    }

 

가장 좋은 방법은 믿을수 있는 호스트로부터의 certificate 만 허용하는 것입니다.

그래서 만약 api host 가 api.my_app이라면 이 host만 허용합니다.

HttpClient client = new HttpClient();
client.badCertificateCallback = ((X509Certificate cert, String host, int port) {
 final isValidHost = host == "api.my_app";
 return isValidHost;
});

 

그래서 lib/_http/http.dart에서 Chopper로 직접 HttpClient를 전달할 수 없었습니다. ChopperClient는 ioclient.IOClient에 래핑된 생성자에서 HttpClient를 수신할 수 있습니다.

HttpClient webHttpClient = new HttpClient();
webHttpClient.badCertificateCallback = ((X509Certificate cert, String host, int port) => true);
dynamic ioClient = new ioclient.IOClient(webHttpClient);
final chopper = ChopperClient(
  baseUrl: "https://example.com",
  client: ioClient,
  services: [
    MfService.create()
  ],
  converter: JsonConverter(),
);
final mfService = MfService.create(chopper);

 

tls 1.3 request URL도 문제 없습니다.

import 'dart:io';

main() async {
  HttpClient client = new HttpClient();
  // tls 1.2 error
//  var request = await client.getUrl(Uri.parse('https://shop.io.mi-img.com/app/shop/img?id=shop_88f929c5731967cbc8339cfae1f5f0ec.jpeg')); 
  // tls 1.3 normal
  var request = await client.getUrl(Uri.parse('https://ae01.alicdn.com/kf/Ud7cd28ffdf6e475c8dc382380d5d1976o.jpg'));
  var response = await request.close();
  print(response.headers);
  client.close(force: true);
}

 

결론

KEEP FLUTTING!!!

One or more plugins require a higher Android SDK version.
...
android {
  compileSdkVersion 33
  ...
}

 

위와  같은 에러가 날 때가 있다. 그럴때는

project > android > app > build.gradle 에 가서

android {
    compileSdkVersion flutter.compileSdkVersion
    ndkVersion flutter.ndkVersion
    ...
    }

위 부분의 compildSdkVersion을 맞는 버전으로 바꿔주면 된다.

android {
    compileSdkVersion 33
    ndkVersion flutter.ndkVersion
    ...
    }

 

pub.dev는 라이크 순이나 사용 순으로 보여주기 때문에 내가원하는 기능의 패키지를 찾는 것은 어렵다 ㅠㅠ

그러다 찾은 사이트가 나에게 광명의 빛을!!

바로 FLUTTERGEM

이렇게 위와 같이 기능이나 어떤 특정한 기준으로 묶여서 내가 원하는 걸 찾을수 있다.!!! 완전 신세계!

게다가 UI도 이쁘다.

 

 

사이트 안으로 들어가면 이렇게 해당 관련의 패키지들이 쭉 나와 있고 지금은 캡쳐로 보이지만! 실제로는 애니메이션으로 그 기능들을 간략하게 보여주고 있다. 완전 강추다. 물론 pub.dev에 들어가면 다 있겠지만 기능별로 찾고 싶을때는

fluttergem을 이용하는게 좋아보인다.!

flutter로 계산하다 보면  == 연산자와 is 연산자를 종종 사용하게 되는데 어떤때에 == 연산자를 사용하고 is 연산자를 사용하는지 알아보자.


== Operator

공식 사이트를 보면

The default behavior for all Objects is to return true if and only if this object and other are the same object.

Total: It must return a boolean for all arguments. It should never throw.

Reflexive: For all objects o, o == o must be true.

Symmetric: For all objects o1 and o2, o1 == o2 and o2 == o1 must either both be true, or both be false.

Transitive: For all objects o1, o2, and o3, if o1 == o2 and o2 == o3 are true, then o1 == o3 must be true.

라고 나와 있는데 결국 객체가 같은지 보는것이라고 한다.

 

is 

is 의 같은 경우는 객체를 검사하는것이 아닌 타입을 검사하는 것이다.

is 키워드 : 같은 타입이면 true를 반환하고 다른 타입이면 false를 반환.

is! 키워드 : 같은 타입이면 false를 반환하고 다른 타입이면 true를 반환.

 


간단한 예제 1

void main() {
  int a1= 1;
  int a2 = 2;
  int a3 =1 ;
  print(a1 == a2);
  print(a1 == a3);
  print(a1 == int);
}
false
true
false

 

 

 

간단한 예제 2

  int a1 = 1;
  double a2 = 2.2;
  print(a1 is int);
  print(a1 is double);
  print(a2 is int);
  print(a2 is double);
true
true
false
true

 

오류 1 

  int a1 = 1;
  double a2 = 2.2;
  print(a1 is a2);
Error: 'a2' isn't a type.
  print(a1 is a2);
              ^^
Error: Compilation failed.

a2는 타입이 아니기 때문에 is 키워드를 사용시 에러가 난다.

Map<String,String> headers = {'Content-Type':'application/json','authorization':'Basic c3R1ZHlkb3RlOnN0dWR5ZG90ZTEyMw=='};

var response = await post(Urls.getToken,
        headers: headers,
        body: {"grant_type":"password","username":"******","password":"*****","scope":"offline_access"},
      );

위와 같이 작성했는데 "application/json"에러가 났다 ..

왜지?? 분명 headers content-type으로 명시했는데 ...알고보니 body를 jsonEncode해줘야 한다.

import 'package:http/http.dart' as http;
import 'dart:convert';

Map<String,String> headers = {'Content-Type':'application/json','authorization':'Basic c3R1ZHlkb3RlOnN0dWR5ZG90ZTEyMw=='};
final msg = jsonEncode({"grant_type":"password","username":"******","password":"*****","scope":"offline_access"});

var response = await post(Urls.getToken,
               headers: headers,
               body: msg,
            );

 

개발을 하다보면 패키지? 플러그인?? 뭐가 맞지? 헷갈리는 경우가 있다.


패키지

패키지는 쉽게 말해서 Dart 언어를 써서 작성하면 패키지인것이다.

 

플러그인

Plugin은 특별한 형태의 Package이다.

Plugin은 해당 플랫폼별 기능에 의존적이다. (Plugin relies on that platform-specific functionality.) 

Plugin은 dart언어 뿐만 아니라, 해당 플랫폼의 기능을 사용하기 위해 플랫폼에서 사용하는 언어(Kotilin, Swift, JavaScriipt 등)로 작성된 코드도 포함하고 있다. 

예를 들어 안드로이드폰의 카메라나, 센서에 접근하기 위해서는 코틀린이나 자바로 작성된 플러그인을 사용하거나 직접 작성해야 한다. Plugin의 API는 Dart 로 작성된다. 그러나 기능의 구현은 해당 플랫폼에서 사용하는 native 언어를 이용해서 작성한다. Flutter는 이러한 native code와 소통하기 위해서 Platform channel 을 사용한다.

그리고 플러그인은 아래와 같이 플랫폼 별로 설정도 할수 있다.

flutter:
  plugin:
    platforms:
      android:
        package: com.example.hello
        pluginClass: HelloPlugin
      ios:
        pluginClass: HelloPlugin

 

아래의 영상을 보면 자세히 알 수 있다.

https://www.youtube.com/watch?v=Y9WifT8aN6o 

+ Recent posts