LayoutBuilder

샤워실의 바보·2024년 2월 11일
0
post-thumbnail
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:tiktok_clone/constants/breakpoints.dart';
import 'package:tiktok_clone/constants/gaps.dart';
import 'package:tiktok_clone/constants/sizes.dart';

final tabs = [
  "Top",
  "Users",
  "Videos",
  "Sounds",
  "LIVE",
  "Shopping",
  "Brands",
];

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

  
  State<DiscoverScreen> createState() => _DiscoverScreenState();
}

class _DiscoverScreenState extends State<DiscoverScreen> {
  final TextEditingController _textEditingController =
      TextEditingController(text: "Initial Text");

  void _onSearchChanged(String value) {
    print("Searching form $value");
  }

  void _onSearchSubmitted(String value) {
    print("Submitted $value");
  }

  
  void dispose() {
    _textEditingController.dispose();
    super.dispose();
  }

  
  Widget build(BuildContext context) {
    final width = MediaQuery.of(context).size.width;
    return DefaultTabController(
      length: tabs.length,
      child: Scaffold(
        resizeToAvoidBottomInset: false,
        appBar: AppBar(
          elevation: 1,
          title: CupertinoSearchTextField(
            controller: _textEditingController,
            onChanged: _onSearchChanged,
            onSubmitted: _onSearchSubmitted,
          ),
          bottom: TabBar(
            splashFactory: NoSplash.splashFactory,
            padding: const EdgeInsets.symmetric(
              horizontal: Sizes.size16,
            ),
            isScrollable: true,
            labelStyle: const TextStyle(
              fontWeight: FontWeight.w600,
              fontSize: Sizes.size16,
            ),
            indicatorColor: Colors.black,
            labelColor: Colors.black,
            unselectedLabelColor: Colors.grey.shade500,
            tabs: [
              for (var tab in tabs)
                Tab(
                  text: tab,
                ),
            ],
          ),
        ),
        body: TabBarView(
          children: [
            GridView.builder(
              keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
              itemCount: 20,
              padding: const EdgeInsets.all(
                Sizes.size10,
              ),
              gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: width > Breakpoints.lg ? 5 : 2,
                crossAxisSpacing: Sizes.size10,
                mainAxisSpacing: Sizes.size10,
                childAspectRatio: 9 / 20,
              ),
              itemBuilder: (context, index) => LayoutBuilder(
                builder: (context, constraints) => Column(
                  children: [
                    Container(
                      clipBehavior: Clip.hardEdge,
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(Sizes.size4),
                      ),
                      child: AspectRatio(
                        aspectRatio: 9 / 16,
                        child: FadeInImage.assetNetwork(
                          fit: BoxFit.cover,
                          placeholder: "assets/images/placeholder.jpg",
                          image:
                              "https://images.unsplash.com/photo-1673844969019-c99b0c933e90?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=1480&q=80",
                        ),
                      ),
                    ),
                    Gaps.v10,
                    Text(
                      "${constraints.maxWidth} This is a very long caption for my tiktok that im upload just now currently.",
                      overflow: TextOverflow.ellipsis,
                      maxLines: 2,
                      style: const TextStyle(
                        fontSize: Sizes.size16 + Sizes.size2,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    Gaps.v8,
                    if (constraints.maxWidth < 200 ||
                        constraints.maxWidth > 250)
                      DefaultTextStyle(
                        style: TextStyle(
                          color: Colors.grey.shade600,
                          fontWeight: FontWeight.w600,
                        ),
                        child: Row(
                          children: [
                            const CircleAvatar(
                              radius: 12,
                              backgroundImage: NetworkImage(
                                "https://avatars.githubusercontent.com/u/3612017",
                              ),
                            ),
                            Gaps.h4,
                            const Expanded(
                              child: Text(
                                "My avatar is going to be very long",
                                maxLines: 1,
                                overflow: TextOverflow.ellipsis,
                              ),
                            ),
                            Gaps.h4,
                            FaIcon(
                              FontAwesomeIcons.heart,
                              size: Sizes.size16,
                              color: Colors.grey.shade600,
                            ),
                            Gaps.h2,
                            const Text(
                              "2.5M",
                            )
                          ],
                        ),
                      )
                  ],
                ),
              ),
            ),
            for (var tab in tabs.skip(1))
              Center(
                child: Text(
                  tab,
                  style: const TextStyle(
                    fontSize: 28,
                  ),
                ),
              )
          ],
        ),
      ),
    );
  }
}

이 코드에서 LayoutBuilderGridView.builder 내부에서 사용되어 각 그리드 아이템의 레이아웃을 동적으로 조정하는 역할을 합니다. LayoutBuilder는 부모 위젯의 제약 조건에 기반하여 자식 위젯의 레이아웃을 결정할 수 있게 해주는 위젯입니다. 자식 위젯은 부모로부터 전달받은 공간의 크기(constraints)를 바탕으로 어떻게 자신을 그려낼지 결정할 수 있습니다.

LayoutBuilder의 주요 기능 및 사용법:

  • LayoutBuilderbuilder 함수를 통해 위젯을 반환합니다. 이 함수는 현재 LayoutBuilder 위젯의 부모가 제공하는 공간의 제약 조건(BoxConstraints)을 인자로 받습니다.
  • 개발자는 이 제약 조건을 사용하여 동적인 레이아웃을 구성할 수 있습니다. 예를 들어, 부모 위젯의 크기에 따라 자식 위젯의 크기, 배열, 또는 스타일을 변경할 수 있습니다.

코드 내에서의 로직:

  • GridView.builder를 사용하여 타일 형태의 아이템을 그리드로 배열합니다. 각 타일은 사진과 캡션으로 구성됩니다.
  • 그리드의 열 수는 디바이스의 화면 너비(width)에 따라 동적으로 결정됩니다. 화면 너비가 Breakpoints.lg보다 큰 경우 5열로, 그렇지 않으면 2열로 배열합니다.
  • 각 타일에 LayoutBuilder를 사용하여 타일의 최대 너비(constraints.maxWidth)를 기준으로 조건부 레이아웃 로직을 적용합니다.
    • 사진은 FadeInImage.assetNetwork를 사용하여 네트워크 이미지를 비동기로 로드하고, AspectRatio 위젯을 사용하여 사진의 종횡비를 9:16로 유지합니다.
    • 캡션은 텍스트의 길이에 따라 두 줄까지 표시되며, 너비가 일정 범위를 벗어날 경우 추가적인 UI 요소(예: 사용자 아바타, 좋아요 아이콘 및 카운트)를 조건부로 표시합니다.

결론:

LayoutBuilder를 사용함으로써, 이 코드는 다양한 화면 크기와 해상도에서도 일관된 사용자 경험을 제공할 수 있는 동적 레이아웃을 구현합니다. 특히, GridView 내부에서 개별 아이템의 레이아웃을 화면 크기에 따라 조정함으로써, 앱의 반응형 디자인을 향상시킬 수 있습니다.

이 코드 조각은 LayoutBuilder 위젯 내부에서 사용되는 문자열 표현식입니다. LayoutBuilder는 부모 위젯으로부터 받은 공간의 제약 조건(constraints)에 따라 자식 위젯의 레이아웃을 구성할 때 사용됩니다. 여기서 constraints.maxWidthLayoutBuilder에 의해 제공된 최대 너비 값을 참조하며, 이 값은 부모 위젯이 자식 위젯에게 할당할 수 있는 최대 너비를 나타냅니다.

코드 분석:

  • "${constraints.maxWidth} This is a very long caption for my tiktok that im upload just now currently."
    • 이 문자열은 두 부분으로 구성됩니다:
      1. constraints.maxWidth: LayoutBuilder의 현재 제약 조건에서 제공된 최대 너비 값입니다. 이 값을 문자열 내에 직접 삽입하여 동적으로 표시합니다.
      2. " This is a very long caption for my tiktok that im upload just now currently.": 사용자가 업로드한 TikTok 비디오에 대한 설명을 나타내는 정적인 텍스트 부분입니다.

사용 목적 및 의미:

  • 이 코드 조각은 LayoutBuilder를 사용하여 동적으로 결정되는 최대 너비 값(constraints.maxWidth)과 함께 비디오의 캡션을 표시하는 데 사용됩니다.
  • 개발자는 이 방식을 통해 다양한 화면 크기에서의 UI 반응성을 테스트하고, 특정 너비에서 콘텐츠가 어떻게 보이는지 확인할 수 있습니다. 예를 들어, 최대 너비를 기반으로 텍스트 크기나 레이아웃을 조정할 수 있습니다.
  • "${constraints.maxWidth}"를 통해 실제로 텍스트가 표시될 때 해당 위치에서의 최대 너비를 시각적으로 확인할 수 있으며, 이는 디버깅이나 레이아웃 조정에 유용할 수 있습니다.

실제 사용 시 고려사항:

  • 실제 애플리케이션에서 사용자에게 constraints.maxWidth 값을 직접 보여주는 것은 일반적으로 바람직하지 않습니다. 이 코드는 주로 개발 과정에서 레이아웃을 테스트하고 조정하는 데 사용될 수 있으며, 최종 사용자에게는 최적화된 콘텐츠와 UI만을 제공해야 합니다.
  • 최대 너비를 기반으로 동적으로 콘텐츠를 조정하거나, 반응형 레이아웃을 구현할 때는 해당 값을 직접적으로 사용자에게 표시하기보다는, 이를 통해 조건부 레이아웃 로직을 구성하는 것이 좋습니다.
profile
공부하는 개발자

0개의 댓글