import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:webf/webf.dart';
import 'custom_hybrid_history_delegate.dart';
import 'dart:typed_data' show Uint8List;
import 'package:cronet_http/cronet_http.dart';
import 'cronet_adapter.dart';
import 'package:flutter/cupertino.dart';
final RouteObserver<ModalRoute<void>> routeObserver =
RouteObserver<ModalRoute<void>>();
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
const String demoEntryUrl = 'http://192.168.2.120:5173/';
const String demoControllerName = 'demo';
const String demoInitialRoute = '/';
const Map<String, dynamic>? demoInitialState = null;
void main() async {
// 初始化控制器
WebFControllerManager.instance.initialize(
WebFControllerManagerConfig(
maxAliveInstances: 4,
maxAttachedInstances: 1,
onControllerDisposed: (String name, WebFController controller) {
print('controller disposed: $name $controller');
},
onControllerDetached: (String name, WebFController controller) {
print('controller detached: $name $controller');
},
),
);
WebFControllerManager.instance.addWithPrerendering(
name: demoControllerName,
createController: () => WebFController(
enableBlink: true,
routeObserver: routeObserver,
initialRoute: demoInitialRoute,
initialState: demoInitialState,
),
bundle: WebFBundle.fromUrl(demoEntryUrl),
setup: (controller) {
controller.hybridHistory.delegate = CustomHybridHistoryDelegate();
controller.onLCP = (time, isEvaluated) {
print('LCP: $time ms');
};
},
);
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return MyAppState();
}
}
class MyAppState extends State<MyApp> {
void initState() {
super.initState();
}
Object? _buildHybridRouteArguments(
GoRouterState state, {
required Map<String, String> pathParameters,
required Map<String, String> queryParameters,
}) {
if (state.extra is Map) {
final merged = <String, dynamic>{};
for (final entry in (state.extra as Map).entries) {
merged[entry.key.toString()] = entry.value;
}
return <String, dynamic>{
...merged,
if (pathParameters.isNotEmpty) 'pathParameters': pathParameters,
if (queryParameters.isNotEmpty) 'queryParameters': queryParameters,
};
}
if (state.extra != null) return state.extra;
if (pathParameters.isEmpty && queryParameters.isEmpty) return null;
return <String, dynamic>{
'pathParameters': pathParameters,
'queryParameters': queryParameters,
};
}
late final GoRouter _router = GoRouter(
navigatorKey: navigatorKey,
initialLocation: '/',
observers: [routeObserver],
routes: <RouteBase>[
GoRoute(
path: '/',
builder: (context, state) => const FirstPage(title: 'Landing Bay1'),
),
// webf路由
GoRoute(
path: '/demo',
pageBuilder: (context, state) => MaterialPage<void>(
key: state.pageKey,
name: state.uri.toString(),
child: const WebFDemo(
webfPageName: demoControllerName,
initialRoute: demoInitialRoute,
initialState: demoInitialState,
),
),
),
GoRoute(
path: '/:webfPath(.*)',
name: 'universal-webf-route',
pageBuilder: (context, state) => _buildWebFHybridRoutePage(state),
),
],
errorBuilder: (context, state) => Scaffold(
appBar: AppBar(title: const Text('Route not found')),
body: Center(
child: Text(state.error?.toString() ?? 'Unknown routing error'),
),
),
);
Page<void> _buildWebFHybridRoutePage(GoRouterState state) {
final String path = state.uri.path.replaceFirst('webf', '');
final pathParameters = Map<String, String>.from(state.pathParameters)
..remove('webfPath');
final queryParameters = state.uri.queryParameters;
return MaterialPage<void>(
key: state.pageKey,
name: state.uri.toString(),
arguments: _buildHybridRouteArguments(
state,
pathParameters: pathParameters,
queryParameters: queryParameters,
),
child: WebFRouterView.fromControllerName(
controllerName: demoControllerName,
path: path,
builder: (context, controller) => WebFSubView(
controller: controller,
onAppBarCreated: (title, routeLinkElement) =>
AppBar(title: Text(title)),
path: path,
// pathParameters: pathParameters,
// queryParameters: queryParameters,
),
loadingWidget: _WebFDemoState.buildSplashScreen(),
),
);
}
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: _router,
title: 'WebF Demo',
// showPerformanceOverlay: true,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
);
}
}
class FirstPage extends StatefulWidget {
const FirstPage({super.key, required this.title});
final String title;
@override
State<StatefulWidget> createState() {
return FirstPageState();
}
}
class FirstPageState extends State<FirstPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(widget.title)),
body: Stack(
children: [
Center(
child: ElevatedButton(
onPressed: () {
context.push('/demo');
},
child: Text('Open demo'),
),
),
// WebFInspectorFloatingPanel(),
],
),
);
}
}
class WebFDemo extends StatefulWidget {
final String webfPageName;
final String initialRoute;
final Map<String, dynamic>? initialState;
const WebFDemo({
super.key,
required this.webfPageName,
this.initialRoute = '/',
this.initialState,
});
@override
State<WebFDemo> createState() => _WebFDemoState();
}
class _WebFDemoState extends State<WebFDemo> {
@override
Widget build(BuildContext context) {
// bool isDarkModeEnabled = AdaptiveTheme.of(context).
return Scaffold(
appBar: AppBar(
title: Text('Web1F Demo'),
actions: [Padding(padding: EdgeInsets.fromLTRB(0, 0, 20, 0))],
),
body: Stack(
children: [
WebF.fromControllerName(
controllerName: widget.webfPageName,
loadingWidget: buildSplashScreen(),
initialRoute: widget.initialRoute,
initialState: widget.initialState,
onControllerCreated: (controller) {
print('Controller ready: $controller');
},
onBuildSuccess: () {
print('UI rendered successfully');
},
bundle: WebFBundle.fromUrl(demoEntryUrl),
// createController: () => WebFController(
// routeObserver: routeObserver,
// initialRoute: widget.initialRoute,
// onControllerInit: (controller) async {
// print('LJM---onControllerInit');
// // First Paint (FP)
// controller.loadingState.onFirstPaint((event) {
// print('FP: ${event.elapsed.inMilliseconds}ms');
// });
// },
// httpLoggerOptions: HttpLoggerOptions(
// requestHeader: true,
// requestBody: true,
// ),
// networkOptions: WebFNetworkOptions(
// android: WebFNetworkOptions(
// httpClientAdapter: () async {
// String cacheDirectory =
// await HttpCacheController.getCacheDirectory(
// WebFBundle.fromUrl(demoEntryUrl).resolvedUri!,
// );
// CronetEngine cronetEngine = CronetEngine.build(
// cacheMode: CacheMode.memory,
// cacheMaxSize: 24 * 1024 * 1024,
// enableBrotli: true,
// enableHttp2: true,
// enableQuic: true,
// storagePath: null,
// );
// return CronetAdapter(cronetEngine);
// },
// enableHttpCache:
// false, // Cronet have it's own http cache impls
// ),
// ),
// onLCPContentVerification: (contentInfo, String routePath) {
// print('contentInfo: $contentInfo $routePath');
// },
// ),
setup: (controller) {
print('LJM---controller: $controller');
controller.hybridHistory.delegate = CustomHybridHistoryDelegate();
// Register event listeners for all main phases
controller.loadingState.onConstructor((event) {
print('🏗️ Constructor at ${event.elapsed.inMilliseconds}ms');
});
controller.loadingState.onInit((event) {
print('🚀 Initialize at ${event.elapsed.inMilliseconds}ms');
});
controller.loadingState.onPreload((event) {
print('📦 Preload at ${event.elapsed.inMilliseconds}ms');
});
controller.loadingState.onResolveEntrypointStart((event) {
print(
'🔍 Resolve Entrypoint Start at ${event.elapsed.inMilliseconds}ms',
);
});
controller.loadingState.onResolveEntrypointEnd((event) {
print(
'✅ Resolve Entrypoint End at ${event.elapsed.inMilliseconds}ms',
);
});
controller.loadingState.onParseHTMLStart((event) {
print(
'📄 Parse HTML Start at ${event.elapsed.inMilliseconds}ms',
);
});
controller.loadingState.onParseHTMLEnd((event) {
print('✅ Parse HTML End at ${event.elapsed.inMilliseconds}ms');
});
controller.loadingState.onScriptQueue((event) {
print('📋 Script Queue at ${event.elapsed.inMilliseconds}ms');
});
controller.loadingState.onScriptLoadStart((event) {
print(
'📥 Script Load Start at ${event.elapsed.inMilliseconds}ms',
);
});
controller.loadingState.onScriptLoadComplete((event) {
print(
'✅ Script Load Complete at ${event.elapsed.inMilliseconds}ms ${event.parameters}',
);
});
controller.loadingState.onAttachToFlutter((event) {
print(
'🔗 Attach to Flutter at ${event.elapsed.inMilliseconds}ms',
);
});
controller.loadingState.onScriptExecuteStart((event) {
print(
'▶️ Script Execute Start at ${event.elapsed.inMilliseconds}ms',
);
});
controller.loadingState.onScriptExecuteComplete((event) {
print(
'✅ Script Execute Complete at ${event.elapsed.inMilliseconds}ms',
);
});
controller.loadingState.onDOMContentLoaded((event) {
print(
'📄 DOM Content Loaded at ${event.elapsed.inMilliseconds}ms',
);
});
controller.loadingState.onWindowLoad((event) {
print('🪟 Window Load at ${event.elapsed.inMilliseconds}ms');
});
controller.loadingState.onBuildRootView((event) {
print(
'🏗️ Build Root View at ${event.elapsed.inMilliseconds}ms',
);
});
controller.loadingState.onFirstPaint((event) {
print(
'🎨 First Paint (FP) at ${event.elapsed.inMilliseconds}ms',
);
});
controller.loadingState.onFirstContentfulPaint((event) {
print(
'🖼️ First Contentful Paint (FCP) at ${event.elapsed.inMilliseconds}ms',
);
});
controller.loadingState.onLargestContentfulPaint((event) {
final isCandidate = event.parameters['isCandidate'] ?? false;
final isFinal = event.parameters['isFinal'] ?? false;
final status = isFinal
? 'FINAL'
: (isCandidate ? 'CANDIDATE' : 'UNKNOWN');
print(
'📊 Largest Contentful Paint (LCP) ($status) at ${event.parameters['timeSinceNavigationStart']}ms',
);
});
},
),
// WebFInspectorFloatingPanel(),
],
),
);
}
static Widget buildSplashScreen() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset('assets/webf.png', width: 150, height: 150),
SizedBox(height: 24),
CupertinoActivityIndicator(radius: 14),
SizedBox(height: 16),
Text(
'Loading...',
style: TextStyle(fontSize: 16, color: CupertinoColors.systemGrey),
),
],
),
);
}
@override
void dispose() {
super.dispose();
}
}
Affected version
0.24.2
Flutter versions
3.38.10
No same issues found.
Steps to Reproduce
android运行flutter run 安装app后页面设置的css样式全部都没有生效,ios正常生效
Code example
Expected results
android能正常显示css样式
Actual results
android不显示css样式