Add interactivity to your Flutter app(개인적 정리)

힐링코더·2023년 8월 29일
0

Flutter for a code monkey

목록 보기
3/14

출처: https://docs.flutter.dev/ui/interactivity#stateful-and-stateless-widgets
순전히 내가 이해하려고 정리한 글이라 다른 사람의 학습에 별 도움이 되지 않을 수 있습니다.

1) 여러 위젯의 상태를 동시에 관리하고 싶다. > 커스텀 위젯 만들어야 함.

2) 위젯의 상태는 State 객체에 저장되며, 이를 통해 위젯의 상태와 외관이 분리됩니다. 상태는 슬라이더의 현재 값이나 체크박스의 선택 여부와 같이 변경될 수 있는 값으로 구성됩니다. 위젯의 상태가 변경되면, 상태 객체는 setState()를 호출하여 프레임워크에 위젯을 다시 그리도록 알립니다.

3) StatefulWidget은 StatefulWidget의 하위 클래스와 State의 하위 클래스로 구현된다.

lib/main.dart (FavoriteWidget)
content_copy
class FavoriteWidget extends StatefulWidget {
  const FavoriteWidget({super.key});

  @override
  State<FavoriteWidget> createState() => _FavoriteWidgetState();
}

FavoriteWidget 클래스는 자체 상태를 관리하므로 createState()를 오버라이드하여 State 객체를 생성합니다. 프레임워크는 위젯을 빌드하고자 할 때 createState()를 호출합니다. 이 예제에서 createState()는 다음 단계에서 구현할 _FavoriteWidgetState의 인스턴스를 반환합니다.

lib/main.dart (_FavoriteWidgetState fields)
content_copy
class _FavoriteWidgetState extends State<FavoriteWidget> {
  bool _isFavorited = true;
  int _favoriteCount = 41;

  // ...
}

_FavoriteWidgetState 클래스는 위젯의 수명 동안 변경될 수 있는 가변 데이터를 저장합니다. 앱이 처음 실행될 때 UI는 빨간색으로 채워진 별과 함께 호수가 "즐겨찾기" 상태임을 나타내는 41개의 좋아요를 표시합니다. 이러한 값들은 _isFavorited 및 _favoriteCount 필드에 저장됩니다.


상태 관리
방법은 3개.
1) 위젯 자체가 상태를 관리합니다.
2) 부모 위젯이 위젯의 상태를 관리합니다.
3) 혼합 및 일치하는 방법

만약 고려 중인 상태가 사용자 데이터인 경우, 예를 들어 체크박스의 선택 여부나 슬라이더의 위치와 같은 것이라면, 해당 상태는 부모 위젯에서 관리하는 것이 가장 좋습니다.
만약 고려 중인 상태가 미적인 요소인 경우, 예를 들어 애니메이션과 같은 것이라면, 해당 상태를 위젯 자체에서 관리하는 것이 가장 좋습니다.
의문이 든다면, 먼저 상태를 부모 위젯에서 관리하도록 시작하세요.

1) 위젯 자체가 상태를 관리하는 케이스
가끔은 위젯이 상태를 내부적으로 관리하는 것이 가장 합리적일 수 있습니다. 예를 들어, ListView는 콘텐츠가 렌더 박스를 초과할 때 자동으로 스크롤됩니다. 대부분의 ListView를 사용하는 개발자들은 ListView의 스크롤 동작을 직접 관리하고 싶지 않기 때문에 ListView 자체가 스크롤 오프셋을 관리합니다.

_TapboxAState 클래스:

TapboxA의 상태를 관리합니다.
상자의 현재 색상을 결정하는 _active boolean을 정의합니다.
_handleTap() 함수를 정의합니다. 상자를 탭하면 _active를 업데이트하고 setState() 함수를 호출하여 UI를 업데이트합니다.
위젯의 모든 대화형 동작을 구현합니다.

//코드 안에서 상태를 직접 관리한다.

위 코드는 TapboxA 위젯을 만드는 방법을 보여줍니다. TapboxA 위젯은 자체적으로 상태를 관리하며, 탭할 때마다 상태를 변경하고 UI를 업데이트합니다. 또한 해당 위젯을 사용하는 MyApp 클래스를 정의하여 표시합니다.

2) 부모 위젯이 위젯의 상태를 관리하는 케이스
자주, 상태를 관리하고 언제 자식 위젯을 업데이트할지를 부모 위젯이 결정하는 것이 가장 합리적일 수 있습니다. 예를 들어, IconButton을 사용하면 아이콘을 탭할 수 있는 버튼으로 다룰 수 있습니다. IconButton은 부모 위젯이 버튼이 탭되었는지 여부를 알아야 하기 때문에 상태를 관리하지 않는 위젯입니다.

다음 예제에서 TapboxB는 콜백을 통해 상태를 부모 위젯으로 내보냅니다. TapboxB는 어떤 상태도 관리하지 않기 때문에 StatelessWidget을 하위 클래스로 갖습니다.

ParentWidgetState 클래스:

TapboxB의 _active 상태를 관리합니다.
상자가 탭될 때 호출되는 _handleTapboxChanged() 메서드를 구현합니다.
상태가 변경되면 setState()를 호출하여 UI를 업데이트합니다.
TapboxB 클래스:

모든 상태를 부모 위젯이 처리하므로 StatelessWidget을 확장합니다.
탭이 감지되면 부모에게 알립니다.

import 'package:flutter/material.dart';

//------------------------- TapboxB ----------------------------------

class TapboxB extends StatelessWidget {
  const TapboxB({required this.active, required this.onChanged, super.key});

  final bool active;
  final ValueChanged<bool> onChanged;

  void _handleTap() {
    onChanged(!active);
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: _handleTap,
      child: Container(
        width: 200,
        height: 200,
        decoration: BoxDecoration(
          color: active ? Colors.lightBlue[700] : Colors.grey[600],
        ),
        child: Center(
          child: Text(
            active ? 'Active' : 'Inactive',
            style: const TextStyle(fontSize: 32, color: Colors.white),
          ),
        ),
      ),
    );
  }
}

//------------------------- ParentWidget ----------------------------------

class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

  @override
  State<ParentWidget> createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: TapboxB(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//------------------------- MyApp ----------------------------------

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Flutter Demo'),
        ),
        body: const Center(
          child: ParentWidget(),
        ),
      ),
    );
  }
}

3) 믹스 앤 매치 스타일(스까 스타일)
일부 위젯에는 혼합 및 일치하는 접근 방식이 가장 합리적일 수 있습니다. 이 경우, 상태를 관리하는 위젯이 일부 상태를 관리하고 부모 위젯이 다른 측면의 상태를 관리합니다.

TapboxC 예제에서는 탭 다운 시 상자 주변에 어두운 녹색 테두리가 나타납니다. 탭 업 시 테두리가 사라지고 상자의 색상이 변경됩니다. TapboxC는 _active 상태를 부모 위젯에 내보내지만 _highlight 상태를 내부적으로 관리합니다. 이 예제에는 _ParentWidgetState와 _TapboxCState 두 가지 상태 객체가 있습니다.

_ParentWidgetState 객체:

_active 상태를 관리합니다.
상자가 탭될 때 호출되는 _handleTapboxChanged() 메서드를 구현합니다.
탭이 발생하고 _active 상태가 변경되면 setState()를 호출하여 UI를 업데이트합니다.
_TapboxCState 객체:

_highlight 상태를 관리합니다.
GestureDetector는 모든 탭 이벤트를 수신합니다. 사용자가 탭 다운할 때 하이라이트(어두운 녹색 테두리로 구현)를 추가합니다. 사용자가 탭을 놓을 때 하이라이트를 제거합니다.
탭 다운, 탭 업 또는 탭 취소 시 setState()를 호출하여 UI를 업데이트하고 _highlight 상태가 변경되면, 그리고 해당 상태 변경을 부모 위젯에 전달하여 위젯 속성을 사용하여 적절한 동작을 수행합니다.

import 'package:flutter/material.dart';

//---------------------------- ParentWidget ----------------------------

class ParentWidget extends StatefulWidget {
  const ParentWidget({super.key});

  @override
  State<ParentWidget> createState() => _ParentWidgetState();
}

class _ParentWidgetState extends State<ParentWidget> {
  bool _active = false;

  void _handleTapboxChanged(bool newValue) {
    setState(() {
      _active = newValue;
    });
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      child: TapboxC(
        active: _active,
        onChanged: _handleTapboxChanged,
      ),
    );
  }
}

//----------------------------- TapboxC ------------------------------

class TapboxC extends StatefulWidget {
  const TapboxC({
    super.key,
    this.active = false,
    required this.onChanged,
  });

  final bool active;
  final ValueChanged<bool> onChanged;

  @override
  State<TapboxC> createState() => _TapboxCState();
}

class _TapboxCState extends State<TapboxC> {
  bool _highlight = false;

  void _handleTapDown(TapDownDetails details) {
    setState(() {
      _highlight = true;
    });
  }

  void _handleTapUp(TapUpDetails details) {
    setState(() {
      _highlight = false;
    });
  }

  void _handleTapCancel() {
    setState(() {
      _highlight = false;
    });
  }

  void _handleTap() {
    widget.onChanged(!widget.active);
  }

  @override
  Widget build(BuildContext context) {
    // This example adds a green border on tap down.
    // On tap up, the square changes to the opposite state.
    return GestureDetector(
      onTapDown: _handleTapDown, // Handle the tap events in the order that
      onTapUp: _handleTapUp, // they occur: down, up, tap, cancel
      onTap: _handleTap,
      onTapCancel: _handleTapCancel,
      child: Container(
        width: 200,
        height: 200,
        decoration: BoxDecoration(
          color: widget.active ? Colors.lightGreen[700] : Colors.grey[600],
          border: _highlight
              ? Border.all(
                  color: Colors.teal[700]!,
                  width: 10,
                )
              : null,
        ),
        child: Center(
          child: Text(widget.active ? 'Active' : 'Inactive',
              style: const TextStyle(fontSize: 32, color: Colors.white)),
        ),
      ),
    );
  }
}

대안적인 구현은 하이라이트 상태를 부모에게 내보내면서 활성 상태는 내부적으로 유지하는 것일 수 있습니다. 그러나 만약 누군가에게 그 탭 박스를 사용하도록 요청한다면, 그 사람들은 아마 그게 많이 이치에 맞지 않다고 불평할 것입니다. 개발자는 상자가 활성 상태인지 신경쓰지만, 아마 하이라이트가 어떻게 관리되는지는 신경쓰지 않을 것이며, 탭 박스가 그런 세부 사항을 처리하길 원할 것입니다.

결론: 상황에 맞게 개발자가 알잘딱깔센 써야 한다.

다른 대화형 위젯들
Flutter는 다양한 버튼 및 유사한 대화형 위젯을 제공합니다. 대부분의 이 위젯들은 머티리얼 디자인 가이드라인을 구현합니다. 이 가이드라인은 의견이 분분한 UI를 갖는 구성 요소 집합을 정의합니다.

원한다면, GestureDetector를 사용하여 사용자 상호작용을 사용자 정의 위젯에 구축할 수 있습니다. GestureDetector의 예제는 상태 관리에서 찾을 수 있습니다. GestureDetector에 대한 더 많은 정보는 "Handle taps"라는 플러터 쿡북의 레시피에서 찾을 수 있습니다.

팁: 플러터는 "Cupertino"이라는 이름의 iOS 스타일의 위젯 집합도 제공합니다.

사용자 상호작용이 필요할 때는 사전 제작된 위젯 중 하나를 사용하는 것이 가장 쉽습니다. 여기 일부 목록을 제공합니다:

표준 위젯
Form
FormField

머티리얼 컴포넌트
Checkbox
DropdownButton
TextButton
FloatingActionButton
IconButton
Radio
ElevatedButton
Slider
Switch
TextField

profile
여기는 일상 블로그, 기술 블로그는 https://yourhealingcoder.tistory.com/

0개의 댓글