質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

OAuth 2.0

OAuth 2.0(Open Authorization 2.0)は、APIを通して保護されたリソース(サードパーティのアプリケーション)へアクセスする為のオープンプロトコルです。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

Q&A

0回答

2338閲覧

flutterでflutter_webview_pluginを使ってoauthログインを実装したい

sagme

総合スコア10

Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

OAuth 2.0

OAuth 2.0(Open Authorization 2.0)は、APIを通して保護されたリソース(サードパーティのアプリケーション)へアクセスする為のオープンプロトコルです。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

Dart

Dartは、Googleによって開発されたJavaScriptの代替となることを目的に作られた、ウェブ向けのプログラミング言語である。

0グッド

0クリップ

投稿2020/05/07 13:53

編集2020/05/08 11:16

前提・実現したいこと

アプリの初期画面から、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

説明が長くなってしまいましたが、どうぞよろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sagme

2020/05/08 01:10

この実装を試す前に、AppAuth Plugin を使おうとしたのですが、用意されているデモ用サーバでは 動作するのですが、自分のサーバに変更すると異常終了してしまい、解決できませんでした。 そちらでも質問を立てています(英語併記で読みにくいですがすみません) https://teratail.com/questions/254452 もし、AppAuth plugin で解決できるならそちらでも構わないのですが 何かご教示頂けますとありがたいです。
nakasho_dev

2020/05/08 05:49

ご自身で用意されたサーバはhttpなのでしょうか?SSL、かつ、妥当性のある証明書出ないと動かなかったりしませんか?
sagme

2020/05/08 06:00

AppAuth pluginを試した時と、今回の自サーバはhttp接続です。 今回はAWS上にあり、AppAuth pluginの時はDockerでローカルPC上に立てましたが どちらもブラウザ(chrome)からアクセスした場合は普通に動作することを確認しました。
nakasho_dev

2020/05/08 09:53

AndroidやiOSのアプリの設定にhttpでも通信できる設定はしましたか。デフォルトではHTTPS通信、かつ、妥当性のあるSSL証明書でないと通信できず、設定が必要です。
sagme

2020/05/08 10:33 編集

戻るボタンを押さないで画面遷移する場合は、レスポンスが取れているため通信の設定の問題では無いような気がしますが、確認してみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問