提交 4726730d authored 作者: MrQi's avatar MrQi

first commit

上级
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"webview_flutter_wkwebview","path":"/Users/mrqi/.pub-cache/hosted/pub.flutter-io.cn/webview_flutter_wkwebview-3.22.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"android":[{"name":"webview_flutter_android","path":"/Users/mrqi/.pub-cache/hosted/pub.flutter-io.cn/webview_flutter_android-4.7.0/","native_build":true,"dependencies":[],"dev_dependency":false}],"macos":[{"name":"webview_flutter_wkwebview","path":"/Users/mrqi/.pub-cache/hosted/pub.flutter-io.cn/webview_flutter_wkwebview-3.22.0/","shared_darwin_source":true,"native_build":true,"dependencies":[],"dev_dependency":false}],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"webview_flutter","dependencies":["webview_flutter_android","webview_flutter_wkwebview"]},{"name":"webview_flutter_android","dependencies":[]},{"name":"webview_flutter_wkwebview","dependencies":[]}],"date_created":"2025-06-12 11:05:33.639103","version":"3.29.0","swift_package_manager_enabled":{"ios":false,"macos":false}}
\ No newline at end of file
## 0.0.1
* TODO: Describe initial release.
TODO: Add your license here.
<!--
This README describes the package. If you publish this package to pub.dev,
this README's contents appear on the landing page for your package.
For information about how to write a good package README, see the guide for
[writing package pages](https://dart.dev/guides/libraries/writing-package-pages).
For general information about developing packages, see the Dart guide for
[creating packages](https://dart.dev/guides/libraries/create-library-packages)
and the Flutter guide for
[developing packages and plugins](https://flutter.dev/developing-packages).
-->
TODO: Put a short description of the package here that helps potential users
know whether this package might be useful for them.
## Features
TODO: List what your package can do. Maybe include images, gifs, or videos.
## Getting started
TODO: List prerequisites and provide or point to information on how to
start using the package.
## Usage
TODO: Include short and useful examples for package users. Add longer examples
to `/example` folder.
```dart
const like = 'sample';
```
## Additional information
TODO: Tell users more about the package: where to find more information, how to
contribute to the package, how to file issues, what response they can expect
from the package authors, and more.
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
library clx_webview_flutter;
export 'custom_webview/view_model/custom_controller.dart';
export 'custom_webview/widgets/custom_webview.dart';
export 'custom_webview/model/java_script_channel.dart';
export 'package:webview_flutter/webview_flutter.dart';
export 'utils/command.dart';
export 'utils/result.dart';
export 'utils/captcha_util.dart';
import 'dart:async';
import 'package:clx_webview_flutter/utils/command.dart';
import 'package:clx_webview_flutter/utils/result.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../../clx_webview_flutter.dart';
typedef JavaScriptCallback = FutureOr<CLXResult<dynamic>> Function(
JavaScriptMessage message);
class JavaScriptChannel {
final String name;
late CommandArgument<dynamic, JavaScriptMessage> channelCallback;
JavaScriptChannel(
{required this.name, required JavaScriptCallback callback}) {
channelCallback = CommandArgument(callback);
}
@override
operator ==(Object other) {
if (other is JavaScriptChannel) {
return name == other.name;
}
return false;
}
@override
int get hashCode => super.hashCode;
}
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../model/java_script_channel.dart';
import '../widgets/custom_webview.dart';
class CustomController extends ChangeNotifier {
CustomController.loadRequest(
{required Uri requestedUrl, String? title, TextStyle? appBarTextStyle}) {
_controller = WebViewController();
_title = title;
_appBarTextStyle = appBarTextStyle;
_controller.loadRequest(requestedUrl);
}
CustomController.loadFile(
{required String html,
String? baseUrl,
String? title,
TextStyle? appBarTextStyle}) {
_controller = WebViewController();
_title = title;
_appBarTextStyle = appBarTextStyle;
_controller.loadHtmlString(html, baseUrl: baseUrl);
}
CustomController.loadHtmlString({
required String absoluteFilePath,
String? title,
TextStyle? appBarTextStyle,
}) {
_controller = WebViewController();
_title = title;
_appBarTextStyle = appBarTextStyle;
_controller.loadFile(absoluteFilePath);
}
/// 内部访问WebView的接口
late CustomWebViewState _customWebViewState;
void bindState(CustomWebViewState state) {
_customWebViewState = state;
}
/// 外部访问WebViewController的接口
late WebViewController _controller;
WebViewController get controller => _controller;
/// 内部访问AppBar的title接口
String? _title;
AppBar? get appBar => _title == null
? null
: AppBar(
title: ListenableBuilder(
listenable: this,
builder: (context, child) {
return Text(_title!, style: _appBarTextStyle);
},
),
);
Future<bool> setTitle(String title) async {
if (!_customWebViewState.mounted) {
return Future.value(false);
} else {
_title = title;
notifyListeners();
return Future.value(true);
}
}
/// 外部访问AppBar的title样式接口
TextStyle? _appBarTextStyle;
TextStyle? get appBarTextStyle => _appBarTextStyle;
Future<bool> setAppBarTextStyle(TextStyle? style) async {
if (!_customWebViewState.mounted) {
return Future.value(false);
} else {
_appBarTextStyle = style;
notifyListeners();
return Future.value(true);
}
}
/// 外部访问JavaScriptChannel的接口
final List<JavaScriptChannel> _javaScriptChannels = [];
List<JavaScriptChannel>? get javaScriptChannels => _javaScriptChannels;
Future<bool> setJavaScriptChannels(JavaScriptChannel channel) async {
if (_javaScriptChannels
.where((element) => element.name == channel.name)
.isNotEmpty) {
return Future.value(false);
}
_javaScriptChannels.add(channel);
controller.addJavaScriptChannel(channel.name,
onMessageReceived: channel.channelCallback.run);
controller.reload();
return Future.value(true);
}
}
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import '../view_model/custom_controller.dart';
class CustomWebView extends StatefulWidget {
final CustomController customController;
const CustomWebView({super.key, required this.customController});
@override
State<StatefulWidget> createState() {
return CustomWebViewState();
}
}
class CustomWebViewState extends State<CustomWebView> {
@override
void initState() {
super.initState();
widget.customController.bindState(this);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: widget.customController.appBar,
body: ListenableBuilder(
listenable: widget.customController,
builder: (context, child) {
return WebViewWidget(controller: widget.customController.controller);
},
),
);
}
}
import 'package:flutter/material.dart';
import '../clx_webview_flutter.dart';
abstract class CaptchaUtil {
static Future<bool>? generateCaptcha(
{required BuildContext context,
required String uri,
required Future<bool> Function(JavaScriptMessage message)
onCaptchaGenerated}) async {
return await Navigator.push(context, PageRouteBuilder(
pageBuilder: (context, animation, secondaryAnimation) {
final CustomController customController = CustomController.loadRequest(
requestedUrl: Uri.parse(uri), title: 'Captcha');
customController.setJavaScriptChannels(JavaScriptChannel(
name: 'H5CallFlutterInterface',
callback: (message) async {
if (!context.mounted) {
return CLXResult.fail(Exception('context not mounted'));
}
final result = await onCaptchaGenerated(message);
if (result) {
Navigator.pop(context, true);
return CLXResult.fail(Exception('captcha failed'));
} else {
customController.controller.reload();
return CLXResult.succ(result);
}
},
));
return CustomWebView(customController: customController);
},
));
}
}
import 'dart:async';
import 'package:flutter/material.dart';
import 'result.dart';
typedef CommandCallback<T> = FutureOr<CLXResult<T>> Function();
typedef CommandBuilder<T, Argument> = FutureOr<CLXResult<T>> Function(Argument);
sealed class Command<T> extends ChangeNotifier {
Command();
/// 进行操作拦截
bool _running = false;
bool get running => _running;
/// 处理结果
CLXResult<T>? _result;
CLXResult<T>? get result => _result;
bool get fail => _result is CLXFail;
bool get success => _result is CLXSucc;
/// 清除最后一次执行结果
void clearResult() {
_result = null;
notifyListeners();
}
FutureOr<dynamic> _run(CommandCallback<T> action) async {
if (_running) return;
_running = true;
_result = null;
notifyListeners();
try {
_result = await action();
} catch (e) {
_result = CLXResult.fail(Exception(e));
} finally {
_running = false;
notifyListeners();
}
return _result;
}
}
class CommandAction<T> extends Command<T> {
CommandAction(this._action);
final CommandCallback<T> _action;
FutureOr<dynamic> run() async {
await _run(_action);
}
}
class CommandArgument<T, Argument> extends Command<T> {
CommandArgument(this._action);
final CommandBuilder<T, Argument> _action;
FutureOr<dynamic> run(Argument argument) async {
await _run(() => _action(argument));
}
}
sealed class CLXResult<T> {
const CLXResult();
const factory CLXResult.succ(T value) = CLXSucc._;
const factory CLXResult.fail(Exception error) = CLXFail._;
}
final class CLXSucc<T> extends CLXResult<T> {
const CLXSucc._(this.value);
final T value;
@override
String toString() => 'Result<$T>.succ($value)';
}
final class CLXFail<T> extends CLXResult<T> {
const CLXFail._(this.error);
final Exception error;
@override
String toString() => 'Result<$T>.fail($error)';
}
name: clx_webview_flutter
description: "自定义交互以及加载方式等WebView"
version: 0.0.1
homepage:
environment:
sdk: '>=3.3.4 <4.0.0'
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
webview_flutter: ^4.8.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
flutter:
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论