Skip to content

Commit 0cbbcaf

Browse files
feat: bootstrap flutter app
1 parent 2527912 commit 0cbbcaf

10 files changed

Lines changed: 348 additions & 91 deletions

File tree

examples/flutter_app/lib/main.dart

Lines changed: 2 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:flutter/material.dart';
2+
import 'package:flutter_app/screens/login_screen.dart';
23

34
void main() {
45
runApp(const MyApp());
@@ -30,93 +31,7 @@ class MyApp extends StatelessWidget {
3031
// tested with just a hot reload.
3132
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
3233
),
33-
home: const MyHomePage(title: 'Flutter Demo Home Page'),
34-
);
35-
}
36-
}
37-
38-
class MyHomePage extends StatefulWidget {
39-
const MyHomePage({super.key, required this.title});
40-
41-
// This widget is the home page of your application. It is stateful, meaning
42-
// that it has a State object (defined below) that contains fields that affect
43-
// how it looks.
44-
45-
// This class is the configuration for the state. It holds the values (in this
46-
// case the title) provided by the parent (in this case the App widget) and
47-
// used by the build method of the State. Fields in a Widget subclass are
48-
// always marked "final".
49-
50-
final String title;
51-
52-
@override
53-
State<MyHomePage> createState() => _MyHomePageState();
54-
}
55-
56-
class _MyHomePageState extends State<MyHomePage> {
57-
int _counter = 0;
58-
59-
void _incrementCounter() {
60-
setState(() {
61-
// This call to setState tells the Flutter framework that something has
62-
// changed in this State, which causes it to rerun the build method below
63-
// so that the display can reflect the updated values. If we changed
64-
// _counter without calling setState(), then the build method would not be
65-
// called again, and so nothing would appear to happen.
66-
_counter++;
67-
});
68-
}
69-
70-
@override
71-
Widget build(BuildContext context) {
72-
// This method is rerun every time setState is called, for instance as done
73-
// by the _incrementCounter method above.
74-
//
75-
// The Flutter framework has been optimized to make rerunning build methods
76-
// fast, so that you can just rebuild anything that needs updating rather
77-
// than having to individually change instances of widgets.
78-
return Scaffold(
79-
appBar: AppBar(
80-
// TRY THIS: Try changing the color here to a specific color (to
81-
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
82-
// change color while the other colors stay the same.
83-
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
84-
// Here we take the value from the MyHomePage object that was created by
85-
// the App.build method, and use it to set our appbar title.
86-
title: Text(widget.title),
87-
),
88-
body: Center(
89-
// Center is a layout widget. It takes a single child and positions it
90-
// in the middle of the parent.
91-
child: Column(
92-
// Column is also a layout widget. It takes a list of children and
93-
// arranges them vertically. By default, it sizes itself to fit its
94-
// children horizontally, and tries to be as tall as its parent.
95-
//
96-
// Column has various properties to control how it sizes itself and
97-
// how it positions its children. Here we use mainAxisAlignment to
98-
// center the children vertically; the main axis here is the vertical
99-
// axis because Columns are vertical (the cross axis would be
100-
// horizontal).
101-
//
102-
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
103-
// action in the IDE, or press "p" in the console), to see the
104-
// wireframe for each widget.
105-
mainAxisAlignment: MainAxisAlignment.center,
106-
children: <Widget>[
107-
const Text('You have pushed the button this many times:'),
108-
Text(
109-
'$_counter',
110-
style: Theme.of(context).textTheme.headlineMedium,
111-
),
112-
],
113-
),
114-
),
115-
floatingActionButton: FloatingActionButton(
116-
onPressed: _incrementCounter,
117-
tooltip: 'Increment',
118-
child: const Icon(Icons.add),
119-
), // This trailing comma makes auto-formatting nicer for build methods.
34+
home: LoginScreen(),
12035
);
12136
}
12237
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import 'package:flutter/material.dart';
2+
3+
class LoginScreen extends StatefulWidget {
4+
@override
5+
_LoginScreenState createState() => _LoginScreenState();
6+
}
7+
8+
class _LoginScreenState extends State<LoginScreen> {
9+
bool _isLoggedIn = false;
10+
11+
@override
12+
void initState() {
13+
super.initState();
14+
_checkLoginStatus();
15+
}
16+
17+
Future<void> _checkLoginStatus() async {
18+
// TODO: Implement check login status logic
19+
// You can make an API call to check if the user is already logged in
20+
// and update the _isLoggedIn state accordingly
21+
setState(() {
22+
_isLoggedIn = true;
23+
});
24+
}
25+
26+
Future<void> _login() async {
27+
// TODO: Implement OIDC flow logic
28+
// You can make an API call to initiate the OIDC flow
29+
// and update the _isLoggedIn state accordingly
30+
setState(() {
31+
_isLoggedIn = true;
32+
});
33+
}
34+
35+
Future<void> _logout() async {
36+
// TODO: Implement logout logic
37+
// You can make an API call to log the user out
38+
setState(() {
39+
_isLoggedIn = false;
40+
});
41+
}
42+
43+
@override
44+
Widget build(BuildContext context) {
45+
return Scaffold(
46+
appBar: AppBar(
47+
title: Text('Login'),
48+
),
49+
body: Center(
50+
child: Column(
51+
mainAxisAlignment: MainAxisAlignment.center,
52+
children: <Widget>[
53+
if (!_isLoggedIn)
54+
TextButton(
55+
onPressed: _login,
56+
child: Text('Login'),
57+
),
58+
if (_isLoggedIn)
59+
TextButton(
60+
onPressed: _logout,
61+
child: Text('Logout'),
62+
),
63+
],
64+
),
65+
),
66+
);
67+
}
68+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import 'package:flutter_appauth/flutter_appauth.dart';
2+
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
3+
4+
class AuthService {
5+
final FlutterAppAuth _appAuth = const FlutterAppAuth();
6+
final FlutterSecureStorage _secureStorage = const FlutterSecureStorage();
7+
8+
static const _clientId = 'YOUR_CLIENT_ID';
9+
static const _redirectUrl = 'com.YOUR_APP.app://callback';
10+
static const _discoveryUrl =
11+
'YOUR_SERVER_URL/.well-known/openid-configuration';
12+
static const _scopes = ['openid', 'profile', 'email'];
13+
14+
Future<bool> initialize() async {
15+
final storedRefreshToken = await _secureStorage.read(key: 'refresh_token');
16+
if (storedRefreshToken == null) return false;
17+
18+
try {
19+
final TokenResponse? result = await _appAuth.token(
20+
TokenRequest(
21+
_clientId,
22+
_redirectUrl,
23+
refreshToken: storedRefreshToken,
24+
discoveryUrl: _discoveryUrl,
25+
scopes: _scopes,
26+
),
27+
);
28+
29+
if (result != null) {
30+
await _persistTokens(result);
31+
return true;
32+
}
33+
return false;
34+
} catch (e) {
35+
return false;
36+
}
37+
}
38+
39+
Future<bool> login() async {
40+
try {
41+
final AuthorizationTokenResponse? result =
42+
await _appAuth.authorizeAndExchangeCode(
43+
AuthorizationTokenRequest(
44+
_clientId,
45+
_redirectUrl,
46+
discoveryUrl: _discoveryUrl,
47+
scopes: _scopes,
48+
),
49+
);
50+
51+
if (result != null) {
52+
await _persistTokens(result);
53+
return true;
54+
}
55+
return false;
56+
} catch (e) {
57+
return false;
58+
}
59+
}
60+
61+
Future<void> logout() async {
62+
await Future.wait([
63+
_secureStorage.delete(key: 'access_token'),
64+
_secureStorage.delete(key: 'refresh_token'),
65+
_secureStorage.delete(key: 'id_token'),
66+
]);
67+
}
68+
69+
Future<void> _persistTokens(TokenResponse response) async {
70+
await Future.wait([
71+
if (response.accessToken != null)
72+
_secureStorage.write(
73+
key: 'access_token',
74+
value: response.accessToken,
75+
),
76+
if (response.refreshToken != null)
77+
_secureStorage.write(
78+
key: 'refresh_token',
79+
value: response.refreshToken,
80+
),
81+
if (response.idToken != null)
82+
_secureStorage.write(
83+
key: 'id_token',
84+
value: response.idToken,
85+
),
86+
]);
87+
}
88+
89+
Future<String?> getAccessToken() async {
90+
return await _secureStorage.read(key: 'access_token');
91+
}
92+
}

examples/flutter_app/linux/flutter/generated_plugin_registrant.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
#include "generated_plugin_registrant.h"
88

9+
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
910

1011
void fl_register_plugins(FlPluginRegistry* registry) {
12+
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
13+
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
14+
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
1115
}

examples/flutter_app/linux/flutter/generated_plugins.cmake

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#
44

55
list(APPEND FLUTTER_PLUGIN_LIST
6+
flutter_secure_storage_linux
67
)
78

89
list(APPEND FLUTTER_FFI_PLUGIN_LIST

examples/flutter_app/macos/Flutter/GeneratedPluginRegistrant.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@
55
import FlutterMacOS
66
import Foundation
77

8+
import flutter_appauth
9+
import flutter_secure_storage_macos
10+
import path_provider_foundation
811

912
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
13+
FlutterAppauthPlugin.register(with: registry.registrar(forPlugin: "FlutterAppauthPlugin"))
14+
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
15+
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
1016
}

0 commit comments

Comments
 (0)