Flutter

2024. 11 .11 플러터 기본기 다지기 - 3

정훈5 2024. 11. 11. 09:49

GridView 위젯

  • 리스트 뷰와 마찬가지로 항목을 나열하는 위젯이지만, 리스트 뷰와 달리 한 줄에 여러 개를 함께 나열할 수 있음
    (그리드 형태)
  • 리스트 뷰와 마찬가지로 그리드뷰도 GridView.builder() 생성자를 제공하며,
    • itemCount, itemBuilder 속성을 통해 항목의 개수와 위젯을 지정
    • gridDelegate 속성을 설정해야 함
      • 이 속성에는 SliverGridDelegateWithFixedCrossAxisCount 객체를 지정해줘야 함
      • 해당 객체에서 crossAxisCount 값이 한 줄에 함께 나와야 하는 항목의 개수임
      • 그리드 두 방향을 설정하지 않으면, crossAxisCount는 가로를 가리킴
  • 참고: scrollDirection 속성에 Axis.horizontal을 설정하면, crossAxisCount는 세로 방향을 가리킴

 

프로젝트 생성

프로젝트 이름 : class_v03

 

main.dart

더보기
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  // build 메서드가 Painting 한다.
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('그리드뷰 위젯 연습'),
        ),
        body: GridView.builder(
            gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
              crossAxisCount: 2,
              crossAxisSpacing: 16,
              mainAxisSpacing: 16
            ),
            itemCount: 10,
            itemBuilder: (context, index) {
              return Container(
                color: Colors.blueGrey,
                child: Center(child: Text('index number :  + $index')),
              );
            },
        ),
      ),
    );
  }
}

 

 


PageView 위젯

  • 스와이프 이벤트에 반응하여 항목을 보여주는 위젯
  • PageController의 initialPage로 처음 보일 페이지를 설정할 수 있고, viewportFraction으로 현재 페이지가 화면에 차지하는 비율도 설정 가능
    • 예: viewportFraction: 0.7이면 현재 페이지가 화면에 70%, 나머지 30%가 이전과 다음 페이지가 살짝 보이게 됨
  • PageController 객체를 PageView의 controller 속성에 적용하여, PageController 설정을 반영시킬 수 있음

 

main1.dart

더보기
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp1());
}

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

  @override
  // build 메서드가 Painting 한다.
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('페이지 뷰 위젯 연습'),
        ),
        body: PageView(
          controller: PageController(
            initialPage: 2,
            viewportFraction: 0.9
          ),
          children: [
            Container(
              margin: EdgeInsets.all(10),
              color: Colors.redAccent,
            ),

            Container(
              margin: EdgeInsets.all(10),
              color: Colors.blueGrey,
            ),

            Container(
              margin: EdgeInsets.all(10),
              color: Colors.green,
            )

          ],
        ),
      ),
    );
  }
}

 

 

쇼핑 카트 앱 만들어

https://github.com/flutter-coder/flutter-book/tree/master/flutter_shoppingcart

 

flutter-book/flutter_shoppingcart at master · flutter-coder/flutter-book

Contribute to flutter-coder/flutter-book development by creating an account on GitHub.

github.com

 

 

작업 순서

pubspec.yaml 이미지 폴더 위치 설정

constants.dart 파일 작업

theme.dart 파일 작업

.. header.dart

… details.dart

 

assets 폴더 만들기

 

사진 업로드

 

pubspec.yaml 

더보기
# To add assets to your application, add an assets section, like this:
assets:
  - assets//
#   - images/a_dot_ham.jpeg

 

constants.dart

더보기
import 'package:flutter/material.dart';

const kPrimaryColor = MaterialColor(
  0xFFeeeeee,
  <int, Color>{
    50: Color(0xFFeeeeee),
    100: Color(0xFFeeeeee),
    200: Color(0xFFeeeeee),
    300: Color(0xFFeeeeee),
    400: Color(0xFFeeeeee),
    500: Color(0xFFeeeeee),
    600: Color(0xFFeeeeee),
    700: Color(0xFFeeeeee),
    800: Color(0xFFeeeeee),
    900: Color(0xFFeeeeee),
  },
);

const kSecondaryColor = Color(0xFFc6c6c6); // 기본 버튼 색
const kAccentColor = Color(0xFFff7643); // 활성화 버튼 색

 

theme.dart

더보기
import 'package:flutter/material.dart';
import 'package:flutter_shoppingcart/constants.dart';

ThemeData theme() {
  return ThemeData(
    primarySwatch: kPrimaryColor,
    scaffoldBackgroundColor: kPrimaryColor,
  );
}

 

shoppingcart_header.dart

더보기
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_shoppingcart/constants.dart';

class ShoppingCartHeader extends StatefulWidget {
  const ShoppingCartHeader({Key? key}) : super(key: key);

  @override
  State<ShoppingCartHeader> createState() => _ShoppingCartHeaderState();
}

class _ShoppingCartHeaderState extends State<ShoppingCartHeader> {
  int selectedId = 0;

  List<String> selectedPic = [
    "assets/p1.jpeg",
    "assets/p2.jpeg",
    "assets/p3.jpeg",
    "assets/p4.jpeg"
  ];

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        _buildHeaderPic(),
        _buildHeaderSelector(),
      ],
    );
  }

  Widget _buildHeaderPic() {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: AspectRatio(
        aspectRatio: 5 / 3,
        child: Image.asset(
          selectedPic[selectedId],
          fit: BoxFit.cover,
        ),
      ),
    );
  }

  Widget _buildHeaderSelector() {
    return Padding(
      padding: const EdgeInsets.only(left: 30, right: 30, top: 10, bottom: 30),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        children: [
          _buildHeaderSelectorButton(0, Icons.directions_bike),
          _buildHeaderSelectorButton(1, Icons.motorcycle),
          _buildHeaderSelectorButton(2, CupertinoIcons.car_detailed),
          _buildHeaderSelectorButton(3, CupertinoIcons.airplane),
        ],
      ),
    );
  }

  // 1. 다른 화면에서도 재사용하면 공통 컴포넌트 위젯으로 관리하는 것이 좋다.
  Widget _buildHeaderSelectorButton(int id, IconData mIcon) {
    return Container(
      width: 70,
      height: 70,
      decoration: BoxDecoration(
        color: id == selectedId ? kAccentColor : kSecondaryColor,
        borderRadius: BorderRadius.circular(20),
      ),
      child: IconButton(
        icon: Icon(mIcon, color: Colors.black),
        onPressed: () {
          setState(() {
            selectedId = id;
          });
        },
      ),
    );
  }
}

 

main.dart

더보기
import 'package:flutter/material.dart';
import 'package:flutter_shoppingcart/components/shoppingcart_detail.dart';
import 'package:flutter_shoppingcart/components/shoppingcart_header.dart';
import 'package:flutter_shoppingcart/theme.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: theme(),
      home: ShoppingCartPage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: _buildShoppingCartAppBar(),
      body: Column(
        children: [
          ShoppingCartHeader(),
          Expanded(child: ShoppingcartDetail())
        ],
      ),
    );
  }

  AppBar _buildShoppingCartAppBar() {
    return AppBar(
      leading: IconButton(
        icon: Icon(Icons.arrow_back),
        onPressed: () {},
      ),
      actions: [
        IconButton(
          icon: Icon(Icons.shopping_cart),
          onPressed: () {},
        ),
        SizedBox(width: 16),
      ],
      elevation: 0.0,
    );
  }
}

 

shoppingcart_detail.dart

더보기
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_shoppingcart/constants.dart';

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

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(40),
      ),
      child: Padding(
        padding: const EdgeInsets.all(30.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            _buildDetailNameAndPrice(),
            _buildDetailRatingAndReviewCount(),
            _buildDetailColorOptions(),
            _buildDetailButton(context),
          ],
        ),
      ),
    );
  }

  Widget _buildDetailNameAndPrice() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.spaceBetween,
      children: [
        Text(
          'Urban Soft AL 10.0',
          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
        ),
        Text(
          '\$699',
          style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
        ),
      ],
    );
  }

  Widget _buildDetailRatingAndReviewCount() {
    return Padding(
      padding: const EdgeInsets.only(bottom: 20.0),
      child: Row(
        children: [
          Icon(Icons.star, color: Colors.yellow),
          Icon(Icons.star, color: Colors.yellow),
          Icon(Icons.star, color: Colors.yellow),
          Icon(Icons.star, color: Colors.yellow),
          Icon(Icons.star, color: Colors.yellow),

          // row 위젯 기준으로 남은 여백 다 차지

          Spacer(),
          Text('Review '),
          Text(
            '(26)',
            style: TextStyle(color: Colors.blue),
          ),
        ],
      ),
    );
  }

  Widget _buildDetailColorOptions() {
    return Padding(
      padding: const EdgeInsets.all(20.0),
      child: Column(
        children: [
          Text('Color Options'),
          SizedBox(height: 10),
          Row(
            children: [
              // 재 사용을 위해 함수로 설계
              _buildDetailIcons(Colors.black),
              _buildDetailIcons(Colors.green),
              _buildDetailIcons(Colors.orange),
              _buildDetailIcons(Colors.white),
            ],
          )
        ],
      ),
    );
  }

  // 여러번 추후 호출 해야 한다.
  Widget _buildDetailIcons(Color mColor) {
    return Padding(
      padding: const EdgeInsets.only(right: 10),
      child: Stack(
        children: [
          Container(
            width: 50,
            height: 50,
            decoration: BoxDecoration(
                color: Colors.white,
                border: Border.all(),
                shape: BoxShape.circle),
          ),
          Positioned(
            left: 5,
            top: 5,
            child: ClipOval(
              child: Container(
                color: mColor,
                width: 40,
                height: 40,
              ),
            ),
          )
        ],
      ),
    );
  }

  Widget _buildDetailButton(BuildContext context) {
    return Align(
      child: TextButton(
        onPressed: () {
          // alert dialog 사용법
          // 플랫폼 별 준비,
          // 1. showDialog 호출
          // 2.ios -> showCupertinoDialog
          showCupertinoDialog(context: context, builder: (context) {
            return CupertinoAlertDialog(
              actions: [

                CupertinoDialogAction(child: Text('확인'),onPressed: () {
                  // 플러터 프레임워크가
                  // 화면간이 이동시에 위젯들이
                  // 스택구조로 관리해주는 객체이다.
                  Navigator.pop(context);
                }),
              ],
            );

          },);

        },
        style: TextButton.styleFrom(
          backgroundColor: kAccentColor,
          maximumSize: Size(300, 50),
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
        ),
        child: Text(
          'Add to Cart',
          style: TextStyle(
            color: Colors.white,
          ),
        ),
      ),
    );
  }
}