ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • UI Development Exam - Image, Expanded Widget
    DEVELOPMENT/FLUTTER 2022. 1. 22. 17:20

    플러터의 장점, 강력한 화면 구성

     

    제가 느끼는 플러터의 장점은 UI 구성 시스템이 머리 속에 정리가 되고 Widget 코드가 손에만 익는다면 화면 구성이 매우 쉽다는 점인 것 같습니다.

    이번에 보는 책인 「모두가 할 수 있는 플러터 UI 입문」의 예제를 저 나름대로 가공하여 따라 하면서 플러터는 사용하는 자의 능력에 따라 UI 구성을 정말 쉽게 해주는 위젯과 속성이 무척 많다는 걸 다시 느낍니다.

     

    AppBar 없는 Scaffold

    Scaffold 위젯에서 AppBar는 있어도 그만 없어도 그만인 속성입니다. 보통 있긴 하지만 커스터마이징한 나만의 AppBar 클래스를 활용하거나, 전체 화면을 사용하려면 굳이 AppBar가 있을 필요가 없죠. 이번 예제도 AppBar가 없더라구요.

    예제의 화면 구성을 눈으로 익힌 후 바로 화면 코드를 짜 봤는데... 이런..........

     

    Column 위젯이 안드로이드 상태표시줄에 가려저서....-_-;;; 안 보입니다.

    이를 해결하기 위해 Flutter는 SafeArea 위젯을 지원합니다. SafeArea 위젯은 상태표시줄, 카메라, 노치 등의 다양한 상황에 맞게 Child 위젯에 Padding 값을 강제하여 화면이 안 보이게 되는 현상을 막아주는 역할을 합니다.

    import 'package:flutter/material.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      const MyHomePage({Key? key, required this.title}) : super(key: key);
      final String title;
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: SafeArea(
            child: Column(
              children: [
                Container(
                  width: double.infinity,
                  color: Colors.white,
                  child: Padding(
                    padding: EdgeInsets.all(20),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: [
                        Text('WOMAN'),
                        Text('KIDS'),
                        Text('SHOES'),
                        Text('BAG')
                      ],
                    )
                  ),
                )
              ],
            ),
          ),
        );
      }
    }

     

    Body를 SafeArea로 감싸니 다음과 같이 해결이 되었습니다. 이렇게 또 하나 배우는구나.. 라는 생각을 하게 되고, 플러터가 생각보다 UI 구성이 쉽고 다양한 상황에 잘 대비가 되어 있구나 하는 생각이 듭니다.

     

     

    Image 위젯

     

    플러터에 이미지를 추가하는 방법은 크게 2가지 입니다. 인터넷 서버 상의 이미지를 불러오는 방법과 프로젝트 폴더에 내장된 이미지를 불러 오는 방법이죠. 그래서 이 2가지 방법을 모두 다 사용하여 이미지를 불러와 보았습니다.

    프로젝트 폴더에 내장된 이미지를 불러 오기 위해선 다음과 같은 준비가 필요합니다.


    1. 프로젝트 폴더 하위에 다음 그림과 같이 폴더를 생성한 후 이미지를 저장합니다. 저는 assets라는 폴더를 생성하고 해당 폴더 내에 asset1.jpg 파일을 저장하였습니다.

     

    2. pubspec.yaml 파일을 클릭한 후 assets 항목의 주석을 삭제한 후 이미지 경로를 지정합니다.

    name: my_store
    description: A new Flutter project.
    
    # The following line prevents the package from being accidentally published to
    # pub.dev using `flutter pub publish`. This is preferred for private packages.
    publish_to: 'none' # Remove this line if you wish to publish to pub.dev
    
    # The following defines the version and build number for your application.
    # A version number is three numbers separated by dots, like 1.2.43
    # followed by an optional build number separated by a +.
    # Both the version and the builder number may be overridden in flutter
    # build by specifying --build-name and --build-number, respectively.
    # In Android, build-name is used as versionName while build-number used as versionCode.
    # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
    # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
    # Read more about iOS versioning at
    # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
    version: 1.0.0+1
    
    environment:
      sdk: ">=2.15.1 <3.0.0"
    
    # Dependencies specify other packages that your package needs in order to work.
    # To automatically upgrade your package dependencies to the latest versions
    # consider running `flutter pub upgrade --major-versions`. Alternatively,
    # dependencies can be manually updated by changing the version numbers below to
    # the latest version available on pub.dev. To see which dependencies have newer
    # versions available, run `flutter pub outdated`.
    dependencies:
      flutter:
        sdk: flutter
    
    
      # The following adds the Cupertino Icons font to your application.
      # Use with the CupertinoIcons class for iOS style icons.
      cupertino_icons: ^1.0.2
    
    dev_dependencies:
      flutter_test:
        sdk: flutter
    
      # The "flutter_lints" package below contains a set of recommended lints to
      # encourage good coding practices. The lint set provided by the package is
      # activated in the `analysis_options.yaml` file located at the root of your
      # package. See that file for information about deactivating specific lint
      # rules and activating additional ones.
      flutter_lints: ^1.0.0
    
    # For information on the generic Dart part of this file, see the
    # following page: https://dart.dev/tools/pub/pubspec
    
    # The following section is specific to Flutter.
    flutter:
    
      # The following line ensures that the Material Icons font is
      # included with your application, so that you can use the icons in
      # the material Icons class.
      uses-material-design: true
    
      # To add assets to your application, add an assets section, like this:
      assets:
        - assets/
      #   - images/a_dot_ham.jpeg
    
      # An image asset can refer to one or more resolution-specific "variants", see
      # https://flutter.dev/assets-and-images/#resolution-aware.
    
      # For details regarding adding assets from package dependencies, see
      # https://flutter.dev/assets-and-images/#from-packages
    
      # To add custom fonts to your application, add a fonts section here,
      # in this "flutter" section. Each entry in this list should have a
      # "family" key with the font family name, and a "fonts" key with a
      # list giving the asset and other descriptors for the font. For
      # example:
      # fonts:
      #   - family: Schyler
      #     fonts:
      #       - asset: fonts/Schyler-Regular.ttf
      #       - asset: fonts/Schyler-Italic.ttf
      #         style: italic
      #   - family: Trajan Pro
      #     fonts:
      #       - asset: fonts/TrajanPro.ttf
      #       - asset: fonts/TrajanPro_Bold.ttf
      #         weight: 700
      #
      # For details regarding fonts from package dependencies,
      # see https://flutter.dev/custom-fonts/#from-packages

    이렇게 이미지를 사용할 준비가 되었다면 Image Widget을 생성한 후 매개변수로 사용할 이미지의 상대경로를 설정하면 됩니다.

     

    Image.asset('assets/asset1.jpg', fit: BoxFit.cover),

     

    fit 속성은 이미지 위젯이 화면에 보여지는 크기를 지정하는 속성입니다.

    BoxFit.fill - 이미지 영역을 최대화하여 표시
    BoxFit.contain - 이미지 비율을 유지하며 최대한 크게 표시
    BoxFit.cover - 이미지 비율을 유지한 채 지정한 범위를 최대한 크게 표시
    BoxFit.fitWidth - 최대 너비에 맞춰 표시
    BoxFit.fitheight - 최대 높이에 맞춰 표시
    BoxFit.none - 원본 사이즈대로 표시
    BoxFit.scaleDown - 이미지 해상도를 조절하여 표시

     

    다음으로 인터넷 서버 상의 이미지를 불러 오려면 다음과 같이 코드를 작성하면 됩니다. BoxFit 속성은 다른 이미지 에셋과 동일합니다.

     

    Image.network('https://cdn.pixabay.com/photo/2020/05/03/19/09/nike-5126389_960_720.jpg', fit: BoxFit.cover)

     

    이렇게 Column을 활용한 메뉴와 두 이미지(BoxFit.cover)를 배치한 결과는 다음과 같습니다.

     

     

    이렇게 보니 밑이 약간 비어 있습니다. 두 이미지를 화면에 꽉 채우기 위해선 또다른 작업이 필요한 것 같습니다.

     

    Expanded 위젯의 Flex

     

    Flex는 XAML을 이용하여 너비와 높이를 %를 활용하여 쉽게 비율을 조정했습니다. (개인적으로 Adobe가 왜 Flex를 그리 홀대하고 결국엔 넘겨버렸는지 이해가 잘 안된다는......) 이러한 기능을 플러터에서 구현한다면 가장 쉽게 하는 방법이 Expanded 위젯을 이용하는 것 같습니다.

    Expanded 위젯은 빈 영역을 최대한 확장시키는 위젯입니다. 부모가 Column 위젯이라면 높이(Height)를, Row 위젯이라면 너비(Width)를 말이죠. 또한 flex 속성을 통해 확장 비율을 정할 수도 있습니다.

    따라서 위의 두 이미지 위젯을 각각 Expanded 위젯으로 감싸면 아래의 남는 공간을 모두 채울 수 있습니다.

     

    import 'package:flutter/material.dart';
    import 'package:flutter/widgets.dart';
    
    void main() {
      runApp(const MyApp());
    }
    
    class MyApp extends StatelessWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          debugShowCheckedModeBanner: false,
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: const MyHomePage(title: 'Flutter Demo Home Page'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      const MyHomePage({Key? key, required this.title}) : super(key: key);
      final String title;
    
      @override
      State<MyHomePage> createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: SafeArea(
            child: Column(
              children: [
                Container(
                  width: double.infinity,
                  color: Colors.white,
                  child: Padding(
                    padding: EdgeInsets.all(20),
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                      children: [
                        Text('WOMAN'),
                        Text('KIDS'),
                        Text('SHOES'),
                        Text('BAG')
                      ],
                    )
                  ),
                ),
                Expanded(
                  child: Image.asset('assets/asset1.jpg', fit: BoxFit.cover),
                ),
                SizedBox(
                  height: 5,
                ),
                Expanded(
                  child: Image.network('https://cdn.pixabay.com/photo/2020/05/03/19/09/nike-5126389_960_720.jpg', fit: BoxFit.cover)
                )
              ],
            ),
          ),
        );
      }
    }

     

    Expanded 위젯의 사이에 약간은 틈을 주기 위해 SizedBox 위젯으로 빈 공간을 주었습니다. 그리고 그 결과 아래 그림과 같이 화면을 꽉 채우는 결과를 얻게 되었습니다.

     

     

    그리고 Expanded 위젯의 flex 속성을 다음과 같이 다르게 주게 되면 이미지 위젯 또한 해당 비율에 맞춰 크기가 달라지게 됩니다.

     

    Expanded(
      flex: 1,
      child: Image.asset('assets/asset1.jpg', fit: BoxFit.cover),
    ),
    SizedBox(
      height: 5,
    ),
    Expanded(
      flex: 2,
      child: Image.network('https://cdn.pixabay.com/photo/2020/05/03/19/09/nike-5126389_960_720.jpg', fit: BoxFit.cover)
    )

     

     

     

Designed by Tistory.