app working with sandbox
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
/// In-app browser for Enable Banking hosted auth.
|
||||
///
|
||||
/// Enable Banking only allows http(s) redirect URLs, so we keep
|
||||
/// `http://localhost:8000/api/connect/callback` and capture `code` here
|
||||
/// when the WebView navigates to that callback.
|
||||
library;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:webview_flutter/webview_flutter.dart';
|
||||
|
||||
/// Result popped from [BankAuthScreen] after bank login.
|
||||
class BankAuthResult {
|
||||
const BankAuthResult({this.code, this.state, this.error});
|
||||
|
||||
final String? code;
|
||||
final String? state;
|
||||
final String? error;
|
||||
|
||||
bool get hasCode => code != null && code!.isNotEmpty;
|
||||
}
|
||||
|
||||
class BankAuthScreen extends StatefulWidget {
|
||||
const BankAuthScreen({
|
||||
super.key,
|
||||
required this.authUrl,
|
||||
this.pendingState,
|
||||
});
|
||||
|
||||
final String authUrl;
|
||||
final String? pendingState;
|
||||
|
||||
@override
|
||||
State<BankAuthScreen> createState() => _BankAuthScreenState();
|
||||
}
|
||||
|
||||
class _BankAuthScreenState extends State<BankAuthScreen> {
|
||||
late final WebViewController _controller;
|
||||
var _finishing = false;
|
||||
var _loading = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = WebViewController()
|
||||
..setJavaScriptMode(JavaScriptMode.unrestricted)
|
||||
..setNavigationDelegate(
|
||||
NavigationDelegate(
|
||||
onPageStarted: (url) {
|
||||
if (mounted) setState(() => _loading = true);
|
||||
_handleUrl(url);
|
||||
},
|
||||
onPageFinished: (_) {
|
||||
if (mounted) setState(() => _loading = false);
|
||||
},
|
||||
onNavigationRequest: (request) {
|
||||
if (_handleUrl(request.url)) {
|
||||
return NavigationDecision.prevent;
|
||||
}
|
||||
return NavigationDecision.navigate;
|
||||
},
|
||||
onWebResourceError: (error) {
|
||||
// Callback to localhost often fails to load on emulators; if we
|
||||
// already captured the code, ignore the load error.
|
||||
if (_finishing) return;
|
||||
debugPrint('Bank auth WebView error: ${error.description}');
|
||||
},
|
||||
),
|
||||
)
|
||||
..loadRequest(Uri.parse(widget.authUrl));
|
||||
}
|
||||
|
||||
/// Returns true when [url] is the Enable Banking redirect callback.
|
||||
bool _handleUrl(String url) {
|
||||
final uri = Uri.tryParse(url);
|
||||
if (uri == null || _finishing) return false;
|
||||
if (!_isConnectCallback(uri)) return false;
|
||||
|
||||
_finishing = true;
|
||||
final error = uri.queryParameters['error'];
|
||||
final code = uri.queryParameters['code'];
|
||||
final state = uri.queryParameters['state'] ?? widget.pendingState;
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
Navigator.of(context).pop(
|
||||
BankAuthResult(
|
||||
code: code,
|
||||
state: state,
|
||||
error: error,
|
||||
),
|
||||
);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _isConnectCallback(Uri uri) {
|
||||
final path = uri.path;
|
||||
return path == '/api/connect/callback' ||
|
||||
path.endsWith('/api/connect/callback');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Connect bank'),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
),
|
||||
body: Stack(
|
||||
children: [
|
||||
WebViewWidget(controller: _controller),
|
||||
if (_loading)
|
||||
const Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: LinearProgressIndicator(minHeight: 2),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user