Flutter Web 应用程序加载前显示进度指示器?

27

你好,我是一名移动应用开发者,对于Web开发不太熟悉。我正在寻找一种方法,在加载Flutter web应用程序之前实现进度指示器,就像Gmail加载屏幕一样。Flutter web很酷,但在加载应用程序之前需要等待几秒钟。我们能否添加任何指示器来表示此加载时间?在Flutter中实现的任何代码都将成为Flutter应用程序的一部分,并且它不起作用,因此必须采取另一种方法来实现此目标。

8个回答

35

在 @Abhilash 的帮助下,我成功完成了这个任务。我从 w3schools 获取了加载器代码。

我的 project/web/index.html 文件如下。

<html>

<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <script defer src="index.dart.js" type="application/javascript"></script>

  <style>
    .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 {
      border: 16px solid #f3f3f3;
      border-radius: 50%;
      border-top: 16px solid blue;
      border-right: 16px solid green;
      border-bottom: 16px solid red;
      border-left: 16px solid pink;
      width: 120px;
      height: 120px;
      -webkit-animation: spin 2s linear infinite;
      animation: spin 2s linear infinite;
    }
    
    @-webkit-keyframes spin {
      0% {
        -webkit-transform: rotate(0deg);
      }
      100% {
        -webkit-transform: rotate(360deg);
      }
    }
    
    @keyframes spin {
      0% {
        transform: rotate(0deg);
      }
      100% {
        transform: rotate(360deg);
      }
    }
  </style>
</head>

<body>
  <div class="loading">
    <div class="loader"></div>
  </div>

  <script src="main.dart.js" type="application/javascript"></script>
</body>

</html>


这个方法对我有效,但在进入下一个屏幕之前需要大约10到15秒才会消失。 - Aqeel Mughal

10

在你的问题中,你提到:

任何在Flutter中实现的代码都是Flutter应用程序的一部分,它将无法运行...

我猜你试着为Android或iOS添加启动画面。因为Flutter Web只是一个index.html和几个js文件(例如main.dart.js),你可以尝试使用CSS加载动画技巧。由于你没有分享任何代码,所以我不会写任何代码,但以下是我的方法,如这个red stapler视频中解释的那样。他/她在这里提供了许多基于CSS的动画,包括其codepen实现。

因此,以下是我在flutter_web_project\web\index.html文件中的步骤:

  1. index.html的body中添加一个元素来显示css动画本身。
  2. 创建一个
    包装器来定位动画在你的index.html中的位置。
  3. 然后监听window的onLoad事件,并从你的页面中删除
    元素或按照视频中的描述淡化它。

9
除了@Shahzad Akram的回答之外,您还应该删除loading div,因为在Safari浏览器中可能会导致闪烁。 因此,您需要在第一个屏幕上实现以下代码(例如,在initState方法中):
import 'package:universal_html/html.dart'

...

@override
void initState() {
  super.initState()
  // Remove `loading` div
  final loader = document.getElementsByClassName('loading');
  if(loader.isNotEmpty) {
    loader.first.remove();
  }
}

P.S. 如果你需要漂亮的加载动画,可以访问 loading.io


在Flutter 2.2中,是否可以仅使用index.html内的js实现? - Adam Smaka
@AdamSmaka,如果你知道Flutter何时完成下载所有应用程序初始化所需的脚本,是的, - BambinoUA
很有趣。我可以看到可能涉及到上述Flutter版本的更多代码。把所有东西放在一个文件里会很好。 - Adam Smaka

8

Adam的回答会在Flutter实际加载之前移除加载器。

我认为这个脚本是最完整的答案:

<html>
<head>
  <style>
        .loading {
          display: flex;
          flex-flow: column;
          justify-content: center;
          align-items: center;
          margin: 0;
          position: absolute;
          top: 50%;
          left: 50%;
          -ms-transform: translate(-50%, -50%);
          transform: translate(-50%, -50%);
        }

        .loader {
          border: 8px solid #f3f3f3;
          border-radius: 50%;
          border-top: 8px solid #00AD87;
          border-right: 8px solid #C30E48;
          border-bottom: 8px solid #00AD87;
          border-left: 8px solid #C30E48;
          width: 60px !important;
          height: 60px !important;
          -webkit-animation: spin 2s linear infinite;
          animation: spin 2s linear infinite;
        }

        @-webkit-keyframes spin {
          0% {
            -webkit-transform: rotate(0deg);
          }
          100% {
            -webkit-transform: rotate(360deg);
          }
        }

        @keyframes spin {
          0% {
            transform: rotate(0deg);
          }
          100% {
            transform: rotate(360deg);
          }
        }
  </style>
</head>

<body>

  <!-- First time loading -->
  <div class="loading">
      <div class="loader"></div>
  </div>

  <!-- Ensure first time loading progress is gone after app loads -->
  <script>
      window.addEventListener("flutter-first-frame", function() {
          var element = document.getElementsByClassName("loading");
          element[0].parentNode.removeChild(element[0]);
      });
  </script>

<script defer src="main.dart.js" type="application/javascript"</script>

</body>
</html>

3
如果您想仅使用JS删除加载div,请使用此代码。
  <script>
    window.onload = (event) => {
      console.log('page is fully loaded');
      var element = document.getElementById("loader");
      element.parentNode.removeChild(element);
    };
  </script>

注意它假定加载器是一个 <div id="loader"></div> 标签。

1
除了@Shahzad和@BambinoUA的回答外,我还需要在main.dart.js脚本标签中添加defer关键字。

<script defer src="main.dart.js" type="application/javascript"></script>

以下是我需要这样做的情况:

  • 应用程序托管在Gitlab页面上
  • 浏览器是Chrome(网络较慢)

在这种情况下,只有空白屏幕可见,直到整个脚本下载完成。然后动画仅可见0.5秒,Flutter小部件立即加载。因此无法实现加载动画的目的。在本地测试中不会发生这种情况。

我还尝试将动画div放在所有脚本之前,但没有帮助。


1

我认为被接受的答案部分正确,因为它呈现了一个加载指示器而不是进度指示器。从flutter doc中,您可以大致估计Flutter的实际加载进度。我使用this indicator编译了一个示例,并在此处展示。

添加这个样式

.container {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
  width: 100%;
}

.progress-bar__container {
  width: 80%;
  height: 2rem;
  border-radius: 2rem;
  position: relative;
  overflow: hidden;
  transition: all 0.5s;
  will-change: transform;
  box-shadow: 0 0 5px #e76f51;
}

.progress-bar {
  position: absolute;
  height: 100%;
  width: 100%;
  content: "";
  background-color: #e76f51;
  top:0;
  bottom: 0;
  left: -100%;
  border-radius: inherit;
  display: flex;
  justify-content: center;
  align-items:center;
  color: white;
  font-family: sans-serif;
}

.progress-bar__text {
  display: none;
}

并将此代码附加到您的Flutter应用程序的index.html中。

function updateProgress(num) {
  const progressBarContainer = document.querySelector('.progress-bar__container');
  const progressBar = document.querySelector('.progress-bar');
  const progressBarText = document.querySelector('.progress-bar__text');

  let time = 0;
  let endState = 100;

  gsap.to(progressBar, {
    x: num + "%",
    duration: 2,
  });
}

window.addEventListener('load', function(ev) {
  var loading = document.querySelector('#loading');
  loading.textContent = "Loading entrypoint...";
  updateProgress(15);
  _flutter.loader.loadEntrypoint({
    serviceWorker: {
      serviceWorkerVersion: serviceWorkerVersion,
    },
    onEntrypointLoaded: async function(engineInitializer) {
      loading.textContent = "Initializing engine...";
      updateProgress(50);
      let appRunner = await engineInitializer.initializeEngine();
      updateProgress(80);
      loading.textContent = "Running app...";
      await appRunner.runApp();
      updateProgress(100);
    }
  });
});

-1
在启动Flutter Web应用程序时,我们有两个阶段:第一阶段是当index.html页面已经加载但实际的Flutter应用程序正在加载时。然后,当Flutter应用程序加载完成后,我们仍然可能需要在Flutter应用程序内进行一些准备工作。我希望这两个阶段都显示一个加载指示,并且我希望它们相同。所以...我做了以下事情:
  1. 首先,我的index.html显示一个gif,其中显示了一个类似于我在Flutter中拥有的圆形进度指示器的进度指示器(请参见2)loading.gif

我按照此人描述的方式进行操作:https://retroportalstudio.medium.com/indicate-website-loading-for-flutter-web-apps-7dc5e2c59e24

  1. 然后,在我的Flutter应用程序中,我显示了这个指示器:

    return Container( decoration: BoxDecoration(color: Colors.white), child: Center(child: CircularProgressIndicator()) );

这个指示器与 gif 差不多。我使用 https://gifcap.dev/ 和 gimp 的组合来裁剪它,制作了这个 gif。

结果是一个相当平滑的加载圆形进度指示器,几乎在打开我的网站时立即出现,一直到我的 flutterweb 应用程序打开。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接