Skip to content

Commit

Permalink
feat: add settings to Flutter App (#88)
Browse files Browse the repository at this point in the history
* feat: get current session information

* feat: create login flow

* feat: login with email and password

* feat: register with email and password

* fix: handle all errors incl deserialization error

* feat: logout & get session on home page init

* feat: show session information

* feat: add design and refactor blocs

* fix: change package and folder name

* fix: change package name

* docs: update README.md

* refactor: delete unused icon

* refactor: remove unused packages

* chore: format code

* refactor: add missing package and delete unused test

* feat: change password in settings

* design: add padding to messages

* feat: refresh session when updating settings

* design: add assets' variants

* feat: display settings dynamically

* chore: create  widgets

* fix: chnge submit button value

* chore: format code

* chore: display components dynamically

* chore: clean up and format code

* fix: aal2 navigation

* chore: remove unnecessary code

* feat: sign in/up with google account on iOS

* chore: add ios url scheme and web clientId

* chore: fix typo and rename variables

* chore: separate exception handlings

* fix: prevent null exception

* chore: delete unused assets & widgets

* fix: remove pop & show provider buttons correctly

* chore: separate exception handlings

* chore: format code

* chore: change package name and add auth packages

* feat: add apple & google sign in

* chore: delete unnecessary exception handling

* feat: add flutter app Ory Network example  (#82)

* feat: get current session information

* feat: create login flow

* feat: login with email and password

* feat: register with email and password

* fix: handle all errors incl deserialization error

* feat: logout & get session on home page init

* feat: show session information

* feat: add design and refactor blocs

* fix: change package and folder name

* fix: change package name

* docs: update README.md

* refactor: delete unused icon

* refactor: remove unused packages

* chore: format code

* refactor: add missing package and delete unused test

* chore: display components dynamically

* chore: clean up and format code

* fix: aal2 navigation

* chore: remove unnecessary code

* chore: fix typo and rename variables

* chore: separate exception handlings

* fix: prevent null exception

* chore: delete unused assets and widgets

* chore: remove misleading widget

* Update README.md

* Update README.md

* chore: add formatting

* chore: add formatting

* chore: regenerate freezed files

* fix: rebuild settings page or reset buttons

* chore: refactor auth state & add missing social login functionality

* chore: use helpers to get nodes of group

* fix: reset settings when navigating back

* chore: delete build artifacts

* fix: typo

Co-authored-by: Jonas Hungershausen <jonas.hungershausen@gmail.com>

---------

Co-authored-by: Jonas Hungershausen <jonas.hungershausen@gmail.com>
  • Loading branch information
sashatalalasha and jonas-jonas committed Dec 13, 2023
1 parent 4aa769d commit b23f3a2
Show file tree
Hide file tree
Showing 51 changed files with 1,506 additions and 138 deletions.
Binary file added .DS_Store
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.ory.flutter_ory_network

import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity() {
}
Binary file added flutter-ory-network/assets/icons/1.5x/eye-off.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/1.5x/eye.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/1.5x/logout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/1.5x/settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/2.0x/eye-off.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/2.0x/eye.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/2.0x/settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/3.0x/eye-off.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/3.0x/eye.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/3.0x/logout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/4.0x/eye-off.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/4.0x/eye.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/4.0x/logout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/logout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/icons/settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/images/1.5x/apple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/images/1.5x/google.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/images/2.0x/apple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/images/2.0x/google.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/images/3.0x/apple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/images/3.0x/google.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/images/4.0x/apple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added flutter-ory-network/assets/images/apple.png
Binary file added flutter-ory-network/assets/images/google.png
7 changes: 4 additions & 3 deletions flutter-ory-network/lib/blocs/auth/auth_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ part of 'auth_bloc.dart';
@immutable
sealed class AuthEvent extends Equatable {
@override
List<Object> get props => [];
List<Object?> get props => [];
}

final class ChangeAuthStatus extends AuthEvent {
final AuthStatus status;
final Session? session;

ChangeAuthStatus({required this.status});
ChangeAuthStatus({required this.status, this.session});
@override
List<Object> get props => [status];
List<Object?> get props => [status, session];
}

final class GetCurrentSessionInformation extends AuthEvent {}
Expand Down
10 changes: 5 additions & 5 deletions flutter-ory-network/lib/blocs/auth/auth_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,26 @@ part of 'auth_bloc.dart';
@freezed
sealed class AuthState with _$AuthState {
const factory AuthState.uninitialized(
{@Default(AuthStatus.uninitialized) AuthStatus status,
{@Default(AuthStatus.uninitialized) final AuthStatus status,
@Default(false) bool isLoading,
String? errorMessage}) = AuthUninitialized;
const factory AuthState.unauthenticated(
{@Default(AuthStatus.unauthenticated) AuthStatus status,
{@Default(AuthStatus.unauthenticated) final AuthStatus status,
@Default(false) bool isLoading,
String? errorMessage}) = AuthUnauthenticated;
const factory AuthState.authenticated(
{@Default(AuthStatus.authenticated) AuthStatus status,
{@Default(AuthStatus.authenticated) final AuthStatus status,
required Session session,
@Default(false) bool isLoading,
String? errorMessage}) = AuthAuthenticated;

const factory AuthState.aal2Requested(
{@Default(AuthStatus.aal2Requested) AuthStatus status,
{@Default(AuthStatus.aal2Requested) final AuthStatus status,
@Default(false) bool isLoading,
String? errorMessage}) = Aal2Requested;

const factory AuthState.locationChangeRequired(
{@Default(AuthStatus.locationChangeRequired) AuthStatus status,
{@Default(AuthStatus.locationChangeRequired) final AuthStatus status,
required String url,
@Default(false) bool isLoading,
String? errorMessage}) = LocationChangeRequired;
Expand Down
6 changes: 3 additions & 3 deletions flutter-ory-network/lib/blocs/login/login_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,8 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
CreateLoginFlow event, Emitter<LoginState> emit) async {
try {
emit(state.copyWith(isLoading: true, message: null));

final loginFlow = await repository.createLoginFlow(aal: event.aal);

final loginFlow = await repository.createLoginFlow(
aal: event.aal, refresh: event.refresh);
emit(state.copyWith(loginFlow: loginFlow, isLoading: false));
} on UnknownException catch (e) {
emit(state.copyWith(isLoading: false, message: e.message));
Expand Down Expand Up @@ -102,6 +101,7 @@ class LoginBloc extends Bloc<LoginEvent, LoginState> {
value: event.value,
nodes: state.loginFlow!.ui.nodes.toList());
authBloc.add(AddSession(session: session));
emit(state.copyWith(isLoading: false));
} on BadRequestException<LoginFlow> catch (e) {
emit(state.copyWith(loginFlow: e.flow, isLoading: false));
} on LocationChangeRequiredException catch (e) {
Expand Down
5 changes: 3 additions & 2 deletions flutter-ory-network/lib/blocs/login/login_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ sealed class LoginEvent extends Equatable {

final class CreateLoginFlow extends LoginEvent {
final String aal;
final bool refresh;

CreateLoginFlow({required this.aal});
CreateLoginFlow({this.refresh = false, required this.aal});
@override
List<Object> get props => [aal];
List<Object> get props => [refresh, aal];
}

class ChangeNodeValue extends LoginEvent {
Expand Down
128 changes: 128 additions & 0 deletions flutter-ory-network/lib/blocs/settings/settings_bloc.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright © 2023 Ory Corp
// SPDX-License-Identifier: Apache-2.0

import 'package:bloc/bloc.dart';
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:equatable/equatable.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:ory_client/ory_client.dart';
import 'package:ory_network_flutter/repositories/auth.dart';

import '../../repositories/settings.dart';
import '../../services/exceptions.dart';
import '../auth/auth_bloc.dart';

part 'settings_event.dart';
part 'settings_state.dart';

part 'settings_bloc.freezed.dart';

class SettingsBloc extends Bloc<SettingsEvent, SettingsState> {
final AuthBloc authBloc;
final SettingsRepository repository;
late SettingsEvent? _previousEvent;
SettingsBloc({required this.authBloc, required this.repository})
: super(const SettingsState()) {
on<CreateSettingsFlow>(_onCreateSettingsFlow);
on<GetSettingsFlow>(_onGetSettingsFlow);
on<ChangeSettingsNodeValue>(_onChangeNodeValue, transformer: sequential());
on<ResetSettings>(_onResetSettings);
on<UpdateSettingsFlow>(_onUpdateSettingsFlow, transformer: sequential());
}

@override
void onEvent(SettingsEvent event) {
super.onEvent(event);
_previousEvent = event;
}

void retry() {
if (_previousEvent != null) {
add(_previousEvent!);
}
}

_onChangeNodeValue(
ChangeSettingsNodeValue event, Emitter<SettingsState> emit) {
if (state.settingsFlow != null) {
final newSettingsState = repository.changeNodeValue(
settings: state.settingsFlow!, name: event.name, value: event.value);
emit(state.copyWith(settingsFlow: newSettingsState, message: null));
}
}

_onResetSettings(ResetSettings event, Emitter<SettingsState> emit) async {
if (state.settingsFlow != null) {
emit(state.copyWith(isLoading: true));
final settings =
await repository.getSettingsFlow(flowId: state.settingsFlow!.id);
emit(state.copyWith(
settingsFlow: settings,
isSessionRefreshRequired: false,
isLoading: false));
}
}

Future<void> _onUpdateSettingsFlow(
UpdateSettingsFlow event, Emitter<SettingsState> emit) async {
try {
if (state.settingsFlow != null) {
emit(state.copyWith(
isLoading: true, isSessionRefreshRequired: false, message: null));
final settings = await repository.updateSettingsFlow(
flowId: state.settingsFlow!.id,
group: event.group,
nodes: state.settingsFlow!.ui.nodes.toList());
emit(state.copyWith(isLoading: false, settingsFlow: settings));
}
} on UnauthorizedException catch (_) {
// change auth status as the user is not authenticated
authBloc.add(ChangeAuthStatus(status: AuthStatus.unauthenticated));
} on FlowExpiredException catch (e) {
// get new settings flow
add(GetSettingsFlow(flowId: e.flowId));
} on SessionRefreshRequiredException catch (_) {
// set session refresh required flag to navigate to login page
emit(state.copyWith(isSessionRefreshRequired: true, isLoading: false));
} on UnknownException catch (e) {
emit(state.copyWith(isLoading: false, message: e.message));
} catch (_) {
emit(state.copyWith(isLoading: false));
}
}

Future<void> _onCreateSettingsFlow(
SettingsEvent event, Emitter<SettingsState> emit) async {
try {
emit(state.copyWith(isLoading: true, message: null));

final settingsFlow = await repository.createSettingsFlow();

emit(state.copyWith(isLoading: false, settingsFlow: settingsFlow));
} on UnauthorizedException catch (_) {
// change auth status as the user is not authenticated
authBloc.add(ChangeAuthStatus(status: AuthStatus.unauthenticated));
} on UnknownException catch (e) {
emit(state.copyWith(isLoading: false, message: e.message));
} catch (_) {
emit(state.copyWith(isLoading: false));
}
}

Future<void> _onGetSettingsFlow(
GetSettingsFlow event, Emitter<SettingsState> emit) async {
try {
emit(state.copyWith(isLoading: true));
final settingsFlow =
await repository.getSettingsFlow(flowId: event.flowId);
emit(state.copyWith(isLoading: false, settingsFlow: settingsFlow));
} on UnauthorizedException catch (_) {
// change auth status as the user is not authenticated
authBloc.add(ChangeAuthStatus(status: AuthStatus.unauthenticated));
} on UnknownException catch (e) {
emit(state.copyWith(isLoading: false, message: e.message));
} catch (_) {
emit(state.copyWith(isLoading: false));
}
}
}
Loading

0 comments on commit b23f3a2

Please sign in to comment.