mobile developer

[Futter] 코딩 지침 본문

FLUTTER

[Futter] 코딩 지침

keyman4949 2022. 5. 10. 09:20

Flutter의 코딩 지침

이 코딩 지침은 Flutter 개발자가 코드 스타일과 앱의 전반적인 성능을 개선하는 데 도움이 되도록 만들어졌습니다.

1. 가능하면 상수를 사용하십시오.

const 키워드는 변수 값이 컴파일 타임에 알려지고 변경되지 않을 때 사용됩니다.
여기에는 const 클래스, 변수, 목록 등이 포함됩니다.

2. 정말로 필요한 경우 nullable을 사용하십시오.

런타임에 변수를 초기화해야 하는 것이 확실하다면 late를 사용하십시오.
널 입력 가능 유형은 훌륭하지만 변수가 널일 수 있거나 널이 될 것이라고 확신할 때만 사용하십시오.

3. 큰 위젯을 작은 위젯으로 나눕니다.

빌드 기능의 크기 를 최소화 하고 위젯을 가능한 한 작고 모듈식으로 유지해야 합니다. 상태 변경이 있을 때 상태 저장 위젯이 다시 그려지고 있습니다 . 즉, 전체 위젯을 다시 빌드해야 하고 빌드 트리가 클수록 다시 빌드하는 데 더 많은 리소스 가 필요합니다.

4. 짧은 함수와 메소드에 => 사용을 고려하십시오.

그러나 함수 선언을 포함하여 모든 것이 ± 120자에 맞는 경우에만 =>를 사용하십시오.
또한 목록 또는 매핑 리터럴만 반환하는 인라인 콜백에 유용합니다.

5. 일관된 imports

상대 경로 또는 패키지를 가져와도 성능에 영향을 주지 않습니다. 그러나 어떤 것을 상대 경로로 가져온 다음 패키지로 가져오고 다른 상대 경로로 가져오면 혼란스러워 하고 큰 혼란에 빠진 자신을 쉽게 찾을 수 있습니다.
어느 것을 선택하느냐는 중요하지 않지만 일관성을 유지하십시오.
상대 경로 대신 패키지를 사용하는 것을 선호합니다.

6. InitState 및 Dispose 순서

코드는 어느 쪽이든 작동하지만 Flutter 설명서에 따르면 올바른 순서는 먼저 super.initState() 를 호출하고 마지막으로 super.dispose() 를 호출하는 것입니다.

//GOOD EXAMPLE

@override 
void initState() { 
  super.initState();    
  // 작업을 수행합니다. 
} 

@override 
void dispose() { 
  // 작업을 수행합니다. 
  super.dispose(); 
}

7. Final 사용

"Final"은 단일 할당을 의미합니다 . 최종 변수 또는 필드에는 이니셜라이저가 있어야 합니다. 값이 할당되면 최종 변수의 값은 변경할 수 없습니다.

//BAD EXAMPLE

List<String> toppings = ['mozarella', 'pepperoni'];
toppings = ['olives', 'cheddar'];

//GOOD EXAMPLE

final List<String> toppings = ['mozarella', 'pepperoni'];
toppings.clear()
toppings.addAll(['olives', 'cheddar']);

8. 연산자를 효율적으로 사용

이렇게 하면 논리적 오류를 더 쉽게 피할 수 있고 코드가 더 강력하고 읽기 쉽습니다.

//BAD EXAMPLE

String? pizza;
pizza == null ? 'margherita' : pizza;

//GOOD EXAMPLE

String? pizza;
pizza ?? 'margherita';

9. 위젯 반환 기능 대신 위젯 반환

이것은 큰 안티 패턴입니다. 전체 위젯을 변경하고 새로 고치면 메서드 내에 있는 위젯도 새로 고쳐져 CPU 주기를 낭비하게 됩니다. 대신 새 상태 비저장 위젯으로 위젯을 내보냅니다.
또 다른 나쁜 점은 BuildContext를 매개변수로 전달하는 것입니다. 일반적으로 빨간색 플래그인 컨텍스트를 전달해야 하는 경우 이를 위한 새 위젯이 필요합니다.

//BAD EXAMPLE

@override
Widget build(BuildContext context) {
  return Padding(
    padding: const EdgeInsets.all(8.0),
    child: _buildPizzaWidget(context),
  );
}

Widget _buildPizzaWidget(BuildContext context) {
  return Center(
    child: Text(
      context.read<PizzaCubit>().phoneNumber,
    ),
  );
}

//GOOD EXAMPLE

@override
  Widget build(BuildContext context) {
    return const Padding(
      padding: const EdgeInsets.all(8.0),
      child: PizzaWidget(),
    );
  }
}

class PizzaWidget extends StatelessWidget {
  const PizzaWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        context.read<PizzaCubit>().phoneNumber,
      ),
    );
  }
}


보시다시피 BuildContext를 전달하지 않고 전체 위젯은 상수인 반면에 위젯 반환 함수는 절대 상수가 될 수 없습니다.

10. 가능하면 모든 것에 주석을 달자

여기에는 변수, 끝점에서 받은 데이터, 함수 반환까지 포함됩니다.

//BAD EXAMPLE

//BAD, we need to annotate the return type
makePizza(String lastTopping) => 'The last is: $lastTopping';

//GOOD EXAMPLE

//The return type is String
String makePizza(String lastTopping) => 'The last is: $lastTopping';

11. 기본 인수를 작성할 필요가 없습니다.

이것은 간단한 것이므로 필요하지 않으면 쓰지 마십시오.

//BAD EXAMPLE

@override
Widget build(BuildContext context) {
  return const Padding(
    padding: EdgeInsets.only(
      left: 5.0,
      //We dont need this
      right: 0,
    ),
  );
}

//GOOD EXAMPLE

@override
Widget build(BuildContext context) {
  return const Padding(
    padding: EdgeInsets.only(left: 5.0),
  );
}

12. 불필요한 중첩 기능 피하기

코드를 읽을 수 없게 만들고 () =>는 전혀 필요하지 않은 추가 기능입니다. 또한 앞에서 언급했듯이 여기에서는 컨텍스트를 인수로 전달할 필요가 없습니다.

//BAD EXAMPLE

@override
Widget build(BuildContext context) {
  return GestureDetector(
     onTap: () => orderPizza(context),
  );
}

orderPizza(BuildContext context) {
  context.read<PizzaCubit>().orderPizza;
}

//GOOD EXAMPLE

@override
Widget build(BuildContext context) {
  return GestureDetector(
    onTap: context.read<PizzaCubit>().orderPizza();
  );
}

13. 빈 Containers() 대신 SizeBox.shrink() 사용

이것은 많은 설명이 필요하지 않습니다. 아무것도 표시하지 않으려면 Container() 가 잘못된 방법입니다. SizeBox.shrink() 는 가능한 한 적은 공간을 차지합니다.

14. SingleChildScrollView와 Column 조합 대신 ListView 사용

위젯 목록을 표시하려면 일반적으로 Column()에 넣습니다. 그런 다음 위젯이 반응하고 스크롤 가능하기를 원합니다. 이제 이를 Expanded 또는 Flexible Widget에 넣고 SingleChildScrollView()로 래핑합니다.

//BAD EXAMPLE

@override
Widget build(BuildContext context) {
  return SingleChildScrollView(
    child: Expanded(
      child: Column(children: [
        //YOUR WIDGETS
      ],),
    ),
  );
}

//GOOD EXAMPLE

@override
Widget build(BuildContext context) {
  return ListView(
    children: [
      //YOUR WIDGETS
    ],
  );
}

15. 긴 목록을 위한 ListView.builder()

목록에 10개의 위젯이 있는 경우 간단한 ListView로 이동할 수 있습니다. 하지만 10000개의 위젯이 있다면 어떻게 될까요?
ListView() 생성자 는 모든 항목을 한 번에 생성해야 합니다. 목록 항목이 적고 모든 항목이 화면에 표시될 때 유용합니다.
ListView.builder() 생성자는 항목이 주문형 처럼 화면으로 스크롤될 때 항목을 생성합니다. 이것은 항목이 화면에 표시될 때만 항목이 렌더링되는 목록 위젯 개발을 위한 모범 사례입니다.

16. 필요한 매개변수만 전달

많은 양의 데이터가 있는 클래스를 전달하는 대신 간단하게 만들고 실제로 필요한 매개변수를 전달해야 합니다.
피자 클래스가 있다고 가정해 보겠습니다.

class Pizza {
  int inches = 30;
  String sauce = 'Tomato';
  bool pepperoni = true;
  bool mushrooms = false;
  String cheese = 'Cheddar';
}

내 위젯이 반죽에만 관심이 있다면 왜 토핑을 전달해야 합니까?

//BAD EXAMPLE

class PizzaDoughWidget extends StatelessWidget {
  final Pizza pizza;
  const PizzaDoughWidget({Key? key, required this.pizza}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(pizza.doughSize.toString());
  }
}

//GOOD EXAMPLE

class PizzaDoughWidget extends StatelessWidget {
  final int doughSize;
  const PizzaDoughWidget({Key? key, required this.doughSize}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(doughSize.toString());
  }
}

이것은 비교해야 할 때, 얼마나 많은 매개변수가 필요한지, 전체 클래스를 통과해야 할 때 까다롭습니다. 내 충고는 같은 클래스에서 매개변수가 두 개 이상 필요한 경우 전체 클래스를 전달할 수 있다는 것입니다. 코드가 적고 끝에서 더 읽기 쉬워지기 때문입니다.

17. 스위치 케이스 피하기

스위치 케이스에 대한 몇 가지 좋은 용도가 있지만 대부분의 경우 간단한 Map이 더 나은 작업을 수행합니다.
피자 예제로 돌아가 보겠습니다.

//BAD EXAMPLE

List<String> getPizzaFromName(String name) {
  switch (name) {
    case 'pepperoni':
      return ['cheese', 'tomato', 'pepperoni'];
    case 'hawaii':
      return ['cheese', 'tomato', 'pineapple'];
    default :
      return ['cheese', 'tomato'];
  }
}
List<String> toppings = getPizzaFromName('pepperoni');

//GOOD EXAMPLE

Map<String, List<String>> getPizzeFromName = {
  'pepperoni' : ['cheese', 'tomato', 'pepperoni'],
  'hawaii' : ['cheese', 'tomato', 'pineapple'],
};
List<String> toppings = getPizzeFromName['pepperoni'];

우리가 볼 수 있듯이 이것은 코드가 적고 다른 스위치 케이스보다 다른 맵 항목을 추가하는 것이 더 쉽습니다.

18. 사용하지 않는 콜백 매개변수에는 _를 사용하는 것이 좋습니다.

때때로 콜백 함수의 형식 서명에는 매개변수가 필요하지만 콜백 구현에서는 매개변수를 사용 하지 않습니다 . 이 경우 사용하지 않는 매개변수의 이름을 로 지정하는 것이 관용적입니다. 함수에 사용하지 않은 매개변수가 여러 개 있는 경우 이름 충돌을 방지하기 위해 추가 밑줄을 사용하십시오(_ 등).

19. forEach 대신 for 선호

For는 forEach보다 훨씬 낫습니다. 첫째, 성능 친화적이며 둘째, return, break, continue에 대한 액세스 권한이 있으며 인덱스에 대한 액세스도 유용합니다.

//BAD EXAMPLE

pizzaToppings.forEach((topping) {
  print(topping);
});

//GOOD EXAMPLE

for (var topping in pizzaToppings) {
  print(topping);
}

20. 특정 위젯 유형 대신 위젯 반환

예, 항상 특정 유형을 지정해야 하지만 이번에는 그렇지 않습니다. CustomWidget을 간단한 Padding() 으로 감싸고 싶다고 상상해보세요 . 우리의 코드는 깨질 것이고 우리는 그것을 리팩토링하는 데 많은 시간을 할애할 수 있습니다. 따라서 위젯을 반환하려면 간단한 위젯 반환이 가장 좋은 방법입니다.

//BAD EXAMPLE

CustomWidget returnCustomWidget() {
  return CustomWidget();
}

//GOOD EXAMPLE

Widget returnCustomWidget() {
  return CustomWidget();
}

21. 모든 것에 컨테이너를 사용하지 마십시오.

컨테이너는 훌륭하지만 여전히 무거운 위젯입니다.
예를 들어 위젯에 크기만 추가하려는 경우 전체 컨테이너보다 간단한 크기 상자가 더 나은 성능을 보입니다 .
패딩만 원하면 Padding 을 사용 하십시오.
또 다른 예로 위젯을 둥글게 만들거나 색상을 추가하거나 사용자 지정 장식을 추가하려는 경우 DecoratedBox 를 사용하는 것이 좋습니다.
Container 대신 Constraints가 필요한 경우 ConstrainedBox를 사용 하세요 .
콘텐츠만 정렬하려면 Align Widget을 사용하세요.
그러나 이것들이 더 필요하다면 컨테이너 가 이것들을 함께 중첩하는 것보다 더 읽기 쉽습니다.

Comments