재사용 가능한 위젯을 만드는 함수와 클래스의 차이점은 무엇입니까?


125

StatelessWidget 을 서브 클래 싱하는 대신 일반 함수를 사용하여 위젯을 만들 수 있다는 것을 깨달았습니다 . 예는 다음과 같습니다.

Widget function({ String title, VoidCallback callback }) {
  return GestureDetector(
    onTap: callback,
    child: // some widget
  );
}

이것은 본격적인 클래스보다 훨씬 적은 코드가 필요하기 때문에 흥미 롭습니다 . 예:

class SomeWidget extends StatelessWidget {
  final VoidCallback callback;
  final String title;

  const SomeWidget({Key key, this.callback, this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
      return GestureDetector(
        onTap: callback,
        child: // some widget
      );
  }
}

그래서 저는 궁금했습니다. 위젯을 만드는 함수와 클래스 사이에 구문 외에 다른 점이 있습니까? 함수를 사용하는 것이 좋은 습관입니까?


이 스레드가 문제를 이해하는 데 매우 유용하다는 것을 알았습니다. reddit.com/r/FlutterDev/comments/avhvco/…
RocketR

답변:


172

요약 : 재사용 가능한 위젯 트리 를 만들기 위해 함수보다 클래스 사용을 선호 합니다.


편집 : 일부 오해를 보완하기 위해 : 이것은 문제를 일으키는 기능이 아니라 일부를 해결하는 클래스에 관한 것입니다.

함수가 같은 일을 할 수 있다면 Flutter에는 StatelessWidget 이 없을 것입니다.

마찬가지로, 주로 재사용되도록 만들어진 공용 위젯을 대상으로합니다. 이 동작을 인식하는 것이 여전히 좋지만 개인 함수를 한 번만 사용하는 것은 중요하지 않습니다.


클래스 대신 함수를 사용하는 것에는 중요한 차이점이 있습니다. 즉, 프레임 워크가 함수를 인식하지 못하지만 클래스를 볼 수 있습니다.

다음 "위젯"기능을 고려하십시오.

Widget functionWidget({ Widget child}) {
  return Container(child: child);
}

이 방법으로 사용 :

functionWidget(
  child: functionWidget(),
);

그리고 그것은 동등한 클래스입니다.

class ClassWidget extends StatelessWidget {
  final Widget child;

  const ClassWidget({Key key, this.child}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(
      child: child,
    );
  }
}

다음과 같이 사용됩니다.

new ClassWidget(
  child: new ClassWidget(),
);

서류상 둘 다 똑같은 일을하는 것 같습니다 : Create 2 Container, 하나가 다른 하나에 중첩되어 있습니다. 그러나 현실은 약간 다릅니다.

함수의 경우 생성 된 위젯 트리는 다음과 같습니다.

Container
  Container

클래스를 사용하는 동안 위젯 트리는 다음과 같습니다.

ClassWidget
  Container
    ClassWidget
      Container

이것은 위젯을 업데이트 할 때 프레임 워크가 작동하는 방식을 변경하기 때문에 중요합니다.

그게 중요한 이유

기능을 사용하여 위젯 트리를 여러 위젯으로 분할하면 버그에 노출되고 일부 성능 최적화를 놓칠 수 있습니다.

당신이된다는 보장이 없다 할 것이다 함수를 사용하지만 클래스를 사용하여, 당신이하는에 의해 버그가 보장 이러한 문제에 직면하지 않도록이.

다음은 문제를 더 잘 이해하기 위해 직접 실행할 수있는 Dartpad의 몇 가지 대화 형 예제입니다.

결론

다음은 함수와 클래스 사용의 차이점에 대한 선별 된 목록입니다.

  1. 클래스:
  • 성능 최적화 허용 (const 생성자,보다 세분화 된 재구성)
  • 두 개의 서로 다른 레이아웃간에 전환하면 리소스가 올바르게 처리되는지 확인합니다 (함수는 일부 이전 상태를 재사용 할 수 있음).
  • 핫 리로드가 제대로 작동하는지 확인합니다 (기능을 사용하면 핫 리로드가 중단 될 수 있음 showDialogs).
  • 위젯 인스펙터에 통합됩니다.
    • 우리는보다 ClassWidget 화면에 무엇인지 이해하는 데 도움이 devtool에 의해 보였다 위젯 트리에
    • debugFillProperties 를 재정 의하여 위젯에 전달 된 매개 변수가 무엇인지 출력 할 수 있습니다.
  • 더 나은 오류 메시지
    예외가 발생하면 (예 : ProviderNotFound) 프레임 워크가 현재 빌드중인 위젯의 이름을 제공합니다. 위젯 트리를 함수로만 분할했다면 +Builder 오류에 유용한 이름이 없습니다.
  • 키를 정의 할 수 있습니다.
  • 컨텍스트 API를 사용할 수 있습니다.
  1. 기능 :

전반적으로 이러한 이유로 인해 위젯을 재사용하기 위해 클래스보다 함수를 사용하는 것은 나쁜 습관으로 간주됩니다.
당신은 할 수 있지만,이 당신을 물지 수 있습니다.


의견은 확장 된 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Samuel Liew

10

이 문제에 대해 지난 2 일 동안 조사했습니다. 저는 다음과 같은 결론에 도달했습니다. 앱의 일부를 기능으로 나누는 것은 괜찮습니다. 이러한 함수가를 반환하는 것이 이상적 StatelessWidget이므로를 만드는 것과 같은 최적화를 수행 할 수 StatelessWidget const있으므로 필요하지 않은 경우 다시 빌드하지 않습니다. 예를 들어 다음 코드는 완벽하게 유효합니다.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      ++_counter;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
            const MyWidgetClass(key: const Key('const')),
            MyWidgetClass(key: Key('non-const')),
            _buildSomeWidgets(_counter),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }

  Widget _buildSomeWidgets(int val) {
    print('${DateTime.now()} Rebuild _buildSomeWidgets');
    return const MyWidgetClass(key: Key('function'));

    // This is bad, because it would rebuild this every time
    // return Container(
    //   child: Text("hi"),
    // );
  }
}

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

  @override
  Widget build(BuildContext context) {
    print('${DateTime.now()} Rebuild MyWidgetClass $key');

    return Container(
      child: Text("hi"),
    );
  }
}

함수를 사용하면 const StatelessWidget. 내가 틀렸다면 나를 고쳐주세요.


누군가 내가 말한 것이 잘못된 이유를 설명 할 수 있습니까? 내 말은, 나는 그것이 반대표를 감안할 때 잘못되었다고 생각합니다.
Sergiu Iacob

나는 실제로 당신과 동의합니다. 차이점에 대해 훨씬 더 자세한 분석을 작성하려고했지만 아직 다루지 않았습니다. 위젯 v 메소드의 장단점을 이해하는 것이 중요하다고 생각하므로 자유롭게 논의를 구체화하십시오.
TheIT

@SergiuIacob const모든 경우에 상태 비 저장 클래스 앞에서 사용할 수 있습니까 ? 아니면 특정 사례 여야합니까? 그렇다면 그들은 무엇입니까?
aytunch

1
@aytunch const어디에서나 사용할 수 있다고 생각하지 않습니다 . 예를 들어, 변수의 값을 포함 StatelessWidget하는를 반환 하는 클래스가 Text있고 해당 변수가 어딘가에서 변경되는 StatelessWidget경우 다시 빌드해야하므로 다른 값을 표시 할 수 있으므로 const. 나는 그것을 넣는 안전한 방법은 이것이라고 생각합니다 : 당신이 할 수있는 곳이라면 어디에서나 const안전하게 사용하십시오 .
Sergiu Iacob

3
나는이 질문에 스스로 답할 것인지에 대해 토론하고있다. 받아 들여진 대답은 분명 틀렸지 만, 레미는 플러터 커뮤니티를 돕기 위해 많은 노력을 기울 였기 때문에 사람들은 아마도 다른 사람만큼 그의 대답을 면밀히 조사하지 않을 것입니다. 그것은 모든 찬성 투표에서 분명 할 수 있습니다. 사람들은 단지 "단일 소스의 진실"을 원합니다. :-)
DarkNeuron 2019-06-28

4

함수가하는 일과 클래스가하는 일 사이에는 큰 차이가있었습니다.


처음부터 설명하겠습니다 .🙂 ( 명령어에 대해서만)

  • 프로그래밍 역사, 우리 모두는 기본적인 명령 (예 : 어셈블리)으로 시작한다는 것을 알고 있습니다.

  • 다음 구조화 된 프로그래밍은 흐름 제어와 함께 제공됩니다 (예 : if, switch, while, for 등).이 패러다임은 프로그래머에게 프로그램의 흐름을 효과적으로 제어하고 루프별로 코드 줄 수를 최소화 할 수 있도록합니다.

  • 다음 절차 프로그래밍이 왔고 명령어를 절차 (기능)로 그룹화했습니다. 이것은 프로그래머에게 두 가지 주요 이점을 제공했습니다.

    1. 문 (연산)을 별도의 블록으로 그룹화합니다.

    2.이 블록을 재사용 할 수 있습니다. (기능)

그러나 무엇보다도 패러다임은 애플리케이션 관리를위한 솔루션을 제공 하지 못했습니다 . 절차 적 프로그래밍은 소규모 애플리케이션에만 사용할 수 있습니다. 대규모 웹 애플리케이션 (예 : 뱅킹, 구글, 유튜브, 페이스 북, 스택 오버 플로우 등) 개발에는 사용할 수 없으며, 안드로이드 SDK, 플러터 SDK 등과 같은 프레임 워크를 만들 수 없습니다.

따라서 엔지니어는 프로그램을 적절한 방식으로 관리하기 위해 더 많은 연구를 수행합니다.

  • 마지막으로 객체 지향 프로그래밍 은 모든 규모의 애플리케이션을 관리하기위한 모든 솔루션과 함께 제공됩니다 (hello world에서 시스템 생성을 사용하는 수많은 사람들 (예 : Google, amazon 및 현재 애플리케이션의 90 %)에 이르기까지).

  • oop에서 모든 애플리케이션은 객체를 중심으로 구축되며 이는 애플리케이션이 이러한 객체의 모음임을 의미합니다.

따라서 객체는 모든 응용 프로그램의 기본 건물입니다.

클래스 (런타임시 객체)는 해당 변수 (데이터)와 관련된 데이터 및 함수를 그룹화합니다. 따라서 객체는 데이터 및 관련 작업으로 구성됩니다.

[여기에 대해 설명하지 않겠습니다]


👉👉👉Ok Now 플러터 프레임 워크를 시작하겠습니다 .👈👈👈

-Dart는 절차 적 및 oop를 모두 지원하지만, Flutter 프레임 워크는 클래스 (oop)를 사용하여 완전히 구축됩니다. (대형 관리 가능한 프레임 워크는 절차를 사용하여 만들 수 없기 때문에)

여기에서는 위젯을 만드는 데 함수 대신 클래스를 사용하는 이유 목록을 만들 것입니다.


1-대부분의 경우 빌드 메소드 (하위 위젯) 가 동기 및 비동기 함수의 수를 호출 합니다.

전의:

  • 네트워크 이미지를 다운로드하려면
  • 사용자 등으로부터 입력 받기

따라서 빌드 메소드는 별도의 클래스 위젯에 보관해야합니다 (build () 메소드에 의해 호출 된 다른 모든 메소드가 하나의 클래스에 보관 될 수 있기 때문).


2-위젯 클래스를 사용하면 동일한 코드를 반복해서 작성하지 않고도 여러 클래스를 생성 할 수 있습니다 (** 상속 사용 ** (확장)).

또한 상속 (확장) 및 다형성 (재정의)을 사용하여 사용자 정의 클래스를 만들 수 있습니다. (아래 예제 아래에서 MaterialPageRoute를 확장하여 애니메이션을 사용자 정의 (재정의)합니다 (기본 전환을 사용자 정의하고 싶기 때문에) .👇

class MyCustomRoute<T> extends MaterialPageRoute<T> {
  MyCustomRoute({ WidgetBuilder builder, RouteSettings settings })
      : super(builder: builder, settings: settings);

  @override                                      //Customize transition
  Widget buildTransitions(BuildContext context,
      Animation<double> animation,
      Animation<double> secondaryAnimation,
      Widget child) {
    if (settings.isInitialRoute)
      return child;
    // Fades between routes. (If you don't want any animation, 
    // just return child.)
    return new FadeTransition(opacity: animation, child: child);
  }
}

3-함수는 매개 변수에 대한 조건을 추가 할 수 없습니다.하지만 클래스 위젯의 생성자를 사용하면됩니다.

아래 코드 예제 👇 (이 기능은 프레임 워크 위젯에서 많이 사용됨)

const Scaffold({
    Key key,
    this.bottomNavigationBar,
    this.bottomSheet,
    this.backgroundColor,
    this.resizeToAvoidBottomPadding,
    this.resizeToAvoidBottomInset,
    this.primary = true,
    this.drawerDragStartBehavior = DragStartBehavior.start,
    this.extendBody = false,
    this.extendBodyBehindAppBar = false,
    this.drawerScrimColor,
    this.drawerEdgeDragWidth,
  }) : assert(primary != null),
       assert(extendBody != null),
       assert(extendBodyBehindAppBar != null),
       assert(drawerDragStartBehavior != null),
       super(key: key);

4-함수는 const를 사용할 수 없으며 클래스 위젯은 생성자에 const를 사용할 수 있습니다. (메인 스레드의 성능에 영향을 미침)


5-동일한 클래스 (클래스 / 객체의 인스턴스)를 사용하여 여러 개의 독립 위젯을 만들 수 있지만 함수는 독립 위젯 (인스턴스)을 만들 수 없지만 재사용 할 수 있습니다.

[각 인스턴스에는 자체 인스턴스 변수가 있고 다른 위젯 (객체)과 완전히 독립적이지만 함수의 로컬 변수 는 각 함수 호출 *에 따라 다릅니다. 즉, 로컬 변수의 값을 변경하면 다른 모든 부분에 영향을 미칩니다. 이 기능을 사용하는 애플리케이션)]


클래스에는 함수보다 많은 장점이 있습니다. (위에는 몇 가지 사용 사례 만 있습니다)


🤯 나의 최종 생각

따라서 함수를 애플리케이션의 빌딩 블록으로 사용하지 말고 오퍼레이션을 수행 할 때만 사용하십시오. 그렇지 않으면 응용 프로그램이 확장 될 때 처리 할 수없는 많은 문제가 발생합니다 .

  • 작업의 작은 부분을 수행하기위한 기능 사용
  • 클래스를 응용 프로그램의 구성 요소로 사용 (응용 프로그램 관리)

📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍 📍📍📍📍📍📍📍

사용 된 문장 (또는 줄)의 수로 프로그램의 품질을 측정 할 수 없습니다.

📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍📍 📍📍📍📍📍📍📍

읽어 주셔서 감사합니다


Stackoverflow에 오신 것을 환영합니다! 나는 당신이 당신의 대답으로 무엇을 표현하려고하는지 잘 모르겠습니다. 위젯을 빌드하는 데 잘 맞는 함수를 사용할 수 있습니다. 위젯 트리에서 인라인 shrinkHelper() { return const SizedBox.shrink(); }을 사용하는 것과 동일하며 const SizedBox.shrink()도우미 함수를 사용하여 한 위치에서 중첩 양을 제한 할 수 있습니다.
DarkNeuron

@DarkNeuron 공유해 주셔서 감사합니다. 헬퍼 기능을 사용해 보겠습니다.
TDM

2

Flutter 위젯을 호출 할 때 const 키워드를 사용해야합니다. 예를 들면const MyListWidget();


9
이것이 OP 질문에 어떻게 대답하는지 알 수 있습니까?
CopsOnRoad

2
잘못된 섹션이라고 답한 것 같습니다. 리팩토링 된 상태 비 저장 위젯 빌드 메서드가 여전히 호출되고 있다는 Daniel의 질문에 대답하려고했습니다. const리팩토링 된 상태 비 저장 위젯을 호출 할 때 키워드 를 추가하면 한 번만 호출해야합니다.
user4761410 dec.

1
확인. 알았다. 사람들은 OP 질문과 관련이 없기 때문에이 답변을 반대 할 수 있습니다. 따라서 삭제해야합니다. 어쨌든 선택은 당신 것입니다.
CopsOnRoad
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.