액티브 스킬/플러터

[플러터] 화면 전환 시 이미지가 튀어나오는 문제 수정

디벅잉 2024. 2. 3. 14:39
728x90

로컬 이미지 활용

플러터에서 네트워크 이미지가 아닌 로컬로 저장해둔 이미지를 사용하는 경우에도 위젯이 아래로 밀리는 경우가 있습니다.

이미지가 차지할 영역을 미리 알고 있지 못하는 경우입니다.

플러터에서 로컬 이미지 사용시의 일반적인 코드입니다.

Image.asset(
  'assets/images/sea_of_stars.png',
  fit: BoxFit.fitWidth,
),

화면에 보이는 모습은 다음과 같습니다.

이미지 위젯 아래 쪽의 위젯(예시에서는 텍스트 위젯)이 밀려나는 것을 볼 수 있습니다.

이러한 현상을 개선하려면 이미지의 높이 값을 지정해 주면 됩니다.

Image.asset(
  'assets/images/sea_of_stars.png',
  fit: BoxFit.fitWidth,
  height: MediaQuery.sizeOf(context).width * imageRatio,
),

이미지의 높이 값을 사전에 지정할 경우 해당 영역을 미리 확보하여 이미지 아래 쪽의 위젯가 밀리는 것을 방지할 수 있습니다. 

 

하지만 갑자기 이미지가 등장하는 것이 쪼금 아쉽습니다.

그래서 다음과 같이 frameBuilder를 설정해줍니다.

Image.asset(
  'assets/images/sea_of_stars.png',
  fit: BoxFit.fitWidth,
  height: MediaQuery.sizeOf(context).width * imageRatio,
  frameBuilder: (
    BuildContext _,
    Widget child,
    int? frame,
    bool wasSynchronouslyLoaded,
  ) {
    if (wasSynchronouslyLoaded) return child;
    return AnimatedOpacity(
      opacity: frame == null ? 0 : 1,
      duration: const Duration(seconds: 1),
      curve: Curves.easeOut,
      child: child,
    );
  },
),

이미지에 fade-in 효과가 적용되어 조금 더 자연스러워 보입니다.

아래는 해당 페이지 전체 코드입니다.

import 'package:flutter/material.dart';
import 'package:flutter_lorem/flutter_lorem.dart';

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

  static const imageRatio = 9 / 16;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Asset Image Placeholder'),
      ),
      body: Column(
        children: [
          Image.asset(
            'assets/images/sea_of_stars.png',
            fit: BoxFit.fitWidth,
            height: MediaQuery.sizeOf(context).width * imageRatio,
            frameBuilder: (
              BuildContext _,
              Widget child,
              int? frame,
              bool wasSynchronouslyLoaded,
            ) {
              if (wasSynchronouslyLoaded) return child;
              return AnimatedOpacity(
                opacity: frame == null ? 0 : 1,
                duration: const Duration(seconds: 1),
                curve: Curves.easeOut,
                child: child,
              );
            },
          ),
          const SizedBox(height: 16),
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 20),
            child: Text(
              lorem(paragraphs: 2, words: 60),
            ),
          ),
        ],
      ),
    );
  }
}

 

추가한 패키지

- flutter_lorem(https://pub.dev/packages/flutter_lorem): 테스트용 텍스트 생성을 위해 사용

 

728x90