Flutter Web性能优化标签解析(二)

复制代码
<div class="loading-container" id="loading">
    <div class="loading-spinner"></div>
    <div class="loading-text">Loading...</div>
  </div>
  
  <script>
    // Hide loading indicator when Flutter app is ready
    window.addEventListener('flutter-first-frame', function () {
      const loading = document.getElementById('loading');
      if (loading) {
        loading.style.opacity = '0';
        loading.style.transition = 'opacity 0.3s ease-out';
        setTimeout(function() {
          loading.style.display = 'none';
        }, 300);
      }
    });
    
    // Fallback: hide loading after timeout
    setTimeout(function() {
      const loading = document.getElementById('loading');
      if (loading && loading.style.display !== 'none') {
        loading.style.display = 'none';
      }
    }, 10000);
  </script>

这是一个用于 Flutter Web 的自定义加载占位符(Splash Screen),在 Flutter 应用完全加载前显示,主要作用如下:

主要作用

1. 消除白屏/空白页面

  • Flutter Web 应用启动时需要加载 Dart 代码、资源文件等

  • 这个期间会有 1-3 秒的空白页面

  • 自定义加载界面提供更好的用户体验

2. 显示加载状态

  • 告诉用户应用正在加载中

  • 减少用户因等待而离开的可能性

3. 平滑过渡

  • 当 Flutter 应用准备好后,优雅地淡出加载界面

工作原理

事件监听机制

javascript

复制代码
// 监听 Flutter 第一帧渲染完成事件
window.addEventListener('flutter-first-frame', function () {
  // Flutter 已准备好,可以隐藏加载界面
});

生命周期流程

text

复制代码
1. 浏览器加载 HTML → 立即显示 loading 动画
2. Flutter 引擎初始化 → 用户看到 loading
3. Flutter 渲染第一帧 → 触发 'flutter-first-frame' 事件
4. 隐藏 loading → 显示 Flutter 应用界面

在 Flutter 项目中的配置

1. 修改 web/index.html

html

复制代码
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>My Flutter App</title>
  
  <!-- 加载样式 -->
  <style>
    .loading-container {
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      display: flex;
      flex-direction: column;
      justify-content: center;
      align-items: center;
      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
      z-index: 9999;
      transition: opacity 0.5s ease-out;
    }
    
    .loading-spinner {
      width: 50px;
      height: 50px;
      border: 5px solid rgba(255, 255, 255, 0.3);
      border-radius: 50%;
      border-top-color: #fff;
      animation: spin 1s ease-in-out infinite;
    }
    
    .loading-text {
      margin-top: 20px;
      color: white;
      font-size: 18px;
      font-family: Arial, sans-serif;
    }
    
    @keyframes spin {
      to { transform: rotate(360deg); }
    }
  </style>
</head>
<body>
  <!-- 自定义加载界面 -->
  <div class="loading-container" id="loading">
    <div class="loading-spinner"></div>
    <div class="loading-text">加载中...</div>
  </div>
  
  <!-- Flutter 应用容器 -->
  <div id="flutter_target"></div>
  
  <!-- 脚本 -->
  <script>
    window.addEventListener('flutter-first-frame', function() {
      const loading = document.getElementById('loading');
      if (loading) {
        loading.style.opacity = '0';
        setTimeout(() => {
          loading.style.display = 'none';
        }, 500); // 等待淡出动画完成
      }
    });
    
    // 超时保护(10秒后强制隐藏)
    setTimeout(() => {
      const loading = document.getElementById('loading');
      if (loading && loading.style.display !== 'none') {
        loading.style.display = 'none';
      }
    }, 10000);
  </script>
  
  <!-- Flutter 脚本(由 build 过程自动注入) -->
  <script src="flutter.js" defer></script>
</body>
</html>

2. 优化 Flutter 启动速度

dart

复制代码
void main() {
  WidgetsFlutterBinding.ensureInitialized();
  
  // 使用更小的初始应用
  runApp(const LoadingApp());
  
  // 后台加载主应用
  Future.delayed(Duration.zero, () {
    runApp(const MainApp());
  });
}

高级用法

1. 根据 Flutter 状态显示不同提示

javascript

复制代码
<script>
  let loadingMessage = document.getElementById('loading-message');
  
  // 监听 Flutter 服务初始化
  window.addEventListener('flutter-service-worker', function(event) {
    if (loadingMessage) {
      loadingMessage.textContent = '正在初始化服务...';
    }
  });
  
  // 监听资源加载
  window.addEventListener('flutter-assets-loading', function(event) {
    if (loadingMessage) {
      loadingMessage.textContent = `加载资源中... ${event.detail.loaded}/${event.detail.total}`;
    }
  });
  
  // 第一帧就绪
  window.addEventListener('flutter-first-frame', function() {
    const loading = document.getElementById('loading');
    if (loading) {
      loading.style.opacity = '0';
      setTimeout(() => {
        loading.style.display = 'none';
      }, 300);
    }
  });
</script>

2. 与 Flutter 通信

javascript

复制代码
<script>
  // 从 Flutter 接收消息
  window.addEventListener('flutter-initialized', function() {
    console.log('Flutter 已初始化');
  });
  
  // 向 Flutter 发送消息
  function showLoadingMessage(message) {
    const loadingText = document.querySelector('.loading-text');
    if (loadingText) {
      loadingText.textContent = message;
    }
  }
</script>

dart

复制代码
// 在 Flutter 中发送消息给 JavaScript
void sendMessageToJS() {
  js.context.callMethod('showLoadingMessage', ['Flutter 正在启动...']);
}

性能优化技巧

1. 预加载关键资源

html

复制代码
<!-- 在 <head> 中添加预加载 -->
<link rel="preload" href="main.dart.js" as="script">
<link rel="preload" href="assets/fonts/custom.woff2" as="font" type="font/woff2" crossorigin>

2. 使用轻量级加载动画

css

复制代码
/* 使用 CSS 动画而非 GIF,减少资源大小 */
.loading-spinner {
  /* 使用简单的 CSS 动画 */
  animation: spin 1s linear infinite;
}

3. 渐进式加载

javascript

复制代码
<script>
  // 分阶段显示加载进度
  const stages = [
    { event: 'flutter-engine-init', message: '初始化引擎...' },
    { event: 'flutter-assets-loading', message: '加载资源...' },
    { event: 'flutter-widgets-building', message: '构建界面...' },
  ];
  
  stages.forEach(stage => {
    window.addEventListener(stage.event, () => {
      updateLoadingMessage(stage.message);
    });
  });
</script>

常见问题和解决方案

问题1:加载界面闪烁

javascript

复制代码
// 使用 requestAnimationFrame 确保平滑过渡
window.addEventListener('flutter-first-frame', function() {
  requestAnimationFrame(() => {
    const loading = document.getElementById('loading');
    loading.style.transition = 'opacity 0.5s ease';
    loading.style.opacity = '0';
    
    setTimeout(() => {
      loading.style.display = 'none';
    }, 500);
  });
});

问题2:超时处理

javascript

复制代码
// 更智能的超时处理
let hideTimeout = setTimeout(() => {
  const loading = document.getElementById('loading');
  if (loading) loading.style.display = 'none';
}, 10000);

// 如果 Flutter 加载成功,清除超时
window.addEventListener('flutter-first-frame', function() {
  clearTimeout(hideTimeout);
  // ... 隐藏 loading
});

问题3:错误处理

javascript

复制代码
// 监听 Flutter 加载错误
window.addEventListener('error', function(e) {
  if (e.message.includes('flutter') || e.filename.includes('flutter')) {
    const loading = document.getElementById('loading');
    const loadingText = document.querySelector('.loading-text');
    
    if (loadingText) {
      loadingText.textContent = '加载失败,请刷新页面';
      loadingText.style.color = '#ff6b6b';
    }
    
    // 显示重试按钮
    const retryButton = document.createElement('button');
    retryButton.textContent = '重试';
    retryButton.onclick = () => location.reload();
    loading.appendChild(retryButton);
  }
});

最佳实践总结

  1. 立即显示:HTML 加载后立即显示加载界面

  2. 保持简单:使用纯 CSS 动画,减少资源依赖

  3. 提供反馈:显示进度或状态信息

  4. 平滑过渡:使用淡出效果,避免突兀消失

  5. 错误处理:考虑加载失败的情况

  6. 超时保护:避免永远显示加载界面

这个方案能显著提升 Flutter Web 应用的用户体验,让用户感觉应用加载更快、更专业。

相关推荐
零基础的修炼7 小时前
算法---常见位运算总结
java·开发语言·前端
wgslucky7 小时前
sm2 js加密,java服务器端解密
java·开发语言·javascript
●VON7 小时前
Flutter for OpenHarmony:基于三层 Tab 架构与数据模型解耦的 TodoList 产品化演进
学习·flutter·架构·openharmony·布局·技术
zilikew7 小时前
Flutter框架跨平台鸿蒙开发——丢件上报APP的开发流程
flutter·华为·harmonyos·鸿蒙
晚霞的不甘7 小时前
Flutter for OpenHarmony:迈向专业:购物APP的架构演进与未来蓝图
其他·flutter·架构·fiddler·前端框架·harmonyos
wuhen_n8 小时前
@types 包的工作原理与最佳实践
前端·javascript·typescript
我是伪码农8 小时前
Vue 1.27
前端·javascript·vue.js
秋名山大前端8 小时前
前端大规模 3D 轨迹数据可视化系统的性能优化实践
前端·3d·性能优化
H7998742428 小时前
2026动态捕捉推荐:8款专业产品全方位测评
大数据·前端·人工智能
ujainu8 小时前
Flutter + OpenHarmony 底部导航栏:BottomNavigationBar 与 BottomAppBar 在多入口场景中的深度实践
flutter·组件