🔐
Flutter REST API Login
モックを使ってログインをしてみた。
普段は、Firebase AuthとSupabase Authしか使ったことないので、JWT認証とかやってみようと思ったのですが、うまくいかなかったので、モックのAPI作って、HTTPリクエストを送って、ステータスコードが200だったら、画面遷移するロジックを作りました。
バックエンドのAPIを作る
今回は、使い慣れているFlaskでダミーのデータが入ったREST APIを作りました。
name hoge, password 1234という値があるのですが、これとPOSTした値が一致してたら、Flutterが画面遷移するようになっています。
本当はもっと複雑な仕組みなんでしょうね。だって、セッションとか Cookieとかあるんでね。
HTTPが成功するとこんなログが出る
このコードは、仮想環境でFlaskをインストールして作成しました。
from flask import Flask, request, jsonify
app = Flask(__name__)
DUMMY_ACCOUNT = {
"name": "hoge",
"password": "1234"
}
# hellというURLにアクセスしたら、helloのJSON返す
@app.route('/hello')
def hello():
return jsonify({"message": "Hello World!"})
@app.route('/login', methods=['POST'])
def login():
data = request.json
if not data:
return jsonify({"message": "Invalid data"}), 400
name = data.get("name")
password = data.get("password")
if name == DUMMY_ACCOUNT["name"] and password == DUMMY_ACCOUNT["password"]:
return jsonify({"message": "認証に成功"}), 200
else:
return jsonify({"message": "Invalid credentials"}), 401
if __name__ == "__main__":
app.run(debug=True)
Flutterのコード
HTTP POSTでリクエストを送ると、サーバー側にリクエストが送られて成功したら画面遷移して、HomePageへ移動します。
status code 200だったら、画面遷移するコード。
main.dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: LoginPage(),
);
}
}
class LoginPage extends StatefulWidget {
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _nameController = TextEditingController();
final _passwordController = TextEditingController();
bool _isLoading = false;
Future<bool> login(String name, String password) async {
try {
// タイムアウトを設定(例:5秒)
final response = await http.post(
Uri.parse('http://127.0.0.1:5000/login'),
headers: {'Content-Type': 'application/json'},
body: jsonEncode({"name": name, "password": password}),
).timeout(Duration(seconds: 5));
// status code 200なら、ログイン成功
return response.statusCode == 200;
} catch (e) {
// タイムアウトや他のエラーが発生した場合
return false;
}
}
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: _nameController,
decoration: const InputDecoration(hintText: "Name"),
),
const SizedBox(height: 8),
TextField(
controller: _passwordController,
decoration: const InputDecoration(hintText: "Password"),
obscureText: true,
),
const SizedBox(height: 16),
_isLoading
? const CircularProgressIndicator()
: ElevatedButton(
onPressed: () async {
setState(() {
_isLoading = true; // ローディング表示開始
});
final success = await login(
_nameController.text, _passwordController.text);
setState(() {
_isLoading = false; // ローディング表示終了
});
if (success) {
if (mounted) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => HomePage()),
);
}
} else {
if (mounted) {
// ログイン失敗のメッセージ表示
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('ユーザー情報を入力してください'),
),
);
}
}
},
child: const Text("Login"),
)
],
),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
// ログアウトボタン
IconButton(
icon: const Icon(Icons.logout),
onPressed: () => Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context) => LoginPage())),
),
],
title: Text("Home")),
body: Center(
child: Text("Welcome!"),
),
);
}
}
ログインページ
入力をする。
ログイン後のページ
このページが表示されたら成功
まとめ
今回は、ダミーでログインを再現するのをやってみました。
本当は、Webの言語をやってた時みたいにJWT認証でやりたかったのですが、難しくて再現できなかったです🙇♂️
もっとOutputして、外部サービスを使用しないで、認証機能を作れるようになりたいです。
Discussion