렌더링 된 이미지 크기 구하기

렌더링 된 이미지의 크기를 구하고 싶을 때가 있습니다.

화면의 크기에 따라 반응형으로 이미지의 크기가 변경되고, 이미지의 크기에 따라 다른 위젯의 크기를 변경해 주고 싶을 때가 있습니다.


이미지 크기를 가져오는 법

1. 이미지 위젯에 key를 넣어줘야 합니다. 키를 하나 만들어주세요.

/// GlobalKey를 생성하여 이미지 위젯의 상태를 추적
final GlobalKey imageBoardKey = GlobalKey();


2. 이미지 위젯에 key를 넣어주세요.

Image(
image: imageBoard,
key: imageBoardKey,
),


3.  key의 currentContext -> findRenderObject를 통해 RenderBox 객체를 가져옵니다.

/// 이미지의 넓이 또는 높이를 이용하여 기물의 위치를 정의
final renderBox =
imageBoardKey.currentContext?.findRenderObject() as RenderBox;


4. renderBox의 size를 통해 Size 객체를 얻고 width와 height를 구할 수 있습니다.

final size = renderBox.size;


이미지를 미리 로드하기

이미지를 렌더링 하기 위해서 에셋, 네트워크 통신 등 이미지를 읽어와야 합니다. 이 과정은 비동기적으로 이루어지는데, 만약 이미지를 로드 하기 전에 이미지의 크기를 구하려 한다면 size의 width와 height의 크기가 0으로 반환 될 것입니다.

예컨대 StatefulWidget의 initState 함수에 위 단락의 3, 4번과 같이 코드를 작성한다면 이미지의 크기를 구할 수 없을 수도 있습니다. 또는 addPostFrameCallback을 통해 위젯 트리의 완성을 기다려 준다 하더라도 이미지의 로딩 시간이 길면 initState를 통해 크기를 구할 수 없습니다.

초반에 이미지의 크기를 바로 구하고 싶으시다면 이미지를 미리 로드 해야 합니다. 

await precacheImage(imageBoard, context)

이미지를 보여줘야 하는 스크린에 진입하기 직전에 이미지를 미리 로드 해줍니다. 저는 소량의 에셋 이미지 밖에 없어서 그냥 홈 화면에서 한번에 다 로드 해버렸습니다.


파라미터로 ImageProvider를 넣어줘야 합니다. AssetImage, NetworkImage, MemoryImage 등의 클래스를 이용하여 넣어주세요. Image.asset, Image.network, Image.memory는 Image 위젯 자체를 반환합니다. ImageProvider와 구분됩니다.


전체 코드

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:samyeonchoga/core/constant/color.dart';
import 'package:samyeonchoga/provider/in_game/in_game_navigator_provider.dart';
import 'package:samyeonchoga/provider/in_game/in_game_piece_set_provider.dart';
import 'package:samyeonchoga/provider/in_game/in_game_system_notification_provider.dart';
import 'package:samyeonchoga/provider/in_game/in_game_turn_provider.dart';
import 'package:samyeonchoga/ui/common/widget/image_assets.dart';
import 'package:samyeonchoga/ui/in_game/controller/board_position_value.dart';

class InGameBody extends ConsumerStatefulWidget {
const InGameBody({super.key, required this.gameHadSaved});

final bool gameHadSaved;

@override
ConsumerState<InGameBody> createState() => _InGameBodyState();
}

class _InGameBodyState extends ConsumerState<InGameBody> {
/// GlobalKey를 생성하여 이미지 위젯의 상태를 추적
final GlobalKey imageBoardKey = GlobalKey();

@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
/// 이미지의 넓이 또는 높이를 이용하여 기물의 위치를 정의
final renderBox =
imageBoardKey.currentContext?.findRenderObject() as RenderBox;
final size = renderBox.size;

/// 장기판 사이즈에 따른 값 초기화
initBoardPositionValue(boardWidth: size.width, boardHeight: size.height);

if (widget.gameHadSaved) {
/// 기물 저장된 데이터로 초기화
ref.read(inGamePieceSetProvider.notifier).initPieceWithSavedData();
} else {
/// 기물 초기화
ref.read(inGamePieceSetProvider.notifier).initPieceSet();

/// 기물 스폰 애니메이션 기다린 후 게임 시작
Future.delayed(const Duration(seconds: 1), () {
if (mounted) {
ref.read(inGameTurnProvider.notifier).changeTurn();
}
});
}
});
}

@override
Widget build(BuildContext context) {
final pieceSet = ref.watch(inGamePieceSetProvider);
final navigatorBoxList = ref.watch(inGameNavigatorProvider);
final systemNotification = ref.watch(inGameSystemNotificationProvider);

return ColoredBox(
color: inGameBlackColor,
child: Center(
child: Stack(
alignment: AlignmentDirectional.bottomStart,
children: [
Align(
alignment: Alignment.bottomLeft,
child: Image(
image: imageBoard,
key: imageBoardKey,
),
),
...pieceSet,
...navigatorBoxList,
...systemNotification,
],
),
),
);
}
}

댓글

이 블로그의 인기 게시물

카트라이더 개선 방안

팩토리 패턴을 응용한 플러터의 클린 아키텍처

프로젝트 사면초가