webview_ultra 0.0.6+8
webview_ultra: ^0.0.6+8 copied to clipboard
An ultra-optimized, feature-complete, lightweight multi-platform webview plugin for Flutter (Android, iOS, macOS, Windows).
import 'package:flutter/material.dart';
import 'package:webview_ultra/webview_ultra.dart';
void main() {
runApp(const WebviewUltraShowcaseApp());
}
class WebviewUltraShowcaseApp extends StatelessWidget {
const WebviewUltraShowcaseApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'WebviewUltra Showcase',
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: const Color(0xFF6750A4),
brightness: Brightness.dark,
),
),
home: const WebviewUltraHomePage(),
);
}
}
class WebviewUltraHomePage extends StatefulWidget {
const WebviewUltraHomePage({super.key});
@override
State<WebviewUltraHomePage> createState() => _WebviewUltraHomePageState();
}
class _WebviewUltraHomePageState extends State<WebviewUltraHomePage> {
late final WebviewUltraController _controller;
final TextEditingController _urlTextController =
TextEditingController(text: 'https://flutter.dev');
final TextEditingController _findTextController = TextEditingController();
bool _showFindBar = false;
@override
void initState() {
super.initState();
_controller = WebviewUltraController(
settings: const WebviewSettings(
javascriptEnabled: true,
allowsInlineMediaPlayback: true,
allowsBackForwardNavigationGestures: true,
),
onPageStarted: (url) {
_urlTextController.text = url;
},
);
// Register a typed JS RPC handler
_controller.addJavaScriptHandler(
handlerName: 'onUserAction',
callback: (args) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Received RPC from JavaScript: $args')),
);
}
return {'status': 'success', 'timestamp': DateTime.now().toIso8601String()};
},
);
}
@override
void dispose() {
_urlTextController.dispose();
_findTextController.dispose();
_controller.dispose();
super.dispose();
}
Future<void> _runHeadlessDemo() async {
late final HeadlessWebviewUltra headless;
headless = HeadlessWebviewUltra(
initialUrl: 'https://flutter.dev',
onPageFinished: (url) async {
final result = await headless.evaluateJavascript('document.title');
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Headless Result (Title): $result')),
);
}
headless.dispose();
},
);
await headless.run();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Running Headless Webview in background...')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: WebviewTitleBuilder(
controller: _controller,
builder: (context, title, child) {
return Text(
title.isEmpty ? 'WebviewUltra' : title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
);
},
),
actions: <Widget>[
WebviewHistoryBuilder(
controller: _controller,
builder: (context, history, child) {
return Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: history.canGoBack ? () => _controller.goBack() : null,
),
IconButton(
icon: const Icon(Icons.arrow_forward),
onPressed: history.canGoForward ? () => _controller.goForward() : null,
),
],
);
},
),
IconButton(
icon: const Icon(Icons.refresh),
onPressed: () => _controller.reload(),
),
IconButton(
icon: const Icon(Icons.search),
onPressed: () {
setState(() {
_showFindBar = !_showFindBar;
});
if (!_showFindBar) {
_controller.findController.clearMatches();
}
},
),
],
bottom: PreferredSize(
preferredSize: const Size.fromHeight(4.0),
child: WebviewProgressBuilder(
controller: _controller,
builder: (context, progress, child) {
if (progress >= 1.0 || progress <= 0.0) {
return const SizedBox(height: 4.0);
}
return LinearProgressIndicator(
value: progress,
backgroundColor: Colors.transparent,
valueColor: AlwaysStoppedAnimation<Color>(
Theme.of(context).colorScheme.secondary,
),
);
},
),
),
),
body: Column(
children: <Widget>[
// URL Bar
Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
color: Theme.of(context).colorScheme.surfaceContainerHighest,
child: Row(
children: <Widget>[
Expanded(
child: TextField(
controller: _urlTextController,
decoration: InputDecoration(
hintText: 'Enter URL...',
isDense: true,
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(24.0),
),
prefixIcon: const Icon(Icons.public, size: 20),
),
onSubmitted: (urlStr) {
var formatted = urlStr.trim();
if (!formatted.startsWith('http://') &&
!formatted.startsWith('https://')) {
formatted = 'https://$formatted';
}
_controller.loadUrl(formatted);
},
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: () {
final text = _urlTextController.text.trim();
if (text.isNotEmpty) {
var formatted = text;
if (!formatted.startsWith('http://') &&
!formatted.startsWith('https://')) {
formatted = 'https://$formatted';
}
_controller.loadUrl(formatted);
}
},
child: const Text('Go'),
),
],
),
),
// Find on Page Search Bar
if (_showFindBar)
Container(
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.0),
color: Theme.of(context).colorScheme.surfaceContainer,
child: Row(
children: <Widget>[
Expanded(
child: TextField(
controller: _findTextController,
decoration: const InputDecoration(
hintText: 'Find on page...',
isDense: true,
prefixIcon: Icon(Icons.search, size: 18),
),
onChanged: (text) => _controller.findController.findAll(findText: text),
),
),
IconButton(
icon: const Icon(Icons.navigate_before),
onPressed: () => _controller.findController.findNext(forward: false),
),
IconButton(
icon: const Icon(Icons.navigate_next),
onPressed: () => _controller.findController.findNext(forward: true),
),
],
),
),
// Quick Action Presets
SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 6.0),
child: Row(
children: <Widget>[
ActionChip(
avatar: const Icon(Icons.open_in_browser, size: 18),
label: const Text('InAppBrowser Modal'),
onPressed: () {
InAppBrowserUltra.open(
context: context,
url: 'https://flutter.dev',
);
},
),
const SizedBox(width: 6),
ActionChip(
avatar: const Icon(Icons.visibility_off, size: 18),
label: const Text('Headless Scrape'),
onPressed: _runHeadlessDemo,
),
const SizedBox(width: 6),
ActionChip(
avatar: const Icon(Icons.cookie, size: 18),
label: const Text('Set Cookie'),
onPressed: () async {
await CookieManagerUltra.instance.setCookie(
url: 'https://flutter.dev',
name: 'demo_token',
value: 'sample_token_123',
);
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Cookie demo_token set!')),
);
}
},
),
const SizedBox(width: 6),
ActionChip(
avatar: const Icon(Icons.code, size: 18),
label: const Text('Call JS RPC'),
onPressed: () async {
await _controller.runJavaScript(
"window.webview_ultra.callHandler('onUserAction', 'ButtonClicked', 42);",
);
},
),
],
),
),
// Core WebviewUltra Widget
Expanded(
child: WebviewUltra(
controller: _controller,
initialUrl: 'https://flutter.dev',
showProgressBar: true,
pullToRefresh: true,
),
),
],
),
);
}
}