前提・実現したいこと
アプリの初期画面から、webviewで認証サーバのログイン画面を呼び出してログインし
callback画面を経て、次の画面に遷移しようとしています。
flutter_webview_pluginを使って、callback画面にwebviewが変化した際のイベントを検知して
遷移後のURLを取得して、そこから認証コードを取得する方法で実装しました。
発生している問題・エラーメッセージ
◎ 戻るボタンを押すと上手くいかなくなる
初期画面から、認証画面を呼び出して一度も戻らずにログインした場合は、
レスポンスを取得してscreen1に画面遷移するのですが、
認証画面でAppbarの戻るボタン(←)を押し、一度初期画面に戻ってから
再び遷移した認証画面でログインすると、callback画面に遷移まではするのですが
Screen1への画面遷移が行われません。
エラーメッセージは特に無いです。
該当のソースコード
dart
1main.dart 2 3import 'package:flutter/material.dart'; 4import 'dart:async'; 5import 'package:flutter_webview_plugin/flutter_webview_plugin.dart'; 6import 'package:flutter_http_test04/screen1.dart'; 7 8// 認証サーバ情報 9String _authUrl = 'http://.../auth'; //認証サーバのURL 10String _clientId = 'test_id1'; // 認証サーバに登録したクライアントID 11String _redirectUrl = 'http://.../blank // callback画面のURL 12 13String _responseType = 'code'; 14String _scope = 'openid offline_access'; 15// 認証サーバを呼び出す際のリクエストURL 16String _pageUrl = '${_authUrl}?client_id=${_clientId}&redirect_uri=${_redirectUrl}&response_type=${_responseType}&scope=${_scope}'; 17 18void main() { 19 WidgetsFlutterBinding.ensureInitialized(); 20 runApp(MyApp()); 21} 22 23class MyApp extends StatelessWidget { 24 final flutterWebViewPlugin = FlutterWebviewPlugin(); 25 26 27 Widget build(BuildContext context) { 28 return MaterialApp( 29 title: 'Flutter Demo', 30 theme: ThemeData( 31 primarySwatch: Colors.blue, 32 ), 33 34 routes: { 35 '/': (_) => MyHomePage(title: 'Flutter WebView Demo'), 36 '/login': (_) { 37 return WebviewScaffold( 38 url: _pageUrl, 39 mediaPlaybackRequiresUserGesture: false, 40 appBar: AppBar( 41 title: const Text('Widget WebView'), 42 ), 43 withZoom: true, 44 withLocalStorage: true, 45 hidden: true, 46 initialChild: Container( 47 color: Colors.white30, 48 child: const Center( 49 child: Text('Waiting.....'), 50 ), 51 ), 52 ); 53 }, 54 } 55 ); 56 } 57} 58 59class MyHomePage extends StatefulWidget { 60 MyHomePage({Key key, this.title}) : super(key: key); 61 62 final String title; 63 64 65 _MyHomePageState createState() => _MyHomePageState(); 66} 67 68class _MyHomePageState extends State<MyHomePage> { 69 final flutterWebViewPlugin = FlutterWebviewPlugin(); 70 // On urlChanged stream 71 StreamSubscription<String> _onUrlChanged; 72 73 String _code = ''; // 認証コード取得用 74 String _session_state = ''; // session_tate取得用 75 String _url = ''; // webView URL確認用 76 bool _onetime_flg = false; // onUrlChanged イベント制御用 77 78 79 80 void initState() { 81 super.initState(); 82 print('init 1'); 83 // flutterWebViewPlugin.close(); // このタイミングではあってもなくても変わらなかった 84 // _urlget(); // 最初はinitstateでurlを取ろうとしたが、戻るボタンを押した後の遷移でinitstate通らないからやめた 85 86 } 87 88 void _urlget() { 89 print('url get'); 90 _onetime_flg = false; // 2回連続でonUrlChangedが呼び出された時、最初の1回だけ通す用 91 92 // Add a listener to on url changed 93 _onUrlChanged = flutterWebViewPlugin.onUrlChanged.listen(( String url) { // 最初のwebview表示後は、ログイン画面とscreen1画面でなぜか2回ずつイベントをキャッチする 94 // 戻るボタンを押した後もう一度ログイン画面に遷移した後はイベントをキャッチしない 95 print('mounted=$mounted'); 96 print('_onetime_flg=$_onetime_flg'); 97 if (mounted && _onetime_flg == false) { // widgetツリーに存在しているかどうか & 1回目のonUrlChangedイベントの時 98 setState(() { 99 _onetime_flg = true; 100 101 _url = url.split('?')[0]; // 現在のurl確認用に取得 102 var tmp = url.split('?')[1]; // urlの後ろのレスポンスを取得 103 print('=$tmp'); 104 if (tmp.startsWith('session_state')) { // 認証後のcallback画面に遷移してsession_stateが返ってきた時 105 _session_state = tmp.split('&')[0].split('=')[1]; // データAPIアクセス用のsession_state保存 106 _code = tmp.split('&')[1].split('=')[1]; // データAPIアクセス用の認証コード保存 107 108 // 一旦初期画面に戻る 109 print('pop'); 110 Navigator.of(context).pop(); // 初期画面に戻らないと、screen1に画面遷移した後、pushReplacementが有効にならない 111 } 112 113 print('url=$_url'); 114 print('session_state=$_session_state'); 115 print('code=$_code'); 116 117 // 次画面呼び出し 118 if (_session_state.isNotEmpty && _code.isNotEmpty) {// session_stateと認証コードが取れている時だけscreen1に遷移する 119 print('3'); 120 _gotoScreen1(); 121 } 122 }); 123 } 124 }); 125 126 var tmp2 = _onUrlChanged.toString(); // _onUrlChangedって何が返ってきてるのか調べようとした → よくわからなかった 127 print('end _onUrlChanged=$tmp2'); 128 } 129 130 // 認証サーバの画面呼び出し ログイン済みの場合は直接コールバック画面に飛ぶ 131 void _loginFunc() async { 132 print('loginFunc'); 133 // flutterWebViewPlugin.dispose(); //webviewを作り直そうとして、このタイミングでclose(),dispose()を入れたが、認証後に画面遷移しなくなる 134 Navigator.of(context).pushNamed('/login'); 135 //Navigator.of(context).pushReplacementNamed("/login"); // 最初から戻るボタンなしにすると認証後に画面遷移しない 136 137 } 138 139 // 画面遷移 screen1 認証完了後に遷移する画面 140 void _gotoScreen1() { 141 print('goto screen1'); 142 Navigator.pushReplacement( // 初期画面やログイン画面に戻りたくない 認証後はここがメインページになる 143 context, 144 new MaterialPageRoute<Null>( 145 settings: const RouteSettings(name: "/screen1"), 146 builder: (BuildContext context) => Screen1(session_state: _session_state, code:_code), 147 ), 148 ); 149 } 150 151 152 Widget build(BuildContext context) { 153 print('widget'); 154 if (_session_state.isEmpty) { 155 _urlget(); 156 } 157 158 return Scaffold( 159 appBar: AppBar(title: Text(widget.title),), 160 body: Center( 161 162 child: Column( 163 mainAxisAlignment: MainAxisAlignment.center, 164 children: <Widget>[ 165 166 Padding( 167 padding: EdgeInsets.only(bottom: 40), 168 child: Text('アプリ初期画面'), 169 ), 170 171 FlatButton(key:null, onPressed: _loginFunc, 172 shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(3.0)), 173 color: Color(0xFF4c6cb3), // 群青色 174 child: Text('ログインページへ',), 175 ), 176 177 ], 178 ), 179 ), 180 181 ); 182 } 183} 184
screen1.dart import 'package:flutter/material.dart'; //// widget //// class Screen1 extends StatefulWidget { String session_state; String code; Screen1({Key key, this.code ,this.session_state}): super(key: key); @override _Screen1State createState() => _Screen1State(this.session_state, this.code); } class _Screen1State extends State<Screen1> { String _session_state; String _code; _Screen1State(String sesssion_state, String code){ this._session_state = sesssion_state; this._code = code; } @override void initState() { super.initState(); } @override Widget build(BuildContext context) { return Scaffold( resizeToAvoidBottomInset: false, appBar: AppBar(title: Text('screen1'),), body: Column( children: [ // 前画面で取得したsession_stateと認証コードを表示する Text('screen1'), Text('$_session_state'), Text('$_code'), ] ) ); } }
試したこと
print文を入れて、コードがどのように流れているのかを追ってみたのですが
2度目の認証画面を表示した際には、プラグインのonUrlChangedが画面の変化を認識しないため
URLの取得や、その後の画面遷移が行われない事がわかりましたが
なぜ2度目の画面表示ではonUrlChangedが動かないのかわかりません。
補足情報(FW/ツールのバージョンなど)
macOS Mojave
Flutter 1.12.13+hotfix.5
Tools • Dart 2.7.0
説明が長くなってしまいましたが、どうぞよろしくお願いいたします。
あなたの回答
tips
プレビュー