Flutter Web позволяет создавать веб-приложения на Dart. Разберём особенности разработки для Web.
CanvasKit vs HTML
CanvasKit
Рендер через WebGL:
- Лучшая производительность
- Одинаковое отображение на всех платформах
- Больший размер бандла (~2MB)
HTML (HTML renderer)
Рендер через HTML/CSS:
- Меньший размер бандла
- Лучше SEO
- Может отличаться от мобильной версии
Выбор рендерера
// HTML renderer для лучшего SEO
flutter build web --web-renderer html
// CanvasKit для лучшей производительности
flutter build web --web-renderer canvaskit
// Auto (по умолчанию)
flutter build web --web-renderer auto
SEO оптимизация
Meta теги
// web/index.html
<head>
<meta name="description" content="Описание вашего приложения">
<!-- Open Graph -->
<meta property="og:title" content="Заголовок">
<meta property="og:description" content="Описание">
<meta property="og:image" content="https://example.com/image.jpg">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Заголовок">
<meta name="twitter:description" content="Описание">
</head>
Динамические meta теги
dependencies:
flutter_seo: ^1.0.0
import 'package:flutter_seo/flutter_seo.dart';
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Seo.widget(
tree: WidgetTree(text: 'Содержимое страницы'),
// Или вручную
title: 'Заголовок страницы',
description: 'Описание страницы',
keywords: ['flutter', 'web', 'seo'],
child: Scaffold(...),
);
}
}
SSR (Server-Side Rendering)
dependencies:
flutter_web_plugins: ^0.0.0
Для SSR используйте Dart Frog или другие серверные решения.
PWA (Progressive Web App)
Manifest
// web/manifest.json
{
"name": "My Flutter App",
"short_name": "FlutterApp",
"description": "My Flutter PWA",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#0175C2",
"icons": [
{
"src": "icons/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/icon-512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
Подключение manifest
<!-- web/index.html -->
<link rel="manifest" href="manifest.json">
<meta name="theme-color" content="#0175C2">
Service Worker
// web/service_worker.js
const CACHE_NAME = 'flutter-app-cache-v1';
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll([
'/',
'/main.dart.js',
'/assets/',
]);
}),
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
}),
);
});
Регистрация Service Worker
<script>
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('service_worker.js');
});
}
</script>
Оптимизация бандла
Deferred loading
// Отложенная загрузка тяжелых компонентов
import 'package:flutter/material.dart' deferred as ui;
Future<void> loadHeavyComponent() async {
await ui.loadLibrary();
// Теперь можно использовать ui
}
Анализ размера бандла
flutter build web --web-renderer canvaskit --analyze-size
Tree shaking
Убедитесь, что unused код удаляется:
// Используйте только нужные импорты
import 'package:flutter/material.dart'; // Хорошо
import 'package:flutter/material.dart' hide Router; // Если нужно исключить
URL Routing
go_router для Web
dependencies:
go_router: ^14.0.0
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => HomeScreen(),
),
GoRoute(
path: '/user/:id',
builder: (context, state) {
final id = state.pathParameters['id'];
return UserScreen(id: id);
},
),
],
);
void main() {
runApp(MaterialApp.router(routerConfig: router));
}
Query параметры
GoRoute(
path: '/search',
builder: (context, state) {
final query = state.uri.queryParameters['q'];
return SearchScreen(query: query);
},
)
Firebase Hosting
Установка Flutter CLI
dart pub global activate flutter_cli
Сборка для production
flutter build web --release
Firebase CLI
npm install -g firebase-tools
firebase login
Инициализация
firebase init hosting
firebase.json
{
"hosting": {
"public": "build/web",
"ignore": [
"firebase.json",
"**/.*",
"**/node_modules/**"
],
"rewrites": [
{
"source": "**",
"destination": "/index.html"
}
],
"headers": [
{
"source": "**",
"headers": [
{
"key": "Cache-Control",
"value": "max-age=1800"
}
]
}
]
}
}
Deploy
firebase deploy
Vercel
vercel.json
{
"rewrites": [
{
"source": "/(.*)",
"destination": "/index.html"
}
],
"headers": [
{
"source": "/(.*)",
"headers": [
{
"key": "Cache-Control",
"value": "public, max-age=31536000, immutable"
}
]
}
]
}
Deploy
vercel --prod
Web специфичные API
Обработка URL
import 'dart:html' as html;
class UrlHandler {
void openUrl(String url) {
html.window.open(url, '_blank');
}
String get currentUrl => html.window.location.href;
void updateUrl(String path) {
html.window.history.pushState(null, '', path);
}
}
LocalStorage
import 'dart:html' as html;
class WebStorage {
void setString(String key, String value) {
html.window.localStorage[key] = value;
}
String? getString(String key) {
return html.window.localStorage[key];
}
void remove(String key) {
html.window.localStorage.remove(key);
}
}
Clipboard
import 'dart:html' as html;
class ClipboardService {
Future<void> copyText(String text) async {
await html.window.navigator.clipboard?.writeText(text);
}
}
Hot Reload
При разработке
flutter run -d chrome
Auto refresh
void main() {
runApp(MyApp());
// Hot reload при изменениях файлов
if (kDebugMode) {
// Debug only code
}
}
Заключение
Flutter Web в 2025 — это production-ready решение для создания веб-приложений. Используйте HTML renderer для SEO и CanvasKit для производительности.