Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 89 additions & 0 deletions __brick__/web/flutter_bootstrap.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{{=<% %>=}}{{flutter_js}}<%={{ }}=%>
{{=<% %>=}}{{flutter_build_config}}<%={{ }}=%>

const progressBar = document.querySelector('#progress-bar');
const progressText = document.querySelector('#progress-text');
const progressIndicator = document.querySelector('#progress-indicator');

async function readAssets() {
// NOTE: AssetManifest.json will be deprecated in favour of AssetManifest.bin:
// https://github.com/VeryGoodOpenSource/flutter_web_preloader/issues/28
const response = await fetch('assets/AssetManifest.json');
const manifest = await response.json();
const assets = Object.values(manifest)
.map((list) => list.map((url) => 'assets/' + url))
.reduce((arr, curr) => [...arr, ...curr], []);
return assets;
}

async function beginPreloading() {
const assets = await readAssets();

let totalAssets = assets.length;
if (totalAssets === 0) {
// No assets to load, so we can skip the loading process entirely.
return;
}

let loadedAssets = 0;
const batchSize = {{batch_size}};

progressIndicator.style.width = '0%';
progressText.textContent = `Loaded ${loadedAssets} of ${totalAssets} assets`;

async function reportProgress() {
loadedAssets++;

const value = Math.floor((loadedAssets / totalAssets) * 100) + '%';
progressIndicator.style.width = value;

progressText.textContent = `Loaded ${loadedAssets} of ${totalAssets} assets`;
}

async function load(url) {
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();

req.onload = function() {
if (req.status >= 200 && req.status < 300) {
resolve(req.response);
} else {
reject(new Error(`Failed to load: ${req.status} ${req.statusText}`));
}
};

req.onerror = function() {
reject(new Error('Network error'));
};

req.open('GET', url);
req.send();
});
}

async function loadBatch(urls) {
const loadPromises = urls.map(url => load(url).then(reportProgress()));
try {
return await Promise.all(loadPromises);
} catch (error) {
console.error('Error loading one or more asset:', error);
}
}

for (let i = 0; i < assets.length; i += batchSize) {
const batch = assets.slice(i, i + batchSize);
await loadBatch(batch);
}
}

_flutter.loader.load({
serviceWorkerSettings: {
serviceWorkerVersion: {{=<% %>=}}{{flutter_service_worker_version}}<%={{ }}=%>,
},
onEntrypointLoaded: async function(engineInitializer) {
await Promise.all([
beginPreloading(),
engineInitializer.initializeEngine(),
]).then(([_, appRunner]) => appRunner.runApp());
}
});
181 changes: 36 additions & 145 deletions __brick__/web/index.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<!--
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.

Expand All @@ -14,149 +14,40 @@
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">

<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="{{{project_description}}}">

<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="{{project_title}}">
<link rel="apple-touch-icon" href="icons/Icon-192.png">

<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>

<title>{{project_title}}</title>
<link rel="manifest" href="manifest.json">
<style type="text/css">
body.loading-mode {
display: flex;
align-items: center;
justify-content: center;
}
</style>

<script>
// The value below is injected by flutter build, do not touch.
var serviceWorkerVersion = null;
</script>
</head>
<body class="loading-mode">
<div id="progress-bar" style="border: 1px solid blue; width: 250px; height: 50px;">
<div id="progress-indicator" style="background-color: blue; height: 100%; width: 0%;"></div>
</div>
<script>
(function() {
const progressBar = document.querySelector('#progress-bar');
const progressIndicator = document.querySelector('#progress-indicator');

const additionalScripts = [
// Add additional scripts here.
];

function injectScript(url) {
return new Promise(function(resolve) {
let scriptTag = document.createElement('script');
scriptTag.src = url;
scriptTag.type = 'application/javascript';
scriptTag.onload = function() {
resolve();
};

document.body.append(scriptTag);
});
<base href="$FLUTTER_BASE_HREF" />

<meta charset="UTF-8" />
<meta content="IE=Edge" http-equiv="X-UA-Compatible" />
<meta name="description" content="{{{project_description}}}" />

<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="{{project_title}}" />
<link rel="apple-touch-icon" href="icons/Icon-192.png" />

<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png" />

<title>{{project_title}}</title>
<link rel="manifest" href="manifest.json" />

<style type="text/css">
body {
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
}

function beginPreloading(manifestAssets) {
var assets = [
'flutter.js',
'main.dart.js',
{{#canvaskit}}
'canvaskit/canvaskit.wasm',
'canvaskit/canvaskit.js',
{{/canvaskit}}
...additionalScripts,
...manifestAssets,
];
let totalAssets = assets.length + 1;
let loaded = 0;

const batchSize = {{batch_size}};

async function reportProgress() {
loaded++;
const value = Math.floor((loaded / totalAssets) * 100) + '%';
progressIndicator.style.width = value;

if (assets.length == 0) {
dispatchAppLoad();
} else {
load(assets.shift());
}
}

function load(url) {
const req = new XMLHttpRequest();
req.onload = reportProgress;
req.open('get', url);
req.send();
}

function startBatch() {
const end = Math.min(batchSize, assets.length);
for (let i = 0; i < end; i++) {
load(assets.shift());
}
}


var scriptLoaded = false;
async function dispatchAppLoad() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;

for (let i = 0; i < additionalScripts.length; i++) {
await injectScript(additionalScripts[i]);
}

await injectScript('flutter.js');

// Download main.dart.js
_flutter.loader.loadEntrypoint({
serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(async function(appRunner) {
window.addEventListener("flutter-first-frame", function () {
progressBar.remove();
document.body.classList.remove('loading-mode');
});

appRunner.runApp();
});
}
});
}

startBatch();
}

window.addEventListener('load', async function(ev) {
const response = await fetch('assets/AssetManifest.json');
const manifest = await response.json();
const assets = Object.values(manifest)
.map((list) => list.map((url) => 'assets/' + url))
.reduce((arr, curr) => [...arr, ...curr], []);

beginPreloading(assets);
});
})();
</script>
</style>
</head>
<body>
<div>
<div id="progress-bar" style="border: 1px solid blue; width: 250px; height: 50px;">
<div id="progress-indicator" style="background-color: blue; height: 100%; width: 0%;"></div>
</div>
<div id="progress-text" width="100%" style="padding-top: 0.5rem; text-align: center;">Initializing</div>
</div>
<script src="flutter_bootstrap.js" async></script>
</body>
</html>