ConstrainedBox

샤워실의 바보·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: ConstrainedBox(
            constraints: const BoxConstraints(
              maxWidth: Breakpoints.sm,
            ),
            child: 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,
                  ),
                ),
              )
          ],
        ),
      ),
    );
  }
}

이 코드에서 ConstrainedBox 위젯은 CupertinoSearchTextField의 최대 너비를 제한하는 데 사용됩니다. ConstrainedBox는 자식 위젯에 대한 추가적인 제약 조건을 부여할 수 있는 위젯으로, 여기서는 검색 필드의 너비가 특정 최대 값(Breakpoints.sm)을 초과하지 않도록 합니다. 이를 통해 검색 필드가 화면 너비에 관계없이 일관된 크기를 유지할 수 있게 됩니다.

ConstrainedBox의 주요 기능 및 사용법:

  • ConstrainedBox는 자식 위젯에 최소 및 최대 너비와 높이 제약 조건을 적용할 수 있게 해주는 위젯입니다.
  • constraints 속성을 통해 BoxConstraints 객체를 전달함으로써, 이러한 제약 조건을 정의할 수 있습니다.
  • BoxConstraintsminWidth, maxWidth, minHeight, maxHeight 등의 속성을 제공하여, 자식 위젯이 이러한 값들의 범위 내에서 레이아웃이 결정되도록 합니다.

코드 내에서의 로직:

  • AppBartitle로 사용된 CupertinoSearchTextField는 사용자가 검색어를 입력할 수 있는 필드입니다. 이 검색 필드는 앱 바 내에 배치되어 있으며, 사용자에게 일관된 사용자 경험을 제공하기 위해 너비가 제한되어야 합니다.
  • ConstrainedBoxconstraints 속성에 BoxConstraints(maxWidth: Breakpoints.sm)를 지정함으로써, 검색 필드의 최대 너비를 Breakpoints.sm 값으로 제한합니다. 이는 검색 필드가 너무 넓어져서 UI 디자인을 해치는 것을 방지합니다.
  • CupertinoSearchTextField에는 사용자의 검색어 변경 및 제출을 처리하는 콜백(onChanged, onSubmitted)이 등록되어 있습니다.

결론:

ConstrainedBox를 사용함으로써, 개발자는 CupertinoSearchTextField 같은 자식 위젯의 크기를 유연하게 조절할 수 있으며, 디바이스 화면 크기나 방향에 관계없이 일관된 레이아웃을 유지할 수 있습니다. 이러한 접근 방식은 특히 반응형 디자인을 구현할 때 유용하며, 사용자에게 보다 안정적이고 예측 가능한 인터페이스를 제공할 수 있게 합니다.

profile
공부하는 개발자

0개의 댓글