flutter로 만든 web app에 처음 진입했을 때 보여줄 loading indicator를 적용하자.
해당 글은 flutter version 3.22 이상 환경에서 작성하였으며 3.22 이하의 경우 legacy문서를 확인하면 된다.
flutter Web를 빌드할때 dart2js가 dart 코드 전체를 javascript로 컴파일한 다음 결과를 build/web/main.dart.js로 저장한다.
index.html에서는 js를 불러와서 화면을 그리게 되는데 이 때 사용자에게는 계속해서 하얀색의 화면이 노출되게 되는데 사용자 경험을 좋지 않게 만들고 있다.
이것을 해결하기 위해 initialization
를 적용해보고자 한다.
initialization
공식문서를 보면 웹 앱이 초기화 될 때 커스텀을 할 수 있는 방법을 제공해주고 있다.
먼저 프로젝트를 생성하였을 때 web
디렉토리에 있는 index.html
의 코드를 보면 아래와 같이 스크립트 하나가 정의되어 있는 것을 볼 수 있다.
<html>
<!-- ... -->
<body>
<script src="flutter_bootstrap.js" async></script>
</body>
<!-- ... -->
</html>
위 코드에서 볼 수 있듯 flutter_bootstrap.js
로 initialization
을 처리할 수 있는데 해당 파일은 flutter의 web이 빌드 될 때 build 파일 안에 자동으로 생긴다. 해당 파일의 역할은 앱을 초기화 하고 실행하는데 필요한 정보들이 들어있는 파일이라고 생각하면 된다.
만약 web
디렉토리 내부에 flutter_bootstrap.js
를 새로 정의했다면 빌드 시 오버라이딩되며 적용된다. 기본적으로 flutter에서 빌드 시 자동으로 생성해주는 flutter_bootstrap.js
는 아래와 같다.
// 1
{{flutter_js}}
// 2
{{flutter_build_config}}
// 3
_flutter.loader.load();
flutter_js
는 FlutterLoader
object를 사용 가능하게 만들어준다. FlutterLoader
내부에는 _flutter.loader
가 글로벌 변수로 존재한다.
flutter_build_config
는 빌드 과정에서 생성된 메타데이터를 설정하는 역할을 한다. 앱이 정상적으로 부트스트랩되기 위해 FlutterLoader
에 데이터를 제공해준다.
앱을 초기화 하기 호출되는 함수이며 초기화 시 사용할 옵션들을 받는다.
onEntrypointLoaded
목표를 이루기 위해서 중요하게 볼 부분이 onEntrypointLoaded
콜백이다. 위에서도 적은 것 처럼 엔진의 초기화 준비가 되면 해당 콜백이 호출되게 되는데 해당 콜백 내부에서 runApp()
이 호출 되기 전까지 유저에게 흰 화면이 보여지게 되는 것이다.
앱이 실행 되기 전에 초기화 및 로딩 처리에 대한 부분을 해당 콜백에서 정의하면 된다.
loading indicator
적용하기progress-indicator예시에 나온 것 처럼 먼저 document.body
에 div
를 심고 해당 요소를 loading indicator
로 사용하고자 한다.
// fluter_bootstrap.js
{{flutter_js}}
{{flutter_build_config}}
const loadingDiv = document.createElement("div");
loadingDiv.className = "loading";
document.body.appendChild(loadingDiv);
const loaderDiv = document.createElement("div");
loaderDiv.className = "loader";
loadingDiv.appendChild(loaderDiv);
// Customize the app initialization process
_flutter.loader.load();
필자는 css-loaders에서 loading indicator
의 css를 가져와 사용하으며 해당 코드를 index.html
에 추가하였다.
/* style.css */
.loading {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
-ms-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
}
.loader {
width: 50px;
aspect-ratio: 1;
color:#dc1818;
background:
radial-gradient(circle at 60% 65%, currentColor 62%, #0000 65%) top left,
radial-gradient(circle at 40% 65%, currentColor 62%, #0000 65%) top right,
linear-gradient(to bottom left, currentColor 42%,#0000 43%) bottom left ,
linear-gradient(to bottom right,currentColor 42%,#0000 43%) bottom right;
background-size: 50% 50%;
background-repeat: no-repeat;
position: relative;
}
.loader:after {
content: "";
position: absolute;
inset: 0;
background: inherit;
opacity: 0.4;
animation: l3 1s infinite;
}
@keyframes l3 {
to {transform:scale(1.8);opacity:0}
}
위와 같이 작성하면 앱에 들어왔을 때 해당 loading indicator
가 보여질 것이다. loading indicator
는 엔진이 준비되기 전까지 보여주고 사라져야 하기 때문에 onEntrypointLoaded
콜백을 이용하여 loading indicator
로 사용되는 div
요소를 제거해주면 된다.
{{flutter_js}}
{{flutter_build_config}}
const loadingDiv = document.createElement("div");
loadingDiv.className = "loading";
document.body.appendChild(loadingDiv);
const loaderDiv = document.createElement("div");
loaderDiv.className = "loader";
loadingDiv.appendChild(loaderDiv);
_flutter.loader.load({
onEntrypointLoaded: async function (engineInitializer) {
const appRunner = await engineInitializer.initializeEngine();
if (document.body.contains(loadingDiv)) {
document.body.removeChild(loadingDiv);
}
await appRunner.runApp();
},
});